Keithster

01-02-2016, 03:33 PM

Greetings!

I'm working on a simple editor for my engine that lets you pick objects and move them around. All that I got working fine until I tried to do parent-child relations, where the child would move and rotate along with the parent.

I managed to have the child move and rotate along with the parent, but I'm having trouble when it comes to moving the child independently. Children translates correctly, but rotation is totally busted :D

I really appreciate it if could see how I'm doing things and tell me if there's a mistake in my interpretation/understanding of things.

Here's how I'm storing the transform values:

struct transform

{

v3 Position;

m4 Rotation;

r32 Scale; // uniform

transform *Parent;

};

From what I understood, in order to get the proper ModelToWorld matrix for the child, I have to multiply the child's transform with its parents'. Something like this:

internal inline m4

TGetTRS(transform *T)

{

m4 Result;

transform *Parent = T->Parent;

if (Parent)

{

m4 ParentTR = m4_Translate(&Parent->Rotation, Parent->Position);

Result = m4_Scale(&ParentTR, Parent->Scale);

}

else

Result = m4_Identity;

m4 TR = m4_Translate(&T->Rotation, T->Position);

m4 TRS = m4_Scale(&TR, T->Scale);

Result = Result * TRS;

return (Result);

}

This seems to work in that when I move/rotate the parent around, the child moves/rotates correctly.

However; I'm not really sure how all this parent-child stuff affects the local movement of the child.

