【游戏程序设计】Direct3D 骨骼动画

运行结果:

源代码:

AllocateHierarchy.h:

#pragma once

//=============================================================================
// Desc: CAllocateHierarchy.h
// 微软官方DirectX SDK Samples中的骨骼动画类
//=============================================================================

#include <d3d9.h>
#include <d3dx9.h>
#include "D3DUtil.h"


//-----------------------------------------------------------------------------
// Name: struct D3DXFRAME_DERIVED
// Desc: 继承自DXDXFRAME结构的结构
//-----------------------------------------------------------------------------
struct D3DXFRAME_DERIVED: public D3DXFRAME
{
	D3DXMATRIXA16 CombinedTransformationMatrix;
};


//-----------------------------------------------------------------------------
// Name: struct D3DXMESHCONTAINER_DERIVED
// Desc: 继承自D3DXMESHCONTAINER结构的结构
//-----------------------------------------------------------------------------
struct D3DXMESHCONTAINER_DERIVED: public D3DXMESHCONTAINER
{
	LPDIRECT3DTEXTURE9*  ppGameMedia;            //纹理数组
	LPD3DXMESH           pOrigMesh;             //原始网格
	LPD3DXATTRIBUTERANGE pAttributeTable;
	DWORD                NumAttributeGroups;    //属性组数量,即子网格数量
	DWORD                NumInfl;               //每个顶点最多受多少骨骼的影响
	LPD3DXBUFFER         pBoneCombinationBuf;   //骨骼结合表
	D3DXMATRIX**         ppBoneMatrixPtrs;      //存放骨骼的组合变换矩阵
	D3DXMATRIX*          pBoneOffsetMatrices;   //存放骨骼的初始变换矩阵
	DWORD                NumPaletteEntries;     //骨骼数量上限
	bool                 UseSoftwareVP;         //标识是否使用软件顶点处理
};


//-----------------------------------------------------------------------------
// Name: class CAllocateHierarchy
// Desc: 来自微软官方DirectX SDK Samples中的骨骼动画类,这个类用来从.X文件加载框架层次和网格模型数据
// 核心点:      #define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method 
//-----------------------------------------------------------------------------
class CAllocateHierarchy: public ID3DXAllocateHierarchy
{
public:
	STDMETHOD(CreateFrame)(THIS_ LPCSTR Name, LPD3DXFRAME *ppNewFrame);
	STDMETHOD(CreateMeshContainer)( THIS_ LPCSTR              Name, 
		CONST D3DXMESHDATA*       pMeshData,
		CONST D3DXMATERIAL*       pMaterials, 
		CONST D3DXEFFECTINSTANCE* pEffectInstances, 
		DWORD                     NumMaterials, 
		CONST DWORD *             pAdjacency, 
		LPD3DXSKININFO pSkinInfo, 
		LPD3DXMESHCONTAINER *ppNewMeshContainer);    
	STDMETHOD(DestroyFrame)(THIS_ LPD3DXFRAME pFrameToFree);
	STDMETHOD(DestroyMeshContainer)(THIS_ LPD3DXMESHCONTAINER pMeshContainerBase);
};




//-----------------------------------------------------------------------------
// Desc: 来自微软官方DirectX SDK Samples中的骨骼动画全局函数
//-----------------------------------------------------------------------------
void DrawFrame( IDirect3DDevice9* pd3dDevice, LPD3DXFRAME pFrame );
void DrawMeshContainer( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase );
HRESULT SetupBoneMatrixPointers( LPD3DXFRAME pFrameBase, LPD3DXFRAME pFrameRoot );
void UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix );

AllocateHierarchy.cpp:

//=============================================================================
// Desc: AllocateHierarchyClass.cpp
// 微软官方DirectX SDK Samples中的骨骼动画类
//=============================================================================

#include "AllocateHierarchy.h"

