Archived

This topic is now archived and is closed to further replies.

Turn in global space

This topic is 4948 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I''ve been struggling with this problem for a few days now. I''ve read just about every post on these boards to try and find the answer myself, and it''s just given me an even bigger headache. I have set up an Entity class, from which classes like Camera, Light, Mesh, etc. can inherit. The Entity class arranges entities in a tree - with a Global entity being the root. Each entity has 6 floats (position and scale), and a Microsoft.DirectX.Quaternion for rotation. They also have a Microsoft.DirectX.Matrix which holds the complete global transformation for that entity (the floats and Quaternion are for the local position/rotation/scale). Now my Entity.Position(), Entity.Rotate(), and Entity.Scale() methods all work perfectly, which is nice. The problem occurs when I come to do Entity.Turn() and Entity.Move(). Entity.Turn() looks like this: public void Turn(float pitch, float yaw, float roll, bool Global) When Global is false, it works fine. However, I can''t work out hhow to do a turn (delta rotation) in global space. I *think* I need to calculate the current global pitch/yaw/roll, and add it to the delta pitch/yaw/roll, to get my final overall rotation (which can then be given to the working Entity.Rotate() method). However, I can''t seem to find out a way to get the global pitch/yaw/roll back from the transformation matrix. Is there another way to do this? Ideas?

Share this post


Link to post
Share on other sites
quote:
Original post by Rottbott
However, I can''t seem to find out a way to get the global pitch/yaw/roll back from the transformation matrix. Is there another way to do this?


Why not just store it as its own information when you plug it into the matrix, instead of trying to retrieve it back out again?



Red Sodium

Share this post


Link to post
Share on other sites
That was quick!

I can't quite work out what you mean. The values I put into the matrix are local - the global matrix is worked out by multiplying with the parent's matrix.

To illustrate, here's my Entity.RebuildMatrix() and Entity.Position() methods:


// RebuildMatrix - turns the position, rotation and scale into a new matrix

void RebuildMatrix()
{
// Parent matrix (just identity if parent is Global)

this.Transform = this.Parent.Transform;
// Scale

this.Transform = this.Transform * Matrix.Scaling(this.LocalScaleX, this.LocalScaleY, this.LocalScaleZ);
// Translate

this.Transform = this.Transform * Matrix.Translation(this.LocalX, this.LocalY, this.LocalZ);
// Rotate

this.Transform = this.Transform * Matrix.RotationQuaternion(this.LocalRotation);
}


// Position - sets to an absolute position in local or global space

public void Position(float x, float y, float z, bool Global)
{
this.LocalX = x;
this.LocalY = y;
this.LocalZ = z;
if (Global == true)
{
this.LocalX = this.LocalX - this.Parent.LocalX;
this.LocalY = this.LocalY - this.Parent.LocalY;
this.LocalZ = this.LocalZ - this.Parent.LocalZ;
}
this.RebuildMatrix();
}


EDIT: Formatting.

[edited by - Rottbott on May 29, 2004 4:57:41 AM]

Share this post


Link to post
Share on other sites
Sorry, I assumed that you wanted to extract data from the matrix, when in actual fact it would just be easier storing the data seperate from the matrix.

What I meant, in the context of your problem, is that you should just set up a Get...() function to retrieve the global position from the parent, which is stored as a variable. Not sure if i''ve understood your problem though :/

Red Sodium

Share this post


Link to post
Share on other sites
I suppose what I really need is:

1. A way to extract the rotation (in Euler angles) and position from any entity''s matrix, and

2. A way to transform a point between an entity''s local space, to global space, and back again.


Or a whole new set of transformation methods

Share this post


Link to post
Share on other sites
quote:

I suppose what I really need is:

1. A way to extract the rotation (in Euler angles) and position from any entity''s matrix,
and

2. A way to transform a point between an entity''s local space, to global space, and back
again.


Or a whole new set of transformation methods



Lets take the general case. If a have a tree of nodes
with transformation at each level, then the following is
true:

if at each Level i, the transformation is represented by
the matrix Li, then the total transformation is:

T = L0*L1*L2*...Ln

where n is the number of levels in the tree. If at any point
in the tree you wish to apply a transform, you simply take
that Li and multiply it by the transformation matrix. This
goes for ALL "delta" transformations. This type of idea
basically represents a heirarchy of objects attached to each
other and effected by the transformation of the node higher
up in the tree.

Now, instead of matrices, you could probably use your quaternions
(as long as proper care is taken to deal with translations/scaling).

one more thing that might be useful (and this is probably mostly
opinion), it is probably a good idea to implement rotations as
an angle around a user''s chosen axis rather than 3 angles around 3 fixed axis. It deals with some of the problems that are caused by Euler angles (gimble lock).

As for extracting rotation angles, unless you have a need to know
these rotations, you probably should not do this (it will slow things down). It is absolutely unnecessary for purposes of transformation. Hope I was helpful.

SporadicFire

Share this post


Link to post
Share on other sites
SporadicFire,

That all makes sense, thanks. It''s starting to come together in my mind. I probably will use an axis-angle for rotation, rather than Euler angles, or perhaps even use the quaternions directly.

However, I still have to work out the transformation. I think my RebuildMatrices() method is applying transformations in the wrong order. At the moment, it does this:

Parent
Scale
Translate
Rotate

I think it should probably be doing this:

Parent
Scale
Rotate
Translate

But that gives me another problem. If I rotate my entity, and rebuild the matrix, the position will also change due to the new rotation, I think.


Here''s a question:

If I have a rotation matrix which turns my entity 90 degrees right, and I multiply it by a translation matrix which moves my entity 5 units forwards on the Z-axis, which will happen?

A) The entity is rotated 90 degrees right, and moves 5 units along the global X-axis (which the local Z-axis is now aligned with due to the rotation).

B) The entity is rotated 90 degrees right, and moves 5 units along the global Z-axis.

Share this post


Link to post
Share on other sites
Ok, ideally, you want to think of a matrix at one level as a coordinate system. So, it consists of a position, orientation and scaling... Let S be scaling, T be translation and R be rotation. IMHO, you rotate first, then translate and
then scale. (This is to make sure that translation does not
screw up rotation and that scaling does not screw up translation).

So, in matrix notation, total transform M = S*T*R
(when concatenating transforms, always multiply to the left).

if you write out the matrices in 2D it will answer your question.
But, it is clear that the above rotates first, and then translates according to the global coordinate system.

As for M = T*R*S, which is what i think what you are proposing
to do, it is not possible to get a proper nonuniform scaling if
scaling is done first and rotation second. Hope it helps.

if you want to check in the future, use following homogenous
2D matrices

R = cos(theta) -sin(theta) 0
sin(theta) cos(theta) 0
0 0 1
T = 1 0 tx
0 1 ty
0 0 1

and S =Sx 0 0
0 Sy 0
0 0 1

just matrix multiply them by hand and see what happens.

SporadicFire.

[edited by - SporadicFire on May 28, 2004 1:28:01 PM]

Share this post


Link to post
Share on other sites
I suggest using

M=P*T*R*S or M=P*T*S*R, they are the same.

P is the parent''s transformation.

I think this is the best way if you use the scaling for making an object bigger and not for increasing distances between objects (I guess this is nonuniform scaling).

Share this post


Link to post
Share on other sites
OK, thanks to the help from you two I've managed to simplify it a lot.

I've scrapped the local quaternion and 6 floats, and I'm now using only the matrix. I don't really need to know the global Euler angles, and I can easily get the global position of any entity from its matrix (M41, M42, M43).

I think I've got the following working:

Rotate()
Position()
Translate()
Move()

I also have Turn() working, but only with global angles. Turning in local space is trickier. It should work the same as Translate(), but that uses something I can't do with angles.

Also I've left scale alone for now to keep things simple I've also not done the code to update the children when a parent is altered but that shouldn't be too hard.

Here's the source for the local co-ordinates Translate() method (x/y/z describe the translation vector):


// Turn global position into local space

this.Parent.PointToLocal(this.Transform.M41, this.Transform.M42, this.Transform.M43);

// Add translation to it

x = this.Parent.PointX - x;
y = this.Parent.PointY - y;
z = this.Parent.PointZ - z;

// Set new position

Matrix M = this.Parent.Transform;
M = Matrix.Translation(x, y, z) * M;
this.Position(M.M41, M.M42, M.M43, true);


PointToLocal() simply runs a point through the matrix like this:


