user788497
user788497

Reputation: 193

Can't get model bones to animate independently in XNA

I am trying to programmatically animate my character model in XNA. I started by looking at the code example here: http://create.msdn.com/en-US/education/catalog/sample/skinned_model By editing the code in this example I was able to get the character to move around as I liked. Then I tried to make a separate program with just the essential code to do this. (I borrowed the "dude" model from the example).

Here is my code:

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 WindowsGame1
{
public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDevice device;
    GraphicsDeviceManager graphics;
    Model myModel;
    float aspectRatio;

    public Matrix[] boneTransforms;
    public Matrix[] originalBoneTransforms;
    Matrix[] worldTransforms;

    float pos = 0;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }

    protected override void Initialize()
    {
        base.Initialize();
    }

    protected override void LoadContent()
    {
        device = graphics.GraphicsDevice;
        myModel = Content.Load<Model>("dude");

        worldTransforms = new Matrix[myModel.Bones.Count];
        boneTransforms = new Matrix[myModel.Bones.Count];
        originalBoneTransforms = new Matrix[myModel.Bones.Count];
        myModel.CopyAbsoluteBoneTransformsTo(boneTransforms);
        myModel.CopyAbsoluteBoneTransformsTo(originalBoneTransforms);


        aspectRatio = graphics.GraphicsDevice.Viewport.AspectRatio;
    }

    protected override void Update(GameTime gameTime)
    {
        UpdateBoneTransforms();
        UpdateWorldTransforms();

        base.Update(gameTime);
    }

    public void UpdateBoneTransforms()
    {
        pos += .1f;
        int boneId = 32; //Right Arm
        boneTransforms[boneId] = Matrix.CreateTranslation(new Vector3(0, pos, 0)) * originalBoneTransforms[boneId];
    }

    public void UpdateWorldTransforms()
    {
        // Root bone.
        worldTransforms[0] = boneTransforms[0];

        // Child bones.
        for (int bone = 1; bone < worldTransforms.Length; bone++)
        {
            int parentBone = myModel.Bones[bone].Parent.Index;

            worldTransforms[bone] = boneTransforms[bone] *
                                         worldTransforms[parentBone];
        }
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        Matrix view = Matrix.CreateLookAt(new Vector3(0, 150, 125),
                    Vector3.Zero, Vector3.Up);
        Matrix projection = Matrix.CreatePerspectiveFieldOfView(
                    MathHelper.ToRadians(45.0f), aspectRatio,
                    1.0f, 10000.0f);

        foreach (ModelMesh mesh in myModel.Meshes)
        {
            foreach (BasicEffect meshEffect in mesh.Effects)
            {
                meshEffect.EnableDefaultLighting();
                meshEffect.World = worldTransforms[mesh.ParentBone.Index];
                meshEffect.View = view;
                meshEffect.Projection = projection;

                meshEffect.SpecularColor = new Vector3(0.25f);
                meshEffect.SpecularPower = 16;
            }
            mesh.Draw();
        }

        base.Draw(gameTime);
    }
}
}

I can't figure out why this is not working. If I set boneId (in UpdateBoneTransforms) to 0, the whole model moves around as expected, but if I change boneId to anything else, he just stands still in the original position. I would have expected a single joint to move even if I did not update the children, but I can't even get that to happen. Am I forgetting something important?

Upvotes: 2

Views: 1984

Answers (1)

David D&#237;az
David D&#237;az

Reputation: 190

Your code seems right but I bet you're not loading the model through the SkinnedModelProcessor. If you're not, your model is being drawn with a BasicEffect shader which not supports the skinning information hold on your model.

So:

1) Verify that dude.fbx uses SkinnedModelProcessor as content processor. For this you will have to include a reference to the SkinnedModelPipeline on your content project. Using this processor will make your model to use the right shader (SkinnedModel.fx).

2) You will have to use an AnimationPlayer to get the model pose (instead of using CopyAbsoluteBoneTransformsTo). Don't Update() the AnimationPlayer if you don't need animation.

3) To the resulting pose (a Matrix[]) do whatever transform you want.

4) Configure the SkinnedModelEffect with the pose (bones) and view and projection matrices:

effect.Parameters["Bones"].SetValue(bones);
effect.Parameters["View"].SetValue(view);
effect.Parameters["Projection"].SetValue(projection);

5) Draw.

All this stuff is in the Skinned Model sample code and you need it to use the skinning data.

Upvotes: 1

Related Questions