【Direct3D游戏开发】——蒙皮骨骼动画学习笔记

蒙皮骨骼动画

        一、蒙皮骨骼动画原理:蒙皮骨骼动画的思想来源于自然界,大多数动物都拥有一套骨骼,骨骼是身体的框架,身体的皮毛血肉都是依附于骨骼。当骨骼开始运动的时候,依附于对应骨骼的皮毛血肉都会随骨骼一起运动。而Mesh模型是由大量的三角形组成,三角形由定点组成。当我们运动时,有必要知道那些定点依附于那块骨骼,根据骨骼的移动来移动对应的顶点。
            蒙皮骨骼动画的加载:和Mesh模型的加载一样,骨骼加载时也是基于本地空间,我们需要把骨骼一块一块的从基于本地空间转换到整个Mesh模型空间。对于一套骨骼,有一个根(root)骨骼,这个根骨骼位于模型的一个定点,如坐标原点,其他骨骼都是直接或者间接与之联系。每根骨骼都有相对于其父母骨骼的相对位置(包括位移,和绕关节进行的转动),这种转换由一个矩阵完成:LocalTransformMatrix,这只是完成了骨骼的定位,要实现动画还需要对骨骼进行各种变换,如你所料,这肯定也是利用矩阵完成:CombinedTransformMatrix。当上层骨骼运动时,下层骨骼并不会主动运动,而是被牵连着运动,所以上层的运动会传递到下层骨骼,这个原理表示为:(子骨骼)CombinedTransformMatrix = (子骨骼)LocalTransformMatrix * (父母骨骼的)CombinedTransformMatrix.当然这中传递是渲染之前计算好的。
            关键结构体:Direct3d中一套骨骼是以树形结构存放的,有两个关键的结构体和一个接口。
                    1:typedef struct _D3DXFRAME
                            {
                                LPSTR                   Name;
                                D3DXMATRIX              TransformationMatrix;

                                LPD3DXMESHCONTAINER     pMeshContainer;

                                struct _D3DXFRAME       *pFrameSibling;
                                struct _D3DXFRAME       *pFrameFirstChild;
                            } D3DXFRAME, *LPD3DXFRAME;
                        这个结构体代表了一块骨骼,有一个标识的Name,变换矩阵TransformationMatrix对应于之前所说的LocalTransformMatrix,还有分别指向兄弟结点和第一个孩子结点的指针,最后是包含了一个指向D3DXMESHCONTAINER结构体的指针,这是第二个关键的结构体。
                    2:typedef struct _D3DXMESHCONTAINER
                            {
                                LPSTR                   Name;

                                D3DXMESHDATA            MeshData;

                                LPD3DXMATERIAL          pMaterials;
                                LPD3DXEFFECTINSTANCE    pEffects;
                                DWORD                   NumMaterials;
                                DWORD                  *pAdjacency;

                                LPD3DXSKININFO          pSkinInfo;

                                struct _D3DXMESHCONTAINER *pNextMeshContainer;
                            } D3DXMESHCONTAINER, *LPD3DXMESHCONTAINER;
                    对于这个结构体还是比较了解的,所以略。
                    3:ID3DXAllocateHierarchy 接口,这个接口只包含四个纯虚函数,我们需要去重写他们,这些函数无需我们自己调用,在我们使用D3DXLoadMeshHierarchyFromX()接口加载SkineMesh数据是自动调用。

                    4:D3DXLoadMeshHierarchyFromX  (
    LPCWSTR Filename,
    DWORD MeshOptions,
    LPDIRECT3DDEVICE9 pD3DDevice,
    LPD3DXALLOCATEHIERARCHY pAlloc,
    LPD3DXLOADUSERDATA pUserDataLoader, 
    LPD3DXFRAME *ppFrameHierarchy,
    LPD3DXANIMATIONCONTROLLER   *ppAnimController
    );
有几个关键的参数:pAlloc,这是一个指向3中所说的
ID3DXAllocateHierarchy 接口的指针;ppFrameHierarchy,骨骼层次,也就是一个代表骨骼的树,是一个返回值;ppAnimController,这是动画控制器,是一个返回值,利用这个动画控制器就可以对SkinMesh进行控制。
     蒙皮骨骼动画的渲染:渲染这部分比较简单,递归渲染每个MeshContainer,这样说可能很奇怪,因为每个骨骼结构体(D3DXFRAME)都包含了一个指向MeshContainer的指针,而每次渲染每个MeshContainer都是整副骨骼进行设置纹理和材质,然后就有问题了?这不是重复渲染了?百度,google和经自己调试发现,网上并没有找到标准答案,就是大多时候一个骨骼的MeshContainer指针都是空的,我在调试时候只有骨骼Name为空字符串时MeshContainer不为空,所以只渲染了一次,哦,我的模型是tiny.x。最后我估计:一套骨骼中也许只有一个或几个不为空的MeshContainer指针,通常是一个如果没必要分割渲染的话,递归只是为了寻找这个不为空的pMeshContainer.

