骨骼动画
游戏中的动画对于创建所喜欢的模拟必不可少。当动画模拟人物模型时,手头上有大量可供选择的内容。近来,许多游戏都选择使用一种名为骨骼动画的技。骨骼动画是一种允许程序员借助名为骨骼或链接的节点层次结构动画模拟模型的技术。链接在一起的骨骼或链接而形成的层次结构就构成了骨架。这些骨架与网格连接在一起,可以制作出真实的动画模拟。
过去,开发人员对人物及其他类似对象使用一种名位关键帧的动画模拟技术。在该技术中,开发人员需要为物体动画的每一帧存储整个网格的备份。必须预处理对象位置,这使得对象和外界或其他物理之间的实际交互变得非常困难。还记得射杀一个游戏角色时,它穿过墙和周围物体倒下的时刻么?例如,如果射杀一个靠近墙的人并且他倒下了,他身体的一半倒在地板上,而另一半倒在了墙的另一侧。这很不真实,而且要耗费大量的内存了爱保存网格的多个备份。记住:不得不动画模拟游戏中人物想要支持的每个单独动作或交互。考虑在游戏中有大量不同的物体的情景,玩家很快就会发现虽然占用的内存很多,那也只是为了在游戏中不真实地用动画模拟人物而已。
有了骨骼动画这种技术,就可以在游戏运行时计算动画,而不是提前作好预处理。这样可以为骨骼增加物理学和碰撞。由此,就可以以射杀靠近墙的人为例子,并解决前面讨论或的问题。如果人开始倒下并和墙发生碰撞,那么模型将停止,并真实地滑倒在墙下。同样,如果人被射杀,那么实际上没必要为其创建动画数据。只需要为模型施加一些物理作用力,并让“虚拟自然”自身发生作用。同样也可以让物体之间进行交互,然后使用骨骼动画,借助施加的某些碰撞和物理学概念,可以让结果显得更逼真。过去,使用关键帧动画技术是不可能真实实现动画模拟的,即使尽力,也是在游戏中需要海量的数据和消耗大量的内存,而这也根本不能获取少许时间内的精确动画模拟。
有了骨骼动画,实际上在创建人物动画时,就不需要动画模拟几何图形数据。作为替代,所要做的工作就是让骨骼动起来。当改动骨骼时,会将那些最终矩阵施加给与骨骼相关联的“顶点”。这将更改模型几何图形的位置,从而显出一种动画效果。由于骨骼在内存中非常小并且可以施加给大量的顶点,因此可以为它们增加物理学特征。这样可以在游戏中有更真实的运动。使用关键帧网格完成这项工作会是一场噩梦,即使它可以很真实地模拟动画。
可以认为骨骼动画中的骨骼是一种扩展矩阵。骨骼包含了大量明确需求的信息。但是从基本层面讲,骨骼由父ID和两个矩阵组成,通过父ID可以访问父骨骼。骨骼的两个矩阵分别是相对矩阵和绝对矩阵。绝对矩阵是骨骼的最终矩阵,通过相对矩阵和其父骨骼的绝对矩阵相乘可以计算得到该矩阵。骨骼的相对矩阵是每个骨骼的位置。例如,如果旋转骨骼30°,相对矩阵就会保存结果。但由于矩阵和层次结构相关联,因此在使用骨骼时,必须将子骨骼(当前骨骼)的相对矩阵施加给父骨骼的最终矩阵,这样父骨骼才会影响到子骨骼。考虑一下旋转胳膊的情形。如果转动胳膊,那么手同样也会动,即使没有做相对的移动。手的相对矩阵并没有改变,但手的父骨骼——胳膊发生了变动。因此,在移动胳膊时手的网格也会在空间中移动,但手自身(手的相对位置)根本没有移动。这就是相对矩阵和绝对矩阵之间的差别。绝对矩阵考虑了父骨骼,而相对矩阵只处理骨骼自身。
BoneAnimation 演示程序
本章将开发一个简单的BoneAnimation演示程序。在该演示程序中,将创建两个骨骼,并将其绘制到屏幕上。同样还要允许用户使用鼠标渲染(旋转)顶部的骨骼,使用箭头键移动整个骨架。要创建的骨骼结构将指明它的父ID(整数)、长度(整数)以及使用于绝对信息和相对信息的两个矩阵。通过长度可以知道骨骼的大小。完整的演示程序名为BoneAnimation,可以在本书配套光盘上的CHAPTER13文件夹中找到。演示程序由6个源文件和头文件组成。前4个文件分别是在第8章创建的matrix.cpp、matrix.h、vector.cpp和vector.h。最后两个文件是main.cpp和bone.h。
bone.h头文件指明了表现一个骨骼所需的全部信息。回忆一下,该演示程序的骨骼将由两个矩阵、父ID和长度组成。骨骼是一个名为CBone的类,包含了三个与其相关的函数。第一个函数是构造函数,用于将类中的成员变量设置为默认值。后两个是SetBone()函数,第一个SetBone()函数用于设置父ID和长度,第二个用于设置两个矩阵。完整的bone.h头文件如程序清单13.22所示。
#ifndef CBONE_H
#define CBONE_H
#include"Matrix.h"
class CBone
{
public:
CBone() : parent(0), length(0) { }
void SetBone(int P, float L)
{
// Set the bone data.
parent = P; length = L;
}
void SetBone(int P, float L, CMatrix4x4 R, CMatrix4x4 A)
{
// Set the bone data.
parent = P; length = L; relative = R; absolute = A;
}
// 父骨骼的id
int parent;
// 骨骼的长度
float length;
// 相对矩阵
CMatrix4x4 relative;
// 绝对矩阵
CMatrix4x4 absolute;
};
#endif
#ifndef _UGP_VECTOR_H_
#define _UGP_VECTOR_H_
#include<math.h>
class CVector3
{
public:
CVector3();
CVector3(float X, float Y, float Z);
void operator=(CVector3 &v);
CVector3 operator-(CVector3 &v);
CVector3 operator+(CVector3 &v);
CVector3 operator*(CVector3 &v);
CVector3 operator/(CVector3 &v);
CVector3 operator+(float f);
CVector3 operator-(float f);
CVector3 operator*(float f);
CVector3 operator/(float f);
void operator +=(CVector3 &v);
void operator -=(CVector3 &v);
void operator *=(CVector3 &v);
void operator /=(CVector3 &v);
void operator +=(float f);
void operator -=(float f);
void operator *=(float f);
void operator /=(float f);
// v1,v2的叉积得到*this
void CrossProduct(CVector3 &v1, CVector3 &v2);
// *this与v1的点积
float DotProduct3(CVector3 &v1);
// 向量长度
float GetLength();
// 归一化向量
void Normal();
// 得到垂直于v1,v2,v3组成平面的归一化后的向量赋值给*this
void Normalize(CVector3 &v1, CVector3 &v2, CVector3 &v3);
float x, y, z;
};
#endif
#include"Vector.h"
CVector3::CVector3()
{
x = y = z = 0.0;
}
CVector3::CVector3(float X, float Y, float Z)
{
x = X;
y = Y;
z = Z;
}
void CVector3::operator =(CVector3 &v)
{
x = v.x;
y = v.y;
z = v.z;
}
CVector3 CVector3::operator -(CVector3 &v)
{
return CVector3(x - v.x, y - v.y, z - v.z);
}
CVector3 CVector3::operator +(CVector3 &v)
{
return CVector3(x + v.x, y + v.y, z + v.z);
}
CVector3 CVector3::operator *(CVector3 &v)
{
return CVector3(x * v.x, y * v.y, z * v.z);
}
CVector3 CVector3::operator /(CVector3 &v)
{
return CVector3(x / v.x, y / v.y, z / v.z);
}
CVector3 CVector3::operator +(float f)
{
return CVector3(x + f, y + f, z + f);
}
CVector3 CVector3::operator -(float f)
{
return CVector3(x - f, y - f, z - f);
}
CVector3 CVector3::operator *(float f)
{
return CVector3(x * f, y * f, z * f);
}
CVector3 CVector3::operator /(float f)
{
f = 1/f;
return CVector3(x * f, y * f, z * f);
}
void CVector3::operator +=(CVector3 &v)
{
x += v.x;
y += v.y;
z += v.z;
}
void CVector3::operator -=(CVector3 &v)
{
x -= v.x;
y -= v.y;
z -= v.z;
}
void CVector3::operator *=(CVector3 &v)
{
x *= v.x;
y *= v.y;
z *= v.z;
}
void CVector3::operator /=(CVector3 &v)
{
x /= v.x;
y /= v.y;
z /= v.z;
}
void CVector3::operator +=(float f)
{
x += f;
y += f;
z += f;
}
void CVector3::operator -=(float f)
{
x -= f;
y -= f;
z -= f;
}
void CVector3::operator *=(float f)
{
x *= f;
y *= f;
z *= f;
}
void CVector3::operator /=(float f)
{
f = 1/f;
x *= f;
y *= f;
z *= f;
}
void CVector3::CrossProduct(CVector3 &v1, CVector3 &v2)
{
x = ((v1.y * v2.z) - (v1.z * v2.y));
y = ((v1.z * v2.x) - (v1.x * v2.z));
z = ((v1.x * v2.y) - (v1.y * v2.x));
}
float CVector3::DotProduct3(CVector3 &v1)
{
return x * v1.x + y * v1.y + z * v1.z;
}
float CVector3::GetLength()
{
return (float)sqrt((x * x + y * y + z * z));
}
void CVector3::Normal()
{
float lenght = GetLength();
if(lenght == 0.0f) lenght = 1.0f;
x = x/lenght;
y = y/lenght;
z = z/lenght;
}
void CVector3::Normalize(CVector3 &v1, CVector3 &v2, CVector3 &v3)
{
CVector3 e1, e2;
e1.x = v2.x - v1.x;
e1.y = v2.y - v1.y;
e1.z = v2.z - v1.z;
e1.Normal();
e2.x = v3.x - v1.x;
e2.y = v3.y - v1.y;
e2.z = v3.z - v1.z;
e2.Normal();
CrossProduct(e1, e2);
Normal();
}
#ifndef CMATRIX_H
#define CMATRIX_H
#include"Vector.h"
class CMatrix4x4
{
public:
CMatrix4x4(); // Constructor.
CMatrix4x4(const CMatrix4x4 &m); // Overloaded constructors.
CMatrix4x4(float r11, float r12, float r13, float r14,
float r21, float r22, float r23, float r24,
float r31, float r32, float r33, float r34,
float r41, float r42, float r43, float r44);
~CMatrix4x4(); // Destructor.
void Clear(); // Reset the matrix.
void operator=(CMatrix4x4 m); // Overloaded = sign.
CMatrix4x4 operator*(CMatrix4x4 m); // Overloaded * sign.
void Translate(float x, float y, float z); // Translate a point in 3D.
void inverseTranslate(); // Translate the opposite way.
// 旋转angle度,(x,y,z)是旋转轴
void Rotate(double angle, float x, float y, float z); // Rotate a 3D point.
CVector3 VectorMatrixMultiply(CVector3 v); // Vector matrix multiply.
CVector3 VectorMatrixMultiply3x3(CVector3 v);// Vector matrix multiply 3x3 matrix.
float matrix[16]; // The 4x4 matrix in a 1D array.
};
#endif
#include"Matrix.h"
CMatrix4x4::CMatrix4x4()
{
// Initialize all array values.
Clear();
}
CMatrix4x4::CMatrix4x4(const CMatrix4x4 &m)
{
matrix[0] = m.matrix[0];
matrix[4] = m.matrix[4];
matrix[8] = m.matrix[8];
matrix[12] = m.matrix[12];
matrix[1] = m.matrix[1];
matrix[5] = m.matrix[5];
matrix[9] = m.matrix[9];
matrix[13] = m.matrix[13];
matrix[2] = m.matrix[2];
matrix[6] = m.matrix[6];
matrix[10] = m.matrix[10];
matrix[14] = m.matrix[14];
matrix[3] = m.matrix[3];
matrix[7] = m.matrix[7];
matrix[11] = m.matrix[11];
matrix[15] = m.matrix[15];
}
CMatrix4x4::CMatrix4x4(float r11, float r12, float r13, float r14,
float r21, float r22, float r23, float r24,
float r31, float r32, float r33, float r34,
float r41, float r42, float r43, float r44)
{
matrix[0] = r11; matrix[1] = r12; matrix[2] = r13; matrix[3] = r14;
matrix[4] = r21; matrix[5] = r22; matrix[6] = r23; matrix[7] = r24;
matrix[8] = r31; matrix[9] = r32; matrix[10] = r33; matrix[11] = r34;
matrix[12] = r41; matrix[13] = r42; matrix[14] = r43; matrix[15] = r44;
}
CMatrix4x4::~CMatrix4x4() { }
void CMatrix4x4::operator =(CMatrix4x4 m)
{
// Set the matrix values to the values sent in to m.
matrix[0] = m.matrix[0]; matrix[1] = m.matrix[1]; matrix[2] = m.matrix[2];
matrix[3] = m.matrix[3];
matrix[4] = m.matrix[4]; matrix[5] = m.matrix[5]; matrix[6] = m.matrix[6];
matrix[7] = m.matrix[7];
matrix[8] = m.matrix[8]; matrix[9] = m.matrix[9]; matrix[10] = m.matrix[10];
matrix[11] = m.matrix[11];
matrix[12] = m.matrix[12]; matrix[13] = m.matrix[13]; matrix[14] = m.matrix[14];
matrix[15] = m.matrix[15];
}
CMatrix4x4 CMatrix4x4::operator *(CMatrix4x4 m)
{
// Return the value of this vector * m.
float newMatrix[16];
const float *m1 = matrix, *m2 = m.matrix;
newMatrix[0] = m1[0] * m2[0] + m1[4] * m2[1] + m1[8] * m2[2];
newMatrix[1] = m1[1] * m2[0] + m1[5] * m2[1] + m1[9] * m2[2];
newMatrix[2] = m1[2] * m2[0] + m1[6] * m2[1] + m1[10] * m2[2];
newMatrix[3] = 0;
newMatrix[4] = m1[0] * m2[4] + m1[4] * m2[5] + m1[8] * m2[6];
newMatrix[5] = m1[1] * m2[4] + m1[5] * m2[5] + m1[9] * m2[6];
newMatrix[6] = m1[2] * m2[4] + m1[6] * m2[5] + m1[10] * m2[6];
newMatrix[7] = 0;
newMatrix[8] = m1[0] * m2[8] + m1[4] * m2[9] + m1[8] * m2[10];
newMatrix[9] = m1[1] * m2[8] + m1[5] * m2[9] + m1[9] * m2[10];
newMatrix[10] = m1[2] * m2[8] + m1[6] * m2[9] + m1[10] * m2[10];
newMatrix[11] = 0;
newMatrix[12] = m1[0] * m2[12] + m1[4] * m2[13] + m1[8] * m2[14] + m1[12];
newMatrix[13] = m1[1] * m2[12] + m1[5] * m2[13] + m1[9] * m2[14] + m1[13];
newMatrix[14] = m1[2] * m2[12] + m1[6] * m2[13] + m1[10] * m2[14] + m1[14];
newMatrix[15] = 1;
return CMatrix4x4(newMatrix[0], newMatrix[1], newMatrix[2], newMatrix[3], newMatrix[4],
newMatrix[5], newMatrix[6], newMatrix[7], newMatrix[8], newMatrix[9],
newMatrix[10], newMatrix[11], newMatrix[12], newMatrix[13], newMatrix[14],
newMatrix[15]);
}
void CMatrix4x4::Clear()
{
// To set the matrix identity you set all the values in the matrix like so...
matrix[0] = 1.0f; matrix[1] = 0.0f; matrix[2] = 0.0f; matrix[3] = 0.0f;
matrix[4] = 0.0f; matrix[5] = 1.0f; matrix[6] = 0.0f; matrix[7] = 0.0f;
matrix[8] = 0.0f; matrix[9] = 0.0f; matrix[10] = 1.0f; matrix[11] = 0.0f;
matrix[12] = 0.0f; matrix[13] = 0.0f; matrix[14] = 0.0f; matrix[15] = 1.0f;
}
void CMatrix4x4::inverseTranslate()
{
matrix[12] = -matrix[12];
matrix[13] = -matrix[13];
matrix[14] = -matrix[14];
}
void CMatrix4x4::Translate(float x, float y, float z)
{
// Manipulate last row to translate.
matrix[12] = x;
matrix[13] = y;
matrix[14] = z;
matrix[15] = 1.0f;
}
void CMatrix4x4::Rotate(double angle, float x, float y, float z)
{
float sine = (float)sin(angle);
float cosine = (float)cos(angle);
float sinAngle = (float)sin(3.14 * angle / 180);
float cosAngle = (float)cos(3.14 * angle / 180);
float oneSubCos = 1.0f - cosAngle;
matrix[0] = (x * x) * oneSubCos + cosAngle;
matrix[4] = (x * y) * oneSubCos - (z * sinAngle);
matrix[8] = (x * z) * oneSubCos + (y * sinAngle);
matrix[1] = (y * x) * oneSubCos + (sinAngle * z);
matrix[5] = (y * y) * oneSubCos + cosAngle;
matrix[9] = (y * z) * oneSubCos - (x * sinAngle);
matrix[2] = (z * x) * oneSubCos - (y * sinAngle);
matrix[6] = (z * y) * oneSubCos + (x * sinAngle);
matrix[10] = (z * z) * oneSubCos + cosAngle;
}
CVector3 CMatrix4x4::VectorMatrixMultiply(CVector3 v)
{
CVector3 out;
out.x = (v.x * matrix[0]) + (v.y * matrix[4]) + (v.z * matrix[8]) + matrix[12];
out.y = (v.x * matrix[1]) + (v.y * matrix[5]) + (v.z * matrix[9]) + matrix[13];
out.z = (v.x * matrix[2]) + (v.y * matrix[6]) + (v.z * matrix[10]) + matrix[14];
return out;
}
CVector3 CMatrix4x4::VectorMatrixMultiply3x3(CVector3 v)
{
CVector3 out;
out.x = (v.x * matrix[0]) + (v.y * matrix[4]) + (v.z * matrix[8]);
out.y = (v.x * matrix[1]) + (v.y * matrix[5]) + (v.z * matrix[9]);
out.z = (v.x * matrix[2]) + (v.y * matrix[6]) + (v.z * matrix[10]);
return out;
}
#include<d3d9.h>
#include<d3dx9.h>
#include"Bone.h"
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#define WINDOW_CLASS "UGPDX"
#define WINDOW_NAME "Bone Animation"
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
#define FULLSCREEN 0
// Function Prototypes...
bool InitializeD3D();
bool InitializeObjects();
void RenderScene();
void Shutdown();
// Global window handle.
HWND g_hwnd = 0;
// Direct3D object and device.
LPDIRECT3D9 g_D3D = NULL;
LPDIRECT3DDEVICE9 g_D3DDevice = NULL;
// Matrices.
D3DXMATRIX g_projection;
D3DXMATRIX g_worldMatrix;
D3DXMATRIX g_ViewMatrix;
// Vertex stucture.
struct Vertex
{
FLOAT x, y, z;
DWORD color;
};
#define MAX_BONES 2
// g_xRot and g_yRot is used to rotate g_bones.
float g_xRot = 0.0f;
float g_yRot = 0.0f;
// Used to move our model with the arrow keys.
// 通过up,left,down,right四个键移动模型
CVector3 g_trans;
// Hierarchy of g_bones kept in a simple array instead of a hierarchy data structure.
CBone g_bones[MAX_BONES];
#define D3DFVF_D3DVertex (D3DFVF_XYZ | D3DFVF_DIFFUSE)
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static POINT oldMousePos;
static POINT currentMousePos;
static bool isMouseActive;
switch(msg)
{
case WM_DESTROY:
case WM_CLOSE:
PostQuitMessage(0);
return 0;
break;
case WM_KEYUP:
if(wParam == VK_ESCAPE)
PostQuitMessage(0);
break;
case WM_KEYDOWN:
switch(wParam)
{
case VK_UP:
g_trans.z -= 0.5f;
break;
case VK_DOWN:
g_trans.z += 0.5f;
break;
case VK_LEFT:
g_trans.x += 0.5f;
break;
case VK_RIGHT:
g_trans.x -= 0.5f;
break;
}
break;
case WM_LBUTTONDOWN:
oldMousePos.x = currentMousePos.x = LOWORD (lParam);
oldMousePos.y = currentMousePos.y = HIWORD (lParam);
isMouseActive = true;
break;
case WM_LBUTTONUP:
isMouseActive = false;
break;
case WM_MOUSEMOVE:
currentMousePos.x = LOWORD (lParam);
currentMousePos.y = HIWORD (lParam);
if(isMouseActive)
{
g_xRot -= (currentMousePos.x - oldMousePos.x);
g_yRot -= (currentMousePos.y - oldMousePos.y);
}
oldMousePos.x = currentMousePos.x;
oldMousePos.y = currentMousePos.y;
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE prevhInst, LPSTR cmdLine, int show)
{
// Register the window class
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
WINDOW_CLASS, NULL };
RegisterClassEx(&wc);
// Create the application's window
HWND hWnd = CreateWindow(WINDOW_CLASS, WINDOW_NAME, WS_OVERLAPPEDWINDOW,
100, 100, WINDOW_WIDTH, WINDOW_HEIGHT,
GetDesktopWindow(), NULL, wc.hInstance, NULL);
// Show the window
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
// Record for global.
g_hwnd = hWnd;
// Initialize Direct3D
if(InitializeD3D())
{
// Enter the message loop
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
RenderScene();
}
}
// Release any and all resources.
Shutdown();
// Unregister our window.
UnregisterClass(WINDOW_CLASS, wc.hInstance);
return 0;
}
bool InitializeD3D()
{
D3DDISPLAYMODE displayMode;
// Create the D3D object.
g_D3D = Direct3DCreate9(D3D_SDK_VERSION);
if(g_D3D == NULL) return false;
// Get the desktop display mode.
if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)))
return false;
// Set up the structure used to create the D3DDevice
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
if(FULLSCREEN)
{
d3dpp.Windowed = FALSE;
d3dpp.BackBufferWidth = WINDOW_WIDTH;
d3dpp.BackBufferHeight = WINDOW_HEIGHT;
}
else
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = displayMode.Format;
d3dpp.BackBufferCount = 1;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
// Create the D3DDevice
if(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hwnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
&d3dpp, &g_D3DDevice))) return false;
// Initialize any objects we will be displaying.
if(!InitializeObjects()) return false;
return true;
}
bool InitializeObjects()
{
// Set default rendering states.
g_D3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
g_D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
g_D3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
// Seeing how our object is simple, we can just create each bone like so.
// Our SetBone function takes the parent index and bone length.
// SetBone create each bone for us.
g_bones[0].SetBone(-1, 4.5f);
g_bones[1].SetBone(0, 4.5f);
// Set the projection matrix.
D3DXMatrixPerspectiveFovLH(&g_projection, D3DX_PI / 4,
WINDOW_WIDTH/WINDOW_HEIGHT, 0.1f, 1000.0f);
g_D3DDevice->SetTransform(D3DTS_PROJECTION, &g_projection);
// Define camera information.
D3DXVECTOR3 cameraPos(0.0f, 4.0f, -15.0f);
D3DXVECTOR3 lookAtPos(0.0f, 4.0f, 0.0f);
D3DXVECTOR3 upDir(0.0f, 1.0f, 0.0f);
// Build view matrix.
D3DXMatrixLookAtLH(&g_ViewMatrix, &cameraPos,
&lookAtPos, &upDir);
return true;
}
void UpdateBones()
{
// Temp matrices to hold rotations and translations.
CMatrix4x4 rotTemp1, rotTemp2, tempRelative;
// Here we will loop through the list of g_bones and
// update the skeleton(骨骼).
for(int i = 0; i < MAX_BONES; i++)
{
// Only the root bone will have a -1 value for its
// parent. Root g_bones pretty much are directly
// translated and rotated but it does not
// inherit anything.
if(g_bones[i].parent == -1)
{
// Manipulate the position of the root bone.
g_bones[i].relative.Translate(g_trans.x, g_trans.y, g_trans.z);
g_bones[i].absolute = g_bones[i].relative;
}
else
{
// Reset the bone's relative matrix.
g_bones[i].relative.Clear();
// First move this bone into position from parent.
g_bones[i].relative.Translate(0, g_bones[g_bones[i].parent].length * i, 0);
// Rotate the non-root g_bones with the mouse.
// 得到绕y轴旋转g_xRot的旋转矩阵
rotTemp1.Rotate(g_xRot, 0, 1, 0);
// 得到绕z轴旋转g_yRot的旋转矩阵
rotTemp2.Rotate(g_yRot, 0, 0, 1);
// Inverse translate the relative matrix.
tempRelative = g_bones[i].relative;
tempRelative.inverseTranslate();
// Calculate new relative matrix.
g_bones[i].relative = g_bones[i].relative * (rotTemp1 * rotTemp2) * tempRelative;
// Calculate final matrix (absolute).
g_bones[i].absolute = g_bones[g_bones[i].parent].absolute * g_bones[i].relative;
}
}
}
void RenderScene()
{
// Clear the backbuffer.
g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
// Begin the scene. Start rendering.
g_D3DDevice->BeginScene();
// Apply the view (camera).
g_D3DDevice->SetTransform(D3DTS_VIEW, &g_ViewMatrix);
// Update the bone list.
UpdateBones();
// Loop through and draw each bone. This is for display
// purposes only just so we can see the bones.
for(int i = 0; i < MAX_BONES; i++)
{
g_D3DDevice->SetTransform(D3DTS_WORLD, (D3DMATRIX*)g_bones[i].absolute.matrix);
unsigned long col = D3DCOLOR_XRGB(255,255,255);
// Generate mesh to represent bones.
// Draw a quad in lines.
// 用四根lines画一个四方形(首尾相连)
Vertex baseLines[] = {
-0.4f, 0 + (g_bones[i].length * i), -0.4f, col,
0.4f, 0 + (g_bones[i].length * i), -0.4f, col,
0.4f, 0 + (g_bones[i].length * i), -0.4f, col,
0.4f, 0 + (g_bones[i].length * i), 0.4f, col,
0.4f, 0 + (g_bones[i].length * i), 0.4f, col,
-0.4f, 0 + (g_bones[i].length * i), 0.4f, col,
-0.4f, 0 + (g_bones[i].length * i), 0.4f, col,
-0.4f, 0 + (g_bones[i].length * i), -0.4f, col
};
// Now from each end point go up until the bone length.
Vertex coneLines[] = {
-0.4f, 0 + (g_bones[i].length * i), -0.4f, col,
0, g_bones[i].length + (g_bones[i].length * i), 0, col,
0.4f, 0 + (g_bones[i].length * i), -0.4f, col,
0, g_bones[i].length + (g_bones[i].length * i), 0, col,
0.4f, 0 + (g_bones[i].length * i), 0.4f, col,
0, g_bones[i].length + (g_bones[i].length * i), 0, col,
-0.4f, 0 + (g_bones[i].length * i), 0.4f, col,
0, g_bones[i].length + (g_bones[i].length * i), 0, col,
};
g_D3DDevice->SetFVF(D3DFVF_D3DVertex);
// 当定点数据经常变化时(例如GUI系统),使用DrawPrimitiveUP比较合适
g_D3DDevice->DrawPrimitiveUP(D3DPT_LINELIST, 4, baseLines, sizeof(Vertex));
g_D3DDevice->DrawPrimitiveUP(D3DPT_LINELIST, 4, coneLines, sizeof(Vertex));
}
// End the scene. Stop rendering.
g_D3DDevice->EndScene();
// Display the scene.
g_D3DDevice->Present(NULL, NULL, NULL, NULL);
}
void Shutdown()
{
if(g_D3DDevice != NULL) g_D3DDevice->Release();
g_D3DDevice = NULL;
if(g_D3D != NULL) g_D3D->Release();
g_D3D = NULL;
}