Navigation: DirectX 9.0

Tutorial 7 - Character Animation


Overview


Figure 23.

In the same way that humans have bones and skin, we can make our characters also have bones and skin. After you have exported a character from 3d modelling software you can use the SkinnedMesh class to load it and display it animated with various animation sets like walk or jump.

For a complete description about how skinned meshes work I have written a lengthly article on gamedev.net that you can find here: Game Dev Article

Files

Before you begin, click here to download the files for this tutorial: SkinnedMeshTutFiles.zip

SkinnedMesh

The SkinnedMesh class looks like this:

Notice the public members; these are the methods that let you interact with the 3d character.
class SkinnedMesh{
public:
	SkinnedMesh();
	~SkinnedMesh();
	void Load(WCHAR filename[], IDirect3DDevice9* pDevice);
	void Render(Bone* bone, ID3DXEffect* pEffect, IDirect3DDevice9* pDevice);

private:
	void CalculateWorldMatrices(Bone* child, D3DXMATRIX* parentMatrix);
	void AddBoneMatrixPointers(Bone* bone);
	void FreeBoneMatrixPointers(Bone* bone);
	//Animation functions
	void GetAnimationSets();

protected:
	D3DXFRAME* m_pRootNode;
	ID3DXAnimationController* m_pAnimControl;
private:
	mapanimationSets;
	string current_animation;
public:
	void SetAnimation(string name);
	void PlayAnimation(float time);
	void SetTransform(D3DXVECTOR3 Pos, D3DXVECTOR3 Dir, D3DXVECTOR3 Scale);
	string GetCurrentAnimation();

private:
	D3DXMATRIX world;
	D3DXVECTOR3 Pos;
	D3DXVECTOR3 Direction;
	D3DXVECTOR3 Scale;

public:
	D3DXVECTOR3 GetPos();
	D3DXVECTOR3 GetDirection();
	D3DXVECTOR3 GetScale();
};

Loading a Character

You need to make sure a .x character you intend to use does not have too many bones just to make you aware. Otherwise you will get strange results.

Basically, you can create a class that inherits from SkinnedMesh if you want to give the skinned mesh character some unique character behaviour. Or you can just declare a simple SkinnedMesh instance for basic behaviour. For demonstration purposes, let's just use the SkinnedMesh for now.

We will use a character named Jandro. He has a "walk" animation, and a "jump" animation, which are stored in the "jandro.x" file.

Declare a SkinnedMesh instance in the game class:
class Game
{
...

private:
	SkinnedMesh anim_character;

...
};
Load the character in InitGame(); And set the initial animation. Position the character where you want him in the world:
bool Game::InitGame(HWND render_window)
{
	this->m_mainWindow = render_window;

	if(!InitializeDirect3D()){
		return false;
	}

	...

	anim_character.Load(L"jandro.x", m_d3dDevice);
	anim_character.SetAnimation("walk");
	anim_character.SetTransform(D3DXVECTOR3(0,0,0), D3DXVECTOR3(0,0,1), 
					D3DXVECTOR3(0.2,0.2,0.2));

	return true;
}
Call PlayAnimation() in the UpdateGame() function with deltaTime as the speed the animation will play at:
void Game::UpdateGame(float deltaTime)
{
	//Handle device lost event.
	HRESULT coop = m_d3dDevice->TestCooperativeLevel();

	if(coop != D3D_OK)
	{
		if(coop == D3DERR_DEVICELOST)
		{
			if(m_deviceStatus == DEVICE_LOST_OR_OPERATIONAL)
				OnDeviceLost();		
		}
		else if(coop == D3DERR_DEVICENOTRESET)
		{
			if(m_deviceStatus == DEVICE_NOTRESET)
				OnDeviceGained();
		}
	}

	anim_character.PlayAnimation(deltaTime*0.001f);

	...
}
Render the character:
void Game::RenderWorld()
{
	if(m_deviceStatus == DEVICE_LOST_OR_OPERATIONAL)
	{
		//TODO: Add Device lost handling.
		m_d3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xff000022, 1.0f, 0);

		if(SUCCEEDED(m_d3dDevice->BeginScene()))
		{
			...

			m_pEffect->SetMatrix("matVP", &(view * proj));
			D3DXVECTOR4 lightPos(10.0f, 10.0f, -50.0f, 0.0f);
			m_pEffect->SetVector("lightPos", &lightPos);

			anim_character.Render(NULL, m_pEffect, m_d3dDevice);

			...

			m_d3dDevice->EndScene();
		}

		// Swap buffers.
		m_d3dDevice->Present(NULL, NULL, NULL, NULL);
	}
}

Conclusion


Figure 24.

If all went well, you should have a 3d animated character walking on the spot. Now you can extend the SkinnedMesh class to make your characters a bit more interesting. For example here is the Jandro character with collision detection CharacterPlatformDemo.zip