public void PointToLocal(float x, float y, float z)
{
Matrix M = this.Transform;
M.Invert();
PointX = (M.M11 * x) + (M.M21 * y) + (M.M31 * z) + M.M41;
PointY = (M.M12 * x) + (M.M22 * y) + (M.M32 * z) + M.M42;
PointZ = (M.M13 * x) + (M.M23 * y) + (M.M33 * z) + M.M43;
}


So - can anyone tell me if it's possible to write a QuaternionToLocal() method to run a rotation through the matrix in the same way as a point? That would enable me to finish my Turn() method.

Thanks for all the help so far!

EDIT: Tidied code.

EDIT AGAIN: I got Turn() working in local space by simply reversing the order of the matrix multiplication! However, my global Turn() doesn't seem to work quite right still.

[edited by - Rottbott on May 28, 2004 5:28:48 PM]

Share this post


Link to post
Share on other sites
Let's see:

M is your entity's matrix now.
GlobalRot is your global rotation matrix
So after rotating you would get the following matrix:

GlobalRot * M

This should be equal to another expression, in which you apply an unknown local rotation matrix:

M*LocalRot

So

GlobalRot * M == M * LocalRot is true, from this

LocalRot = M^(-1) * GlobalRot * M

This will be the local rotation matrix, I think.

Try it, hope it's a good idea.


[edited by - szinkopa on May 28, 2004 5:48:19 PM]

Share this post


Link to post
Share on other sites

Lets say,
M = TR, where R is the linear component (that means rotations/
scaling and sheering. T is translational component. When you
look at matrix elements of M you see that:

M = M11 M12 M13 Tx
M21 M22 M23 Ty
M31 M32 M33 Tz
0 0 0 1

If you need to do an absolute transform at any level, just
separate the two components of the matrix and rewrite it.
So, if you want it to translate to someplace else, just
change Tx,Ty,Tz...if you want to rotate in a different
way, just replace the top 3x3 with a new rotation matrix.
If you have to do a "turn" or "move" instead, then just
left multiply the change in transformation to existing matrix.


Now, inverse matrices will allow you to untransfrom things from global
down to local without worrying about what an angle was. Just
multiply the inverse matrices of the higher levels on the left
and they will bring it back down to a lower level.

Well, one more things, you probably want to store your coordinates in their untransformed states. This means
that Local takes them to the locally transformed coordinate
system and global * local takes them to the global coordinate
system. This removes the need to always have to transform
things back from global before doing local transformations.

Also, only transform coordinates on demand. Meaning, store
the matrices, modify them as much as you want, and then compute
for a concatenated transformation of points or a set of points
when you need it. This will take more advantage of the matrices than if you transformed each time your matrix changed.

As for transforming quaternion through a matrix, its done by converting the quaternion to a matrix and then multiply
it through. There's a formula for that somewhere. It would
help in writing a turn method that uses a quaternion. But, be
sure to multiply it to the left.

hope this helps,

SporadicFire.

EDIT: typo


[edited by - SporadicFire on May 28, 2004 9:42:06 PM]

Share this post


Link to post
Share on other sites
szinkopa,
That makes sense. How do I do M^(-1)? Is it just M.Invert()?

SporadicFire,
I've taken your advice and included the local transformation components again.

However I'm still having trouble with Turn(). I want to turn on the global axes... but how do I work out the new *local* quaternion which will result in a given global rotation? To do that with translation is easy - I use my PointToLocal() method - but I can't seem to get a working RotationToLocal() method, even with szinkopa's equation above.


EDIT: A global Turn() now semi-works. However, if I turn an entity slowly to the right, it works up until about 270', and then goes crazy. Weird.

[edited by - Rottbott on May 29, 2004 1:26:54 PM]

Share this post


Link to post
Share on other sites
yes, M^(-1) is its invert.

Ah, i think i see what you mean now...
but correct me if i am wrong

T = G * L is the total transformation G is global, L is local.
Now, to achieve a rotation in the total transform:

RT = R*G*L

but, you want this represented locally,

RT = G*S*L

R*G*L = G*S*L
inverse(G)*R*G*L*inverse(L) = S
S = inverse(G)*R*G

and S is the transformation that you have to apply at local level
to get RT = R*G*L to be the new total transformation.

Ofcourse, this will work for any general transform, not just
rotations.

SporadicFire.

Share this post


Link to post
Share on other sites