From my understanding (correct me if I'm wrong), when an object becomes a child of another, all its movements become relative to that object, i.e. the parent now is the origin of its new coordinate system/space. So if parent was at (1,1,0) and child(local) = (2,1,0) that means child(global) = (3,2,0).

So I don't have to do much to achieve local movement, so when I translate by an amount vector I just add directly to the child's position:

internal inline void

TMove(transform *T, v3 Movement, coordinate_space RelativeTo = Space_World)

{

if (RelativeTo == Space_Local)

Movement = TLocalDirection(T, Movement);

T->Position += Movement;

}

internal inline v3

TLocalDirection(transform *T, v3 WorldDirection)

{

m4 Rotation = TGetGlobalRotation(T); // I'll show this in a bit

v3 LocalDirection = m4_Mulv(&Rotation, WorldDirection, 0); // 0 for W component

return (LocalDirection);

}

Is that correct?

I just have to make sure that I adjust the position vector correctly when I first parent the object. Something like:

internal void

TSetParent(transform *Child, transform *Parent)

{

Child->Position = Child->Position - Parent->Position; // convert to local position

// adjust rotation as well ...

Child->Parent = Parent;

}

I guess that code is incomplete when it comes to switching parents. I guess it would be adjusted to

Child->Position = TGetGlobalPos(Child) - TGetGlobalPos(Parent);

Where TGetGlobalPos will take into consideration the positions of parents:

internal inline v3

TGetGlobalPos(transform *T)

{

v3 Result = T->Position;

if (T->Parent)

Result += TGetGlobalPos(T->Parent);

return (Result);

}

Is that correct so far?

Now for rotation, do we need to handle it any differently than how we handled translation? i.e. to get the global rotation we look up to our parents and multiply our rotation with theirs':

internal inline m4

TGetGlobalRot(transform *T)

{

m4 Result = T->Rotation;

if (T->Parent)

Result = TGetGlobalRot(T->Parent) * Result;

return (Result);

}

And so if I wanted to rotate an object 'independently' of its parent I just directly change its rotation matrix because that its' local rotation if I understand correctly:

internal inline void

TRotate(transform *T, r32 Angle, v3 Axis)

{

T->Rotation = m4_Rotate(Angle, Axis) * T->Rotation;

}

We also have to remember to adjust the rotation when we change parent, so (in place of my comment in TSetParent):

Child->Rotation = TGetGlobalRo(Child->Parent) * TGetGlobalRot(Child);

But is that even correct?

Hopefully you can see I _sort of_ know what I need to do but just not sure exactly how to do it.

Is the current way of storing a transform good or should I use a single 4x4 matrix instead? (Currently with the way I'm doing it, I guess I don't need an 4x4 matrix for the rotation, just a 3x3) - Am I missing something? I read in some sources that I need to apply the inverse of the parents' matrices, but I'm not sure how that fits into the picture. Also what about storing children pointers instead of a parent pointer, does that make any difference/make it easier to transform things or no?

I really appreciate any help/advice/explanations and corrections if necessary on my understandings/interpretations.

Thanks!

-Keithster

I'm working on a simple editor for my engine that lets you pick objects and move them around. All that I got working fine until I tried to do parent-child relations, where the child would move and rotate along with the parent.

I managed to have the child move and rotate along with the parent, but I'm having trouble when it comes to moving the child independently. Children translates correctly, but rotation is totally busted :D

I really appreciate it if could see how I'm doing things and tell me if there's a mistake in my interpretation/understanding of things.

Here's how I'm storing the transform values:

struct transform

{

v3 Position;

m4 Rotation;

r32 Scale; // uniform

transform *Parent;

};

From what I understood, in order to get the proper ModelToWorld matrix for the child, I have to multiply the child's transform with its parents'. Something like this:

internal inline m4

TGetTRS(transform *T)

{

m4 Result;

transform *Parent = T->Parent;

if (Parent)

{

m4 ParentTR = m4_Translate(&Parent->Rotation, Parent->Position);

Result = m4_Scale(&ParentTR, Parent->Scale);

}

else

Result = m4_Identity;

m4 TR = m4_Translate(&T->Rotation, T->Position);

m4 TRS = m4_Scale(&TR, T->Scale);

Result = Result * TRS;

return (Result);

}

This seems to work in that when I move/rotate the parent around, the child moves/rotates correctly.

However; I'm not really sure how all this parent-child stuff affects the local movement of the child.

From my understanding (correct me if I'm wrong), when an object becomes a child of another, all its movements become relative to that object, i.e. the parent now is the origin of its new coordinate system/space. So if parent was at (1,1,0) and child(local) = (2,1,0) that means child(global) = (3,2,0).

So I don't have to do much to achieve local movement, so when I translate by an amount vector I just add directly to the child's position:

internal inline void

TMove(transform *T, v3 Movement, coordinate_space RelativeTo = Space_World)

{

if (RelativeTo == Space_Local)

Movement = TLocalDirection(T, Movement);

T->Position += Movement;

}

internal inline v3

TLocalDirection(transform *T, v3 WorldDirection)

{

m4 Rotation = TGetGlobalRotation(T); // I'll show this in a bit

v3 LocalDirection = m4_Mulv(&Rotation, WorldDirection, 0); // 0 for W component

return (LocalDirection);

}

Is that correct?

I just have to make sure that I adjust the position vector correctly when I first parent the object. Something like:

internal void

TSetParent(transform *Child, transform *Parent)

{

Child->Position = Child->Position - Parent->Position; // convert to local position

// adjust rotation as well ...

Child->Parent = Parent;

}

I guess that code is incomplete when it comes to switching parents. I guess it would be adjusted to

Child->Position = TGetGlobalPos(Child) - TGetGlobalPos(Parent);

Where TGetGlobalPos will take into consideration the positions of parents:

internal inline v3

TGetGlobalPos(transform *T)

{

v3 Result = T->Position;

if (T->Parent)

Result += TGetGlobalPos(T->Parent);

return (Result);

}

Is that correct so far?

Now for rotation, do we need to handle it any differently than how we handled translation? i.e. to get the global rotation we look up to our parents and multiply our rotation with theirs':

internal inline m4

TGetGlobalRot(transform *T)

{

m4 Result = T->Rotation;

if (T->Parent)

Result = TGetGlobalRot(T->Parent) * Result;

return (Result);

}

And so if I wanted to rotate an object 'independently' of its parent I just directly change its rotation matrix because that its' local rotation if I understand correctly:

internal inline void

TRotate(transform *T, r32 Angle, v3 Axis)

{

T->Rotation = m4_Rotate(Angle, Axis) * T->Rotation;

}

We also have to remember to adjust the rotation when we change parent, so (in place of my comment in TSetParent):

Child->Rotation = TGetGlobalRo(Child->Parent) * TGetGlobalRot(Child);

But is that even correct?

Hopefully you can see I _sort of_ know what I need to do but just not sure exactly how to do it.

Is the current way of storing a transform good or should I use a single 4x4 matrix instead? (Currently with the way I'm doing it, I guess I don't need an 4x4 matrix for the rotation, just a 3x3) - Am I missing something? I read in some sources that I need to apply the inverse of the parents' matrices, but I'm not sure how that fits into the picture. Also what about storing children pointers instead of a parent pointer, does that make any difference/make it easier to transform things or no?

I really appreciate any help/advice/explanations and corrections if necessary on my understandings/interpretations.

Thanks!

-Keithster