//--------------------------------------------------------------------------------------
// Name: AllocateName()
// Desc: 为骨骼或网格名称的字符串分配内存
//--------------------------------------------------------------------------------------
HRESULT AllocateName( LPCSTR Name, LPSTR* pNewName )
{
	UINT cbLength;

	if( Name != NULL )
	{
		cbLength = ( UINT )strlen( Name ) + 1;
		*pNewName = new CHAR[cbLength];
		memcpy( *pNewName, Name, cbLength * sizeof( CHAR ) );
	}
	else
	{
		*pNewName = NULL;
	}

	return S_OK;
}

//--------------------------------------------------------------------------------------
// Name: GenerateSkinnedMesh
// Desc: 生成蒙皮网格模型。该函数判断当前网格容器是否包含有蒙皮信息,如果当前网格模型
//       中不包含蒙皮信息,则直接退出该函数。接下来确定所需要的矩阵调色板容量。最后调
//       用ID3DXSkinInfo::ConvertToIndexedBlendedMesh()函数生成索引蒙皮网格模型
//--------------------------------------------------------------------------------------
HRESULT GenerateSkinnedMesh( IDirect3DDevice9* pd3dDevice, D3DXMESHCONTAINER_DERIVED* pMeshContainer )
{
	D3DCAPS9 d3dCaps;
	pd3dDevice->GetDeviceCaps( &d3dCaps );

	if( pMeshContainer->pSkinInfo == NULL )
		return S_OK;

	SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
	SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );


	if (FAILED(pMeshContainer->pSkinInfo->ConvertToBlendedMesh(
		pMeshContainer->pOrigMesh,								//原始网格 
		D3DXMESH_MANAGED | D3DXMESHOPT_VERTEXCACHE,
		pMeshContainer->pAdjacency,
		NULL, NULL, NULL,
		&pMeshContainer->NumInfl,
		&pMeshContainer->NumAttributeGroups,
		&pMeshContainer->pBoneCombinationBuf,
		&pMeshContainer->MeshData.pMesh)))
		return E_FAIL;
	return S_OK;
}



//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::CreateFrame()
// Desc: 创建框架, 仅仅是分配内存和初始化,还没有对其成员赋予合适的值
//--------------------------------------------------------------------------------------
HRESULT CAllocateHierarchy::CreateFrame( LPCSTR Name, LPD3DXFRAME* ppNewFrame )
{
	HRESULT hr = S_OK;
	D3DXFRAME_DERIVED *pFrame;

	*ppNewFrame = NULL;

	// 为框架指定名称
	pFrame = new D3DXFRAME_DERIVED;  // 创建框架结构对象
	if (FAILED(AllocateName(Name, (LPSTR*)&pFrame->Name)))
	{
		delete pFrame;
		return hr;
	}

	// 初始化D3DXFRAME_DERIVED结构其它成员变量
	D3DXMatrixIdentity(&pFrame->TransformationMatrix);
	D3DXMatrixIdentity(&pFrame->CombinedTransformationMatrix);

	pFrame->pMeshContainer   = NULL;
	pFrame->pFrameSibling    = NULL;
	pFrame->pFrameFirstChild = NULL;

	*ppNewFrame = pFrame;
	pFrame = NULL;

	return hr;
}


//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::CreateMeshContainer()
// Desc: 创建蒙皮网格容器,以加载蒙皮信息
//--------------------------------------------------------------------------------------
HRESULT CAllocateHierarchy::CreateMeshContainer( LPCSTR Name,
	CONST D3DXMESHDATA *pMeshData,
	CONST D3DXMATERIAL *pMaterials,
	CONST D3DXEFFECTINSTANCE *pEffectInstances,
	DWORD NumMaterials,
	CONST DWORD *pAdjacency,
	LPD3DXSKININFO pSkinInfo,
	LPD3DXMESHCONTAINER *ppNewMeshContainer )
{
	HRESULT hr;
	UINT NumFaces;
	UINT iMaterial;
	UINT iBone, cBones;
	LPDIRECT3DDEVICE9 pd3dDevice = NULL;
	D3DXMESHCONTAINER_DERIVED *pMeshContainer = NULL;

	LPD3DXMESH pMesh = NULL;

	*ppNewMeshContainer = NULL;

	// this sample does not handle patch meshes, so fail when one is found
	if( pMeshData->Type != D3DXMESHTYPE_MESH )
	{
		hr = E_FAIL;
		goto e_Exit;
	}

	// get the pMesh interface pointer out of the mesh data structure
	pMesh = pMeshData->pMesh;

	// this sample does not FVF compatible meshes, so fail when one is found
	if( pMesh->GetFVF() == 0 )
	{
		hr = E_FAIL;
		goto e_Exit;
	}

	// allocate the overloaded structure to return as a D3DXMESHCONTAINER
	pMeshContainer = new D3DXMESHCONTAINER_DERIVED;
	memset( pMeshContainer, 0, sizeof( D3DXMESHCONTAINER_DERIVED ) );

	// make sure and copy the name.  All memory as input belongs to caller, interfaces can be addref'd though
	hr = AllocateName( Name, &pMeshContainer->Name );
	if( FAILED( hr ) )
		goto e_Exit;

	pMesh->GetDevice( &pd3dDevice );
	NumFaces = pMesh->GetNumFaces();

	// if no normals are in the mesh, add them
	if( !( pMesh->GetFVF() & D3DFVF_NORMAL ) )
	{
		pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;

		// clone the mesh to make room for the normals
		hr = pMesh->CloneMeshFVF( pMesh->GetOptions(), pMesh->GetFVF() | D3DFVF_NORMAL,
			pd3dDevice, &pMeshContainer->MeshData.pMesh );
		if( FAILED( hr ) )
			goto e_Exit;

		// get the new pMesh pointer back out of the mesh container to use
		// NOTE: we do not release pMesh because we do not have a reference to it yet
		pMesh = pMeshContainer->MeshData.pMesh;

		// now generate the normals for the pmesh
		D3DXComputeNormals( pMesh, NULL );
	}
	else  // if no normals, just add a reference to the mesh for the mesh container
	{
		pMeshContainer->MeshData.pMesh = pMesh;
		pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;

		pMesh->AddRef();
	}

	// allocate memory to contain the material information.  This sample uses
	//   the D3D9 materials and texture names instead of the EffectInstance style materials
	pMeshContainer->NumMaterials = max( 1, NumMaterials );
	pMeshContainer->pMaterials = new D3DXMATERIAL[pMeshContainer->NumMaterials];
	pMeshContainer->ppGameMedia = new LPDIRECT3DTEXTURE9[pMeshContainer->NumMaterials];		//纹理数组
	pMeshContainer->pAdjacency = new DWORD[NumFaces*3];

	memcpy( pMeshContainer->pAdjacency, pAdjacency, sizeof( DWORD ) * NumFaces*3 );
	memset( pMeshContainer->ppGameMedia, 0, sizeof( LPDIRECT3DTEXTURE9 ) * pMeshContainer->NumMaterials );	//纹理数组

	// if materials provided, copy them
	if( NumMaterials > 0 )
	{
		memcpy( pMeshContainer->pMaterials, pMaterials, sizeof( D3DXMATERIAL ) * NumMaterials );

		for( iMaterial = 0; iMaterial < NumMaterials; iMaterial++ )
		{
			if( pMeshContainer->pMaterials[iMaterial].pTextureFilename != NULL )
			{
				if( FAILED( D3DXCreateTextureFromFileA( pd3dDevice, pMeshContainer->pMaterials[iMaterial].pTextureFilename,
					&pMeshContainer->ppGameMedia[iMaterial] ) ) )
					pMeshContainer->ppGameMedia[iMaterial] = NULL;

				// don't remember a pointer into the dynamic memory, just forget the name after loading
				pMeshContainer->pMaterials[iMaterial].pTextureFilename = NULL;
			}
		}
	}
	else // if no materials provided, use a default one
	{
		pMeshContainer->pMaterials[0].pTextureFilename = NULL;
		memset( &pMeshContainer->pMaterials[0].MatD3D, 0, sizeof( D3DMATERIAL9 ) );
		pMeshContainer->pMaterials[0].MatD3D.Diffuse.r = 0.5f;
		pMeshContainer->pMaterials[0].MatD3D.Diffuse.g = 0.5f;
		pMeshContainer->pMaterials[0].MatD3D.Diffuse.b = 0.5f;
		pMeshContainer->pMaterials[0].MatD3D.Specular = pMeshContainer->pMaterials[0].MatD3D.Diffuse;
	}

	// if there is skinning information, save off the required data and then setup for HW skinning
	if( pSkinInfo != NULL )
	{
		// first save off the SkinInfo and original mesh data
		pMeshContainer->pSkinInfo = pSkinInfo;
		pSkinInfo->AddRef();

		pMeshContainer->pOrigMesh = pMesh;				//原始网格
		pMesh->AddRef();

		// Will need an array of offset matrices to move the vertices from the figure space to the bone's space
		cBones = pSkinInfo->GetNumBones();
		pMeshContainer->pBoneOffsetMatrices = new D3DXMATRIX[cBones];

		// get each of the bone offset matrices so that we don't need to get them later
		for( iBone = 0; iBone < cBones; iBone++ )
		{
			pMeshContainer->pBoneOffsetMatrices[iBone] = *( pMeshContainer->pSkinInfo->GetBoneOffsetMatrix( iBone ) );
		}

		// GenerateSkinnedMesh will take the general skinning information and transform it to a HW friendly version
		hr = GenerateSkinnedMesh( pd3dDevice, pMeshContainer );
		if( FAILED( hr ) )
			goto e_Exit;
	}

	*ppNewMeshContainer = pMeshContainer;
	pMeshContainer = NULL;

e_Exit:
	SAFE_RELEASE( pd3dDevice );

	// call Destroy function to properly clean up the memory allocated 
	if( pMeshContainer != NULL )
	{
		DestroyMeshContainer( pMeshContainer );
	}

	return hr;
}

//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::DestroyFrame()
// Desc: 释放骨骼框架
//--------------------------------------------------------------------------------------
HRESULT CAllocateHierarchy::DestroyFrame( LPD3DXFRAME pFrameToFree )
{
	SAFE_DELETE_ARRAY( pFrameToFree->Name );
	SAFE_DELETE( pFrameToFree );
	return S_OK;
}

//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::DestroyMeshContainer()
// Desc: 释放网格容器
//--------------------------------------------------------------------------------------
HRESULT CAllocateHierarchy::DestroyMeshContainer( LPD3DXMESHCONTAINER pMeshContainerBase )
{
	UINT iMaterial;
	D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;

	SAFE_DELETE_ARRAY( pMeshContainer->Name );
	SAFE_DELETE_ARRAY( pMeshContainer->pAdjacency );
	SAFE_DELETE_ARRAY( pMeshContainer->pMaterials );
	SAFE_DELETE_ARRAY( pMeshContainer->pBoneOffsetMatrices );

	// release all the allocated GameMedia
	if( pMeshContainer->ppGameMedia != NULL )
	{
		for( iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++ )
		{
			SAFE_RELEASE( pMeshContainer->ppGameMedia[iMaterial] );
		}
	}

	SAFE_DELETE_ARRAY( pMeshContainer->ppGameMedia );
	SAFE_DELETE_ARRAY( pMeshContainer->ppBoneMatrixPtrs );
	SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );
	SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
	SAFE_RELEASE( pMeshContainer->pSkinInfo );
	SAFE_RELEASE( pMeshContainer->pOrigMesh );		//原始网格
	SAFE_DELETE( pMeshContainer );
	return S_OK;
}

//--------------------------------------------------------------------------------------
// Name: SetupBoneMatrixPointers()
// Desc: 设置好各级框架的组合变换矩阵。
//--------------------------------------------------------------------------------------
HRESULT SetupBoneMatrixPointers( LPD3DXFRAME pFrameBase, LPD3DXFRAME pFrameRoot )
{
	if( pFrameBase->pMeshContainer != NULL )
	{
		D3DXFRAME_DERIVED* pFrame = NULL;
		D3DXMESHCONTAINER_DERIVED* pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pFrameBase->pMeshContainer;

		// if there is a skinmesh, then setup the bone matrices
		if (pMeshContainer->pSkinInfo != NULL)
		{
			UINT cBones = pMeshContainer->pSkinInfo->GetNumBones();
			pMeshContainer->ppBoneMatrixPtrs = new D3DXMATRIX*[cBones];
			for (UINT iBone = 0; iBone < cBones; iBone++)
			{
				pFrame = (D3DXFRAME_DERIVED*)D3DXFrameFind(pFrameRoot, pMeshContainer->pSkinInfo->GetBoneName(iBone));
				if (pFrame == NULL) return E_FAIL;

				pMeshContainer->ppBoneMatrixPtrs[iBone] = &pFrame->CombinedTransformationMatrix;
			}
		}
	}

	if (pFrameBase->pFrameSibling != NULL)
	{
		if (FAILED(SetupBoneMatrixPointers(pFrameBase->pFrameSibling, pFrameRoot)))
			return E_FAIL;
	}

	if (pFrameBase->pFrameFirstChild != NULL)
	{
		if (FAILED(SetupBoneMatrixPointers(pFrameBase->pFrameFirstChild, pFrameRoot)))
			return E_FAIL;
	}

	return S_OK;
}

//--------------------------------------------------------------------------------------
// Name: DrawMeshContainer()
// Desc: 绘制蒙皮容器中的蒙皮网格
//--------------------------------------------------------------------------------------
void DrawMeshContainer( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase )
{
	D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;
	D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )pFrameBase;
	UINT iMaterial;
	UINT NumBlend;
	UINT iAttrib;
	DWORD AttribIdPrev;
	LPD3DXBONECOMBINATION pBoneComb;

	UINT iMatrixIndex;
	D3DXMATRIXA16 matTemp;
	D3DCAPS9 d3dCaps;
	pd3dDevice->GetDeviceCaps( &d3dCaps );

	// first check for skinning
	if( pMeshContainer->pSkinInfo != NULL )
	{
		AttribIdPrev = UNUSED32;
		pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>( pMeshContainer->pBoneCombinationBuf->GetBufferPointer() );

		// Draw using default vtx processing of the device (typically HW)
		for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )
		{
			NumBlend = 0;
			for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
			{
				if( pBoneComb[iAttrib].BoneId[i] != UINT_MAX )
				{
					NumBlend = i;
				}
			}

			if( d3dCaps.MaxVertexBlendMatrices >= NumBlend + 1 )
			{
				// first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends
				for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
				{
					iMatrixIndex = pBoneComb[iAttrib].BoneId[i];
					if( iMatrixIndex != UINT_MAX )
					{
						D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],
							pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
						pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp );
					}
				}

				pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend );

				// lookup the material used for this subset of faces
				if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) )
				{
					pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D );
					pd3dDevice->SetTexture( 0, pMeshContainer->ppGameMedia[pBoneComb[iAttrib].AttribId] );
					AttribIdPrev = pBoneComb[iAttrib].AttribId;
				}

				// draw the subset now that the correct material and matrices are loaded
				pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib );
			}
		}
		pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, 0 );
	}
	else  // standard mesh, just draw it after setting material properties
	{
		pd3dDevice->SetTransform( D3DTS_WORLD, &pFrame->CombinedTransformationMatrix );

		for( iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++ )
		{
			pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[iMaterial].MatD3D );
			pd3dDevice->SetTexture( 0, pMeshContainer->ppGameMedia[iMaterial] );
			pMeshContainer->MeshData.pMesh->DrawSubset( iMaterial );
		}
	}
}


//--------------------------------------------------------------------------------------
// Name: DrawFrame()
// Desc: 绘制骨骼
//--------------------------------------------------------------------------------------
void DrawFrame( IDirect3DDevice9* pd3dDevice, LPD3DXFRAME pFrame )
{
	if (pFrame == NULL) return;
	LPD3DXMESHCONTAINER pMeshContainer;
	pMeshContainer = pFrame->pMeshContainer;                    // 取得网格容器
	while( pMeshContainer != NULL )                      
	{
		DrawMeshContainer(pd3dDevice, pMeshContainer, pFrame);  // 绘制非空蒙皮网格
		pMeshContainer = pMeshContainer->pNextMeshContainer;    // 遍历所有网格容器
	}

	DrawFrame(pd3dDevice, pFrame->pFrameSibling);               // 绘制兄弟框架
	DrawFrame(pd3dDevice, pFrame->pFrameFirstChild);            // 绘制子框架
}

//--------------------------------------------------------------------------------------
// Name: UpdateFrameMatrics()
// Desc: 更新框架中的变换矩阵
//--------------------------------------------------------------------------------------
void UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix )
{
	if (pFrameBase == NULL || pParentMatrix == NULL) return;
	D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )pFrameBase;

	// 将当前骨骼的相对于父骨骼的偏移矩阵作累积运算
	D3DXMatrixMultiply(&pFrame->CombinedTransformationMatrix, &pFrame->TransformationMatrix, pParentMatrix);

	UpdateFrameMatrices(pFrame->pFrameSibling, pParentMatrix);                              // 更新兄弟骨骼
	UpdateFrameMatrices(pFrame->pFrameFirstChild, &pFrame->CombinedTransformationMatrix);   // 更新子骨骼
}

WinMain.cpp:

#include <d3d9.h>
#include <d3dx9.h>
#include <tchar.h>
#include "CameraClass.h"
#include "DirectInputClass.h"
#include "SkyBoxClass.h"
#include "SnowParticleClass.h"
#include "AllocateHierarchyClass.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;															//雪花粒子系统的指针
//四个和骨骼动画有关的全局变量
CAllocateHierarchy *g_pAllocateHier;
LPD3DXFRAME g_pFrameRoot;
D3DXMATRIX *g_pBoneMatrices;
LPD3DXANIMATIONCONTROLLER g_pAnimController;
LPDIRECT3DVERTEXBUFFER9 g_pFloorVBuffer;													//地板顶点缓存对象
LPDIRECT3DTEXTURE9 g_pFloorTexture;															//地板纹理对象

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"Textures\\frontaw2.jpg", L"Textures\\backaw2.jpg", 
					L"Textures\\leftaw2.jpg", L"Textures\\rightaw2.jpg", L"Textures\\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"Textures\\wood.jpg", &g_pFloorTexture));
	g_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTEXF_LINEAR);
	g_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTEXF_LINEAR);
	//创建骨骼动画
	g_pAllocateHier = new CAllocateHierarchy();
	D3DXLoadMeshHierarchyFromX(L"lxq.X", D3DXMESH_MANAGED, g_pd3dDevice,
		g_pAllocateHier, NULL, &g_pFrameRoot, &g_pAnimController);
	SetupBoneMatrixPointers(g_pFrameRoot, g_pFrameRoot);
	//因为这个X文件中仅有一个默认的舞剑动作,所以以下代码可用可不用 
	LPD3DXANIMATIONSET pAnimationSet = NULL;
	g_pAnimController->GetAnimationSetByName("sworddance", &pAnimationSet);
	g_pAnimController->SetTrackAnimationSet(1, pAnimationSet);

	return S_OK;
}

void Direct3D_Update(float fDeltaTime)
{
	//使用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);

	// 设置骨骼动画的矩阵
	D3DXMATRIX matFinal , matScal;
	D3DXMatrixIdentity(&matFinal);
	D3DXMatrixScaling(&matScal, 5.0f, 7.0f, 9.0f);
	matFinal = matScal *matFinal;

	g_pAnimController->AdvanceTime(fDeltaTime, NULL);	
	UpdateFrameMatrices(g_pFrameRoot, &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渲染步骤三】:正式绘制
	//---------------------------------------------------------------------------------------------------------------

	//绘制天空
	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);
	//绘制骨骼动画
	DrawFrame(g_pd3dDevice, g_pFrameRoot);

	//绘制雪花粒子系统
	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_RELEASE(g_pAnimController);
	SAFE_DELETE(g_pAllocateHier);
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值