笔记到此先告一段落,由于理解只在十分基本的层面,没法就很多细节进行分析,故在此只记录十分表面的东西,以后补足。PS:笔记主要参考《Direct3D游戏开发技术详解》,MSDN


参考:Direct3D游戏开发技术详解 和 SDK simple 简单的加载了tiny.x动画,只实现了SOFTWARE处理方式和走路的动画。

 

#include "CLD3D.h"
#include <stdio.h>

//常量
const wchar_t wndName[50] = L"蒙皮骨骼动画演示";
const wchar_t wndTitle[100] = L"【Direct3D】——蒙皮骨骼动画演示-coderling";
const wchar_t xFileName[20] = L"tiny.x";
const unsigned int wndWidth = 800;
const unsigned int wndHeight = 600;

//为每个骨骼添加一个控制矩阵
struct D3DXFRAME_EX: public D3DXFRAME
{
	D3DXMATRIXA16 combinedTransformtionMatrix; 
};

//保存蒙皮骨骼动画的结构体,包含网格模型
struct D3DXMESHCONTAINER_EX: public D3DXMESHCONTAINER
{
	LPDIRECT3DTEXTURE9* ppTextures; //指向纹理信息指针的指针数组

	//蒙皮骨骼信息
	LPD3DXMESH pOrigMesh;
	LPD3DXATTRIBUTERANGE pAttributeTable; //子集分布表格?
	DWORD numAttributeGroups;
	DWORD numInfl;
	LPD3DXBUFFER pBoneCombinatonBuf; //骨骼名字缓冲区
	D3DXMATRIX** ppBoneMatrixPtrs; //最终变换矩阵
	D3DXMATRIX* pBoneOffsetMatrices; //offset矩阵数组指针
	DWORD numPaletteEntries;
	//bool useSoftwareVP;


};

HRESULT GenerateSkinnedMesh( D3DXMESHCONTAINER_EX* );

//重写ID3DXAllocateHierarchy接口
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* pEffectInstance,
		DWORD numMaterials,
		CONST DWORD* pAdjacency,
		LPD3DXSKININFO pSkinInfo,
		LPD3DXMESHCONTAINER* ppNewMeshContainer );
	STDMETHOD(DestroyFrame)( THIS_ LPD3DXFRAME pFrameToFree );
	STDMETHOD(DestroyMeshContainer)( THIS_ LPD3DXMESHCONTAINER pMeshContainer );

};

//创建新的帧
HRESULT CAllocateHierarchy::CreateFrame( LPCSTR name, LPD3DXFRAME* ppNewFrame )
{
	HRESULT hr = S_OK;

	D3DXFRAME_EX *pFrame;

	*ppNewFrame = NULL;
	
	pFrame = new D3DXFRAME_EX;
	if( pFrame == NULL )
	{
		hr = E_OUTOFMEMORY;
		return hr;
	}

	if( name )
	{
		unsigned int len = strlen( name ) + 1;
		pFrame->Name = new char[len];
		memcpy( pFrame->Name, name, sizeof(char)*len );
	}

	//初始化frame
	D3DXMatrixIdentity( &pFrame->TransformationMatrix );
	D3DXMatrixIdentity( &pFrame->combinedTransformtionMatrix );

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

	*ppNewFrame = pFrame; //返回

	pFrame = NULL;

	return hr;
}


//创建新的meshcontainner
HRESULT CAllocateHierarchy::CreateMeshContainer(
	LPCSTR name,
	CONST D3DXMESHDATA* pMeshData,
	CONST D3DXMATERIAL* pMaterials,
	CONST D3DXEFFECTINSTANCE* pEffectInstances,
	DWORD numMaterials,
	CONST DWORD* pAdjacency,
	LPD3DXSKININFO pSkinInfo,
	LPD3DXMESHCONTAINER* ppNewMeshContainer )
{
	D3DXMESHCONTAINER_EX* pMeshContainer = NULL;
	unsigned int numFaces;
	unsigned int iMaterial;
	unsigned int iBone, cBones;
	LPDIRECT3DDEVICE9 pD3DDevice = NULL;

	LPD3DXMESH pMesh = NULL;
	
	*ppNewMeshContainer = NULL;

	if( pMeshData->Type != D3DXMESHTYPE_MESH )
		return E_FAIL;

	pMesh = pMeshData->pMesh;

	if( pMesh->GetFVF() == 0 )
		return E_FAIL;

	pMeshContainer = new D3DXMESHCONTAINER_EX;
	if( pMeshContainer == NULL )
		return E_FAIL;
	memset( pMeshContainer, 0, sizeof(D3DXMESHCONTAINER_EX) );

	if( name )
	{
		unsigned int len = strlen( name ) + 1;
		pMeshContainer->Name = new char[len];
		memcpy( pMeshContainer->Name, name, sizeof(char)*len );
	}

	pMesh->GetDevice( &pD3DDevice );
	numFaces = pMesh->GetNumFaces();

	//添加顶点法线
	if( !( pMesh->GetFVF() & D3DFVF_NORMAL ) )
	{
		pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;

		HRESULT hr = pMesh->CloneMeshFVF( pMesh->GetOptions(), pMesh->GetFVF() | D3DFVF_NORMAL,
										  pD3DDevice, &pMeshContainer->MeshData.pMesh );
		if( FAILED( hr ) )
			return E_FAIL;

		pMesh = pMeshContainer->MeshData.pMesh;

		D3DXComputeNormals( pMesh, NULL );

	}
	else
	{
		pMeshContainer->MeshData.pMesh = pMesh;
		pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;

		pMesh->AddRef(); //增加引用计数
	}

	pMeshContainer->NumMaterials = max( 1, numMaterials );
	pMeshContainer->pMaterials = new D3DXMATERIAL[pMeshContainer->NumMaterials];
	pMeshContainer->ppTextures = new LPDIRECT3DTEXTURE9[pMeshContainer->NumMaterials];
	pMeshContainer->pAdjacency = new DWORD[numFaces*3];
	if( (pMeshContainer->pAdjacency == NULL ) || ( pMeshContainer->pMaterials == NULL ) )
		return E_FAIL;

	memcpy( pMeshContainer->pAdjacency, pAdjacency, sizeof(DWORD) * numFaces*3 );
	memset( pMeshContainer->ppTextures, 0, sizeof(LPDIRECT3DTEXTURE9) * pMeshContainer->NumMaterials );

	if( numMaterials > 0 )
	{
		memcpy( pMeshContainer->pMaterials, pMaterials, sizeof(D3DXMATERIAL) * pMeshContainer->NumMaterials );
		
		for( iMaterial = 0; iMaterial < numMaterials; iMaterial++ )
		{
			if( pMeshContainer->pMaterials[iMaterial].pTextureFilename != NULL )
			{
				HRESULT hr = D3DXCreateTextureFromFileA( pD3DDevice,
								pMeshContainer->pMaterials[iMaterial].pTextureFilename,
								&pMeshContainer->ppTextures[iMaterial] );
				if( FAILED( hr ) )
					pMeshContainer->ppTextures[iMaterial] = NULL;
			}
		}
	}
	else
	{//默认
		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( pSkinInfo != NULL )
	{
		pMeshContainer->pSkinInfo = pSkinInfo;
		pSkinInfo->AddRef();

		pMeshContainer->pOrigMesh = pMesh;
		pMesh->AddRef();

		//
		cBones = pSkinInfo->GetNumBones();
		pMeshContainer->pBoneOffsetMatrices = new D3DXMATRIX[cBones];
		if( pMeshContainer->pBoneOffsetMatrices == NULL )
			return E_FAIL;

		//获得offsetmatrix
		for( iBone = 0; iBone < cBones; iBone++ )
		{
			pMeshContainer->pBoneOffsetMatrices[iBone] = *( pMeshContainer->pSkinInfo->GetBoneOffsetMatrix( iBone ) );
		}

		HRESULT hr = GenerateSkinnedMesh( pMeshContainer );
		if( FAILED( hr ) )
			return E_FAIL;
	}

	*ppNewMeshContainer = pMeshContainer;

	//SAFE_RELEASE( pD3DDevice );

	//if( pMeshContainer != NULL )
		//DestroyMeshContainer( pMeshContainer );

	return S_OK;
}

//销毁Frame
HRESULT CAllocateHierarchy::DestroyFrame( LPD3DXFRAME pFrameToFree )
{
	SAFE_DELETE_ARRAY( pFrameToFree->Name );
	SAFE_DELETE( pFrameToFree );

	return S_OK;
}

//销毁container
HRESULT CAllocateHierarchy::DestroyMeshContainer( LPD3DXMESHCONTAINER pMeshContainerBase )
{
	unsigned int iMaterial;
	D3DXMESHCONTAINER_EX* pMeshContainer = (D3DXMESHCONTAINER_EX*)pMeshContainerBase;

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

	if( pMeshContainer->ppTextures != NULL )
	{
		for( iMaterial = 0; iMaterial< pMeshContainer->NumMaterials; iMaterial++ )
		{
			SAFE_RELEASE( pMeshContainer->ppTextures[iMaterial] );
		}
	}

	SAFE_DELETE_ARRAY( pMeshContainer->ppTextures );
	SAFE_DELETE_ARRAY( pMeshContainer->ppBoneMatrixPtrs );
	SAFE_RELEASE( pMeshContainer->pBoneCombinatonBuf );
	SAFE_RELEASE( pMeshContainer->pSkinInfo );
	SAFE_RELEASE( pMeshContainer->pOrigMesh );
	SAFE_DELETE( pMeshContainer );

	return S_OK;
}


//---------------我是分割线---------------------------------------------------------------------------------------------

//全局变量
LPDIRECT3D9 g_pD3D = NULL; //direct3d对象用于创建direct设备
LPDIRECT3DDEVICE9 g_pD3DDevice = NULL; //direct3d设备指针
LPD3DXFONT g_pText = NULL;
LPD3DXFRAME g_pFrameRoot = NULL; //骨骼的根
LPD3DXANIMATIONCONTROLLER g_pAnimController; //动画控制句柄
double g_startTime = 0.0;
wchar_t* g_pAdapter = NULL;
unsigned int g_numBoneMatricesMax = 0;
D3DXMATRIXA16* g_pBoneMatrices = NULL;

//相关函数声明
LRESULT WINAPI MsgProc( HWND, UINT, WPARAM, LPARAM );
HRESULT InitApp( HWND, HINSTANCE );
HRESULT InitGeometry(); //场景数据
VOID InitTranformMatrix(); //初始变换
HRESULT InitText(); //字体接口
VOID Render( HWND ); //渲染
VOID RenderText( HWND ); //渲染提示信息
VOID UpdateScene( HWND ); //更新场景
VOID UpdateFrameMatrices( const LPD3DXFRAME, const LPD3DXMATRIX ); //为个骨骼更新变换矩阵,每帧
VOID InitLight(); //设置光源
VOID Cleanup(); 
HRESULT SetupBoneMatrixPointers( LPD3DXFRAME ); //设置骨骼链中的混合数组到container中,方便访问
HRESULT SetupBoneMatrixPointersOnMesh( LPD3DXMESHCONTAINER ); //
VOID DrawFrame( LPD3DXFRAME ); //绘制骨骼
VOID DrawMeshContainer( const LPD3DXMESHCONTAINER, const LPD3DXFRAME ); //绘制单个pMeshContainer

int WINAPI WinMain( HINSTANCE hInst, HINSTANCE preInst, LPSTR lpCmdLine, int nCmdShow )
{
//设计并注册窗口类
	WNDCLASSEX wcEx = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
						GetModuleHandle( NULL ), NULL, NULL, NULL, NULL,
						wndName, NULL };
	RegisterClassEx( &wcEx );

	//创建窗口
	HWND hWnd = CreateWindow( wndName, wndTitle, WS_OVERLAPPEDWINDOW,
							  100, 100, wndWidth, wndHeight, GetDesktopWindow(),
							  NULL, hInst, NULL );

	if( SUCCEEDED( InitApp( hWnd, hInst ) ) )
	{
		if( SUCCEEDED( InitGeometry() ) )
		{
			//显示并更新窗口
			ShowWindow( hWnd, SW_SHOWDEFAULT );
			UpdateWindow( hWnd );

			//程序消息循环
			MSG msg;
			SecureZeroMemory( &msg, sizeof(msg) );
			while( msg.message != WM_QUIT )
			{
				if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
				{
					TranslateMessage( &msg );
					DispatchMessage( &msg );
				}
				else
					Render( hWnd );
			}
		}
	}

	UnregisterClass( wndName, wcEx.hInstance );
	return 0;
}

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	switch( msg )
	{
	case WM_KEYDOWN:
		if( wParam == VK_ESCAPE )
			DestroyWindow( hWnd );
		break;
	case WM_DESTROY:
		Cleanup();
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc( hWnd, msg, wParam, lParam );
	}
	return S_OK;
}

