**0**

# FBX SDK (Again): PreRotation and Post Rotation

Started by texel3d, Dec 15 2008 04:19 AM

8 replies to this topic

###
#1
Members - Reputation: **156**

Posted 15 December 2008 - 04:19 AM

Hi,
i try to convert skinned meshes from .fbx file to my own file format and it's not easy to understand how this file format works (i don't no how Maya handle data, and i am not an artist).
I have already posted some questions in this forum and RobTheBloke was nice to answer me. But i have some difficulties to understand all this things because this is a little bit confused in my head.
I need Absolut Bind pose. I think i have found it in KFbxCluster::GetTransformLinkMatrix(bindMatrix); stored in meshes.
It seems to be the absolute position/Orientation at Bind Pose Time. It's OK, i can display it.
The default position of a bone seems to be among the x axis from (0,0,0) to (1,0,0) for a bone of length = 1
I use this file for my test:
http://texel3d.free.fr/bugs/ReverseSkinMeshLongCyl.fbx
Here is a picture of the bones:
http://texel3d.free.fr/bugs/Bone.JPG
Now i need the variation of rotation of bones relative to their parent
With my file format inspired from Lightwave file format (.lws), for each frame of animation, i compute 2 matrices:
1) matAbsoluteBone: Used to transform a bone made of 2 vertices (0,0,0)-(0,0,1) to its absolute position. This matrix is used to see the bone on the screen (for debug). This matrix is computed using the relative rotation of the bone around its "base" and relative translation (z translation of Lenght from parent base) and using the parent's absolute matrix.
2) matAbsoluteMesh: Used to transform a vertex position from its Bind Pose to its new absolute position at a given frame time. matAbsoluteMesh is computed using matAbsoluteBone and the Inverse of the Absolute BindPose Matrix of the bone. This matrix is sent to the shader.
I have something like that:
bones[j].matAbsoluteBone = bones[j].matRelativeRotation * bones[j].matRelativeTranslation * bones[bones[j].parentId].matAbsoluteBone;
bones[j].matAbsoluteMesh = bones[j].matInverseBindPose * bones[j].matAbsoluteBone;
But with FBX SDK, i don't know how to get the data i need.
I think relative rotation are stored in KFCurve ( KeyGetValue() )
But this rotation must be "mixed" with PreRotation and PostRotation. I don't know how, and i don't even know what is PreRotation and PostRotation.
It's seems to be used only for Relative rotation, and not used by absolute Bind Pose matrix.
My Questions:
_ How to get my "Final" relative rotation ?
_ What must i exactly do with PreRotation and Post Rotation ?
If you don't understand whant i mean, tell me.
Thanks.

###
#2
Crossbones+ - Reputation: **2532**

Posted 15 December 2008 - 11:37 PM

IIRC, you get 3 euler rotation values from the fcurves, and then need to convert that to a quat. Get the Pre & Post rotations as quats, then it's just a case of multiplying them together, i.e.

TotalRot = Pre * Rot * Post

from the maya docs: (JointOrient == Pre, Rotate Orient == Post)

And the transform node docs:

FYI:

spx/y/z = scale pivot

sptx/y/z = scale pivot translate

sx/y/z = scale

rpx/y/z = rotate pivot

rptx/y/z = rotate pivot translate

shx/y/z = shearing

rax/y/z = rotate axis - aka post rotation

rx/y/z = rotate

tx/y/z = translate

TotalRot = Pre * Rot * Post

from the maya docs: (JointOrient == Pre, Rotate Orient == Post)

A joint is a transform which is the basic node of a kinematic skeleton. A

hierarchy of joints is a kinematics skeleton. In a 3D view, a joint is drawn

as a combination of three circles perpendicular to each other. If a joint is

the child of another joint, a bone is drawn between them.

The joint node is derived from the transform node, so it inherits some of its

basic transformation from its parent node (see the documentation for transform

node for details). Many elements in the inherited transformation matrix have

been suppressed so the following invariances are maintained:

rotatePivot = 0

rotatePivotTranslate = 0

scalePivot = 0

scalePivotTranslate = 0

shear = 0

There are also attributes used only by joint nodes. The transformation matrix

for a joint node is below.

matrix = [S] * [RO] * [R] * [JO] * [IS] * [T]

(where '*' denotes matrix multiplication).

[S]: scale.

[RO]: Rotate Orient (attribute name is rotateAxis).

[R]: rotate.

[JO]: jointOrient.

[IS]: parentScaleInverse.

[T]: translate.

And the transform node docs:

transform nodes are dagNodes that are used to group and transform other

