XNA Bone Based Animation

3 11 2011

I willl show you how you can accomplish in XNA relative transforms like the ones shown in the next video.

The model we are going to use can be found in the microsoft app hub website or just follow this link.  This model has its mesh parts named with the following architecture:

tank_geo
	r_engine_geo
		r_back_wheel_geo
		r_steer_geo
			r_front_wheel_geo
	l_engine_geo
		l_back_wheel_geo
		l_steer_geo
			l_front_wheel_geo
	turret_geo
		canon_geo
		hatch_geo
When you import this model to your xna game it will be processed on the ContentManager and this architecture is going to be transformed into a single array where the parent-child relation is made by its index.  The resulting array is stored in the Bones property of the Model object with the order shown in the following image.
Each ModelBone present in the Model.Bones property has the following properties : (int) Index and (ModelBone) Parent. Its this data structure that we will be using in the solution.

Everytime we load an asset with the ContentManager it get cached so that if you try to load the same asset twice it wont load the second one and used the cached version so, having this in mind let’s build the following class:

public class ModelObject
    {
        Vector3 m_position;
        Vector3 m_rotation;
        Matrix[] m_localTransforms;
        Matrix[] m_absoluteTransforms;
        Model m_model;
        float m_turretGeoRotation = 0;
        float m_canonGeoRotation = 0;

        public ModelObject(Model model, Vector3 pos)
        {
            m_model = model;
            m_position = pos;

            //Initiate the local tranforms array
            int boneCount = m_model.Bones.Count;
            m_localTransforms = new Matrix[boneCount];
            for (int i = 0; i < boneCount; i++)
            {
                m_localTransforms[i] = Matrix.Identity;
            }

            //Initiate the placeholder for the model transforms
            m_absoluteTransforms = new Matrix[boneCount];
        }

        public void Update(GameTime gameTime)
        {
            KeyboardState ks = Keyboard.GetState();
            if (ks.IsKeyDown(Keys.Down))
                m_canonGeoRotation += 1;
            if (ks.IsKeyDown(Keys.Up))
                m_canonGeoRotation += -1;
            if (ks.IsKeyDown(Keys.Left))
                m_turretGeoRotation += 1;
            if (ks.IsKeyDown(Keys.Right))
                m_turretGeoRotation += -1;

            //We transform directly the bone that we want to transform locally
            m_localTransforms[9] *= Matrix.CreateRotationY(MathHelper.ToRadians(m_turretGeoRotation));
            m_localTransforms[10] *= Matrix.CreateRotationX(MathHelper.ToRadians(m_canonGeoRotation));

            m_canonGeoRotation = 0;
            m_turretGeoRotation = 0;
        }

        private void CalculateAbsoluteTransforms()
        {
            //Copy all the transformations imported on the model
            m_model.CopyAbsoluteBoneTransformsTo(m_absoluteTransforms);

            //Iterate trough all of the bones transformations and apply the
            //local transformations that we have made to the model.
            ModelBoneCollection bones = m_model.Bones;
            for (int i = 0; i < bones.Count; i++)
            {
                m_absoluteTransforms[i] = m_localTransforms[i] * bones[i].Transform;

                ModelBone bone = bones[i];
                //If the actual bone has a parent then we have to transform it so that
                //it keep relative to his parent.
                if (bone.Parent != null)
                {
                    int parentIndex = bone.Parent.Index;
                    m_absoluteTransforms[i] *= m_absoluteTransforms[parentIndex];
                }
            }
        }

        public void Draw(Matrix projection, Matrix view)
        {
            //When we want to draw the model we need to apply the local
            //transformations before we apply the world transformation to each mesh on the model
            CalculateAbsoluteTransforms();
            foreach (ModelMesh mesh in m_model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.Projection = projection;
                    effect.View = view;
                    effect.World = m_absoluteTransforms[mesh.ParentBone.Index] * Matrix.CreateTranslation(m_position);
                    effect.EnableDefaultLighting();
                }
                mesh.Draw();
            }
        }
    }

With the ModelObject class we can transform the bones of a Model without changing on all occurences of the same Model around the game and that’s it!

Suggestions and/or questions? Comment out please! :)

Advertisement

Actions

Information

One response

29 11 2011
Álison Fernandes

Nice article, simple and effective! Small typo in the mesh architecture, “turrent_geo” should be “turret_geo” (without the N).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s




Follow

Get every new post delivered to your Inbox.