XNA: Rotating Bones
- by MLM
XNA 4.0
I am trying to learn how to rotate bones on a very simple tank model I made in Cinema 4D.
It is rigged by 3 bones, Root - Main - Turret - Barrel
I have binded all of the objects to the bones so that all translations/rotations work as planned in C4D. I exported it as .fbx
I based my test project after: http://create.msdn.com/en-US/education/catalog/sample/simple_animation
I can build successfully with no errors but all the rotations I try to do to my bones have no effect. I can transform my Root successfully using below but the bone transforms have no effect:
myModel.Root.Transform = world;
Matrix turretRotation = Matrix.CreateRotationY(MathHelper.ToRadians(37));
Matrix barrelRotation = Matrix.CreateRotationX(barrelRotationValue);
MainBone.Transform = MainTransform;
TurretBone.Transform = turretRotation * TurretTransform;
BarrelBone.Transform = barrelRotation * BarrelTransform;
I am wondering if my model is just not right or something important I am missing in the code.
Here is my Game1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace ModelTesting
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
float aspectRatio;
Tank myModel;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
myModel = new Tank();
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
myModel.Load(Content);
aspectRatio = graphics.GraphicsDevice.Viewport.AspectRatio;
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
float time = (float)gameTime.TotalGameTime.TotalSeconds;
// Move the pieces
/*
myModel.TurretRotation = (float)Math.Sin(time * 0.333f) * 1.25f;
myModel.BarrelRotation = (float)Math.Sin(time * 0.25f) * 0.333f - 0.333f;
*/
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// Calculate the camera matrices.
float time = (float)gameTime.TotalGameTime.TotalSeconds;
Matrix rotation = Matrix.CreateRotationY(MathHelper.ToRadians(45));
Matrix view = Matrix.CreateLookAt(new Vector3(2000, 500, 0),
new Vector3(0, 150, 0),
Vector3.Up);
Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
graphics.GraphicsDevice.Viewport.AspectRatio,
10,
10000);
// TODO: Add your drawing code here
myModel.Draw(rotation, view, projection);
base.Draw(gameTime);
}
}
}
And here is my tank class:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace ModelTesting
{
public class Tank
{
Model myModel;
// Array holding all the bone transform matrices for the entire model.
// We could just allocate this locally inside the Draw method, but it
// is more efficient to reuse a single array, as this avoids creating
// unnecessary garbage.
public Matrix[] boneTransforms;
// Shortcut references to the bones that we are going to animate.
// We could just look these up inside the Draw method, but it is more
// efficient to do the lookups while loading and cache the results.
ModelBone MainBone;
ModelBone TurretBone;
ModelBone BarrelBone;
// Store the original transform matrix for each animating bone.
Matrix MainTransform;
Matrix TurretTransform;
Matrix BarrelTransform;
// current animation positions
float turretRotationValue;
float barrelRotationValue;
/// <summary>
/// Gets or sets the turret rotation amount.
/// </summary>
public float TurretRotation
{
get { return turretRotationValue; }
set { turretRotationValue = value; }
}
/// <summary>
/// Gets or sets the barrel rotation amount.
/// </summary>
public float BarrelRotation
{
get { return barrelRotationValue; }
set { barrelRotationValue = value; }
}
/// <summary>
/// Load the model
/// </summary>
public void Load(ContentManager Content)
{
// TODO: use this.Content to load your game content here
myModel = Content.Load<Model>("Models\\simple_tank02");
MainBone = myModel.Bones["Main"];
TurretBone = myModel.Bones["Turret"];
BarrelBone = myModel.Bones["Barrel"];
MainTransform = MainBone.Transform;
TurretTransform = TurretBone.Transform;
BarrelTransform = BarrelBone.Transform;
// Allocate the transform matrix array.
boneTransforms = new Matrix[myModel.Bones.Count];
}
public void Draw(Matrix world, Matrix view, Matrix projection)
{
myModel.Root.Transform = world;
Matrix turretRotation = Matrix.CreateRotationY(MathHelper.ToRadians(37));
Matrix barrelRotation = Matrix.CreateRotationX(barrelRotationValue);
MainBone.Transform = MainTransform;
TurretBone.Transform = turretRotation * TurretTransform;
BarrelBone.Transform = barrelRotation * BarrelTransform;
myModel.CopyAbsoluteBoneTransformsTo(boneTransforms);
// Draw the model, a model can have multiple meshes, so loop
foreach (ModelMesh mesh in myModel.Meshes)
{
// This is where the mesh orientation is set
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = boneTransforms[mesh.ParentBone.Index];
effect.View = view;
effect.Projection = projection;
effect.EnableDefaultLighting();
}
// Draw the mesh, will use the effects set above
mesh.Draw();
}
}
}
}