//////////////////////////////////////////////////////////////////////// SkinMesh.h: interface for the CSkinMesh class.////////////////////////////////////////////////////////////////////////#if !defined(AFX_SKINMESH_H)#define AFX_SKINMESH_H#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000#include <tchar.h>#include <d3dx9.h>#include <d3dx9mesh.h>#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }#define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } }#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }//-----------------------------------------------------------------------------// Name: struct D3DXFRAME_DERIVED// Desc: Structure derived from D3DXFRAME so we can add some app-specific// info that will be stored with each frame//-----------------------------------------------------------------------------struct D3DXFRAME_DERIVED: public D3DXFRAME{ D3DXMATRIXA16 CombinedTransformationMatrix;};//-----------------------------------------------------------------------------// Name: struct D3DXMESHCONTAINER_DERIVED// Desc: Structure derived from D3DXMESHCONTAINER so we can add some app-specific// info that will be stored with each mesh//-----------------------------------------------------------------------------struct D3DXMESHCONTAINER_DERIVED: public D3DXMESHCONTAINER{ LPDIRECT3DTEXTURE9* ppTextures; // array of textures, entries are NULL if no texture specified // SkinMesh info LPD3DXMESH pOrigMesh; LPD3DXATTRIBUTERANGE pAttributeTable; DWORD NumAttributeGroups; DWORD NumInfl; LPD3DXBUFFER pBoneCombinationBuf; D3DXMATRIX** ppBoneMatrixPtrs; D3DXMATRIX* pBoneOffsetMatrices; DWORD NumPaletteEntries; bool UseSoftwareVP; DWORD iAttributeSW; // used to denote the split between SW and HW if necessary for non-indexed skinning};// enum for various skinning modes possibleenum METHOD { D3DNONINDEXED, D3DINDEXED, SOFTWARE, NONE};class CSkinMesh;//-----------------------------------------------------------------------------// Name: class CAllocateHierarchy// Desc: Custom version of ID3DXAllocateHierarchy with custom methods to create// frames and meshcontainers.//-----------------------------------------------------------------------------class CAllocateHierarchy: public ID3DXAllocateHierarchy{public: STDMETHOD(CreateFrame)(THIS_ LPCTSTR Name, LPD3DXFRAME *ppNewFrame);#if ((D3D_SDK_VERSION & 0xFF)==31) //ÕâÀïÊÇDirectx9.0bµÄ STDMETHOD(CreateMeshContainer)(THIS_ LPCTSTR Name, LPD3DXMESHDATA pMeshData, LPD3DXMATERIAL pMaterials, LPD3DXEFFECTINSTANCE pEffectInstances, DWORD NumMaterials, DWORD *pAdjacency, LPD3DXSKININFO pSkinInfo, LPD3DXMESHCONTAINER *ppNewMeshContainer);#else //ÕâÀïÊÇDirectx9.0cµÄ STDMETHOD(CreateMeshContainer)( THIS_ LPCSTR Name, CONST D3DXMESHDATA *pMeshData, CONST D3DXMATERIAL *pMaterials, CONST D3DXEFFECTINSTANCE *pEffectInstances, DWORD NumMaterials, CONST DWORD *pAdjacency, LPD3DXSKININFO pSkinInfo, LPD3DXMESHCONTAINER *ppNewMeshContainer);#endif STDMETHOD(DestroyFrame)(THIS_ LPD3DXFRAME pFrameToFree); STDMETHOD(DestroyMeshContainer)(THIS_ LPD3DXMESHCONTAINER pMeshContainerBase); CAllocateHierarchy(CSkinMesh *pSkinMesh) :m_pSkinMesh(pSkinMesh) {}public: CSkinMesh* m_pSkinMesh;};//ÃÉƤ¶¯»class CSkinMesh {public: VOID SetAnim(BOOL bAnim); BOOL CSkinMesh::InterSect( D3DVECTOR *pRayOrig, D3DVECTOR *pRayDir,D3DVECTOR* pVRet); VOID UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix ); LPD3DXANIMATIONCONTROLLER GetAnimController() {return m_pAnimController;}; BOOL SetAnimationName(char *strAnimName);// void SetPosition(D3DXVECTOR3 &vPos) {m_vPos=vPos; };// void SetPosition(float x,float y,float z) {m_vPos.x=x;m_vPos.y=y;m_vPos.z=z;}; VOID Render(float fTimeFromLastFrame,D3DXVECTOR3 vPos,D3DXVECTOR3 rotation,float scale=1.0f); HRESULT LoadFromXFile(char* strFileName); HRESULT GenerateSkinnedMesh(D3DXMESHCONTAINER_DERIVED *pMeshContainer); LPD3DXMESH getMesh(); //Retuns of Mesh CSkinMesh(LPDIRECT3DDEVICE9 pD3DDevice);// CSkinMesh(LPDIRECT3DDEVICE9 pD3DDevice,char* strFileName); HRESULT CalculateBondingBox(LPD3DXFRAME pFrameParent, D3DXVECTOR3 *pVmin,D3DXVECTOR3 *pVmax) { static LPVOID pV; static LPD3DXMESH pMesh; static LPD3DXMATRIX pMat; static D3DXVECTOR3 vMin,vMax; static D3DXVECTOR3 vTransFormed; if(pFrameParent->pMeshContainer) { pMesh=pFrameParent->pMeshContainer->MeshData.pMesh; pMat=&(((D3DXFRAME_DERIVED*)pFrameParent)->CombinedTransformationMatrix); pMesh->LockVertexBuffer(0,&pV); D3DXComputeBoundingBox((LPD3DXVECTOR3)pV,pMesh->GetNumVertices(), pMesh->GetNumBytesPerVertex(),&vMin,&vMax); vTransFormed.x=pMat->_11*vMin.x+pMat->_21*vMin.y+pMat->_31*vMin.z+pMat->_41; vTransFormed.y=pMat->_12*vMin.x+pMat->_22*vMin.y+pMat->_32*vMin.z+pMat->_42; vTransFormed.z=pMat->_13*vMin.x+pMat->_23*vMin.y+pMat->_33*vMin.z+pMat->_43; if(vTransFormed.x<pVmin->x) pVmin->x=vTransFormed.x; if(vTransFormed.y<pVmin->y) pVmin->y=vTransFormed.y; if(vTransFormed.z<pVmin->z) pVmin->z=vTransFormed.z; vTransFormed.x=pMat->_11*vMax.x+pMat->_21*vMax.y+pMat->_31*vMax.z+pMat->_41; vTransFormed.y=pMat->_12*vMax.x+pMat->_22*vMax.y+pMat->_32*vMax.z+pMat->_42; vTransFormed.z=pMat->_13*vMax.x+pMat->_23*vMax.y+pMat->_33*vMax.z+pMat->_43; if(vTransFormed.x>pVmax->x) pVmax->x=vTransFormed.x; if(vTransFormed.y>pVmax->y) pVmax->y=vTransFormed.y; if(vTransFormed.z>pVmax->z) pVmax->z=vTransFormed.z; pMesh->UnlockVertexBuffer(); } //else return S_OK; if(pFrameParent->pFrameSibling) CalculateBondingBox(pFrameParent->pFrameSibling,pVmin,pVmax); if(pFrameParent->pFrameFirstChild) CalculateBondingBox(pFrameParent->pFrameFirstChild,pVmin,pVmax); return S_OK; } virtual ~CSkinMesh(); LPDIRECT3DVERTEXSHADER9 m_pIndexedVertexShader[4]; D3DXMATRIXA16* m_pBoneMatrices; UINT m_NumBoneMatricesMax; METHOD m_SkinningMethod; D3DCAPS9 m_d3dCaps; D3DXVECTOR3 m_vObjectCenter; // Center of bounding sphere of object FLOAT m_fObjectRadius; // Radius of bounding sphere of object D3DXVECTOR3 m_vMin; // Center of bounding sphere of object D3DXVECTOR3 m_vMax; // Center of bounding sphere of objectprotected: VOID DrawMeshContainer(LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase); VOID DrawFrame(LPD3DXFRAME pFrame); HRESULT SetupBoneMatrixPointersOnMesh( LPD3DXMESHCONTAINER pMeshContainerBase ); HRESULT SetupBoneMatrixPointers( LPD3DXFRAME pFrame ); // FLOAT m_fRotateAngle; BOOL m_bMoving; LPDIRECT3DDEVICE9 m_pd3dDevice; LPD3DXFRAME m_pFrameRoot; LPD3DXANIMATIONCONTROLLER m_pAnimController; FLOAT m_fElapsedTime; // Time elapsed since last frame LPD3DXMESH m_SkinMesh;};#endif // !defined(AFX_SKINMESH_H__AE3ADAAF_4BE2_4B23_9A1D_9379FA1BAFD9__INCLUDED_)
// SkinMesh.cpp: implementation of the CSkinMesh class.////////////////////////////////////////////////////////////////////////#include "SkinMesh.h"//-----------------------------------------------------------------------------// Name: AllocateName()// Desc: Allocates memory for a string to hold the name of a frame or mesh//-----------------------------------------------------------------------------HRESULT AllocateName( LPCTSTR Name, LPTSTR *pNewName ){ UINT cbLength; if (Name != NULL) { cbLength = lstrlen(Name) + 1; *pNewName = new TCHAR[cbLength]; if (*pNewName == NULL) return E_OUTOFMEMORY; memcpy(*pNewName, Name, cbLength*sizeof(TCHAR)); } else { *pNewName = NULL; } return S_OK;}//-----------------------------------------------------------------------------// Name: CAllocateHierarchy::CreateFrame()// Desc: //-----------------------------------------------------------------------------HRESULT CAllocateHierarchy::CreateFrame(LPCTSTR Name, LPD3DXFRAME *ppNewFrame){ HRESULT hr = S_OK; D3DXFRAME_DERIVED *pFrame; *ppNewFrame = NULL; pFrame = new D3DXFRAME_DERIVED; if (pFrame == NULL) { hr = E_OUTOFMEMORY; goto e_Exit; } hr = AllocateName(Name, &pFrame->Name); if (FAILED(hr)) goto e_Exit; // initialize other data members of the frame D3DXMatrixIdentity(&pFrame->TransformationMatrix); D3DXMatrixIdentity(&pFrame->CombinedTransformationMatrix); pFrame->pMeshContainer = NULL; pFrame->pFrameSibling = NULL; pFrame->pFrameFirstChild = NULL; *ppNewFrame = pFrame; pFrame = NULL; e_Exit: delete pFrame; return hr;}//-----------------------------------------------------------------------------// Name: CAllocateHierarchy::CreateMeshContainer()// Desc: //-----------------------------------------------------------------------------#if ((D3D_SDK_VERSION&0xFF) ==31) //ÕâÀïÊÇDirectx9.0bµÄHRESULT CAllocateHierarchy::CreateMeshContainer( LPCTSTR Name, LPD3DXMESHDATA pMeshData, LPD3DXMATERIAL pMaterials, LPD3DXEFFECTINSTANCE pEffectInstances, DWORD NumMaterials, DWORD *pAdjacency, LPD3DXSKININFO pSkinInfo, LPD3DXMESHCONTAINER *ppNewMeshContainer) #else //Direct9.0c LRESULT CAllocateHierarchy::CreateMeshContainer( LPCTSTR Name, CONST D3DXMESHDATA *pMeshData, CONST D3DXMATERIAL *pMaterials, CONST D3DXEFFECTINSTANCE *pEffectInstances, DWORD NumMaterials, CONST DWORD *pAdjacency, LPD3DXSKININFO pSkinInfo, LPD3DXMESHCONTAINER *ppNewMeshContainer) #endif{ HRESULT hr; D3DXMESHCONTAINER_DERIVED *pMeshContainer = NULL; UINT NumFaces; UINT iMaterial; UINT iBone, cBones; LPDIRECT3DDEVICE9 pd3dDevice = 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; if (pMeshContainer == NULL) { hr = E_OUTOFMEMORY; goto e_Exit; } 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->ppTextures = new LPDIRECT3DTEXTURE9[pMeshContainer->NumMaterials]; pMeshContainer->pAdjacency = new DWORD[NumFaces*3]; if ((pMeshContainer->pAdjacency == NULL) || (pMeshContainer->pMaterials == NULL)) { hr = E_OUTOFMEMORY; goto e_Exit; } memcpy(pMeshContainer->pAdjacency, pAdjacency, sizeof(DWORD) * NumFaces*3); memset(pMeshContainer->ppTextures, 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) {// TCHAR strTexturePath[MAX_PATH] = _T("");// DXUtil_FindMediaFileCb( strTexturePath, sizeof(strTexturePath), pMeshContainer->pMaterials[iMaterial].pTextureFilename ); if( FAILED( D3DXCreateTextureFromFile( pd3dDevice, pMeshContainer->pMaterials[iMaterial].pTextureFilename, &pMeshContainer->ppTextures[iMaterial] ) ) ) pMeshContainer->ppTextures[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]; if (pMeshContainer->pBoneOffsetMatrices == NULL) { hr = E_OUTOFMEMORY; goto e_Exit; } // 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 = m_pSkinMesh->GenerateSkinnedMesh(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 textures 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->pBoneCombinationBuf ); SAFE_RELEASE( pMeshContainer->MeshData.pMesh ); SAFE_RELEASE( pMeshContainer->pSkinInfo ); SAFE_RELEASE( pMeshContainer->pOrigMesh ); SAFE_DELETE( pMeshContainer ); return S_OK;}//////////////////////////////////////////////////////////////////////// Construction/Destruction//////////////////////////////////////////////////////////////////////CSkinMesh::CSkinMesh(LPDIRECT3DDEVICE9 pD3DDevice):m_vMax(0,0,0),m_vMin(0,0,0) { m_pd3dDevice=pD3DDevice; m_fElapsedTime=0.0f; m_pd3dDevice->GetDeviceCaps( &m_d3dCaps ); m_bMoving=TRUE; m_pAnimController = NULL; m_pFrameRoot = NULL; m_SkinningMethod = D3DINDEXED; m_pBoneMatrices = NULL; m_NumBoneMatricesMax = 0;}CSkinMesh::~CSkinMesh(){ CAllocateHierarchy Alloc(this); D3DXFrameDestroy(m_pFrameRoot, &Alloc); SAFE_RELEASE(m_pAnimController); }HRESULT CSkinMesh::LoadFromXFile(char *strFileName){ HRESULT hr; CAllocateHierarchy Alloc(this); //¼ÓÔØÖ¡µÄÊ÷Ðͽṹ hr = D3DXLoadMeshHierarchyFromX(strFileName, D3DXMESH_MANAGED, m_pd3dDevice, &Alloc, NULL, &m_pFrameRoot, &m_pAnimController); if (FAILED(hr)) return hr; //ÉèÖþØÕóÖ¸Õë hr = SetupBoneMatrixPointers(m_pFrameRoot); if (FAILED(hr)) return hr; hr = D3DXFrameCalculateBoundingSphere(m_pFrameRoot, &m_vObjectCenter, &m_fObjectRadius); if (FAILED(hr)) return hr; if(m_pFrameRoot) CalculateBondingBox(m_pFrameRoot,&m_vMin,&m_vMax); LPD3DXANIMATIONSET pAnim; m_pAnimController->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_DERIVED *pMeshContainer){ //m_SkinMesh = pMeshContainer->pOrigMesh; m_SkinMesh = pMeshContainer->MeshData.pMesh; HRESULT hr = S_OK; if (pMeshContainer->pSkinInfo == NULL) return hr; SAFE_RELEASE( pMeshContainer->MeshData.pMesh ); SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf ); // if non-indexed skinning mode selected, use ConvertToBlendedMesh to generate drawable mesh if (m_SkinningMethod == D3DNONINDEXED) { 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_pd3dDevice, &pMeshTmp); if (FAILED(hr)) { goto e_Exit; } pMeshContainer->MeshData.pMesh->Release(); pMeshContainer->MeshData.pMesh = pMeshTmp; pMeshTmp = NULL; } } // if indexed skinning mode selected, use ConvertToIndexedsBlendedMesh to generate drawable mesh else if (m_SkinningMethod == 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; } // if software skinning selected, use GenerateSkinnedMesh to create a mesh that can be used with UpdateSkinnedMesh else if (m_SkinningMethod == SOFTWARE) { hr = pMeshContainer->pOrigMesh->CloneMeshFVF(D3DXMESH_MANAGED, pMeshContainer->pOrigMesh->GetFVF(), m_pd3dDevice, &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; } } } else // invalid m_SkinningMethod value { // return failure due to invalid skinning method value hr = E_INVALIDARG; goto e_Exit; }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 iBone, cBones; D3DXFRAME_DERIVED *pFrame; D3DXMESHCONTAINER_DERIVED *pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)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_DERIVED*)D3DXFrameFind(m_pFrameRoot, pMeshContainer->pSkinInfo->GetBoneName(iBone)); if (pFrame == NULL) return E_FAIL; //³õʼ»¯¾ØÕó pMeshContainer->ppBoneMatrixPtrs[iBone] = &pFrame->CombinedTransformationMatrix; } } return S_OK;}VOID CSkinMesh::Render(float fElapsedAppTime,D3DXVECTOR3 vPos,D3DXVECTOR3 rotation,float scale){ // Get the app's time, in seconds. Skip rendering if no time elapsed// FLOAT fElapsedAppTime = DXUtil_Timer( TIMER_GETELAPSEDTIME ); if( 0.00f == fElapsedAppTime ) return; m_fElapsedTime = fElapsedAppTime; // Setup world matrix D3DXMATRIXA16 matWorld,matWorld2; // set the rotation matrix D3DXMatrixRotationYawPitchRoll(&matWorld2,rotation.x,rotation.y,rotation.z); D3DXMatrixScaling(&matWorld,scale,scale,scale); D3DXMatrixMultiply(&matWorld,&matWorld,&matWorld2); D3DXMatrixTranslation( &matWorld2, vPos.x, vPos.y,vPos.z ); D3DXMatrixMultiply(&matWorld,&matWorld,&matWorld2);// m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); if(m_pAnimController) { if (m_bMoving)#if ((D3D_SDK_VERSION & 0xFF)==31) m_pAnimController->SetTime(m_pAnimController->GetTime()+m_fElapsedTime ); else m_pAnimController->SetTime(0); #else m_pAnimController->AdvanceTime(m_fElapsedTime,NULL); else m_pAnimController->ResetTime();#endif } UpdateFrameMatrices(m_pFrameRoot, &matWorld); DrawFrame(m_pFrameRoot);}VOID CSkinMesh::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); }}//-----------------------------------------------------------------------------// Name: DrawMeshContainer()// Desc: Called to render a mesh in the hierarchy//-----------------------------------------------------------------------------VOID CSkinMesh::DrawMeshContainer(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; UINT iPaletteEntry; D3DXMATRIXA16 matTemp; // first check for skinning if (pMeshContainer->pSkinInfo != NULL) { if (m_SkinningMethod == D3DNONINDEXED) { 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 != UINT_MAX) { NumBlend = i; } } if (m_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; if (iMatrixIndex != UINT_MAX) { D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex], pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] ); m_pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ); } } m_pd3dDevice->SetRenderState(D3DRS_VERTEXBLEND, NumBlend); // lookup the material used for this subset of faces if ((AttribIdPrev != pBoneComb[iAttrib].AttribId) || (AttribIdPrev == UNUSED32)) { m_pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ); m_pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ); AttribIdPrev = pBoneComb[iAttrib].AttribId; } // draw the subset now that the correct material and matrices are loaded pMeshContainer->MeshData.pMesh->DrawSubset(iAttrib); } } // If necessary, draw parts that HW could not handle using SW if (pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups) { AttribIdPrev = UNUSED32; m_pd3dDevice->SetSoftwareVertexProcessing(TRUE); for (iAttrib = pMeshContainer->iAttributeSW; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++) { NumBlend = 0; for (DWORD i = 0; i < pMeshContainer->NumInfl; ++i) { if (pBoneComb[iAttrib].BoneId != UINT_MAX) { NumBlend = i; } } if (m_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; if (iMatrixIndex != UINT_MAX) { D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex], pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] ); m_pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ); } } m_pd3dDevice->SetRenderState(D3DRS_VERTEXBLEND, NumBlend); // lookup the material used for this subset of faces if ((AttribIdPrev != pBoneComb[iAttrib].AttribId) || (AttribIdPrev == UNUSED32)) { m_pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ); m_pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ); AttribIdPrev = pBoneComb[iAttrib].AttribId; } // draw the subset now that the correct material and matrices are loaded pMeshContainer->MeshData.pMesh->DrawSubset(iAttrib); } } m_pd3dDevice->SetSoftwareVertexProcessing(FALSE); } m_pd3dDevice->SetRenderState(D3DRS_VERTEXBLEND, 0); } else if (m_SkinningMethod == D3DINDEXED) { // if hw doesn't support indexed vertex processing, switch to software vertex processing if (pMeshContainer->UseSoftwareVP) { m_pd3dDevice->SetSoftwareVertexProcessing(TRUE); } // set the number of vertex blend indices to be blended if (pMeshContainer->NumInfl == 1) m_pd3dDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_0WEIGHTS); else m_pd3dDevice->SetRenderState(D3DRS_VERTEXBLEND, pMeshContainer->NumInfl - 1); if (pMeshContainer->NumInfl) m_pd3dDevice->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, TRUE); // for each attribute group in the mesh, calculate the set of matrices in the palette and then draw the mesh subset pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>(pMeshContainer->pBoneCombinationBuf->GetBufferPointer()); for (iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++) { // first calculate all the world matrices for (iPaletteEntry = 0; iPaletteEntry < pMeshContainer->NumPaletteEntries; ++iPaletteEntry) { iMatrixIndex = pBoneComb[iAttrib].BoneId[iPaletteEntry]; if (iMatrixIndex != UINT_MAX) { D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex], pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] ); m_pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( iPaletteEntry ), &matTemp ); } } // setup the material of the mesh subset - REMEMBER to use the original pre-skinning attribute id to get the correct material id m_pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ); m_pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ); // finally draw the subset with the current world matrix palette and material state pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ); } // reset blending state m_pd3dDevice->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE); m_pd3dDevice->SetRenderState(D3DRS_VERTEXBLEND, 0); // remember to reset back to hw vertex processing if software was required if (pMeshContainer->UseSoftwareVP) { m_pd3dDevice->SetSoftwareVertexProcessing(FALSE); } } else if (m_SkinningMethod == SOFTWARE) { D3DXMATRIX Identity; DWORD cBones = pMeshContainer->pSkinInfo->GetNumBones(); DWORD iBone; PBYTE pbVerticesSrc; PBYTE pbVerticesDest; // set up bone transforms for (iBone = 0; iBone < cBones; ++iBone) { D3DXMatrixMultiply ( &m_pBoneMatrices[iBone], // output &pMeshContainer->pBoneOffsetMatrices[iBone], pMeshContainer->ppBoneMatrixPtrs[iBone] ); } // set world transform D3DXMatrixIdentity(&Identity); m_pd3dDevice->SetTransform(D3DTS_WORLD, &Identity); pMeshContainer->pOrigMesh->LockVertexBuffer(D3DLOCK_READONLY, (LPVOID*)&pbVerticesSrc); pMeshContainer->MeshData.pMesh->LockVertexBuffer(0, (LPVOID*)&pbVerticesDest); // generate skinned mesh pMeshContainer->pSkinInfo->UpdateSkinnedMesh(m_pBoneMatrices, NULL, pbVerticesSrc, pbVerticesDest); pMeshContainer->pOrigMesh->UnlockVertexBuffer(); pMeshContainer->MeshData.pMesh->UnlockVertexBuffer(); for (iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++) { m_pd3dDevice->SetMaterial(&(pMeshContainer->pMaterials[pMeshContainer->pAttributeTable[iAttrib].AttribId].MatD3D)); m_pd3dDevice->SetTexture(0, pMeshContainer->ppTextures[pMeshContainer->pAttributeTable[iAttrib].AttribId]); pMeshContainer->MeshData.pMesh->DrawSubset(pMeshContainer->pAttributeTable[iAttrib].AttribId); } } else // bug out as unsupported mode { return; } } else // standard mesh, just draw it after setting material properties { m_pd3dDevice->SetTransform(D3DTS_WORLD, &pFrame->CombinedTransformationMatrix); for (iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++) { m_pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[iMaterial].MatD3D ); m_pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[iMaterial] ); pMeshContainer->MeshData.pMesh->DrawSubset(iMaterial); } }}VOID CSkinMesh::UpdateFrameMatrices(LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix){ D3DXFRAME_DERIVED *pFrame = (D3DXFRAME_DERIVED*)pFrameBase; if (pParentMatrix != NULL) D3DXMatrixMultiply(&pFrame->CombinedTransformationMatrix, &pFrame->TransformationMatrix, pParentMatrix); else pFrame->CombinedTransformationMatrix = pFrame->TransformationMatrix; if (pFrame->pFrameSibling != NULL) { UpdateFrameMatrices(pFrame->pFrameSibling, pParentMatrix); } if (pFrame->pFrameFirstChild != NULL) { UpdateFrameMatrices(pFrame->pFrameFirstChild, &pFrame->CombinedTransformationMatrix); }} VOID CSkinMesh::SetAnim(BOOL bAnim) { m_bMoving=bAnim; if(!m_bMoving && m_pAnimController) { #if ((D3D_SDK_VERSION&0xFF)== 31) m_pAnimController->SetTime(0); #else m_pAnimController->ResetTime();#endif } } BOOL CSkinMesh::SetAnimationName(char *strAnimName) { if(!m_bMoving || !m_pAnimController ) return FALSE; DWORD nAnimSet; char strTempName[256]; nAnimSet=m_pAnimController->GetNumAnimationSets(); LPD3DXANIMATIONSET pAnimSet; for(DWORD i=0;i<nAnimSet;i++) { m_pAnimController->GetAnimationSet(i,&pAnimSet); strcpy(strTempName, pAnimSet->GetName()); if(strcmp(strAnimName,strTempName)==0) { m_pAnimController->SetTrackAnimationSet(0,pAnimSet); return TRUE; } } return FALSE; };//-----------------------------------------//Name:Intersect//Desc:ʹÓÃË÷ÒýºÍ¶¥µã»º³åÇø£¬ÇóMeshºÍÉäÏß½»µã//2006/6/27 JohnsonFeng//Use: IntersectIndexBuffer//-----------------------------------------BOOL CSkinMesh::InterSect( D3DVECTOR *pRayOrig, D3DVECTOR *pRayDir,D3DVECTOR* pVRet){ return S_OK;}//// Note: this returns the current Mesh.//LPD3DXMESH CSkinMesh::getMesh(){ return m_SkinMesh; }