1. 程式人生 > >【遊戲程式設計】Direct 3D 骨骼模型動畫進階:多動畫

【遊戲程式設計】Direct 3D 骨骼模型動畫進階:多動畫

執行結果:

通過鍵盤或者滑鼠來控制角色播放指定的動畫是遊戲中十分常見的功能,而我們之前的程式只能播放預設的一個動畫,而且每個動畫的例項都包含了資源,而我們繪製的時候需要資源,例項並不需要包含資源。我們只用指標指向那個資源就可以了。

我們可以首先對微軟官方的那個骨骼動畫檔案進行一次封裝。用來建立動畫例項。功能就是實現各種動畫的操作,比如根據名字播放骨骼動畫例項、渲染骨骼動畫等,並且儲存了動畫的資源。

原始碼:

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 <d3d9.h>
#include <d3dx9.h>
#include <tchar.h>
#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, &lt);
	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);
}