⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 skinmesh.cpp

📁 骨骼动画 此程序演示了如何在你的游戏中使用骨骼动画技术。
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// SkinMesh.cpp: 实现 CSkinMesh 类
//
//////////////////////////////////////////////////////////////////////
#include "SkinMesh.h"


// 构造函数
CSkinMesh::CSkinMesh(LPDIRECT3DDEVICE9 pD3DDevice)
:m_vMax(0,0,0),
m_vMin(0,0,0),
m_pID3dDevice(pD3DDevice),
m_fElapsedTime(0.0f),
m_bMoving(TRUE),
m_pIAnimController(NULL),
m_pFrameRoot(NULL),
m_SkinningMethod(D3DINDEXED),
m_pBoneMatrices(NULL),
m_NumBoneMatricesMax(0)
{
	m_pID3dDevice->GetDeviceCaps( &m_d3dCaps );
}


// 析构函数
CSkinMesh::~CSkinMesh()
{
	CAllocateHierarchy *pAlloc = new CAllocateHierarchy( this );		// 自定义数据容器
	D3DXFrameDestroy( m_pFrameRoot, pAlloc );		// 释放所有框架
	SAFE_DELETE( pAlloc );		// 释放自定义数据容器
	SAFE_RELEASE( m_pIAnimController );		// 释放动画控制器
}


// 加载X文件
HRESULT CSkinMesh::LoadFromXFile( char *strFileName )
{
	// 数据容器,该类的构造函数需要一个CShinMesh*类型的参数,所以传递this进去
    HRESULT hr;
	CAllocateHierarchy *pAlloc = new CAllocateHierarchy( this );

	// 从.X文件加载层次框架和动画数据:自定义数据容器、根框架指针、动画控制器
	// 函数调用后,m_pFrameRoot就已经保存了X文件的框架层次
    hr = D3DXLoadMeshHierarchyFromX( 
		strFileName, 
		D3DXMESH_MANAGED, 
		m_pID3dDevice, 
		pAlloc,					// ID3DXAllocateHierarchy接口(在这里提供了一个实现该接口的类对象指针)
		NULL, 
		&m_pFrameRoot, 
		&m_pIAnimController );	// ID3DXAnimationController接口(在这里提供了一个实现该接口的类对象指针)
	SAFE_DELETE( pAlloc );		// 释放对象

    if (FAILED(hr))
        return hr;
	
	// 递归,设置每块骨骼的矩阵
    hr = SetupBoneMatrixPointers( m_pFrameRoot );
    if (FAILED(hr))
        return hr;
	
	// 计算包围球,m_vObjectCenter球的中心,m_fObjectRadius球的半径
    //hr = D3DXFrameCalculateBoundingSphere( m_pFrameRoot, &m_vObjectCenter, &m_fObjectRadius );
    //if (FAILED(hr))
        //return hr;

	if(m_pFrameRoot)	// 创建包围盒,m_vMin最小点,m_vMax最大点
		CalculateBondingBox(m_pFrameRoot, &m_vMin, &m_vMax );
	

	// 以下代码没有用到 ??
	//LPD3DXANIMATIONSET pAnim;
	//m_pIAnimController->GetAnimationSet( 0, &pAnim );	 

	return S_OK;
}

//-----------------------------------------------------------------------------
// Name: GenerateSkinnedMesh()
// Desc: Called either by CreateMeshContainer when loading a skin mesh, or when 
//       changing methods.  This function uses the pSkinInfo of the mesh 
//       container to generate the desired drawable mesh and bone combination 
//       table.
//-----------------------------------------------------------------------------
HRESULT CSkinMesh::GenerateSkinnedMesh( D3DXMESHCONTAINER_EX *pMeshContainer )
{
    HRESULT hr = S_OK;

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

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

	// 蒙皮模式
	switch(m_SkinningMethod)
	{
	case D3DNONINDEXED:	// 如果选择的模式是non-indexed(无索引),使用ConvertToBlendedMesh()函数生成可绘制模型
    {

        hr = pMeshContainer->pSkinInfo->ConvertToBlendedMesh
                                   (
                                       pMeshContainer->pOrigMesh,
                                       D3DXMESH_MANAGED|D3DXMESHOPT_VERTEXCACHE, 
                                       pMeshContainer->pAdjacency, 
                                       NULL, NULL, NULL, 
                                       &pMeshContainer->NumInfl,
                                       &pMeshContainer->NumAttributeGroups, 
                                       &pMeshContainer->pBoneCombinationBuf, 
                                       &pMeshContainer->MeshData.pMesh
                                   );
        if (FAILED(hr))
            goto e_Exit;


        /* If the device can only do 2 matrix blends, ConvertToBlendedMesh cannot approximate all meshes to it
           Thus we split the mesh in two parts: The part that uses at most 2 matrices and the rest. The first is
           drawn using the device's HW vertex processing and the rest is drawn using SW vertex processing. */
        LPD3DXBONECOMBINATION rgBoneCombinations  = reinterpret_cast<LPD3DXBONECOMBINATION>(pMeshContainer->pBoneCombinationBuf->GetBufferPointer());

        // look for any set of bone combinations that do not fit the caps
        for (pMeshContainer->iAttributeSW = 0; pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups; pMeshContainer->iAttributeSW++)
        {
            DWORD cInfl   = 0;

            for (DWORD iInfl = 0; iInfl < pMeshContainer->NumInfl; iInfl++)
            {
                if (rgBoneCombinations[pMeshContainer->iAttributeSW].BoneId[iInfl] != UINT_MAX)
                {
                    ++cInfl;
                }
            }

            if (cInfl > m_d3dCaps.MaxVertexBlendMatrices)
            {
                break;
            }
        }

        // if there is both HW and SW, add the Software Processing flag
        if (pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups)
        {
            LPD3DXMESH pMeshTmp;

            hr = pMeshContainer->MeshData.pMesh->CloneMeshFVF(D3DXMESH_SOFTWAREPROCESSING|pMeshContainer->MeshData.pMesh->GetOptions(), 
                                                pMeshContainer->MeshData.pMesh->GetFVF(),
                                                m_pID3dDevice, &pMeshTmp);
            if (FAILED(hr))
            {
                goto e_Exit;
            }

            pMeshContainer->MeshData.pMesh->Release();
            pMeshContainer->MeshData.pMesh = pMeshTmp;
            pMeshTmp = NULL;
        }
    }
	break;

    // if indexed skinning mode selected, use ConvertToIndexedsBlendedMesh to generate drawable mesh
	// 如果选择了索引模式,使用ConvertToIndexedsBlendedMesh()函数生成可绘制模型
	case D3DINDEXED:
    {
        DWORD NumMaxFaceInfl;
        DWORD Flags = D3DXMESHOPT_VERTEXCACHE;

        LPDIRECT3DINDEXBUFFER9 pIB;
        hr = pMeshContainer->pOrigMesh->GetIndexBuffer(&pIB);
        if (FAILED(hr))
            goto e_Exit;

        hr = pMeshContainer->pSkinInfo->GetMaxFaceInfluences(pIB, pMeshContainer->pOrigMesh->GetNumFaces(), &NumMaxFaceInfl);
        pIB->Release();
        if (FAILED(hr))
            goto e_Exit;

        // 12 entry palette guarantees that any triangle (4 independent influences per vertex of a tri)
        // can be handled
        NumMaxFaceInfl = min(NumMaxFaceInfl, 12);

        if (m_d3dCaps.MaxVertexBlendMatrixIndex + 1 < NumMaxFaceInfl)
        {
            // HW does not support indexed vertex blending. Use SW instead
            pMeshContainer->NumPaletteEntries = min(256, pMeshContainer->pSkinInfo->GetNumBones());
            pMeshContainer->UseSoftwareVP = true;
            Flags |= D3DXMESH_SYSTEMMEM;
        }
        else
        {
            // using hardware - determine palette size from caps and number of bones
            // If normals are present in the vertex data that needs to be blended for lighting, then 
            // the number of matrices is half the number specified by MaxVertexBlendMatrixIndex.
            pMeshContainer->NumPaletteEntries = min( ( m_d3dCaps.MaxVertexBlendMatrixIndex + 1 ) / 2, 
                                                     pMeshContainer->pSkinInfo->GetNumBones() );
            pMeshContainer->UseSoftwareVP = false;
            Flags |= D3DXMESH_MANAGED;
        }

        hr = pMeshContainer->pSkinInfo->ConvertToIndexedBlendedMesh
                                                (
                                                pMeshContainer->pOrigMesh,
                                                Flags, 
                                                pMeshContainer->NumPaletteEntries, 
                                                pMeshContainer->pAdjacency, 
                                                NULL, NULL, NULL, 
                                                &pMeshContainer->NumInfl,
                                                &pMeshContainer->NumAttributeGroups, 
                                                &pMeshContainer->pBoneCombinationBuf, 
                                                &pMeshContainer->MeshData.pMesh);
        if (FAILED(hr))
            goto e_Exit;
    }
	break;


     // if software skinning selected, use GenerateSkinnedMesh to create a mesh that can be used with UpdateSkinnedMesh
	case SOFTWARE:
    {
        hr = pMeshContainer->pOrigMesh->CloneMeshFVF(D3DXMESH_MANAGED, pMeshContainer->pOrigMesh->GetFVF(),
                                              m_pID3dDevice, &pMeshContainer->MeshData.pMesh);
        if (FAILED(hr))
            goto e_Exit;

        hr = pMeshContainer->MeshData.pMesh->GetAttributeTable(NULL, &pMeshContainer->NumAttributeGroups);
        if (FAILED(hr))
            goto e_Exit;

        delete[] pMeshContainer->pAttributeTable;
        pMeshContainer->pAttributeTable  = new D3DXATTRIBUTERANGE[pMeshContainer->NumAttributeGroups];
        if (pMeshContainer->pAttributeTable == NULL)
        {
            hr = E_OUTOFMEMORY;
            goto e_Exit;
        }

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

        // allocate a buffer for bone matrices, but only if another mesh has not allocated one of the same size or larger
        if (m_NumBoneMatricesMax < pMeshContainer->pSkinInfo->GetNumBones())
        {
            m_NumBoneMatricesMax = pMeshContainer->pSkinInfo->GetNumBones();

            // Allocate space for blend matrices
            delete []m_pBoneMatrices; 
            m_pBoneMatrices  = new D3DXMATRIXA16[m_NumBoneMatricesMax];
            if (m_pBoneMatrices == NULL)
            {
                hr = E_OUTOFMEMORY;
                goto e_Exit;
            }
        }
    }
    break;
	
	default: // invalid m_SkinningMethod value
    {        
        // return failure due to invalid skinning method value
        hr = E_INVALIDARG;
        goto e_Exit;
    }
	} // switch()

e_Exit:
    return hr;
}



