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
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!

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