1
\$\begingroup\$

I am writing a simple BVH parser in C# using the XNA framework. At the moment I have managed to parse in all the data and my goal is to find out the global coordinates of all the joints in the skeleton.

The problem I am encountering is that the bones don't seem to take into account the rotation of its parents. For instance, if I move my lower leg, I would expect my foot to change orientation as well and keep the same angles between the foot and the lower leg. What I am noticing is that the translation of the bone joints is correct, but the global rotation of a bone is the same as the local rotation of the bone. The picture below might help you understand what the problem is:

enter image description here

note: there is a mistake in the pic captions, it should read "the left upper leg is rotated around the z-axis" not the x-axis.

I would have expected the lower leg portion to also be rotated by 1 radian as a result of its parents' change in orientation.

So the code is as below (I am using XNA and C#):

foreach (Bone b in bones)
{
            //Calculate the local transform matrix
            b.localTransform = Matrix.CreateTranslation(b.Offset) * Matrix.CreateRotationZ(MathHelper.ToRadians(b.z)) * Matrix.CreateRotationY(MathHelper.ToRadians(b.y)) * Matrix.CreateRotationX(MathHelper.ToRadians(b.x));
            Bone parent = b.parent;
            if (parent != null)
            {
                //Calculate global transform matrix
                b.globalTransform = parent.globalTransform * b.localTransform;
                b.location = b.globalTransform.Translation;
                lines.Add(new Tuple<Vector3, Vector3>(parent.location, b.location));
            }
}

Each bone assumes its parents global transformation is correct as the list is ordered so the parents always come before the children. The root bone has no rotations and no offset and has its global transform already set based on this. I am wondering what I am doing wrong here?

I have done a simple example using only 2D:

Matrix bone1 = Matrix.CreateTranslation(new Vector3(3, 0, 0)) * Matrix.CreateRotationZ(1); 
Matrix bone2 = Matrix.CreateTranslation(new Vector3(3, 0, 0)) * Matrix.CreateRotationZ(1); 

lines.Add(new Tuple<Vector3, Vector3>(Vector3.Zero, bone1.Translation)); 
lines.Add(new Tuple<Vector3, Vector3>(bone1.Translation, (bone1 *bone2).Translation));

Drawing this onto the screen shows what I expect. The 1st bone is rotated 1 radian around the origin, 2nd bone is rotated 2 radians around the origin.

I am using the below as a reference for parsing and interpreting the BVH data: http://www.dcs.shef.ac.uk/intranet/research/resmes/CS0111.pdf

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

I'm not sure how your 3D input data works, but I think your local matrix should be:

 b.localTransform = Matrix.CreateRotationZ(MathHelper.ToRadians(b.z)) * Matrix.CreateRotationY(MathHelper.ToRadians(b.y)) * Matrix.CreateRotationX(MathHelper.ToRadians(b.x)) * Matrix.CreateTranslation(b.Offset);

This is my code for this 2d bone editor:

 //-----------------------------------------------------------------------------------------------------------------------
        public void UpdateLocal( )
        {  
            Vector2 traslation = Translator.GetValue();
            float rotation = Rotator.GetValue();
            Vector2 scale = Scalator.GetValue();

            Matrix mTraslation, mRotation, mScale;
            Matrix.CreateTranslation( traslation.X, traslation.Y, 0, out mTraslation );
            Matrix.CreateRotationZ( rotation, out mRotation );
            Matrix.CreateScale(scale.X, scale.Y, 1 , out mScale );
            Matrix.Multiply( ref mScale, ref mRotation, out Local );
            Matrix.Multiply( ref Local, ref mTraslation, out Local );  
        }

        //-----------------------------------------------------------------------------------------------------------------------
        public virtual void UpdateTransformTopToDown( )
        {
            global_counter++;
            SpecialUpdateTransformTopToDown( );
        }

        //-----------------------------------------------------------------------------------------------------------------------
        protected virtual void SpecialUpdateTransformTopToDown( )
        {
            if ( counter == global_counter ) return;

            counter = global_counter;

            UpdateLocal( );

            if ( Parent != null )
            {
                 Matrix.Multiply( ref Local, ref (Parent as BoneData).Absolute, out Absolute );
            }
            else
            {
                Absolute = Local;
            }

            foreach ( BoneData child in _children.OfType<BoneData>() )
            {
                child.SpecialUpdateTransformTopToDown( );
            }
        }

And this is my code to animate Warcraft3 models, (based in MagosX War3ModelEditor code):

public void BuildTransformMatrix(Intervalo Time, Camara Camara)
    {

        Vector3 TranslationVector;
        Vector4 RotationVector;
        Vector3 ScalingVector;

        TranslationVector = Translation.GetValue(Time);
        RotationVector = Rotation.GetValue(Time);
        ScalingVector = Scaling.GetValue(Time);

        Matrix mPivotPoint = Matrix.CreateTranslation(PivotPoint);
        Matrix mScaling = Matrix.CreateScale(ScalingVector);
        Matrix mRotation = Matrix.CreateFromQuaternion(RotationVector.ToQuaternion());
        Matrix mTranslation = Matrix.CreateTranslation(TranslationVector);

        Matrix mPivotPointInverse = Matrix.Invert(mPivotPoint);


        Transform = mPivotPointInverse * mScaling * mRotation * mPivotPoint * mTranslation;           

        if (Parent != null && Parent.NodeId != -1)
        {
            Transform = Matrix.Multiply(Transform, Parent.Transform);
        }

        foreach (BaseNode child in Children)
        {
            child.BuildTransformMatrix(Time, Camara);
        }
    }
\$\endgroup\$
5
  • \$\begingroup\$ Thanks for the answer, changing the order of matrix transforms solved the problem. My knowledge of the coordinate systems is a bit shaky, is there a good reference which explains why the orders are switched around when changing from right hand to left hand? All the XNA tutorials I have seen say that XNA is a right hand system, got me quite confused. \$\endgroup\$
    – Jkh2
    Jan 27, 2012 at 16:47
  • \$\begingroup\$ Uupps, maybe Xna is right hand, but then opengl is left hand ... :), I reached that conclusion related to the matrix order and the system coordinates by myself, but maybe I'm wrong. May be this question is a good one to ask in gamedev. \$\endgroup\$
    – Blau
    Jan 27, 2012 at 21:07
  • \$\begingroup\$ I have removed the reference to the order because I'm not sure is right. \$\endgroup\$
    – Blau
    Jan 27, 2012 at 21:09
  • \$\begingroup\$ I found out that the default coord system in OpenGL and XNA is right-handed. DirectX uses Left handed by default \$\endgroup\$
    – Jkh2
    Jan 28, 2012 at 14:38
  • \$\begingroup\$ I think the confusion is that left vs right is not related to the coordinate system, it is related to matrix multiplication order. \$\endgroup\$
    – Blau
    Jan 29, 2012 at 18:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .