• Advertisement
Sign in to follow this  

Bone transformation - could someone explain this?

This topic is 4900 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 am struggling with one thing and cant understand why this doesnt work. As far I know to position bones milkshape needs: 1. Position of a bone (vector3D - xyz) local coordinates elative to parent 2. Rotation (euler angles xyz) of a bone in local coordinates (relative to its parent bone). Please correct me if I am wrong. Now. The bones information I have stored in my data structure is exactly what milkshape needs. It contains a list of bones with positions and rotations in local coords relative to its parents. now when I loop though my list of bones and store into milkshape list of bones: for(allbones) {     // ...other stuff     msBone_SetPosition(pBone, mesh.MyListBones.Vec3Pos);     msBone_SetRotation(pBone, mesh.MyListBones.Vec3Rot); // euler angles } I get this: (this is obviously incorrect!) I thought well maybe I should subtract each bone position from its parent position and same thing with rotation. So I've done this: for(allbones) {     // ...other stuff     msBone_SetPosition(pBone, mesh.MyListBones.Vec3Pos - mesh.MyListBones[parent].Vec3Pos );     msBone_SetRotation(pBone, mesh.MyListBones.Vec3Rot - mesh.MyListBones[parent].Vec3Rot); // euler angles } and this is the result (very close but incorrect): As you can see its very close but still the bones are not aligned with the characters body. Subtracting vectors is definetely wrong so I abandoned this idea already. I guess I have to turn my positions and rotations from xyz position in to matrices and then I need to know the correct formula to come up with teh final transformation. I guess its a similar idea like I've done with vectors but using matrices. Something like take current transformation and subtract from parent transformation. etc.. Now as somebody suggested I checked MSViewer and found a bit of code which possibly could fix my problem. But before I polute the rest of this thread with source code could somebody with good understanding of matrices or anyone who solved this problem explain what could be done to solve it. In MSViewer in Model.cpp there is this function SetupBones() that has an interesting bit of code: int nBoneCount = msModel_GetBoneCount (m_pModel); if (!m_pBones) {     m_pBones = new myBone_t[nBoneCount]; } int i, j; for (i = 0; i < nBoneCount; i++) {     msBone *pBone = msModel_GetBoneAt (m_pModel, i);     msVec3 vRot;     vRot[0] = pBone->Rotation[0] * 180 / (float) Q_PI;     vRot[1] = pBone->Rotation[1] * 180 / (float) Q_PI;     vRot[2] = pBone->Rotation[2] * 180 / (float) Q_PI;     AngleMatrix(vRot, m_pBones.mRelative); // HERE I GUESS HE IS SIMPLY CONVERTING ROTATION VEC3D INTO ROTATION MATRIX     // NOT SURE ABOUT THESE 3 LINES BELOW (UPDATING MATRIX WITH TRANSLATION PERHAPS?)     m_pBones.mRelative[0][3] = pBone->Position[0];     m_pBones.mRelative[1][3] = pBone->Position[1];     m_pBones.mRelative[2][3] = pBone->Position[2];     int nParentBone = msModel_FindBoneByName(m_pModel, pBone->szParentName); // LOOKING FOR PARENT BONE     if (nParentBone != -1)     {         R_ConcatTransforms (m_pBones[nParentBone].mAbsolute, m_pBones.mRelative, m_pBones.mAbsolute); // MULTIPLYING PARENT ABS WITH CURRENT BONES RELATIVE POS TO GET FINAL TRANSFORMATION         memcpy(m_pBones.mFinal, m_pBones.mAbsolute, sizeof (matrix_t));     }     else     {         memcpy(m_pBones.mAbsolute, m_pBones.mRelative, sizeof (matrix_t));         memcpy(m_pBones.mFinal, m_pBones.mRelative, sizeof (matrix_t));     } }

Share this post

Link to post
Share on other sites
Well it may be a stupid question but here we go.
Do you have a working hierarchy system? I mean, can you link a bone to another and have it follow whatever its parent motion is?

A hierarchy is mandatory in setting up bones.
Basically using 4x4 matrix you first run through your skelegon and update their local transformations (position/rotation/scale) then in a second pass (or on the fly whatever) you go through your hierarchy again and whenever you encounter a bone you compute its world transformation by multiplying its local transformation matrix by its parent's world transformation matrix (effectivily combining both transformations. Warning: multiplication order is important).

Let's take an example:

We have a, b, c, 3 objects linked together. a being parent of b, b parent of c.

When you come to update c's world matrix you need to fetch b's world matrix wich in turn require that you fetch a's world matrix. a's world matrix is simply a's local matrix since it doesn't have any parent. So b's world matrix = b's local matrix * a's local(world) matrix and c's world matrix = c's local matrix * b's world matrix.
Hope this makes sense.

What you did when substracting positions/rotations is kind of a hack to a proper hierarchy processing.

One word left. To effectively use bones you need to store the inverse bind matrice for each bones. That is, the world matrix of each bones when the hierarchy is in bind pose (the pose used to map it to the 3d model, or reference pose). But ask again if you have any problem after solving that one. (:

Share this post

Link to post
Share on other sites
I think I possibly know where you are getting screwed up, because I had gotten screwed up over the same thing. I don't know if you addressed this or not, but what the initialization code does (at least on rsn.gamedev.net, and I would assume in other places) is it must initialize the bones to their base pose.

This means that, for whatever reason, when you go to use the bones, you can't just load the bone information and then use it, you must take the various information and then calculate where the bone is in base pose...the position/orientation of each bone when the time of animation is zero.

Does this make sense, and did you already address this?

Even though I can't actually solve this for you I hope that at least I have pointed out something you might not have previously known.

Share this post

Link to post
Share on other sites
Guest Anonymous Poster
Ok. I'll try to explain my probelm in a bit simpler way. Lets forget about key frames. There is no animation. Just static bones that must be positioned in correct place.

At the moment I have a text file with bone position and rotation that has been exported from some pretty advanced animation package. I was told by the creator that all these bone positions and rotations are in local coordinates (relative to the parent bone). So. All I know are those positions and rotations (euler angles) in txt file and that they are correct.
Obviously he must have had a mtrix hierarchy but in the end he extracted this information in form:
Vec3D Position & Vec3D Rotation so you get in text file eg. POS 0.1, 0.0, 1.0 ROT 0.001, 0.0, 0.0

Now. I am writing an importer for Milkshape (similar to 3D Studio Max) that requires me to supply bone position (in local coords) and rotation (in euler angles). Both should be in same form I get from text file ie. Vec3D's not matrices. I have no clue how it renders internally. All I am sure is that Milkshape requires me to supply bone info in local coordinates (relative to parent) which is exactly what I am getting from TXT file.

So I did that and it clearly doest work. (See the top image in prev post) SO now you can see me in between these two interface. On one side I am getting bone information from TXT file and on the other there is milkshape waiting for bone information. The end result is on the top image.

Now I am in the middle and I need to figure out what needs to be done with the data I am given so thta Milkshape is happy.

My idea is to convert those 3DVec's from TXT file into matrices
and do some matrix magic so Milkshape is happy. I know nothing whats happening in Milkshape internally as there is no documentation available and its an exe file :)

Share this post

Link to post
Share on other sites
Sorry the post was me above. Please read it. :)

So basically you guys know as much about it as me by just looking at the end result rendered on the top image.

The code above shows how the conversion is done but cant really figure it out.

I can see it this way.

for( i=0; allbones )
// Calculate the current bone matrix.
Matrix mxTranslate, mxRotate, mxTransf ;
mxTranslate.SetTranslation( BoneList.bonePosVec );
mxRotate.SetRotation( BoneList.bonePosVec );
mxTranslate.Muliply( mxRotate );
mxTransf = mxTranslate;

// Calculate the parent bone matrix.
Matrix mxTranslate2, mxRotate2, ParentMxTransf;
mxTranslate2.SetTranslation( BoneList.bonePosVec[parent] );
mxRotate2.SetRotation( BoneList.bonePosVec[parent] );
mxTranslate2.Muliply( mxRotate2 );
ParentMxTransf = mxTranslate2;

// Up to here I have a current bone matrix and parent bone matrix. Then I am stuck :)
mxTransf // Current bone transformation
ParentMxTransf // Parent bone tranformation

.... ???? No clue whats next I guess I would have to
multiply current bone tranformation by the inverse of parent
transf but I am not sure and if so then whats next?


Yes all angles are in radians.

[Edited by - papa on September 17, 2004 7:10:11 PM]

Share this post

Link to post
Share on other sites
Guest Anonymous Poster
Since you're using Euler: Are you sure of the expected unit? Radian or degrees? Make sure you keep it consistent...

Share this post

Link to post
Share on other sites
since your bones are 'slightly' off, and not widely screwed, I'd be more inclined towards a bug in the Euler-To-Matrix conversion. Maybe the conversion is done with a different order for angles (pitch-yaw-roll conversion can give a different matrix than yaw-pitch-roll, ect...).

If MSviewer works, maybe check their Euler-To-Matrix conversion code. Else, I don't really know, I'm not an animation specialist. It also depends what the data of the bone is related to, and the order of the transforms.

I'd play around with a very simple example, of say, just one joint (like just the elbow on its own), and try to match it up in both Milkshake, my code, and a build of MSViewer (since you seem to have the source code).

And I'd use quaternions, but it's no biggy to convert from matrix to quaterions afterwards.

Share this post

Link to post
Share on other sites
Original post by papa
The code above shows how the conversion is done but cant really figure it out.

The code your looking at from the MilkShape viewer is building final transforms from local transforms. It follows the bone heirarchy and concatenates the transformation matrices to get the absolute result.

If you're trying to apply this code to solve your problem, then what you're implying is that the data in your file is actually origin-relative and not parent-relative. In otherwords, it is the rotation and translation necessary to move the bone from the origin to it's final position, and not from the parent to its final position. That IS what your original picture looks like. The results seem to indicate that you're dealing with final transforms and not local transforms.

What you're doing in the code above looks correct to me. It's going to build a parent-relative transform from the origin-relative transforms in the file. Continuing from where you've started.

// 'subtract' the parent matrix from the current
Matrix mxFinalTransf = mxTransf * ParentMxTransf.Inverse();

// Get the Translation component
Vec3 finalTranslation, finalEulerRotation;
// Get the Euler Rotation component

// Pass the new local transforms to MilkShape
msBone_SetPosition(pBone, finalTranslation);
msBone_SetRotation(pBone, finalEulerRotation);

Getting Euler angles from a Matrix should be easy. I believe it's something like this, but be sure to look it up because I'm no textbook!!!

Yaw = arctan(-m20/m00)
Pitch = arctan(-m12/m11)
Roll = arcsin(m10)

Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement