运行结果:
通过键盘或者鼠标来控制角色播放指定的动画是游戏中十分常见的功能,而我们之前的程序只能播放默认的一个动画,而且每个动画的实例都包含了资源,而我们绘制的时候需要资源,实例并不需要包含资源。我们只用指针指向那个资源就可以了。
最终从puppet_master这里学习了如何来实现这个功能。
传送门:骨骼动画进阶
我们可以首先对微软官方的那个骨骼动画文件进行一次封装。用来创建动画实例。功能就是实现各种动画的操作,比如根据名字播放骨骼动画实例、渲染骨骼动画等,并且保存了动画的资源。
源代码:
AnimationClass.h.h:
#pragma once
//=============================================================================
//一个封装了微软官方DirectX SDK中的骨骼动画类的类的头文件
//=============================================================================
#include "AllocateHierarchyClass.h"
class AnimInstanceClass;
class AnimationClass
{
friend class AnimInstanceClass;
private:
LPDIRECT3DDEVICE9 m_pd3dDevice; //D3D设备对象
CAllocateHierarchy *m_pAllocateHier; //骨骼动画网格模型指针
LPD3DXFRAME m_pFrameRoot; //帧
LPD3DXANIMATIONCONTROLLER m_pAnimController; //动画控制器
protected:
void SetAnimationMatrix(D3DXMATRIX *pMatrix); //设置骨骼动画矩阵
void RenderAnimation(); //渲染骨骼动画
//克隆一个动画控制器,使用的是克隆的动画控制器,而非原始的动画控制器,原始的仅供克隆
LPD3DXANIMATIONCONTROLLER CloneAnimController();
public:
AnimationClass(LPDIRECT3DDEVICE9 pd3dDevice); //构造函数
~AnimationClass(void); //析构函数
HRESULT InitAnimation(wchar_t *fileName); //创建骨骼动画
};
AnimationClass.cpp:
//=============================================================================
//一个封装了微软官方DirectX SDK中的骨骼动画类的类的源文件
//=============================================================================
#include "AnimationClass.h"
//-----------------------------------------------------------------------------
//构造函数
//-----------------------------------------------------------------------------
AnimationClass::AnimationClass(LPDIRECT3DDEVICE9 pd3dDevice)
{
m_pd3dDevice = pd3dDevice;
m_pAllocateHier = NULL;
m_pFrameRoot = NULL;
m_pAnimController = NULL;
}
//-----------------------------------------------------------------------------
//析构函数
//-----------------------------------------------------------------------------
AnimationClass::~AnimationClass()
{
D3DXFrameDestroy(m_pFrameRoot, m_pAllocateHier);
SAFE_DELETE(m_pAllocateHier);
SAFE_RELEASE(m_pAnimController);
}
//-----------------------------------------------------------------------------
//创建骨骼动画
//-----------------------------------------------------------------------------
HRESULT AnimationClass::InitAnimation(wchar_t *fileName)
{
HRESULT hr;
m_pAllocateHier = new CAllocateHierarchy();
HR(D3DXLoadMeshHierarchyFromX(fileName, D3DXMESH_MANAGED, m_pd3dDevice, m_pAllocateHier,
NULL, &m_pFrameRoot, &m_pAnimController));
//设置好各个帧的组合变换矩阵
SetupBoneMatrixPointers(m_pFrameRoot, m_pFrameRoot);
return S_OK;
}
//-----------------------------------------------------------------------------
//设置骨骼动画矩阵
//-----------------------------------------------------------------------------
void AnimationClass::SetAnimationMatrix(D3DXMATRIX *pMatrix)
{
//更新帧中的变换矩阵
UpdateFrameMatrices(m_pFrameRoot, pMatrix);
}
//-----------------------------------------------------------------------------
//渲染骨骼动画
//-----------------------------------------------------------------------------
void AnimationClass::RenderAnimation()
{
//绘制骨骼
DrawFrame(m_pd3dDevice, m_pFrameRoot);
}
//-----------------------------------------------------------------------------------
//克隆一个动画控制器,使用的是克隆的动画控制器,而非原始的动画控制器,原始的仅供克隆
//-----------------------------------------------------------------------------------
LPD3DXANIMATIONCONTROLLER AnimationClass::CloneAnimController()
{
LPD3DXANIMATIONCONTROLLER pAnimController = NULL;
if(FAILED(m_pAnimController->CloneAnimationController(
m_pAnimController->GetMaxNumAnimationOutputs(),
m_pAnimController->GetMaxNumAnimationSets(),
m_pAnimController->GetMaxNumTracks(),
m_pAnimController->GetMaxNumEvents(),
&pAnimController
)))
return NULL;
return pAnimController;
}
然后我们对这个类进行二次封装,来创建一个动画实例类。每个实例仅仅包含一个指向动画资源的指针,这样就可以仅仅保存一份资源了。然后我们还需要拷贝一份动画控制器。为什么呢?因为如果我们有多个实例,一个动画控制器控制了所有的实例的动画播放,那么他们做的动画就会完全一样,这完全就不是我们想要的结果啊。所以我们为每个实例拷贝一份动画控制器,分别播放不用的动画。实现方法直接调用微软写好的API就OK了。注意第一个骨骼动画类的动画控制器不可以直接使用,它仅仅供动画实例类克隆用的。
AnimInstanceClass.h:
#pragma once
//=============================================================================
//一个封装了AnimationClass类的骨骼动画实例类的头文件
//=============================================================================
#include "AnimationClass.h"
class AnimInstanceClass
{
private:
AnimationClass *m_pAnimation; //骨骼动画类
LPD3DXANIMATIONCONTROLLER m_pAnimController; //动画控制器
D3DXMATRIX m_pMatrix; //骨骼动画实例矩阵
public:
AnimInstanceClass(AnimationClass *mesh); //构造函数
~AnimInstanceClass(void); //析构函数
HRESULT SetAnimationByName(LPCSTR animName); //根据名字播放骨骼动画实例
void SetAnimationMatrix(D3DXMATRIX *pMatrix); //设置骨骼动画实例矩阵
D3DXMATRIX GetAnimationMatrix(); //获得骨骼动画实例矩阵
void RenderAnimation(float fDeltaTime); //渲染骨骼动画实例
UINT GetAnimationNum() const; //获得骨骼动画个数
LPD3DXANIMATIONSET GetAnimationSet(UINT index) const; //根据动画编号获得动画集
};
AnimInstanceClass.cpp:
//=============================================================================
//一个封装了AnimationClass类的骨骼动画实例类的源文件
//=============================================================================
#include "AnimInstanceClass.h"
//-----------------------------------------------------------------------------
//构造函数 获得骨骼动画类指针,并拷贝一个单独的控制器
//-----------------------------------------------------------------------------
AnimInstanceClass::AnimInstanceClass(AnimationClass *pAnimation)
{
m_pAnimation = pAnimation;
m_pAnimController = pAnimation->CloneAnimController();
}
//-----------------------------------------------------------------------------
//析构函数
//-----------------------------------------------------------------------------
AnimInstanceClass::~AnimInstanceClass(void)
{
SAFE_RELEASE(m_pAnimController);
}
//-----------------------------------------------------------------------------
//根据名字播放骨骼动画实例
//-----------------------------------------------------------------------------
HRESULT AnimInstanceClass::SetAnimationByName(LPCSTR animName)
{
HRESULT hr;
LPD3DXANIMATIONSET pAnimationSet = NULL;
//根据动画名字获得骨骼动画的集
HR(m_pAnimController->GetAnimationSetByName(animName, &pAnimationSet));
//设置指定轨道为该动画集
HR(m_pAnimController->SetTrackAnimationSet(0, pAnimationSet));
}
//-----------------------------------------------------------------------------
//设置骨骼动画实例矩阵
//-----------------------------------------------------------------------------
void AnimInstanceClass::SetAnimationMatrix(D3DXMATRIX *pMatrix)
{
m_pMatrix = *pMatrix;
}
//-----------------------------------------------------------------------------
//获得骨骼动画实例矩阵
//-----------------------------------------------------------------------------
D3DXMATRIX AnimInstanceClass::GetAnimationMatrix()
{
return m_pMatrix;
}
//-----------------------------------------------------------------------------
//渲染骨骼动画实例
//-----------------------------------------------------------------------------
void AnimInstanceClass::RenderAnimation(float fTimeDelta)
{
//使动画动起来,并将全局动画时间指定为一个数目。
m_pAnimController->AdvanceTime(fTimeDelta, NULL);
//更新帧中的变换矩阵
m_pAnimation->SetAnimationMatrix(&m_pMatrix);
//渲染骨骼动画
m_pAnimation->RenderAnimation();
}
//-----------------------------------------------------------------------------
//获得骨骼动画个数
//-----------------------------------------------------------------------------
UINT AnimInstanceClass::GetAnimationNum() const
{
return m_pAnimController->GetMaxNumAnimationSets();
}
//-----------------------------------------------------------------------------
//根据动画编号获得动画集
//-----------------------------------------------------------------------------
LPD3DXANIMATIONSET AnimInstanceClass::GetAnimationSet(UINT index) const
{
LPD3DXANIMATIONSET pAnimationSet = NULL;
if(index >=0 && index < GetAnimationNum())
m_pAnimController->GetAnimationSet(index, &pAnimationSet); //根据编号获得动画集
return pAnimationSet;
}
最后当我们创建一个角色时直接创建对应的动画实例类,当然在此之前先创建骨骼动画类,把动画资源加载进来。
然后通过动画实例类来进行各种操作,就会十分的方便了。
源代码:
WinMain.cpp:
#include
#include
#include
#include "CameraClass.h"
#include "DirectInputClass.h"
#include "SkyBoxClass.h"
#include "SnowParticleClass.h"
#include "AnimationClass.h"
#include "AnimInstanceClass.h"
#define WINDOW_TITLE L"Direct 3D 骨骼模型动画进阶:多动画"
//地板的顶点结构
struct CUSTOMVERTEX
{
float x, y, z;
float u, v;
CUSTOMVERTEX(float _x, float _y, float _z, float _u, float _v):
x(_x), y(_y), z(_z), u(_u), v(_v){}
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_TEX1)
//---------------------------------------【全局变量声明部分】------------------------------------------------
//描述:全局变量的声明
//-----------------------------------------------------------------------------------------------------------
LPDIRECT3DDEVICE9 g_pd3dDevice; //Direct 3D设备对象
D3DXMATRIX g_matWorld; //世界矩阵
DirectInputClass *g_pDInput; //一个DirectInput类的指针
LPD3DXFONT g_pTextAdapterName; //显卡名字的2D文本
LPD3DXFONT g_pTextHelper; //帮助文本的2D文本
LPD3DXFONT g_pTextInfor; //绘制信息的2D文本
LPD3DXFONT g_pTextFPS; //FPS文本的2D文本
wchar_t g_strAdapterName[60]; //包括显卡名字的字符串
wchar_t g_strFPS[50]; //包含帧频率的字符数组
CameraClass *g_pCamera; //虚拟摄像机指针
SkyBoxClass *g_pSkyBox; //天空盒类的指针
SnowParticleClass *g_pSnowParticle; //雪花粒子系统的指针
LPDIRECT3DVERTEXBUFFER9 g_pFloorVBuffer; //地板顶点缓存对象
LPDIRECT3DTEXTURE9 g_pFloorTexture; //地板纹理对象
AnimationClass *g_pAnimation; //动画指针
AnimInstanceClass *g_pAnimInstance1; //动画实例1
AnimInstanceClass *g_pAnimInstance2; //动画实例2
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HRESULT Direct3D_Init(HWND); //在这个函数中继续Direct3D的初始化
HRESULT Objects_Init(HWND); //在这个函数中进行要绘制的物体的资源初始化
void Direct3D_Render(HWND, float); //在这个函数中进行Direct3D渲染代码的书写
void Direct3D_ClearUp(); //在这个函数中清理COM资源以及其他资源
float Get_FPS();
void Direct3D_Update(float);
void HelpText_Render(HWND);
//----------------------------------------【WinMain()函数】-------------------------------------------------
//描述:Windows应用程序的入口函数
//-------------------------------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wndClass = {0};
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = (WNDPROC)WndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = (HICON)LoadImage(NULL, L"icon.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = L"3DGameBase";
if(!RegisterClassEx(&wndClass))
return -1;
HWND hWnd = CreateWindow(L"3DGameBase", WINDOW_TITLE, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);
MoveWindow(hWnd, 250, 80, WINDOW_WIDTH, WINDOW_HEIGHT, true);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
g_pDInput = new DirectInputClass();
if(FAILED(g_pDInput->Init(hWnd, hInstance, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE)))
MessageBox(hWnd, L"Direct3D Input初始化失败!", L"消息窗口", 0);
PlaySound(L"Final Fantasy XIII.wav", NULL, SND_LOOP | SND_ASYNC | SND_FILENAME);
if(FAILED(Direct3D_Init(hWnd)))
MessageBox(hWnd, L"Direct3D 初始化失败!", L"消息窗口", 0);
MSG msg = {0};
while(msg.message != WM_QUIT)
{
static float fLastTime = timeGetTime(); //上帧渲染时间
static float fCurrTime = timeGetTime(); //此帧渲染时间
static float fTimeDelta = 0.0f;
fCurrTime = timeGetTime();
fTimeDelta = (fCurrTime - fLastTime) / 1000.f; //上帧到此帧经过的时间
fLastTime = fCurrTime; //把此次渲染时间赋给上次渲染时间
if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
Direct3D_Update(fTimeDelta);
Direct3D_Render(hWnd, fTimeDelta);
}
}
UnregisterClass(L"3DGameBase", wndClass.hInstance);
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_PAINT:
ValidateRect(hWnd, NULL); //使窗口区域生效
break;
case WM_KEYDOWN:
if(wParam == VK_ESCAPE)
DestroyWindow(hWnd);
break;
case WM_DESTROY:
//调用自定义的资源清理函数Direct3D_ClearUp();进行退出前的资源清理
Direct3D_ClearUp();
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
//---------------------------------------------【Direct3D_Init()函数】-----------------------------------------
//描述:Direct3D初始化函数,进行Direct3D的初始化
//---------------------------------------------------------------------------------------------------------------
HRESULT Direct3D_Init(HWND hWnd)
{
//---------------------------------------------------------------------------------------------------------------
//【Direct3D初始化步骤一】:创建Direct3D接口对象,以便用该Direct3D对象创建Direct3D设备对象
//---------------------------------------------------------------------------------------------------------------
LPDIRECT3D9 pD3D = NULL; //Direct3D接口对象的创建。
if((pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL) //初始化Direct3D接口对象,并进行DirectX版本协商。
return E_FAIL;
//---------------------------------------------------------------------------------------------------------------
//【Direct3D初始化步骤二】:获取硬件设备信息
//---------------------------------------------------------------------------------------------------------------
D3DCAPS9 caps;
int vp = 0;
if(FAILED(pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps)))
return E_FAIL;
if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; //支持硬件顶点运算,采用硬件顶点运算
else
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //不支持硬件顶点运算,采用软件顶点运算
//---------------------------------------------------------------------------------------------------------------
//【Direct3D初始化步骤三】:填充D3DPRESENT_PARAMETERS结构体
//---------------------------------------------------------------------------------------------------------------
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.BackBufferWidth = WINDOW_WIDTH;
d3dpp.BackBufferHeight = WINDOW_HEIGHT;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.BackBufferCount = 1;
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dpp.MultiSampleQuality = 0;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hWnd;
d3dpp.Windowed = true;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz = 0;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
//---------------------------------------------------------------------------------------------------------------
//【Direct3D初始化步骤四】:创建Direct3D设备接口。
//---------------------------------------------------------------------------------------------------------------
if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, vp, &d3dpp, &g_pd3dDevice)))
return E_FAIL;
//获取显卡信息到g_strAdapterName中,并在显卡名称前加上“当前显卡型号:”字符串
//定义一个D3DADAPTER_IDENTIFIER9结构体,用于存储显卡信息
D3DADAPTER_IDENTIFIER9 Adapter;
//调用GetAdapterIdentifier,获取显卡信息
if(FAILED(pD3D->GetAdapterIdentifier(0, 0, &Adapter)))
return E_FAIL;
//显卡名称现在已经在Adapter.Description中了,但是其为char类型,我们要将其转为wchar_t类型
int len = MultiByteToWideChar(CP_ACP, 0, Adapter.Description, -1, NULL, 0);
//这步操作完成后,g_strAdapterName中就为当前我们的显卡类型名的wchar_t型字符串了
MultiByteToWideChar(CP_ACP, 0, Adapter.Description, -1, g_strAdapterName, len);
//定义一个临时字符串,且方便了把"当前显卡型号:"字符串引入我们的目的字符串中
wchar_t tempName[50] = L"当前显卡型号:";
//把当前我们的显卡名加到“当前显卡型号:”字符串后面,结果存在TempName中
wcscat_s(tempName, g_strAdapterName);
//把TempName中的结果拷贝到全局变量g_strAdapterName中
wcscpy_s(g_strAdapterName, tempName);
SAFE_RELEASE(pD3D); //LPDIRECT3D9接口对象的使命完成,将其释放掉
if(FAILED(Objects_Init(hWnd))) // 调用一次Objects_Init,进行渲染资源的初始化
return E_FAIL;
return S_OK;
}
//------------------------------------------【Objects_Init()】函数---------------------------------------------
//描述:渲染资源初始化函数,在此函数中进行要被渲染的物体的资源的初始化
//---------------------------------------------------------------------------------------------------------------
HRESULT Objects_Init(HWND hWnd)
{
//创建字体
if(FAILED(D3DXCreateFont(g_pd3dDevice, 36, 0, 0, 1000, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
DEFAULT_QUALITY, 0, L"Calibri", &g_pTextFPS)))
return E_FAIL;
if(FAILED(D3DXCreateFont(g_pd3dDevice, 20, 0, 0, 1000, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
DEFAULT_QUALITY, 0, L"华文中宋", &g_pTextAdapterName)))
return E_FAIL;
if(FAILED(D3DXCreateFont(g_pd3dDevice, 23, 0, 0, 1000, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
DEFAULT_QUALITY, 0, L"微软雅黑", &g_pTextHelper)))
return E_FAIL;
if(FAILED(D3DXCreateFont(g_pd3dDevice, 26, 0, 0, 1000, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
DEFAULT_QUALITY, 0, L"黑体", &g_pTextInfor)))
return E_FAIL;
//创建并初始化虚拟摄像机
g_pCamera = new CameraClass(g_pd3dDevice);
g_pCamera->SetCameraPosition(&D3DXVECTOR3(0.0f, 300.0f, -800.0f)); //设置摄像机所在的位置
g_pCamera->SetTargetPosition(&D3DXVECTOR3(0.0f, 400.0f, 0.0f)); //设置目标观察点所在的位置
g_pCamera->SetViewMatrix(); //设置取景变换矩阵
g_pCamera->SetProjMatrix();
//创建并初始化天空对象
g_pSkyBox = new SkyBoxClass(g_pd3dDevice);
//从文件加载前,后,左,右,上的纹理图
g_pSkyBox->LoadSkyTextureFromFile(L"GameMedia\\frontaw2.jpg", L"GameMedia\\backaw2.jpg",
L"GameMedia\\leftaw2.jpg", L"GameMedia\\rightaw2.jpg", L"GameMedia\\topaw2.jpg");
//设置天空盒的边长
g_pSkyBox->InitSkyBox(50000);
//创建并初始化雪花粒子系统
g_pSnowParticle = new SnowParticleClass(g_pd3dDevice);
g_pSnowParticle->InitSnowParticle();
//设置投影变换矩阵
//以下这段代码用于限制鼠标光标移动区域
POINT lt, rb;
RECT rect;
GetClientRect(hWnd, &rect);//取得窗口内部矩形
//将矩形左上点坐标存入lt中
lt.x = rect.left;
lt.y = rect.top;
//将矩形右下坐标存入rb中
rb.x = rect.right;
rb.y = rect.bottom;
//将lt和rb的窗口坐标转换为屏幕坐标
ClientToScreen(hWnd, <);
ClientToScreen(hWnd, &rb);
//以屏幕坐标重新设定矩形区域
rect.left = lt.x;
rect.top = lt.y;
rect.right = rb.x;
rect.bottom = rb.y;
//限制鼠标光标移动区域
ClipCursor(&rect);
ShowCursor(false);
// 设置光照
D3DLIGHT9 light;
light.Type = D3DLIGHT_DIRECTIONAL;
light.Ambient = D3DXCOLOR(0.7f, 0.7f, 0.7f, 1.0f);
light.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
light.Specular = D3DXCOLOR(0.9f, 0.9f, 0.9f, 1.0f);
light.Direction = D3DXVECTOR3(1.0f, 1.0f, 1.0f);
g_pd3dDevice->SetLight(0, &light);
g_pd3dDevice->LightEnable(0, true);
g_pd3dDevice->SetRenderState(D3DRS_NORMALIZENORMALS, true);
g_pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, true);
//创建地面顶点缓存
HRESULT hr;
HR(g_pd3dDevice->CreateVertexBuffer(4*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_MANAGED, &g_pFloorVBuffer, NULL));
CUSTOMVERTEX *pVertices = NULL;
HR(g_pFloorVBuffer->Lock(0, 4*sizeof(CUSTOMVERTEX), (void**)&pVertices, 0));
pVertices[0] = CUSTOMVERTEX(-5000.0f, 0.0f, -5000.0f, 0.0f, 30.0f);
pVertices[1] = CUSTOMVERTEX(-5000.0f, 0.0f, 5000.0f, 0.0f, 0.0f);
pVertices[2] = CUSTOMVERTEX( 5000.0f, 0.0f, -5000.0f, 30.0f, 30.0f);
pVertices[3] = CUSTOMVERTEX( 5000.0f, 0.0f, 5000.0f, 30.0f, 0.0f);
g_pFloorVBuffer->Unlock();
//创建地面纹理
HR(D3DXCreateTextureFromFile(g_pd3dDevice, L"GameMedia\\wood.jpg", &g_pFloorTexture));
g_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTEXF_LINEAR);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTEXF_LINEAR);
//创建并初始化动画
g_pAnimation = new AnimationClass(g_pd3dDevice);
g_pAnimation->InitAnimation(L"GameMedia\\tiny_4anim.x");
//创建动画实例1并设置播放动画名字
g_pAnimInstance1 = new AnimInstanceClass(g_pAnimation);
g_pAnimInstance1->SetAnimationByName("Walk");
//创建动画实例2并设置播放动画名字
g_pAnimInstance2 = new AnimInstanceClass(g_pAnimation);
g_pAnimInstance2->SetAnimationByName("Loiter");
return S_OK;
}
void Direct3D_Update(float fTimeDelta)
{
//使用DirectInput类读取数据
g_pDInput->GetInput();
// 沿摄像机各分量移动视角
if (g_pDInput->IsKeyDown(DIK_A)) g_pCamera->MoveAlongRightVec(-3.0f);
if (g_pDInput->IsKeyDown(DIK_D)) g_pCamera->MoveAlongRightVec(3.0f);
if (g_pDInput->IsKeyDown(DIK_W)) g_pCamera->MoveAlongLookVec( 3.0f);
if (g_pDInput->IsKeyDown(DIK_S)) g_pCamera->MoveAlongLookVec(-3.0f);
if (g_pDInput->IsKeyDown(DIK_R)) g_pCamera->MoveAlongUpVec( 3.0f);
if (g_pDInput->IsKeyDown(DIK_F)) g_pCamera->MoveAlongUpVec(-3.0f);
//沿摄像机各分量旋转视角
if (g_pDInput->IsKeyDown(DIK_LEFT)) g_pCamera->RotationUpVec(-0.003f);
if (g_pDInput->IsKeyDown(DIK_RIGHT)) g_pCamera->RotationUpVec( 0.003f);
if (g_pDInput->IsKeyDown(DIK_UP)) g_pCamera->RotationRightVec(-0.003f);
if (g_pDInput->IsKeyDown(DIK_DOWN)) g_pCamera->RotationRightVec( 0.003f);
if (g_pDInput->IsKeyDown(DIK_Q)) g_pCamera->RotationLookVec(0.001f);
if (g_pDInput->IsKeyDown(DIK_E)) g_pCamera->RotationLookVec( -0.001f);
//鼠标控制右向量和上向量的旋转
g_pCamera->RotationUpVec(g_pDInput->MouseDX()* 0.001f);
g_pCamera->RotationRightVec(g_pDInput->MouseDY() * 0.001f);
//计算并设置取景变换矩阵
g_pCamera->SetViewMatrix();
//鼠标滚轮控制观察点收缩操作
static float fPosZ = 0.0f;
fPosZ += g_pDInput->MouseDZ() * 0.03f;
//把正确的世界变换矩阵存到g_matWorld中
D3DXMatrixTranslation(&g_matWorld, 0, 0, fPosZ);
//设置实例1的各个动画
if (g_pDInput->IsKeyDown(DIK_1))
g_pAnimInstance1->SetAnimationByName("Walk");
if (g_pDInput->IsKeyDown(DIK_2))
g_pAnimInstance1->SetAnimationByName("Loiter");
if (g_pDInput->IsKeyDown(DIK_3))
g_pAnimInstance1->SetAnimationByName("Jog");
if (g_pDInput->IsKeyDown(DIK_4))
g_pAnimInstance1->SetAnimationByName("Wave");
//设置实例2的各个动画
if (g_pDInput->IsKeyDown(DIK_5))
g_pAnimInstance2->SetAnimationByName("Walk");
if (g_pDInput->IsKeyDown(DIK_6))
g_pAnimInstance2->SetAnimationByName("Loiter");
if (g_pDInput->IsKeyDown(DIK_7))
g_pAnimInstance2->SetAnimationByName("Jog");
if (g_pDInput->IsKeyDown(DIK_8))
g_pAnimInstance2->SetAnimationByName("Wave");
//设置骨骼动画实例1的矩阵
D3DXMATRIX matFinal , matRotX, matRotY;
D3DXMatrixIdentity(&matFinal);
D3DXMatrixRotationX(&matRotX, -D3DX_PI / 2.0f);
D3DXMatrixRotationY(&matRotY, -D3DX_PI);
matFinal = matRotX * matRotY;
g_pAnimInstance1->SetAnimationMatrix(&matFinal);
//设置骨骼动画实例1的矩阵
D3DXMatrixTranslation(&matFinal, 400, 200, 0);
matFinal = matRotX * matRotY * matFinal;
g_pAnimInstance2->SetAnimationMatrix(&matFinal);
}
//----------------------------------------【Direct3D_Render()函数】--------------------------------------------
//描述:使用Direct3D进行渲染
//---------------------------------------------------------------------------------------------------------------
void Direct3D_Render(HWND hWnd, float fTimeDelta)
{
//---------------------------------------------------------------------------------------------------------------
//【Direct3D渲染步骤一】:清屏操作
//---------------------------------------------------------------------------------------------------------------
g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 108, 255), 1.0f, 0);
//---------------------------------------------------------------------------------------------------------------
//【Direct3D渲染步骤二】:开始绘制
//---------------------------------------------------------------------------------------------------------------
g_pd3dDevice->BeginScene(); //开始绘制
//---------------------------------------------------------------------------------------------------------------
//【Direct3D渲染步骤三】:正式绘制
//---------------------------------------------------------------------------------------------------------------
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE); //关闭光照
//更新并绘制骨骼动画实例1
g_pAnimInstance1->RenderAnimation(fTimeDelta);
//更新并绘制骨骼动画实例2
g_pAnimInstance2->RenderAnimation(fTimeDelta);
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE); //开启光照
//绘制天空
D3DXMATRIX matSky,matTransSky,matRotSky;
D3DXMatrixTranslation(&matTransSky,0.0f,-13000.0f,0.0f);
D3DXMatrixRotationY(&matRotSky, -0.00002f*timeGetTime()); //旋转天空网格, 简单模拟云彩运动效果
matSky=matTransSky*matRotSky;
g_pSkyBox->RenderSkyBox(&matSky);
//绘制地板
D3DXMATRIX matFloor;
D3DXMatrixTranslation(&matFloor, 0.0f, 0.0f, 0.0f);
g_pd3dDevice->SetTransform(D3DTS_WORLD, &matFloor);
g_pd3dDevice->SetStreamSource(0, g_pFloorVBuffer, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->SetTexture(0, g_pFloorTexture);
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
//绘制雪花粒子系统
g_pSnowParticle->UpdateSnowParticle(fTimeDelta);
g_pSnowParticle->RenderSnowParticle();
//绘制文字信息
HelpText_Render(hWnd);
//---------------------------------------------------------------------------------------------------------------
//【Direct3D渲染步骤四】:结束绘制
//---------------------------------------------------------------------------------------------------------------
g_pd3dDevice->EndScene(); //结束绘制
//---------------------------------------------------------------------------------------------------------------
//【Direct3D渲染步骤五】:显示翻转
//---------------------------------------------------------------------------------------------------------------
g_pd3dDevice->Present(NULL, NULL, NULL, NULL); //翻转与显示
}
void HelpText_Render(HWND hWnd)
{
//定义一个矩形,用来获取主窗口矩形
RECT formatRect;
GetClientRect(hWnd, &formatRect);
//在窗口右上角处,显示每秒帧数
swprintf_s(g_strFPS, L"FPS:%.3f", Get_FPS());
g_pTextFPS->DrawText(0, g_strFPS, -1, &formatRect, DT_TOP | DT_RIGHT, D3DCOLOR_XRGB(255,255,255));
//显示显卡类型名
g_pTextAdapterName->DrawText(0, g_strAdapterName, -1, &formatRect, DT_TOP | DT_LEFT, D3DXCOLOR(1.0f, 0.5f, 0.0f, 1.0f));
// 输出帮助信息
formatRect.left = 0,formatRect.top = 380;
g_pTextInfor->DrawText(NULL, L"控制说明:", -1, &formatRect,
DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(235,123,230,255));
formatRect.top += 35;
g_pTextHelper->DrawText(NULL, L" W:向前飞翔 S:向后飞翔 ", -1, &formatRect,
DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));
formatRect.top += 25;
g_pTextHelper->DrawText(NULL, L" A:向左飞翔 D:向右飞翔", -1, &formatRect,
DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));
formatRect.top += 25;
g_pTextHelper->DrawText(NULL, L" R:垂直向上飞翔 F:垂直向下飞翔", -1, &formatRect,
DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));
formatRect.top += 25;
g_pTextHelper->DrawText(NULL, L" Q:向左倾斜 E:向右倾斜", -1, &formatRect,
DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));
formatRect.top += 25;
g_pTextHelper->DrawText(NULL, L" 上、下、左、右方向键、鼠标移动:视角变化 ", -1, &formatRect,
DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));
formatRect.top += 25;
g_pTextHelper->DrawText(NULL, L" 鼠标滚轮:人物模型Y轴方向移动", -1, &formatRect,
DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));
formatRect.top += 25;
g_pTextHelper->DrawText(NULL, L" ESC键 : 退出程序", -1, &formatRect,
DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));
}
//-----------------------------------------【Get_FPS()函数】---------------------------------------------------
//描述:用于计算帧频率
//---------------------------------------------------------------------------------------------------------------
float Get_FPS()
{
//定义四个静态变量
static int frameCount = 0; //帧数
static float currentTime = 0; //当前时间
static float lastTime = 0; //上次计算帧频率的时间
static float fps = 0; //需要计算的fps值
++frameCount; //每调用一次此函数,帧数加一
//获取系统时间, timeGetTime() 返回系统时间,以毫秒为单位,乘以0.001得到秒
currentTime = timeGetTime() * 0.001f;
//如果当前时间减去之前计算帧频率的时间大于1秒钟,就进行帧频率的更新,并将帧数归零
if(currentTime - lastTime > 1.0f) //将时间控制在1秒钟
{
fps = frameCount / (currentTime - lastTime); //计算这一秒的fps值
frameCount = 0; //将本次帧数清零
lastTime = currentTime; //将当前时间赋给上次计算帧频率的时间,作为下一秒的基准时间
}
return fps;
}
//------------------------------------------------【 Direct3D_ClearUp函数】------------------------------------------
//描述:资源清理函数,在此函数中进行程序退出前资源的清理工作
//-------------------------------------------------------------------------------------------------------------------
void Direct3D_ClearUp()
{
//释放COM接口对象
SAFE_RELEASE(g_pd3dDevice);
SAFE_RELEASE(g_pTextFPS);
SAFE_RELEASE(g_pTextHelper);
SAFE_RELEASE(g_pTextAdapterName);
SAFE_RELEASE(g_pTextInfor);
SAFE_DELETE(g_pDInput);
SAFE_DELETE(g_pCamera);
SAFE_DELETE(g_pSkyBox);
SAFE_DELETE(g_pSnowParticle);
SAFE_RELEASE(g_pFloorVBuffer);
SAFE_RELEASE(g_pFloorTexture);
SAFE_DELETE(g_pAnimation);
SAFE_DELETE(g_pAnimInstance1);
SAFE_DELETE(g_pAnimInstance2);
}