HRESULT InitApp( HWND hWnd , HINSTANCE hInst )
{
	//创建direct3d对象
	if( FAILED( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION )) )
		return E_FAIL;

	//填写Direct3dDevice配置参数
	D3DPRESENT_PARAMETERS d3dpp;
	SecureZeroMemory( &d3dpp, sizeof(d3dpp) );
	d3dpp.BackBufferWidth = wndWidth;
	d3dpp.BackBufferHeight = wndHeight;
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
	d3dpp.BackBufferCount = 1; //缓冲数
	d3dpp.hDeviceWindow = hWnd;
	d3dpp.Windowed = true;
	d3dpp.EnableAutoDepthStencil = true; //开启深度缓冲
	d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.Flags = 0;
	d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

	//检测硬件
	D3DCAPS9 caps;
	g_pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps );
	int vp = 0;
	if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
		vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
	else
		vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

	//创建Direct3d设备
	if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
				hWnd, vp, &d3dpp, &g_pD3DDevice ) ) )
	{
		return E_FAIL;
	}

	InitText();

	g_startTime = (double)timeGetTime();

	return S_OK;
}

HRESULT InitGeometry()
{
	HRESULT hr;
	CAllocateHierarchy alloc;

	hr = D3DXLoadMeshHierarchyFromX( xFileName, D3DXMESH_MANAGED, g_pD3DDevice, &alloc, NULL, &g_pFrameRoot, &g_pAnimController );
	if( FAILED( hr ) )
		return E_FAIL;
	
	//设置骨骼链中的混合矩阵到Container中,方便访问
	hr = SetupBoneMatrixPointers( g_pFrameRoot );

	InitLight();
	
	InitTranformMatrix();
	g_pD3DDevice->SetRenderState( D3DRS_NORMALIZENORMALS, true ); //标准化顶点法线
	//g_pD3DDevice->SetRenderState( D3DRS_SPECULARENABLE, true ); //开启镜面光

	//显卡信息
	wchar_t adapterTemp[200] = L"显卡型号:";
	D3DADAPTER_IDENTIFIER9 adapter; //获取显卡信息的结构体
	g_pAdapter = new wchar_t[200];
	g_pD3D->GetAdapterIdentifier( 0, 0, &adapter ); //获取显卡信息,保存在adapter中

	//将char*转化为wchar_t*
	int len = MultiByteToWideChar( CP_ACP, 0, adapter.Description, -1, NULL, 0 );
	MultiByteToWideChar( CP_ACP, 0, adapter.Description, -1, g_pAdapter, len );
	wcscat_s(adapterTemp,g_pAdapter ); //链接两个字符串
	memcpy( g_pAdapter, adapterTemp, sizeof(adapterTemp) ); //从新拷贝回g_pAdapter


	return S_OK;
}

