Skip to content

Conversation

@skyne98
Copy link

@skyne98 skyne98 commented Mar 23, 2020

Solves #16.
This is the first introduction of proper matrix transformations for the sprite rendering. It includes scale, translation and rotation with origin. Also a new, simple example is included.

Please note, this pull request has a lot of cleaning to do, so I am waiting for your first review!

@cloudhead
Copy link
Owner

Thanks! I will take a look soon.

@cloudhead
Copy link
Owner

To merge this, the first thing we would have to do is take out the ultraviolet dependency -- if there's anything missing from the built-in math library, I'm happy to add it in there.

@skyne98 skyne98 marked this pull request as ready for review April 23, 2020 22:59
@skyne98
Copy link
Author

skyne98 commented Apr 23, 2020

Removed the ultraviolet dependency, had to add a matrix rotation function to the default implementation as well (copied it from cgmath in a simplified manner). Everything seems to work, however, when you enable the cgmath feature flag, then things are not compiling anymore, even inside the core libraries this pull request doesn't touch. Any ideas on that matter? Is it ok on this stage?

Also, I think it might be a good option to incorporate ultraviolet itself as a math library as it seems to be the fastest one at the moment, according to my benchmarks.

@zicklag
Copy link

zicklag commented Apr 24, 2020

You might want to keep an eye out for nalgebra now, too, because with nalgebra 0.21 and the new SIMD optimizations it is competitive with ultraviolet according to the posted benchmarks:

https://rustsim.org/blog/2020/03/23/simd-aosoa-in-nalgebra/

nalgebra's biggest disadvantage to me seems to be it's complication in the documentation and such, but nalgebra-glm seems like it helps with that a lot. The post above has a good comparison between nalgebra and ultraviolet.

Personally I could go with either and I don't have a particular suggestion. They both are interesting for their own reasons. :)

@skyne98
Copy link
Author

skyne98 commented Apr 24, 2020

@zicklag, hmm, interesting, in some benchmarks it is more than 2 or 3 times faster than the ultraviolet counterpart. Also, giving some options to decide between more precise 64-bit floats and faster 16-bit floats might be a good idea, however, I might imagine there are more low-hanging optimisations rgx can achieve before we will need 16-bit floats.

@cloudhead
Copy link
Owner

Thanks! I've pushed a fix to master regarding the cgmath compilation, maybe this will solve the problem.

With regards to third party algebra libraries, rgx used to depend on cgmath, but when it went unmaintained, I decided to copy the parts I needed into rgx, make certain improvements, and make the dependency optional. nalgebra is too big of a dependency to pull in for simple 2D math. As for all the other libraries, they are good, but they come and go, so I'd rather wait and see which ones are still around in a year.

h: u32,
src: Rect<f32>,
dst: Rect<f32>,
dst: Vector2<f32>,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why you've changed the API here from a rect to a position + scale? This seems like an unnecessary change and will break a lot of things.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory, Rect should be sufficient in this situation as it is basically a combination of two 2d vectors itself. However, I think it's a little clearer for an average user to have them separate as it explicitly tells you the roles of both vectors. Also, this way of handling position + scale seems to be more common among the engines used by lots of game developers (especially 2d-oriented): Love2d, XNA, Game Maker.

In the end, I think it makes sense to have both APIs available, as some users coming from Unity or even XNA as well might find the Rect API more intuitive. This approach will also allow things to not break, as you have mentioned above.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I understand that it's a more common API, but this change is unrelated to adding rotation to sprites, and is a breaking change.

The advantage of the rect is when a specific area needs to be filled with a sprite (eg. the screen), it's easier than having to calculate the required scale.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of code out there that's going to have to be changed if the API is changed like this. If we only add angle, it's an easy fix, but changing from a Rect to a Vector pair is going to be pretty annoying.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I will add back the rect API, but I think we can keep both, in the end. What do you think about this?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, that could be confusing. I would rather have a builder-like API as we do for shape2d in the long term. Something like: batch.add(Sprite::new(...).scale(4.0).position([4,4]).repeat(...)..), otherwise it's a lot of parameters to remember positionally, no?

-src.height() * origin.y * scale.y,
0.0,
));
let rotation = Matrix4::from_angle_z(*angle * 3.14 / 180.0);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, didn't know that, will replace it in the next commit.

@skyne98
Copy link
Author

skyne98 commented May 1, 2020

@cloudhead, did the changes you mentioned, can you have a look?

let vec5 = Vector3::new(0.0, 1.0, 1.0);
let vec5 = transformation * vec5;
let vec6 = Vector3::new(1.0, 1.0, 1.0);
let vec6 = transformation * vec6;
Copy link
Owner

@cloudhead cloudhead May 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can iterate over a Vec<Vertex> and apply the transformation on each Vector2, it would be a lot cleaner :)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could still use some work.

@cloudhead
Copy link
Owner

@cloudhead, did the changes you mentioned, can you have a look?

Yup, we're getting there! Don't forget to fix the examples when you've finalized the library code.

@skyne98
Copy link
Author

skyne98 commented Jul 16, 2020

@cloudhead, finally got some time away from work and studying - so here are a couple of commits that should solve all the discussed issues.

Copy link
Owner

@cloudhead cloudhead left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice progress, there are still some issues though 🧐

origin: Vector2<f32>
) -> Sprite {
Sprite::new(src).position(pos).scale(scale).origin(origin)
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two methods (sprite_pos and sprite_origin) are not needed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean the function calls, not the functions themselves?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I mean the functions themselves, since we have the builder.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha.

pub fn angle(mut self, angle: f32) -> Self {
self.angle = angle;
self
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This API should look the same as for shape2d. See here: https://github.com/cloudhead/rgx/blob/master/src/kit/shape2d/mod.rs#L173

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, my choice of the name is not great, I agree, will change it. However, do you think passing the rotation origin each time you want to rotate the sprite is a good idea? In my mind, people will expect it to work as it does in any other modern game engine or library - set origin once and then expect all the rotations to happen around that origin point.
Also, I guess, it is more performant to avoid copying 60% more information each time you want to change the angle.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a game engine though, it's a graphics library. The game entity can store its origin and then pass it in when it needs to be rendered. The extra copy of the origin will not impact performance.

src/lib.rs Outdated
@@ -1,4 +1,5 @@
#![deny(clippy::all)]
#![feature(stmt_expr_attributes)]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe at some point I used attributes for expressions somewhere, I guess it should not be needed anymore, I will investigate 😄

//! Most of the code in this module was borrowed from the `cgmath` package.

pub use num_traits::{cast, Float, One, Zero};
use num_traits::real::Real;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will have a look, thanks for pointing that out!

@cloudhead
Copy link
Owner

Additionally, https://github.com/cloudhead/rgx/pull/19#discussion_r420379790 was not addressed properly.

pub pos: Vector2<f32>,
pub angle: f32,
pub scale: Vector2<f32>,
pub origin: Vector2<f32>,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Origin should be Point2

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So should pos actually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants