World Position of a bone

Aug 31, 2008 at 4:12 AM
Hi,

I'm trying to work out how to get the world position of a specific bone so that I can place an object there.

I've gone through the forums and this post http://www.codeplex.com/xnanimation/Thread/View.aspx?ThreadId=32315 seems to hint at how to accomplish this. However it doesn't really make sense to me.

I can't seem to find a specific bone by name via the animation controller.

There's LocalBonePose, but that returns an array. So I'm not sure how to find the specific bone I'm after.

SkinnedModel has a list of bones by name, but you cannot access it.

Any help is much appreciated


Aug 31, 2008 at 5:01 AM
I'm wondering about this as well. So far I can get the actual bone, and the skinned bone transform, but applying it to the object i want my model to hold onto is giving some odd results. 

Here's what i'm using to get the bones transform:

 

for (int i = 0; i < myModel.SkeletonBones.Count; i++){
    if(myModel.SkeletonBones[i].Name == "L_Hand"){
        holdingMatrix = myAnimationControl.SkinnedBoneTransforms[i];

        break;
    }
}

But if I transform my held object it's nowhere near where I'd like it to be. What am I doing wrong?

 

Aug 31, 2008 at 5:11 AM
Hi Billard,

Thanks for the reply.

I've now gotten about as far as you. Although I think you can save the looping by doing this -

int i = SkinnedModel.Model.Bones["L_Hand"].Index; (- 1??)

I'm just printing out the values, and it appears to be in local space. Working out how to transform that into World Space.

Unfortunately, I've got to goto hockey in a few minutes! I'll let you know if I get further.

Cheers,
Bino
Aug 31, 2008 at 1:14 PM
OK this is what worked for me. It's still in my test code section, but it seems to be working nicely.

//get bone you're interested in
 int i = ((PsiVisualFXObject)mVisualObj).SkinnedModel.Model.Bones["Bone33"].Index;

//get its transforms
Matrix boneLocal = ((PsiVisualFXObject)mVisualObj).AnimationController.SkinnedBoneTransforms[i];

//to world space - this is where you have to adapt it to suit your application. I'm using a 2D physics engine.
Matrix world = boneLocal * Matrix.CreateRotationY(mBody.State.Position.Angular) * Matrix.CreateTranslation(new Vector3(mBody.State.Position.Linear.X, 0, mBody.State.Position.Linear.Y));

I don't actually use the "world" matrix I have there. I'm just interested in the translation information. and it works nicely. So I'm assuming it's all good :P

Hope this helps.

Bino
Aug 31, 2008 at 8:01 PM
Hmm, I get some odd results when I try and access the bone transform using that Index. In the model i'm using (PlayerMarine.fbx, included with the samples) the Bones and SkinnedBoneTransforms lists have different lengths, so instead of getting L_Hand i'm getting the foot or something.

Right now my model and held object just sitting at 0,0,0 and all the animated bone transforms i'm putting on the object look like theyre moving the same as the object I want, just not in the right place. I've already tried transforming the hands animated bone matrix with all of it's parent bones, but thats isnt working either.

I hope bruno has time to come up with a sample or something.

Sep 2, 2008 at 3:32 PM
My apologies, my posts were wrong on both accounts. I've since received better test data to work with and it's totally wrong.

Bit too tired to work out what's going on now. Hope bruno comes to the party :)

Bino
Sep 3, 2008 at 4:28 PM
Hi, because my enlgish is little i don't sure if your problem is the same of mine:

I want "attach" an object to another skinned object (a sword on the hand of my model). I need know the Vector3 and trasform Matrix of the hand bone to attach the weapon to the hand in the animation...

If the problem is the same, read here http://forums.xna.com/forums/t/12194.aspx?PageIndex=2 .I waiting for Bruno reply since about two week (has been out of his office).

Come Bruno we need you! :)




Sep 17, 2008 at 6:27 AM
Hi, first of all, my english very is little...

I had the same problem:
I Solved it by using the "skinnyModel.SkeletonBones[xx].Transform.decompose(out scale,out rot,out trans)" to get the bone's location
And "boneXXtrans = new vector3(abs(trans.z),abs(trans.y),abs(trans.x))" i don`t know why...
Then "myController.Update(gametime,Matrix.CreateTranslation(boneXXtrans)*animationController.SkinnedBoneTransforms[xx])"
it works for me...

I hope this helps...
Sep 19, 2008 at 8:43 AM
 
Tanx Leandro i try now.

But, where is Bruno?
Coordinator
Sep 22, 2008 at 5:37 PM
Edited Sep 22, 2008 at 5:59 PM
$0Hi guys,$0$0$0$0$0Sorry I was not able to help on this thread. Right now I'm incredible busy and I will not be able to work on XNAnimation or help on the forum for some time, but I hope I could start working on it again soon!$0$0$0$0$0Thanks!$0
Feb 10, 2009 at 3:40 PM

Thanks for a good work Bruno!

I have read this discussion over and over again and I still cannot solve it. This is how far I have come:

public void MoveByBones(SkinnedModel model, Matrix[] transformations)
{
             if (!originalPositionsIsSaved)
            {
                for (int i = 0; i < originalPositions.Length; i++)
                    originalPositions[i] = limbs[i].PhysicsBody.Position;
                originalPositionsIsSaved = true;
            }
           MoveLimbByTransform(0, transformations[0]);
}
        private Vector3 sca;
        private Vector3 pos;
        private Quaternion orientation;
        private void MoveLimbByTransform(int limbIndex, Matrix transform)
        {
            transform.Decompose(out sca, out orientation, out pos);
            limbs[limbIndex].PhysicsBody.MoveTo(originalPositions[limbIndex] + (transformationScale * pos),
                Matrix.CreateFromQuaternion(orientation));
            //System.Diagnostics.Debugger.Break();
        }
limbs[] is an array of boxes that I want to move according to the animation, originalPositions[] is set to the first position the boxes got, transformatoinScale is a Vector3 (0.01f, 0.01f, 0.01f) which scales down the delta movement of the animation.

However, I do not know if this is the correct way to extract the position and orientation of a specified bone or if it is my boxes (jiglibx) that dosn't work. Most of the boxes (in limbs) are at the correct position, but those who are animated are not. The move in the same rythm as the animation, but not to the correct position.

(a good way to get information about the bones is by using the System.Diagnostics.Debugger.Break() and then read the locals)

Is there any other ideas about this problem?

Feb 22, 2009 at 5:40 PM

Hello, I also want to place something at the world position of a bone.

Technically I think, it should just be 

Matrix[] m = animationController.SkinnedBoneTransforms;

which also makes the mesh animate with the right bone, but it is not at its position.

thanks for any help on this!

Mar 12, 2009 at 6:41 AM
Has anyone had any luck with this? Its been driving me crazy for quite some time, and I am no closer to getting it solved than the first day I tried. I have read through many of the posts dealing with this same issue, none have an answer.

Anybody?

Dave
Mar 12, 2009 at 7:24 PM

Hello!

I think I got it, and it's not XNAnimation that make this difficult. The problem for me was the bone had wrong rotation and later my mesh also had wrong rotation (which made it impossible for me to se the animation).

You should try this, make a new simple program and load a simple box model. Then draw the box once for each matrix in animationController.SkinnedBonedransforms to see where they appear. If they some or most of them are at the same position you can add the original position of the bone:

            // Save original positions.
            originalPose = new Matrix[skeleton.Model.Bones.Count];
            skeleton.Model.CopyAbsoluteBoneTransformsTo(originalPose);

where skeleton is the model with the animated bones. Then you make a for-statement which go thru all the bones and calculate the world matrix for each box:

            boneAnimations = controller.SkinnedBoneTransforms;
            for (int i = 0; i < boneAnimation.Length; i++)
            {
                // Calculate the new pose.
                animationPosition = originalPose[index] * boneAnimations[index] * rotationCorrection;

                DrawBodyPart(bodyModels[i], animationPositoin);
            }

in this way you can easy see where alla the bones are by looking at the drawn boxes.

However, there is still one problem. The position of the bone is usually at the top of for example the leg, but the mesh you use (or model) have a center point in  the middle, and therfore the mesh will be centred at the top of the leg and not in the middle.

But with the visuallisation of the bones you can easy see if you models need to be altered (probably if you use 3ds ;).

Hope it helps!
Carl

Mar 24, 2009 at 2:29 AM
I have solved this issue in my project, so here is what I did:

What you need to do, is calculate the world matrix of the bone you want.
In the case of the Marine model, the right hand is index 14 in the animationController.SkinnedBoneTransforms array.

For this, you need 3 steps, I am going to organize them into 3 functions, but set them up however you like.  Note that this all happens at the END of the Update() method call.

First, you have a function to create a world matrix:

public Matrix CreateWorldMatrix(Vector3 Translation, Matrix Rotation, Vector3 Scale)
{
       return Matrix.CreateScale(Scale) * Rotation * Matrix.CreateTranslation(Translation);
}

Then, you have a function do calculate the bone world matrix like so:

public Matrix GetBoneWorld(int index) //once again, index is 14 in our case!
{
        //get absolute position of the bone
        Matrix BonePos = animationController.SkinnedBoneTransforms[index];
       //apply world transform
        Matrix TransformedBonePos = BonePos * CreateWorldMatrix(ModelPosition, ModelRotation, ModelScale);
        return TransformedBonePos;
}

At this point, you have all the information you need.  Now, you apply this ifnormation to the machine gun model like so:
(assume you already have a declaration of:
Model machineGun = Content.Load<Model>("WeaponMachineGun");
located somewhere in your code!)

public void SetMachineGunWorld(Matrix world)
{
       Vector3 Scale;
       Vector3 Translation;
       Matrix Rotation;
       Quaternion _rotation;  //needed for decompose function

       if (world.Decompose(out Scale, out _rotation, out Translation))
       {
              //convert Quaternion into a rotation matrix
              Rotation = Matrix.CreateFromQuaternion(_rotation);
              //set machine gun position, rotation, and scale
              machineGun.Scale = Scale;
              machineGun.Rotation = Rotation;
              machineGin.Translation = Translation;
       }
}

Now there are some differences here, i.e. I have a wrapper class to manage the position, rotation and scale of a model, but hopefully you get the general idea that you need to set the values.
Hope it helps!
~Jesse
Apr 28, 2009 at 1:16 PM

Hi,

Is anyone able to help me with this problem I'm facing?

I cannot seem to be able to draw a sphere at the marine's hand using index 14. could it be a problem with my drawing codes?

  public void drawModel()
{

  foreach (ModelMesh mesh in skinnedModel.Model.Meshes)
  {
  Matrix[] transforms2 = new Matrix[test.Bones.Count];
  test.CopyAbsoluteBoneTransformsTo(transforms2);
  foreach (ModelMesh myMesh in test.Meshes)
  {
  graphics.GraphicsDevice.RenderState.FillMode = FillMode.WireFrame;
  Matrix worldMatrix = transforms2[myMesh.ParentBone.Index]
  * animationController.SkinnedBoneTransforms[14]
  * Matrix.CreateTranslation(new Vector3(0, 0, 0));

  foreach (BasicEffect effect in myMesh.Effects)
  {
  effect.EnableDefaultLighting();
  effect.GraphicsDevice.RenderState.DepthBufferEnable = true;
  effect.World = worldMatrix;
  effect.View = cameraView[activeSkinnedModel];
  effect.Projection = cameraProjection;
  }

  // Draw the mesh, using the effects set above.
  myMesh.Draw();
  graphics.GraphicsDevice.RenderState.FillMode = FillMode.Solid;
 }
  }
  }


Apr 28, 2009 at 1:17 PM
My draw model function is called after Draw2D() in the Draw(gameTime) method.
Apr 28, 2009 at 11:59 PM
Okay i managed to solve it.

Matrix.Invert(skinnedModel.SkeletonBones[i].InverseBindPoseTransform)
                            * animationController.SkinnedBoneTransforms[i]
                            * Matrix.CreateScale(scale)
                            * Matrix.CreateRotationY(MathHelper.ToRadians(modelRotation)) 
                            * Matrix.CreateTranslation(modelPosition);

You have to put this in the world.

the animationcontroller stores the animation data like the translations and rotations but it is not applied to the bones yet.
so you have to multiply it to the bones in order to get the exact position of the bones during the animation.

still not very good at explanations but i hopes that helps anyone who runs into the same problem. :)