Design: Matrix or Scale,Rotate,Translate

Started by
11 comments, last by Trienco 12 years, 7 months ago
Hello,

in order to position and orientate the objects in my scene my scene objects currently have a scale, rotate and translate Vector3. From these vectors I build a matrix M that performs a Scale, XYZ rotation and translate: M = Scale * XYZRotate * Translate.

While this approach is very easy to use (for example I can just rotate an object around the y axis by: object->setRotation( Vec3(0, 45, 0)) it also not very flexible, since I have a fixed order (always scale, then rotate, then translate) AND I have a fixed rotation order (always X rotation, then Y, then Z).

Do you think this is a good design or should I change it? Should I just use a general Matrix so the user could apply any matrix? Or should I just make the rotation order variable? Or...?

Thanks for any ideas!
Advertisement
The approach i use (wich i think is good but im not an expert) is to store those 3 things seperately for each entity but to also have a matrix that gets build up from those 3 componants each time one of them changes. So the 3 componants are for "internal" use only. When im rendering/doing other stuff i always use the matrix. The entity just needs to be sure to rebuild te matrix when changes occur.

I take it a step futher and i use a clean/dirty boolean flag for this matrix meaing that whenever this matrix is required it checks the flag to see if its up to date and if its not rebuild it using the 3 base componants.

I take this another step futher and i also store the "absolute matrix" in each entity. wich represents te entity's transformation from the origin. I also use a clean/dirty boolean flag for this. So the "absolute matrix" only gets updated when its needed. Having the absolute matrix of the entity in the entity is usefull if not neccesary for game logic and rendering. The absolute matrix is easily calculated by multiplying the entities parents absolute matrix with the entities relative matrix.

Another thing. when you build up your matrix from the base componants you should probely convert the rotation into 3 separate rotations (1 for x, 1 for y and 1 for z) and convert each of them into quaternions and then multiply the 3 resulting queternions together to get 1 rotation again that does all 3 rotations in one instead of first x then y then z. This should prevent the anoying gimbal lock effect.

Hope this helps.
It really depends on what you are doing.

If you are creating generic 3D editor, your approach may be the best.

If you are doing level or scene editor, I'd go with matrices. You are probably not interested in the actual values of transformation components anyways. Also notice, that (relative) rotation can still be as easy as: M' = M * Matrix::rotation(Vector::Y, 45)

If writing actual game, I would go with constrained matrices - i.e. always strictly orthogonal and without scaling. Then the expensive matrix inversion would simply become transposition + plus translation adjustment. Also you can transform normals (planes) with the same matrix as positions.

But this is only about the physical/rendering part of your code. For game logic you should save whatever transform components, that make sense - for example pitch, yaw, roll for flight simulator, movement direction for biped character and so on.




Lauris Kaplinski

First technology demo of my game Shinya is out: http://lauris.kaplinski.com/shinya
Khayyam 3D - a freeware poser and scene builder application: http://khayyam.kaplinski.com/

Another thing. when you build up your matrix from the base componants you should probely convert the rotation into 3 separate rotations (1 for x, 1 for y and 1 for z) and convert each of them into quaternions and then multiply the 3 resulting queternions together to get 1 rotation again that does all 3 rotations in one instead of first x then y then z. This should prevent the anoying gimbal lock effect.

But it doesn't: Gimbal lock is an issue of concatenating rotations so that a rotation axis gets coincident with an already used rotation axis. It is not an issue of whether one uses matrices or quaternions for rotation representation. Hence the above suggestion doesn't help against gimbal lock but makes things needlessly more complex.

Furthermore, there is no difference (besides perhaps efficiency and rounding errors) whether one applies particular rotations one by one or else computes the composition matrix first and applies that:
( R[sub]1[/sub] * R[sub]2[/sub] ) * v == R[sub]1[/sub] * ( R[sub]2[/sub] * v )
a.k.a. associativity
The method I use, which is common in the industry, is to store a forward, up, and right vector, along with scale and position vectors.
Although it is a bit more complicated to maintain, it is generally only conceptually more difficult, not computationally more difficult.
When rotating, you have to keep your vectors orthogonal. While this may sound like more work, this is only a few cross-products, and is much less expensive than building rotational matrices around individual axes. Plus they can be converted directly to a matrix without performing any matrix math. For example, you do not need to build a rotation, scaling, and translation matrix and then combine them. Just write directly to the components of the matrix the values in your rotational vectors (multiplied by scale of course).


The matrix only needs to be built once per frame, usually just for render, but a simply dirty flag can be used to cause any-time updating whenever it is needed.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid


in order to position and orientate the objects in my scene my scene objects currently have a scale, rotate and translate Vector3. From these vectors I build a matrix M that performs a Scale, XYZ rotation and translate: M = Scale * XYZRotate * Translate.[/quote]
Replacing the XYZRotate with QuatRotate would make the system perfect. It doesn't matter how you try to work them, using Euler triplets will always feel wrong....
The matrices only need to be computed at the point of rendering.


While this approach is very easy to use (for example I can just rotate an object around the y axis by: object->setRotation( Vec3(0, 45, 0))[/quote]

And with quats you can do the same thing.....

object->setRotation( Quat(0, sin(45.0f/2), 0, cos(45.0f/2.0f)) );


it also not very flexible, since I have a fixed order (always scale, then rotate, then translate) AND I have a fixed rotation order (always X rotation, then Y, then Z).[/quote]
The scale->rotate->translate ordering is perfectly fine. What is NOT fine is using euler angle triplets (if you *really* must use them, change the ordering so that the axis the is rotated around least is the middle one. i.e. If a rotation around Y is rare, then you should use XYZ or ZYX ordering. If a rotation around Y occurs all the time, then use YXZ, XZY, YZX or ZXY).

Do you think this is a good design or should I change it? Should I just use a general Matrix so the user could apply any matrix? Or should I just make the rotation order variable? Or...?[/quote]


This is why you should use a Quat instead of the euler triplets. Converting from Matrix to a Quat is generally much more reliable than Matrix->euler XYZ. The upshot is that you can provide a simple method to decompose a user specified matrix into scale translate and rotation (as a quat) components.

The method I use, which is common in the industry, is to store a forward, up, and right vector, along with scale and position vectors.



Citation needed!

You can everything you need to do with nothing more complex than a single pos quat. Very few people use anything more complex than that because it keeps all of the maths simple, and therefore efficient. No one uses scale (unless they absolutely have too). It causes so many annoying problems that it's usually just ignored...
If the design is not suitable for your intended use cases then definitely change it; design should always follow the requirements of usage, not the other way around.

You've already identified some areas where you're lacking flexibility so tackle them first. Have a look at some existing matrix libraries, see how they work, and get inspiration from them. Who knows, you may even find one that does the job you want the way you want it and end up not needing to build your own.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.


[quote name='YogurtEmperor' timestamp='1316172888' post='4862406']
The method I use, which is common in the industry, is to store a forward, up, and right vector, along with scale and position vectors.



Citation needed!

You can everything you need to do with nothing more complex than a single pos quat. Very few people use anything more complex than that because it keeps all of the maths simple, and therefore efficient. No one uses scale (unless they absolutely have too). It causes so many annoying problems that it's usually just ignored...
[/quote]
It was used in Halo 1 and Halo 2, and in at least one commercial game engine. It is also used in at least one game programming book.
If your only goal is to make an orientation, your suggestion is fine.
But if the orientation has any meaning outside of rendering, you will need something more.
CryTech interviews sometimes ask how to detect if an object is to the left or right of a player. A right vector sure makes this trivial.

The poster should decide based of what he or she plans to do in his or her project.


Also, I disagree about ignoring scaling. I know of no game engines that ignore scaling. The original poster is free to ignore them if he or she does not need them, but with certainty I can say that it is not usually ignored.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Personally I just don't see much point in separately storing right, up, forward, position vectors. They are exactly the four columns of the objects transformation matrix you're going to build every frame anyway. Keeping them together as a unit (ie. matrix) seems a lot more convenient than rotating each vector separately (even of mathematically it's the same of course). If anything it will save you a few operations that would have likely be done in parallel anyway.

In terms of memory you just save yourself 4 floats by dropping w (but if that is your concern, you can just store the orientation as a "compressed" quaternion reducing it to 3 floats instead of 9 or 12 floats for a matrix).

Factoring out scaling is a different issue, because it muddles the neatness of the matrix and makes the three axes "useless" for many applications.

Point is: what is your application? Do you expect to concatenate a lot of rotations each frame (skeletal animations)? Quaternions might be worth a look then. Do you want to minimize memory usage? Only calculate "right" when you need it or use a quat. Do you want it to be simple, robust and easy to use? Stick with a 4x4 matrix and only store scaling separately.

But storing the three axes separately without good reason when even the memory layout in a matrix would be exactly the same and you can still always access them separately anyway seems to just make it more troublesome to directly apply transformations.You can still transform each vector separately and ignore w.
f@dzhttp://festini.device-zero.de

This topic is closed to new replies.

Advertisement