VOID InitTranformMatrix()
{
	D3DXMATRIX matWorld;
	D3DXMatrixIdentity( &matWorld );
	g_pD3DDevice->SetTransform( D3DTS_WORLD, &matWorld );

	// 摄像机
	D3DXVECTOR3 pos(100.0f, 100.0f, -250.0f);
	D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
	D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
	//g_camera.ReSetting( &pos, &target, &up );

	//D3DXVECTOR3 right( 1.0f, 0.0f, 0.0f);
	D3DXMATRIX V;
	//g_camera.GetViewMatrix( &V );
	D3DXMatrixLookAtLH(
		&V,
		&pos,
		&target,
		&up);
	g_pD3DDevice->SetTransform(D3DTS_VIEW, &V);
	
	// 设置投影变换矩阵.
	D3DXMATRIX proj;
	D3DXMatrixPerspectiveFovLH(
			&proj,
			D3DX_PI * 0.5f, 
			(float)wndWidth/ (float)wndHeight,
			1.0f,
			1000.0f);
	g_pD3DDevice->SetTransform(D3DTS_PROJECTION, &proj);

	//视口剪切矩阵,实现视口变换
	D3DVIEWPORT9 vp;
	vp.X = 0;
	vp.Y = 0;
	vp.Width = wndWidth;
	vp.Height = wndHeight;
	vp.MinZ = 0;
	vp.MaxZ = 1;
	g_pD3DDevice->SetViewport( &vp );
}

HRESULT InitText()
{
	HRESULT hr;
	//创建字体
	hr = D3DXCreateFont( g_pD3DDevice, 25, 0, 1000, 0, false,
					DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
					DEFAULT_QUALITY, 0, L"微软雅黑", &g_pText );

	return hr;
}

VOID Render( HWND hWnd )
{
	if( NULL == g_pD3DDevice )
		return ;
	//清屏
	g_pD3DDevice->Clear( 0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
						 D3DCOLOR_XRGB( 28, 154, 207 ), 1.0f, 0 );

	if( SUCCEEDED( g_pD3DDevice->BeginScene() ) )
	{
		UpdateScene( hWnd );
		DrawFrame( g_pFrameRoot ); //绘制骨骼动画

		RenderText( hWnd );
		g_pD3DDevice->EndScene();
	}

	g_pD3DDevice->Present( NULL, NULL, NULL, NULL );
	//::Sleep(10000);
}

//递归绘制骨骼动画
VOID DrawFrame( LPD3DXFRAME pFrame )
{
	LPD3DXMESHCONTAINER pMeshContainer;

	pMeshContainer = pFrame->pMeshContainer;
	while( pMeshContainer != NULL )
	{
		DrawMeshContainer( pMeshContainer, pFrame );
		pMeshContainer = pMeshContainer->pNextMeshContainer;
	}

	if( pFrame->pFrameSibling != NULL )
	{
		DrawFrame( pFrame->pFrameSibling );
	}

	if( pFrame->pFrameFirstChild != NULL )
	{
		DrawFrame( pFrame->pFrameFirstChild );
	}
}

//绘制单个pMeshContainer
VOID DrawMeshContainer( const LPD3DXMESHCONTAINER pMeshContainerBase, const LPD3DXFRAME pFrameBase )
{
	D3DXMESHCONTAINER_EX* pMeshContainer = (D3DXMESHCONTAINER_EX*)pMeshContainerBase;
	D3DXFRAME_EX* pFrame = (D3DXFRAME_EX*)pFrameBase;
	unsigned int iMaterial;
	//unsigned int numBlend;
	unsigned int iAttrib;
	//DWORD attribIdPrev;
	//LPD3DXBONECOMBINATION pBoneComb;

	//unsigned int iMatrixIndex;
	//unsigned int iPaletteEntry;
	D3DXMATRIXA16 matTemp;

	
	if( pMeshContainer->pSkinInfo != NULL )
	{
		//只采用软件处理方式
		//D3DXMATRIX Identity;
		DWORD cBones = pMeshContainer->pSkinInfo->GetNumBones();
		DWORD iBone;
		PBYTE pbVerticesSrc;
		PBYTE pbVerticesDest;

		//D3DXMATRIX* pBoneMatrices = new D3DXMATRIX[cBones];
		for( iBone = 0; iBone < cBones; ++iBone )
		{
			D3DXMatrixMultiply(
				&g_pBoneMatrices[iBone],
				&pMeshContainer->pBoneOffsetMatrices[iBone],
				pMeshContainer->ppBoneMatrixPtrs[iBone] 
			);
		}

		pMeshContainer->pOrigMesh->LockVertexBuffer( D3DLOCK_READONLY, (LPVOID*)&pbVerticesSrc );
		pMeshContainer->MeshData.pMesh->LockVertexBuffer( 0, (LPVOID*)&pbVerticesDest );

		pMeshContainer->pSkinInfo->UpdateSkinnedMesh( g_pBoneMatrices, NULL, pbVerticesSrc, pbVerticesDest );

		//delete[] pBoneMatrices;
		//pBoneMatrices = NULL;

		pMeshContainer->pOrigMesh->UnlockVertexBuffer();
		pMeshContainer->MeshData.pMesh->UnlockVertexBuffer();
		
		for( iAttrib = 0; iAttrib < pMeshContainer->numAttributeGroups; iAttrib++ )
		{
			g_pD3DDevice->SetMaterial( &( pMeshContainer->pMaterials[pMeshContainer->pAttributeTable[iAttrib].AttribId].MatD3D ) );
			g_pD3DDevice->SetTexture( 0, pMeshContainer->ppTextures[pMeshContainer->pAttributeTable[iAttrib].AttribId]);
			pMeshContainer->MeshData.pMesh->DrawSubset( pMeshContainer->pAttributeTable[iAttrib].AttribId);
		}
	}
	else
	{
		for( iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++ )
		{
			g_pD3DDevice->SetMaterial( &pMeshContainer->pMaterials[iMaterial].MatD3D );
			g_pD3DDevice->SetTexture( 0, pMeshContainer->ppTextures[iMaterial] );
			pMeshContainer->MeshData.pMesh->DrawSubset( iMaterial );
		}
	}
}

VOID RenderText( HWND hWnd )
{
	RECT clientRect;
	GetClientRect( hWnd, &clientRect );

	//-绘制显卡信息
	g_pText->DrawText( NULL, g_pAdapter, -1, &clientRect,
					DT_TOP | DT_LEFT, D3DXCOLOR( 1.0f, 0.0f, 0.0f, 1.0f ) );

	//绘制FPS
	wchar_t strFPS[20];
	int len = swprintf_s(strFPS, 20, L"FPS:%.3f", GetFPS());
	g_pText->DrawText( NULL, strFPS, len, &clientRect, DT_TOP | DT_RIGHT,
					D3DXCOLOR( 0.0f, 1.0f, 0.0f, 1.0f ) );

}

VOID UpdateScene( HWND hWnd )
{
	if( g_pAnimController != NULL )
	{
		double time = (double)timeGetTime();
		time = ( time - g_startTime ) * 0.001;
		g_startTime = (double)timeGetTime();
		g_pAnimController->AdvanceTime( time, NULL );
	}

	D3DXMATRIX matWorld;
	//D3DXMatrixIdentity( &matWorld );
	g_pD3DDevice->GetTransform( D3DTS_WORLD, &matWorld );
	UpdateFrameMatrices( g_pFrameRoot, &matWorld );
}
	
	

VOID UpdateFrameMatrices( const LPD3DXFRAME pFrameBase, const LPD3DXMATRIX pParentMatrix )
{
	D3DXFRAME_EX* pFrame = (D3DXFRAME_EX*)pFrameBase;

	if( pParentMatrix != NULL )
		D3DXMatrixMultiply( &pFrame->combinedTransformtionMatrix, &pFrame->TransformationMatrix, pParentMatrix );
	else
		pFrame->combinedTransformtionMatrix = pFrame->TransformationMatrix;
	
	if( pFrame->pFrameSibling != NULL )
	{
		UpdateFrameMatrices( pFrame->pFrameSibling, pParentMatrix );
	}

	if( pFrame->pFrameFirstChild != NULL )
	{
		UpdateFrameMatrices( pFrame->pFrameFirstChild, &pFrame->combinedTransformtionMatrix );
	}
}

VOID InitLight()
{
		//------------------------------
	//-1、填写D3DLIGHT9结构体
	//-2、SetLinght
	//-3、LightEnable
	//-----------------------------
	
	//填写光源结构体
	D3DLIGHT9 pointLight;
	SecureZeroMemory( &pointLight, sizeof(pointLight) );
	D3DXCOLOR white = D3DCOLOR_RGBA( 255, 255, 255, 255 );
	D3DXCOLOR black = D3DCOLOR_RGBA( 0, 0, 0, 0 );
	pointLight.Type = D3DLIGHT_POINT;
	pointLight.Ambient = white*0.4f;
	pointLight.Diffuse = white;
	pointLight.Specular = white*0.6f;
	pointLight.Position = D3DXVECTOR3( 200.0f, 200.0f, 0.0f ); //根据需要进行更改
	pointLight.Range = 500.0f;
	pointLight.Attenuation0 = 1.0f;
	pointLight.Attenuation1 = 0.0f;
	pointLight.Attenuation2 = 0.0f;

	g_pD3DDevice->SetLight( 0, &pointLight );
	g_pD3DDevice->LightEnable( 0, true );
}

VOID Cleanup()
{
	SAFE_DELETE( g_pAdapter );
	SAFE_RELEASE( g_pD3D );
	SAFE_RELEASE( g_pD3DDevice );
	SAFE_RELEASE( g_pText );
	SAFE_RELEASE( g_pAnimController );
	

	if( g_pFrameRoot != NULL )
	{
		CAllocateHierarchy alloc;
		D3DXFrameDestroy( g_pFrameRoot, &alloc );
		g_pFrameRoot = NULL;
	}
}

//递归设置骨骼链的混合数组
HRESULT SetupBoneMatrixPointers( LPD3DXFRAME pFrame )
{
	HRESULT hr;
	if( pFrame->pMeshContainer != NULL )
	{
		hr = SetupBoneMatrixPointersOnMesh( pFrame->pMeshContainer );
		if( FAILED( hr ) )
			return hr;
	}

	if( pFrame->pFrameSibling != NULL )
	{
		hr = SetupBoneMatrixPointers( pFrame->pFrameSibling );
		if( FAILED( hr ) )
			return hr;
	}

	if( pFrame->pFrameFirstChild != NULL )
	{
		hr = SetupBoneMatrixPointers( pFrame->pFrameFirstChild );
		if( FAILED( hr ) )
			return hr;
	}

	return S_OK;
}

HRESULT SetupBoneMatrixPointersOnMesh( LPD3DXMESHCONTAINER pMeshContainerBase )
{
	unsigned int iBone;
	unsigned int cBones;
	D3DXFRAME_EX* pFrame;

	D3DXMESHCONTAINER_EX* pMeshContainer = ( D3DXMESHCONTAINER_EX* )pMeshContainerBase;
	
	//如果是骨骼动画
	if( pMeshContainer->pSkinInfo != NULL )
	{
		cBones = pMeshContainer->pSkinInfo->GetNumBones();

		pMeshContainer->ppBoneMatrixPtrs = new D3DXMATRIX*[cBones];
		if( pMeshContainer->ppBoneMatrixPtrs == NULL )
			return E_OUTOFMEMORY;

		for( iBone = 0; iBone < cBones; iBone++ )
		{
			//根据骨骼名字寻找对应骨骼
			pFrame = (D3DXFRAME_EX*)D3DXFrameFind( g_pFrameRoot, pMeshContainer->pSkinInfo->GetBoneName( iBone ) );
			if( pFrame == NULL )
				return E_FAIL;

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

	//--------
	/*HRESULT hr = pMeshContainer->MeshData.pMesh->GetAttributeTable( NULL, &pMeshContainer->numAttributeGroups );
	if( FAILED( hr ) )
		return E_FAIL;
	pMeshContainer->pAttributeTable = new D3DXATTRIBUTERANGE[pMeshContainer->numAttributeGroups];
	if( pMeshContainer->pAttributeTable == NULL )
		return E_FAIL;
	hr = pMeshContainer->MeshData.pMesh->GetAttributeTable( pMeshContainer->pAttributeTable, NULL );
	if( FAILED( hr ) )
		return E_FAIL;*/


	return S_OK;
}

//在SkinMesh加载或者改变渲染方式时是调用
HRESULT GenerateSkinnedMesh( D3DXMESHCONTAINER_EX* pMeshContainer )
{
	HRESULT hr = S_OK;

	if( pMeshContainer->pSkinInfo == NULL )
		return hr;
	SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
	SAFE_RELEASE( pMeshContainer->pBoneCombinatonBuf );

	hr = pMeshContainer->pOrigMesh->CloneMeshFVF( D3DXMESH_MANAGED, pMeshContainer->pOrigMesh->GetFVF(),
												g_pD3DDevice, &pMeshContainer->MeshData.pMesh );
	if( FAILED(hr) )
		return hr;

	hr = pMeshContainer->MeshData.pMesh->GetAttributeTable( NULL, &pMeshContainer->numAttributeGroups );
	if( FAILED(hr) )
		return hr;

	delete[] pMeshContainer->pAttributeTable;
	pMeshContainer->pAttributeTable = new D3DXATTRIBUTERANGE[pMeshContainer->numAttributeGroups];
	if( pMeshContainer->pAttributeTable == NULL )
		return E_OUTOFMEMORY;

	hr = pMeshContainer->MeshData.pMesh->GetAttributeTable( pMeshContainer->pAttributeTable, NULL );
	if( FAILED(hr) )
		return hr;

	if( g_numBoneMatricesMax < pMeshContainer->pSkinInfo->GetNumBones() )
	{
		g_numBoneMatricesMax = pMeshContainer->pSkinInfo->GetNumBones();

		//为材质分配空间
		g_pBoneMatrices = new D3DXMATRIXA16[g_numBoneMatricesMax];
		if( g_pBoneMatrices == NULL )
			return E_OUTOFMEMORY;
	}

	return hr;
}


 


【Direct3D游戏开发】——蒙皮骨骼动画学习笔记_第1张图片                                                                       

后记:好久,好久没更新了,一个由于自己突然抽风了,变得放纵,还有就是还是被其他事情影响到了。

 

 




 

 

 

你可能感兴趣的:(学习笔记)