dagNodes. All dagNodes that are not transform nodes in Maya must exist as a

child of some transform node.

Transformation Matrix (DAG)

transform nodes have many attributes that make up the final transformation

matrix as represented by the matrix attribute. This breakdown provides

animators fine control over the animation of these parameters. Therefore, it is

necessary to describe the order in which these attributes are applied to build

the final matrix attribute.

Note: matrices are post-multiplied in Maya. For example, to transform a point

p from object-space to world-space you would need to post-multiply by the

worldMatrix. (p' = p * wm)

-1 -1

matrix = SP * S * SH * SP * ST * RP * RA * R * RP * RT * T

(where '*' denotes matrix multiplication and '-1' denotes matrix inversion'

SP = | 1 0 0 0 | ST = | 1 0 0 0 |

| 0 1 0 0 | | 0 1 0 0 |

| 0 0 1 0 | | 0 0 1 0 |

| spx spy spz 1 | | sptx spty sptz 1 |

S = | sx 0 0 0 | SH = | 1 0 0 0 |

| 0 sy 0 0 | | shxy 1 0 0 |

| 0 0 sz 0 | | shxz shyz 1 0 |

| 0 0 0 1 | | 0 0 0 1 |

RP = | 1 0 0 0 | RT = | 1 0 0 0 |

| 0 1 0 0 | | 0 1 0 0 |

| 0 0 1 0 | | 0 0 1 0 |

| rpx rpy rpz 1 | | rptx rpty rptz 1 |

RA = AX * AY * AZ

AX = | 1 0 0 0 | AY = | cy 0 -sy 0 |

| 0 cx sx 0 | | 0 1 0 0 |

| 0 -sx cx 0 | | sy 0 cy 0 |

| 0 0 0 1 | | 0 0 0 1 |

AZ = | cz sz 0 0 | sx = sin(rax), cx = cos(rax)

| -sz cz 0 0 | sy = sin(ray), cx = cos(ray)

| 0 0 1 0 | sz = sin(raz), cz = cos(raz)

| 0 0 0 1 |

Rotate:

If the rotationInterpolation attribute specifies quaternion

interpolation, use the following OpenMaya API calls to construct

the matrix:

Mquaternion q( rx, ry, rz, rw )

R = q.asMatrix()

Otherwise, for Euler-angle rotation use:

R = RX * RY * RZ (Note: order is determined by rotateOrder)

RX = | 1 0 0 0 | RY = | cy 0 -sy 0 |

| 0 cx sx 0 | | 0 1 0 0 |

| 0 -sx cx 0 | | sy 0 cy 0 |

| 0 0 0 1 | | 0 0 0 1 |

RZ = | cz sz 0 0 | sx = sin(rx), cx = cos(rx)

| -sz cz 0 0 | sy = sin(ry), cx = cos(ry)

| 0 0 1 0 | sz = sin(rz), cz = cos(rz)

| 0 0 0 1 |

T = | 1 0 0 0 |

| 0 1 0 0 |

| 0 0 1 0 |

| tx ty tz 1 |

FYI:

spx/y/z = scale pivot

sptx/y/z = scale pivot translate

sx/y/z = scale

rpx/y/z = rotate pivot

rptx/y/z = rotate pivot translate

shx/y/z = shearing

rax/y/z = rotate axis - aka post rotation

rx/y/z = rotate

tx/y/z = translate

###
#3
Members - Reputation: **156**

Posted 16 December 2008 - 03:56 AM

I don't understand the Maya documentation. Maybe it's because i have only a few experience with skinned meshes, because i don't know how to use Maya, and because english in not my brith language.

You say i have just have to that to get relative rotation:

TotalRot = Pre * Rot * Post

For Joint1 i have:

PreRotation: RotX = 0, RotY = 0, RotZ = 90

Rotation: RotX = 0, RotY = 0, RotZ = 0 (at any key of the animation because my joint1 never move)

PostRotation: RotX = 0, RotY = 0, RotZ = 0

If i use:

TotalRot = Pre * Rot * Post

to compute relative rotation of my bone at a given frame of animation i will have something different from the Identity Matrix because of the RotZ = 90 in the PreRotation. And a multiplication of 3 quaternions is a "cumulation of rotation". I will have a TotalRot with a rotation of 90 around Z.

But i should have an identity matrix because joint1 never move in my animation and it's a relative rotation.

Thanks for your patience.

You say i have just have to that to get relative rotation:

TotalRot = Pre * Rot * Post

For Joint1 i have:

PreRotation: RotX = 0, RotY = 0, RotZ = 90

Rotation: RotX = 0, RotY = 0, RotZ = 0 (at any key of the animation because my joint1 never move)

PostRotation: RotX = 0, RotY = 0, RotZ = 0

If i use:

TotalRot = Pre * Rot * Post

to compute relative rotation of my bone at a given frame of animation i will have something different from the Identity Matrix because of the RotZ = 90 in the PreRotation. And a multiplication of 3 quaternions is a "cumulation of rotation". I will have a TotalRot with a rotation of 90 around Z.

But i should have an identity matrix because joint1 never move in my animation and it's a relative rotation.

Thanks for your patience.

###
#4
Crossbones+ - Reputation: **2532**

Posted 16 December 2008 - 11:21 PM

Quote:

Original post by texel3d

You say i have just have to that to get relative rotation:

TotalRot = Pre * Rot * Post

Yes.

Quote:

If i use:

TotalRot = Pre * Rot * Post

to compute relative rotation of my bone at a given frame of animation i will have something different from the Identity Matrix because of the RotZ = 90 in the PreRotation.

correct

Quote:

And a multiplication of 3 quaternions is a "cumulation of rotation". I will have a TotalRot with a rotation of 90 around Z.

correct

Quote:

But i should have an identity matrix because joint1 never move in my animation and it's a relative rotation.

Incorrect. You have a pre-rotation of 90 degrees around the Z axis, therefore the combined rotation can never be an identity matrix (unless of course the Rotation was actually 0,0,-90). The correct combined rotation is therefore 0,0,-90 and nothing else. The rotation on it's own (i.e. without Pre and Post included) will produce an identity matrix, but the combined rotation will not. The combined rotation is the correct relative rotation for that joint. End of story.... :p

Use the joint tool in Maya to create a random chain of joints. Select a joint, then press 'e' to get the rotation tool up. Look very very closely at the orientation of the rotation tool. It is definately not an identity matrix! (If it was, the rotation tool would be axis aligned, however it's actually aligned with it's child).

###
#5
Members - Reputation: **156**

Posted 18 December 2008 - 04:00 AM

Ok. I have tried to load my FBX file and i have seen something i have not seen before.

Please tell me if i am true or wrong and give me more explanations (with simple words :) )

1) All joint have a local space. And the local space of joint "n" is not the translation of the local space of joint "n-1" among the x axis + a rotation. It's not like in other program like Ligthwave (maybe because in this program, joint are more dependant from their parent).

2) The rotation value stored in curves seems to be the relative rotation around the local space of each joint.

3) The local space of joint seems to be defined with PreRotation. If i take an animation with no relative rotation (all joint stay in BindPose), Prerotation should give me the Bind Pose of each joint. if i forget translation, and PostRotation (PostRotation is always 0 in my example) for the time beeing, i could find the BindPose orientation of each joint.

4) If i transform a bone defined by the points [(0,0,0)(1,0,0)] with the preRotation of joint "n" converted to matrix, i should get a bone oriented in the same orientation as the bone of joint "n" (but not translated in the good place). It works with joint0 but not for the over joints. So "TotalRot = Pre * Rot * Post" seems to be a simplified way to mean something i do not understand.

4) It seems that if i try to convert PreRotation to Quaternion and if i cumulate them and if i transform the world space axis using:

PreRotation(0)*PreRotation(1)*...*PreRotation(n) for joint n, i get the axis defining the local space. It works for joint 0,1,2 bur not for joint3. If think i am wrong. I don't know where. I try to compute my quaternion using AxisRotation to Quaternion with this order: "x" first, "z" after , and after "y" of PreRotation.

Even without any code, but just my mind, i don't succed in finding the local space of each joints from PreRotation. It seems to be the cumulation of each PreRotation (0 to n) where each PreRotation start around X, Z and after Y, but... not always.

5) I will try to find what is PostRotation, but after having understand what PreRotation is exactly.

Please tell me if i am true or wrong and give me more explanations (with simple words :) )

1) All joint have a local space. And the local space of joint "n" is not the translation of the local space of joint "n-1" among the x axis + a rotation. It's not like in other program like Ligthwave (maybe because in this program, joint are more dependant from their parent).

2) The rotation value stored in curves seems to be the relative rotation around the local space of each joint.

3) The local space of joint seems to be defined with PreRotation. If i take an animation with no relative rotation (all joint stay in BindPose), Prerotation should give me the Bind Pose of each joint. if i forget translation, and PostRotation (PostRotation is always 0 in my example) for the time beeing, i could find the BindPose orientation of each joint.

4) If i transform a bone defined by the points [(0,0,0)(1,0,0)] with the preRotation of joint "n" converted to matrix, i should get a bone oriented in the same orientation as the bone of joint "n" (but not translated in the good place). It works with joint0 but not for the over joints. So "TotalRot = Pre * Rot * Post" seems to be a simplified way to mean something i do not understand.

4) It seems that if i try to convert PreRotation to Quaternion and if i cumulate them and if i transform the world space axis using:

PreRotation(0)*PreRotation(1)*...*PreRotation(n) for joint n, i get the axis defining the local space. It works for joint 0,1,2 bur not for joint3. If think i am wrong. I don't know where. I try to compute my quaternion using AxisRotation to Quaternion with this order: "x" first, "z" after , and after "y" of PreRotation.

Even without any code, but just my mind, i don't succed in finding the local space of each joints from PreRotation. It seems to be the cumulation of each PreRotation (0 to n) where each PreRotation start around X, Z and after Y, but... not always.

5) I will try to find what is PostRotation, but after having understand what PreRotation is exactly.

###
#6
Members - Reputation: **156**

Posted 18 December 2008 - 04:26 AM

I have also noticed that if i rotate the first joint, PreRotation is not modified. And i don't know where Maya save this rotation in the fbx file.

http://texel3d.free.fr/bugs/ReverseSkinMeshLongCylX.fbx

http://texel3d.free.fr/bugs/ReverseSkinMeshLongCylZX.fbx

http://texel3d.free.fr/bugs/ReverseSkinMeshLongCylX.fbx

http://texel3d.free.fr/bugs/ReverseSkinMeshLongCylZX.fbx

###
#7
Crossbones+ - Reputation: **2532**

Posted 19 December 2008 - 03:51 AM

Quote:

1) All joint have a local space. And the local space of joint "n" is not the translation of the local space of joint "n-1" among the x axis + a rotation. It's not like in other program like Ligthwave (maybe because in this program, joint are more dependant from their parent).

It's exactly the same as lightwave, XSI, 3ds max, motionbuilder and every oter 3D app. The local space matrix for the joint has to be computed from the rotation, pre-rot,post-rot, translate and scale values. Just because lightwave may or maynot have a pre-rotation is completely moot. It still has a rotation value that needs to be converted to a matrix - it just so happens that computing a rotation matrix from Maya requires you to concatonate 3 seperate rotations before converting to a matrix.

Quote:

2) The rotation value stored in curves seems to be the relative rotation around the local space of each joint.

Correct. But this is only for the rotation values - the Pre and Post rotations are constant.

Quote:

3) The local space of joint seems to be defined with PreRotation...

? All of the following are true.

The local space of joint seems to be defined with PreRotation...

The local space of joint seems to be defined with PostRotation...

The local space of joint seems to be defined with Rotation...

The local space of joint seems to be defined with Translation...

The local space of joint seems to be defined with Scale...

The local space of joint seems to be defined with Rotation Pivots...

The local space of joint seems to be defined with Scale Pivots...

Quote:

...If i take an animation with no relative rotation (all joint stay in BindPose), Prerotation should give me the Bind Pose of each joint. if i forget translation, and PostRotation (PostRotation is always 0 in my example) for the time beeing, i could find the BindPose orientation of each joint.

And if you choose to ignore all of the other parts needed to compute a matrix, you are going to get BS results.

Quote:

4) If i transform a bone defined by the points [(0,0,0)(1,0,0)] with the preRotation of joint "n" converted to matrix, i should get a bone oriented in the same orientation as the bone of joint "n" (but not translated in the good place). It works with joint0 but not for the over joints. So "TotalRot = Pre * Rot * Post" seems to be a simplified way to mean something i do not understand.

??? No no no no no no no no. A pre-rotation converted to a matrix is 1 tiny component of the final transform. You are dealing with a hierarchical animation system here. If you transform a child bone by it's parents matrix, you'll get the final result - but this does not hold if you ignore it's scale/translation/rotation/parent frame etc. Only bothering to deal with a small percentage of the data is always going to give you incorrect results.

Quote:

4) It seems that if i try to convert PreRotation to Quaternion and if i cumulate them and if i transform the world space axis using:

PreRotation(0)*PreRotation(1)*...*PreRotation(n) for joint n, i get the axis defining the local space. It works for joint 0,1,2 bur not for joint3. If think i am wrong. I don't know where. I try to compute my quaternion using AxisRotation to Quaternion with this order: "x" first, "z" after , and after "y" of PreRotation.

Even without any code, but just my mind, i don't succed in finding the local space of each joints from PreRotation. It seems to be the cumulation of each PreRotation (0 to n) where each PreRotation start around X, Z and after Y, but... not always.

Why are you *not* computing a single local space transform matrix for each bone?

Why are you accumulating pre-rotations for all joints in an array?

This is a hierarchical animation file format - ergo you need to be computing local space transform matrices.

Quote:

5) I will try to find what is PostRotation, but after having understand what PreRotation is exactly.

Pre rotation is a rotation applied before the main rotation

Post rotation is a rotation applied after the main rotation

It's not rocket science - its just 3 matrix mults (or 3 quat mults which is a little cheaper computationally).

###
#8
Members - Reputation: **156**

Posted 19 December 2008 - 05:07 AM

Should it be something like that ?

I don't use scale and pivot because i don't know how to use it, but in my .fbx file scales and pivots are always 0 0 0

But with this code i have wrong results (only the first bone is in right place).

My result:

Bone0: (0 0 0)(0 1 0)

Bone1: (0 1 0)(1 1 0)

Bone2: (1 1 0)(1 1 -1)

Bone3: (1 1 -1)(2 1 -1)

And i should have:

Bone0: (0 0 0)(0 1 0)

Bone1: (0 1 0)(0 1 1)

Bone2: (0 1 1)(0 0 1)

Bone3: (0 0 1)(1 0 1)

And why this files have the same PreRotation/Rotation/PostRotation/Translation in the fbx file but have different orientation in Maya ?

http://texel3d.free.fr/bugs/ReverseSkinMeshLongCylX.fbx

http://texel3d.free.fr/bugs/ReverseSkinMeshLongCylZX.fbx

I don't use scale and pivot because i don't know how to use it, but in my .fbx file scales and pivots are always 0 0 0

But with this code i have wrong results (only the first bone is in right place).

My result:

Bone0: (0 0 0)(0 1 0)

Bone1: (0 1 0)(1 1 0)

Bone2: (1 1 0)(1 1 -1)

Bone3: (1 1 -1)(2 1 -1)

And i should have:

Bone0: (0 0 0)(0 1 0)

Bone1: (0 1 0)(0 1 1)

Bone2: (0 1 1)(0 0 1)

Bone3: (0 0 1)(1 0 1)

int frameNbr = 0;

GLGXVECTOR4 vec4Start; // Bone Start

GLGXVECTOR4 vec4End; // Bone End

GLGXVECTOR3 vec3Start(0.0f,0.0f,0.0f); // Bone Start

GLGXVECTOR3 vec3End(1.0f,0.0f,0.0f); // Bone End

for( int bone = 0; bone < int(m_vBones.size()); ++bone)

{

// Translation

GLGXMATRIX matTranslation;

if( m_vBones[bone]->Parent != NULL)

matTranslation._41 = 1.0f; // Length of bone = 1.0f

// Scale

// Alawys 1.0f => Identity Matrix => not usefull (for my example file)

// Pivot

// Always 0.0 => Identity Matrix => not usefull (for my example file)

// Rotation = PreRotation * Rotation * PostRotation

GLGXQUATERNION quaternion;

GLGXQuaternionMultiply(&quaternion,&m_vBones[bone]->vSequenceKeys[frameNbr].RelativeRotation,&m_vBones[bone]->m_QuatPreRotation);

GLGXQuaternionMultiply(&quaternion,&m_vBones[bone]->m_QuatPostRotation,&quaternion);

GLGXMATRIX matrixRotation;

GLGXMatrixRotationQuaternion( &matrixRotation, &quaternion );

// Combinaison of matrices

GLGXMATRIX matrix;

GLGXMatrixMultiply(&matrix,&matTranslation,&matrixRotation);

// Add parent matrix is parent exist

m_vBones[bone]->m_FinalMatrix = matrix;

if( m_vBones[bone]->Parent != NULL)

{

GLGXMatrixMultiply(&m_vBones[bone]->m_FinalMatrix,&m_vBones[bone]->Parent->m_FinalMatrix,&m_vBones[bone]->m_FinalMatrix);

}

GLGXVec3Transform(&vec4Start,&vec3Start,&m_vBones[bone]->m_FinalMatrix);

GLGXVec3Transform(&vec4End,&vec3End,&m_vBones[bone]->m_FinalMatrix);

}

And why this files have the same PreRotation/Rotation/PostRotation/Translation in the fbx file but have different orientation in Maya ?

http://texel3d.free.fr/bugs/ReverseSkinMeshLongCylX.fbx

http://texel3d.free.fr/bugs/ReverseSkinMeshLongCylZX.fbx