HRESULT CSkinMesh::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 CSkinMesh::SetupBoneMatrixPointersOnMesh( LPD3DXMESHCONTAINER pMeshContainerBase )
{
    UINT i, iNumBone;
    D3DXFRAME_EX *pFrame;
	
    D3DXMESHCONTAINER_EX *pMeshContainer = (D3DXMESHCONTAINER_EX*)pMeshContainerBase;

	//蒙皮动画,则设置骨骼的变换矩阵
    if (pMeshContainer->pSkinInfo != NULL)	// 如果有蒙皮信息
    {
        iNumBone = pMeshContainer->pSkinInfo->GetNumBones();			// 取得骨骼总数量
        pMeshContainer->ppBoneMatrixPtrs = new D3DXMATRIX*[iNumBone];	// 创建骨骼矩阵数组,ppBoneMatrixPtrs指向骨骼矩阵数组
        if (pMeshContainer->ppBoneMatrixPtrs == NULL)
            return E_OUTOFMEMORY;

        for (i=0; i < iNumBone; i++)
        {
			// 返回指定名字的框架,骨骼名字与框架名字是一一对应的
            pFrame = (D3DXFRAME_EX*)D3DXFrameFind( m_pFrameRoot, pMeshContainer->pSkinInfo->GetBoneName(i) );
            if (pFrame == NULL)
                return E_FAIL;

			//初始化矩阵
            pMeshContainer->ppBoneMatrixPtrs[i] = &(pFrame->CombinedTransformationMatrix);	// 保存每块骨骼的组合变换矩阵
        }
    }

    return S_OK;
}

VOID CSkinMesh::Render( float fElapsedAppTime, D3DXVECTOR3 vPos, float angle, float scale )
{
	
	// 取得程序时间,单位秒,如果时间没变化就停止渲染
	// FLOAT fElapsedAppTime = DXUtil_Timer( TIMER_GETELAPSEDTIME );
	
    if( 0.00f == fElapsedAppTime )
		return;
	m_fElapsedTime = fElapsedAppTime;
	
	// 设置世界矩阵
    D3DXMATRIXA16 matWorld, matWorld2;
	D3DXMatrixRotationY( &matWorld2, angle );
	D3DXMatrixScaling( &matWorld, scale, scale, scale );
	D3DXMatrixMultiply( &matWorld, &matWorld, &matWorld2);	// 绽放 * 旋转
    D3DXMatrixTranslation( &matWorld2, vPos.x, vPos.y, vPos.z ); // 平移
	D3DXMatrixMultiply(&matWorld,&matWorld,&matWorld2);		// 平移 * 绽放 * 旋转

	if(m_pIAnimController)
	{
			#if ((D3D_SDK_VERSION & 0xFF)==31)	// Directx9.0b
				if (m_bMoving)
					m_pIAnimController->SetTime( m_pIAnimController->GetTime()+m_fElapsedTime );
				else
					m_pIAnimController->SetTime( 0 );
						
			#else // Directx9.0c
				if (m_bMoving)
					m_pIAnimController->AdvanceTime( m_fElapsedTime, NULL ); 
				else
					m_pIAnimController->ResetTime();
			#endif
	}
	
	UpdateFrameMatrices( m_pFrameRoot, &matWorld );	// 递归更新全部框架的矩阵	
	DrawFrame( m_pFrameRoot );						// 递归绘制全部框架

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -