Sign in to follow this  
  • entries
    45
  • comments
    44
  • views
    50157

Skinned mesh code part 1 - Low level Microsoft code

Sign in to follow this  
Norman Barrows

1426 views

skinned mesh code part 1

low level Microsoft code from DXUT.h and tiny.cpp. June 2010 DX9.0c SDK.
// -------------------------------------------------------------------------------// ----------------------------- directx skinned mesh code -----------------------// ------------------------------------------------------------------------------- // CONTENTS:// section 1: description of the code// section 2: a brief outline of how to use the code// section 3: low level MS code// section 4: high level ROCKAND skinned mesh API// section 5: skinned mesh test routine sample code// section 6: notes on working with skinned meshes // this code is based on the tiny.cpp sample from june 2010 dx sdk. // it allows easy integration of skinned meshes into existing dx9 apps. // it consists of two parts: // 1. low level code copied directly from tiny.cpp that loads and unloads a mesh, // and updates the skeleton with the meshe`s world matrix. this code has been // modified to use ansi strings, dx9 matrices, and to supress compiler warnings// (one unused parameter, and one non-explicit cast).// its also been modified to only search the current directory for files.// for dx10, unicode, and DXUT path searching, just use the corresponding original // tiny.cpp code instead.// the unicode and DXUT path searching has only been commented out, // and can easily be turned back on if desired.// replacing D3DXMATRIX with D3DXMATRIX16A will convert it back to dx10 matrices. // 2. a high level API written by ROCKLAND, based on the tiny.cpp and DXUT code.// this API handles loading and unloading effects and shaders, getting device caps,// cloning and updating controllers, setting up shader parameters for drawing a // skinned mesh, adding the skinned mesh`s world matrix to the skeleton, // drawing the mesh, and changing animations. it also provides a routine to get // a pointer to a bone by name for later drawing of meshes attached to that bone. // this code includes all sample skinned mesh and DXUT code required - macros, struct // definitions, etc. no DXUT required! #include should be all thats required. // note that its not based on the multi-animation sample, so it does not have a// class definition for animated skinned mesh instances. an animated skinned mesh // instance basically consists of the instance`s cloned controller, the instance`s // world matrix, and a pointer to the root frame of the skeleton used by the instance.// you might want to add the instance`s texture and material if you use the// drawmeshcontainer4 routine, which allows materials and textures on a per// instance basis. // in some areas, debug version only code might be commented out. // uncomment it if you`re compiling in debug mode. // note that this code can easily be turned into a stand alone module with its own // header file. i simply haven`t gotten around to it yet.// just copy the declarations for the ROCKLAND skinned mesh API into a header file,// turn off the test routine, and replace any Z3d specifc variable names (IE the device // pointer, the view matrix, and the projection matrix).// so while its not a turnkey library yet, it has everything you need to make one easily. /* a basic outline of how to use the high level ROCKLAND skinned mesh API: // init stuff...get_indexed_behavior_flags // only required for indexed skinning methodsload_skinned_meshload_effect_file // only required for shader based skinning methodsload_shaders // only required for shader based skinning methodsclone_cntroller // optional - for multiple instances of a skinned mesh, each with it own animation.get_bone // optional - get pointer to a bone by name, used to draw meshes attached to a bone.// render loop...while !quit// set the light for the fixed function pipeline. IE: Zsetlite and Zlite in z3d// set the view matrix. IE: Zsetcam in z3d// setup for drawing skinned mesh... set_D3DINDEXEDVS_proj_mat_transpose // only required for D3DINDEXEDVS skinning method. // must be called before set_indexed_shader_proj_mat. set_indexed_shader_proj_mat // only required for indexed shaders. not required for D3DINDEXED skinning method. set_shader_light // only required for shader based skinning methods set_ani // optional: change to a specific animation first// update the animation controller... update_ani // add the world transform for the skinned mesh to the skeleton... add_world_transform_to_skeleton// draw it! draw_skinned_mesh// drawsubset changes the current mesh and fvf! dont forget to reset them before regular drawing!// reset fvf and your state manager`s current mesh here, then draw as usual.// draw some non-skinned meshes here...}// shutdown...unload_shaders // only required for shader based skinning methodsunload_effect_file // only required for shader based skinning methodsunload_skinned_mesh// release any cloned controllers! */ // ----------------------------- LOW LEVEL MS CODE ------------------------------ // ---------------------- DXUT.h macros ----------------------------------------- #if defined(DEBUG) || defined(_DEBUG)#ifndef V#define V(x) { hr = (x); if( FAILED(hr) ) { DXUTTrace( __FILE__, (DWORD)__LINE__, hr, L#x, true ); } }#endif#ifndef V_RETURN#define V_RETURN(x) { hr = (x); if( FAILED(hr) ) { return DXUTTrace( __FILE__, (DWORD)__LINE__, hr, L#x, true ); } }#endif#else#ifndef V#define V(x) { hr = (x); }#endif#ifndef V_RETURN#define V_RETURN(x) { hr = (x); if( FAILED(hr) ) { return hr; } }#endif#endif #ifndef SAFE_DELETE#define SAFE_DELETE(p) { if (p) { delete (p); (p)=NULL; } }#endif #ifndef SAFE_DELETE_ARRAY#define SAFE_DELETE_ARRAY(p) { if (p) { delete[] (p); (p)=NULL; } }#endif #ifndef SAFE_RELEASE#define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p)=NULL; } }#endif // ------------------------------- tiny.cpp code ------------------------------------ //--------------------------------------------------------------------------------------// 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{ D3DXMATRIX 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 int texID;}; // enum for various skinning modes possibleenum METHOD{ D3DNONINDEXED, D3DINDEXED, SOFTWARE, D3DINDEXEDVS, D3DINDEXEDHLSLVS, NONE}; //--------------------------------------------------------------------------------------// Global variables//-------------------------------------------------------------------------------------- METHOD g_SkinningMethod = D3DNONINDEXED; // Current skinning method bool g_bUseSoftwareVP; // Flag to indicate whether software vp is // required due to lack of hardware UINT g_NumBoneMatricesMax = 0; D3DXMATRIX *g_pBoneMatrices = NULL; D3DXMATRIX g_matProjT; // Transpose of projection matrix (for asm shader) ID3DXEffect* g_pEffect = NULL; // D3DX effect interface DWORD g_dwBehaviorFlags; // Behavior flags of the 3D device IDirect3DVertexShader9* g_pIndexedVertexShader[4]; // array of four vertex shaders char g_wszShaderSource[4][30] ={ "skinmesh1.vsh", "skinmesh2.vsh", "skinmesh3.vsh", "skinmesh4.vsh"}; /*WCHAR g_wszShaderSource[4][30] ={ L"skinmesh1.vsh", L"skinmesh2.vsh", L"skinmesh3.vsh", L"skinmesh4.vsh"};*/ //--------------------------------------------------------------------------------------// 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 GenerateSkinnedMesh( IDirect3DDevice9* pd3dDevice, D3DXMESHCONTAINER_DERIVED* pMeshContainer ){ HRESULT hr = S_OK; D3DCAPS9 d3dCaps; pd3dDevice->GetDeviceCaps( &d3dCaps ); if( pMeshContainer->pSkinInfo == NULL ) return hr; g_bUseSoftwareVP = false; SAFE_RELEASE( pMeshContainer->MeshData.pMesh ); SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf ); // if non-indexed skinning mode selected, use ConvertToBlendedMesh to generate drawable mesh if( g_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( 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 > 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(), 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( g_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( d3dCaps.MaxVertexBlendMatrixIndex + 1 < NumMaxFaceInfl ) { // HW does not support indexed vertex blending. Use SW instead pMeshContainer->NumPaletteEntries = min( 256, pMeshContainer->pSkinInfo->GetNumBones() ); pMeshContainer->UseSoftwareVP = true; g_bUseSoftwareVP = 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( ( 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 vertex shader indexed skinning mode selected, use ConvertToIndexedsBlendedMesh to generate drawable mesh else if( ( g_SkinningMethod == D3DINDEXEDVS ) || ( g_SkinningMethod == D3DINDEXEDHLSLVS ) ) { // Get palette size // First 9 constants are used for other data. Each 4x3 matrix takes up 3 constants. // (96 - 9) /3 i.e. Maximum constant count - used constants UINT MaxMatrices = 26; pMeshContainer->NumPaletteEntries = min( MaxMatrices, pMeshContainer->pSkinInfo->GetNumBones() ); DWORD Flags = D3DXMESHOPT_VERTEXCACHE; if( d3dCaps.VertexShaderVersion >= D3DVS_VERSION( 1, 1 ) ) { pMeshContainer->UseSoftwareVP = false; Flags |= D3DXMESH_MANAGED; } else { pMeshContainer->UseSoftwareVP = true; g_bUseSoftwareVP = true; Flags |= D3DXMESH_SYSTEMMEM; } SAFE_RELEASE( pMeshContainer->MeshData.pMesh ); 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; // FVF has to match our declarator. Vertex shaders are not as forgiving as FF pipeline DWORD NewFVF = ( pMeshContainer->MeshData.pMesh->GetFVF() & D3DFVF_POSITION_MASK ) | D3DFVF_NORMAL | D3DFVF_TEX1 | D3DFVF_LASTBETA_UBYTE4; if( NewFVF != pMeshContainer->MeshData.pMesh->GetFVF() ) { LPD3DXMESH pMesh; hr = pMeshContainer->MeshData.pMesh->CloneMeshFVF( pMeshContainer->MeshData.pMesh->GetOptions(), NewFVF, pd3dDevice, &pMesh ); if( !FAILED( hr ) ) { pMeshContainer->MeshData.pMesh->Release(); pMeshContainer->MeshData.pMesh = pMesh; pMesh = NULL; } } D3DVERTEXELEMENT9 pDecl[MAX_FVF_DECL_SIZE]; LPD3DVERTEXELEMENT9 pDeclCur; hr = pMeshContainer->MeshData.pMesh->GetDeclaration( pDecl ); if( FAILED( hr ) ) goto e_Exit; // the vertex shader is expecting to interpret the UBYTE4 as a D3DCOLOR, so update the type // NOTE: this cannot be done with CloneMesh, that would convert the UBYTE4 data to float and then to D3DCOLOR // this is more of a "cast" operation pDeclCur = pDecl; while( pDeclCur->Stream != 0xff ) { if( ( pDeclCur->Usage == D3DDECLUSAGE_BLENDINDICES ) && ( pDeclCur->UsageIndex == 0 ) ) pDeclCur->Type = D3DDECLTYPE_D3DCOLOR; pDeclCur++; } hr = pMeshContainer->MeshData.pMesh->UpdateSemantics( pDecl ); 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( g_NumBoneMatricesMax < pMeshContainer->pSkinInfo->GetNumBones() ) { g_NumBoneMatricesMax = pMeshContainer->pSkinInfo->GetNumBones(); // Allocate space for blend matrices delete[] g_pBoneMatrices; g_pBoneMatrices = new D3DXMATRIX[g_NumBoneMatricesMax]; if( g_pBoneMatrices == NULL ) { hr = E_OUTOFMEMORY; goto e_Exit; } } } // if software skinning selected, use GenerateSkinnedMesh to create a mesh that can be used with UpdateSkinnedMesh else if( g_SkinningMethod == SOFTWARE ) { hr = pMeshContainer->pOrigMesh->CloneMeshFVF( D3DXMESH_MANAGED, pMeshContainer->pOrigMesh->GetFVF(), 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( g_NumBoneMatricesMax < pMeshContainer->pSkinInfo->GetNumBones() ) { g_NumBoneMatricesMax = pMeshContainer->pSkinInfo->GetNumBones(); // Allocate space for blend matrices delete[] g_pBoneMatrices; g_pBoneMatrices = new D3DXMATRIX[g_NumBoneMatricesMax]; if( g_pBoneMatrices == NULL ) { hr = E_OUTOFMEMORY; goto e_Exit; } } } else // invalid g_SkinningMethod value { // return failure due to invalid skinning method value hr = E_INVALIDARG; goto e_Exit; } e_Exit: return hr;} //--------------------------------------------------------------------------------------// Name: class CAllocateHierarchy// Desc: Custom version of ID3DXAllocateHierarchy with custom methods to create// frames and meshcontainers.//--------------------------------------------------------------------------------------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 ); CAllocateHierarchy() { }}; //--------------------------------------------------------------------------------------// Name: AllocateName()// Desc: Allocates memory for a string to hold the name of a frame or mesh//--------------------------------------------------------------------------------------HRESULT AllocateName( LPCSTR Name, LPSTR* pNewName ){ UINT cbLength; if( Name != NULL ) { cbLength = ( UINT )strlen( Name ) + 1; *pNewName = new CHAR[cbLength]; if( *pNewName == NULL ) return E_OUTOFMEMORY; memcpy( *pNewName, Name, cbLength * sizeof( CHAR ) ); } else { *pNewName = NULL; } 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( 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: //--------------------------------------------------------------------------------------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; D3DXMESHCONTAINER_DERIVED *pMeshContainer = NULL; UINT NumFaces; UINT iMaterial; UINT iBone, cBones; LPDIRECT3DDEVICE9 pd3dDevice = NULL; LPD3DXMESH pMesh = NULL; *ppNewMeshContainer = NULL; // ROCKLAND NOTE:// the pEffectInstances parameter isnt used in the code, causing a warning!// so we need to use it to supress the warning.// pEffectInstances is CONST, so we cant just set it to NULL.// we have to assign its value to another variable.// so we define another D3DXEFFECTINSTANCE pointer, and set it equal to pEffectInstances.CONST D3DXEFFECTINSTANCE *tmp;tmp=pEffectInstances; // 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 ) {// ROCKLAND NOTE: caveman uses ansi strings, not unicode.// so this code must be changed from unicode to ansi. // WCHAR strTexturePath[MAX_PATH];// WCHAR wszBuf[MAX_PATH];// MultiByteToWideChar( CP_ACP, 0, pMeshContainer->pMaterials[iMaterial].pTextureFilename, -1, wszBuf, MAX_PATH );// wszBuf[MAX_PATH - 1] = L'\0';// DXUTFindDXSDKMediaFileCch( strTexturePath, MAX_PATH, wszBuf );// if( FAILED( D3DXCreateTextureFromFile( pd3dDevice, strTexturePath,// &pMeshContainer->ppTextures[iMaterial] ) ) ) 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 = 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 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;} //--------------------------------------------------------------------------------------// Called to setup the pointers for a given bone to its transformation matrix//--------------------------------------------------------------------------------------HRESULT SetupBoneMatrixPointersOnMesh( LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME g_pFrameRoot){UINT iBone, cBones;D3DXFRAME_DERIVED* pFrame;D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;if( pMeshContainer->pSkinInfo != NULL ) // if there is a skinmesh, then setup the bone matrices { 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( g_pFrameRoot,pMeshContainer->pSkinInfo->GetBoneName( iBone ) ); if( pFrame == NULL ) return E_FAIL; pMeshContainer->ppBoneMatrixPtrs[iBone] = &pFrame->CombinedTransformationMatrix; } }return S_OK;} //--------------------------------------------------------------------------------------// Called to setup the pointers for a given bone to its transformation matrix//--------------------------------------------------------------------------------------HRESULT SetupBoneMatrixPointers( LPD3DXFRAME pFrame, LPD3DXFRAME g_pFrameRoot ){HRESULT hr;if( pFrame->pMeshContainer != NULL ) { hr = SetupBoneMatrixPointersOnMesh( pFrame->pMeshContainer , g_pFrameRoot ); if ( FAILED( hr ) ) return hr; }if( pFrame->pFrameSibling != NULL ) { hr = SetupBoneMatrixPointers( pFrame->pFrameSibling , g_pFrameRoot ); if( FAILED( hr ) ) return hr; }if( pFrame->pFrameFirstChild != NULL ) { hr = SetupBoneMatrixPointers( pFrame->pFrameFirstChild , g_pFrameRoot ); if( FAILED( hr ) ) return hr; }return S_OK;} //--------------------------------------------------------------------------------------// update the frame matrices//--------------------------------------------------------------------------------------void 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 ); }} /* //--------------------------------------------------------------------------------------// Called to render a mesh in the hierarchy//--------------------------------------------------------------------------------------void DrawMeshContainer( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase, D3DXMATRIX *g_matView){HRESULT hr;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;D3DXMATRIX matTemp;D3DCAPS9 d3dCaps;pd3dDevice->GetDeviceCaps( &d3dCaps ); // first check for skinningif( pMeshContainer->pSkinInfo != NULL ) { if( g_SkinningMethod == D3DNONINDEXED ) { AttribIdPrev = UNUSED32; pBoneComb = reinterpret_cast( 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( 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] ); V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) ); } } V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) ); // lookup the material used for this subset of faces if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) ) { V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) ); V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) ); AttribIdPrev = pBoneComb[iAttrib].AttribId; } // draw the subset now that the correct material and matrices are loaded V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) ); } } // If necessary, draw parts that HW could not handle using SW if( pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups ) { AttribIdPrev = UNUSED32; V( 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( 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] ); V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) ); } } V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) ); // lookup the material used for this subset of faces if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) ) { V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) ); V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) ); AttribIdPrev = pBoneComb[iAttrib].AttribId; } // draw the subset now that the correct material and matrices are loaded V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) ); } } V( pd3dDevice->SetSoftwareVertexProcessing( FALSE ) ); } V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, 0 ) ); } else if( g_SkinningMethod == D3DINDEXED ) { // if hw doesn't support indexed vertex processing, switch to software vertex processing if( pMeshContainer->UseSoftwareVP ) { // If hw or pure hw vertex processing is forced, we can't render the // mesh, so just exit out. Typical applications should create // a device with appropriate vertex processing capability for this // skinning method. if( g_dwBehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING ) return; V( pd3dDevice->SetSoftwareVertexProcessing( TRUE ) ); } // set the number of vertex blend indices to be blended if( pMeshContainer->NumInfl == 1 ) { V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_0WEIGHTS ) ); } else { V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, pMeshContainer->NumInfl - 1 ) ); } if( pMeshContainer->NumInfl ) V( 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( 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] ); V( 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 V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) ); V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) ); // finally draw the subset with the current world matrix palette and material state V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) ); } // reset blending state V( pd3dDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE ) ); V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, 0 ) ); // remember to reset back to hw vertex processing if software was required if( pMeshContainer->UseSoftwareVP ) { V( pd3dDevice->SetSoftwareVertexProcessing( FALSE ) ); } } else if( g_SkinningMethod == D3DINDEXEDVS ) { // Use COLOR instead of UBYTE4 since Geforce3 does not support it // vConst.w should be 3, but due to COLOR/UBYTE4 issue, mul by 255 and add epsilon D3DXVECTOR4 vConst( 1.0f, 0.0f, 0.0f, 765.01f ); if( pMeshContainer->UseSoftwareVP ) { // If hw or pure hw vertex processing is forced, we can't render the // mesh, so just exit out. Typical applications should create // a device with appropriate vertex processing capability for this // skinning method. if( g_dwBehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING ) return; V( pd3dDevice->SetSoftwareVertexProcessing( TRUE ) ); } V( pd3dDevice->SetVertexShader( g_pIndexedVertexShader[pMeshContainer->NumInfl - 1] ) ); pBoneComb = reinterpret_cast( 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] ); D3DXMatrixMultiplyTranspose( &matTemp, &matTemp, g_matView ); V( pd3dDevice->SetVertexShaderConstantF( iPaletteEntry * 3 + 9, ( float* )&matTemp, 3 ) ); } } // Sum of all ambient and emissive contribution D3DXCOLOR color1( pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Ambient ); D3DXCOLOR color2( .25, .25, .25, 1.0 ); D3DXCOLOR ambEmm; D3DXColorModulate( &ambEmm, &color1, &color2 ); ambEmm += D3DXCOLOR( pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Emissive ); // set material color properties V( pd3dDevice->SetVertexShaderConstantF( 8, ( float* )&( pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Diffuse ), 1 ) ); V( pd3dDevice->SetVertexShaderConstantF( 7, ( float* )&ambEmm, 1 ) ); vConst.y = pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Power; V( pd3dDevice->SetVertexShaderConstantF( 0, ( float* )&vConst, 1 ) ); V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) ); // finally draw the subset with the current world matrix palette and material state V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) ); } // remember to reset back to hw vertex processing if software was required if( pMeshContainer->UseSoftwareVP ) { V( pd3dDevice->SetSoftwareVertexProcessing( FALSE ) ); } V( pd3dDevice->SetVertexShader( NULL ) ); } else if( g_SkinningMethod == D3DINDEXEDHLSLVS ) { if( pMeshContainer->UseSoftwareVP ) { // If hw or pure hw vertex processing is forced, we can't render the // mesh, so just exit out. Typical applications should create // a device with appropriate vertex processing capability for this // skinning method. if( g_dwBehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING ) return; V( pd3dDevice->SetSoftwareVertexProcessing( TRUE ) ); } pBoneComb = reinterpret_cast( 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] ); D3DXMatrixMultiply( &g_pBoneMatrices[iPaletteEntry], &matTemp, g_matView ); } } V( g_pEffect->SetMatrixArray( "mWorldMatrixArray", g_pBoneMatrices, pMeshContainer->NumPaletteEntries ) ); // Sum of all ambient and emissive contribution D3DXCOLOR color1( pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Ambient ); D3DXCOLOR color2( .25, .25, .25, 1.0 ); D3DXCOLOR ambEmm; D3DXColorModulate( &ambEmm, &color1, &color2 ); ambEmm += D3DXCOLOR( pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Emissive ); // set material color properties V( g_pEffect->SetVector( "MaterialDiffuse", ( D3DXVECTOR4* )&( pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Diffuse ) ) ); V( g_pEffect->SetVector( "MaterialAmbient", ( D3DXVECTOR4* )&ambEmm ) ); // setup the material of the mesh subset - REMEMBER to use the original pre-skinning attribute id to get the correct material id V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) ); // Set CurNumBones to select the correct vertex shader for the number of bones// ROCKLAND NOTE: had to add explict cast from DWORD to int to suppress warning. V( g_pEffect->SetInt( "CurNumBones", (int)(pMeshContainer->NumInfl - 1) ) ); // Start the effect now all parameters have been updated UINT numPasses; V( g_pEffect->Begin( &numPasses, D3DXFX_DONOTSAVESTATE ) ); for( UINT iPass = 0; iPass < numPasses; iPass++ ) { V( g_pEffect->BeginPass( iPass ) ); // draw the subset with the current world matrix palette and material state V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) ); V( g_pEffect->EndPass() ); } V( g_pEffect->End() ); V( pd3dDevice->SetVertexShader( NULL ) ); } // remember to reset back to hw vertex processing if software was required if( pMeshContainer->UseSoftwareVP ) { V( pd3dDevice->SetSoftwareVertexProcessing( FALSE ) ); } } else if( g_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 ( &g_pBoneMatrices[iBone], // output &pMeshContainer->pBoneOffsetMatrices[iBone], pMeshContainer->ppBoneMatrixPtrs[iBone] ); } // set world transform D3DXMatrixIdentity( &Identity ); V( pd3dDevice->SetTransform( D3DTS_WORLD, &Identity ) ); V( pMeshContainer->pOrigMesh->LockVertexBuffer( D3DLOCK_READONLY, ( LPVOID* )&pbVerticesSrc ) ); V( pMeshContainer->MeshData.pMesh->LockVertexBuffer( 0, ( LPVOID* )&pbVerticesDest ) ); // generate skinned mesh pMeshContainer->pSkinInfo->UpdateSkinnedMesh( g_pBoneMatrices, NULL, pbVerticesSrc, pbVerticesDest ); V( pMeshContainer->pOrigMesh->UnlockVertexBuffer() ); V( pMeshContainer->MeshData.pMesh->UnlockVertexBuffer() ); for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ ) { V( pd3dDevice->SetMaterial( &( pMeshContainer->pMaterials[pMeshContainer->pAttributeTable[iAttrib].AttribId].MatD3D ) ) ); V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pMeshContainer->pAttributeTable[iAttrib].AttribId] ) ); V( pMeshContainer->MeshData.pMesh->DrawSubset( pMeshContainer->pAttributeTable[iAttrib].AttribId ) ); } } else // bug out as unsupported mode { return; } } // end if skin info != NULLelse // standard mesh, just draw it after setting material properties { V( pd3dDevice->SetTransform( D3DTS_WORLD, &pFrame->CombinedTransformationMatrix ) ); for( iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++ ) { V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[iMaterial].MatD3D ) ); V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[iMaterial] ) ); V( pMeshContainer->MeshData.pMesh->DrawSubset( iMaterial ) ); } } } */ // ROCKLAND NOTE: the Z3d game library uses hardware vertex processing only, so the // software vertex processing code in the following routine has been turned off.// uncomment it if you want to use software vertex preocssing as a fallback method.//--------------------------------------------------------------------------------------// Called to render a mesh in the hierarchy. skinned only, non-indexed.//--------------------------------------------------------------------------------------void DrawMeshContainer2( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase){HRESULT hr;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;D3DXMATRIX matTemp;D3DCAPS9 d3dCaps;pd3dDevice->GetDeviceCaps( &d3dCaps ); AttribIdPrev = UNUSED32;pBoneComb = reinterpret_cast( 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( 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] ); V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) ); } } V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) ); // lookup the material used for this subset of faces if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) ) { V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) ); V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) ); AttribIdPrev = pBoneComb[iAttrib].AttribId; } // draw the subset now that the correct material and matrices are loaded V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) ); } } /* // If necessary, draw parts that HW could not handle using SWif( pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups ) { AttribIdPrev = UNUSED32; V( 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( 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] ); V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) ); } } V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) ); // lookup the material used for this subset of faces if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) ) { V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) ); V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) ); AttribIdPrev = pBoneComb[iAttrib].AttribId; } // draw the subset now that the correct material and matrices are loaded V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) ); } } V( pd3dDevice->SetSoftwareVertexProcessing( FALSE ) ); } */V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE ) );} // ROCKLAND NOTE:// this routine was written by rockland, not MS.// bascially its drawmeshcontainer2 with the commented out software vertex procesing code removed.//------------------------------------------------------------------------------------------------// Called to render a mesh in the hierarchy. skinned only, non-indexed. HW vertex processing only.//------------------------------------------------------------------------------------------------void DrawMeshContainer3( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase){HRESULT hr;D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;UINT NumBlend;UINT iAttrib;DWORD AttribIdPrev;LPD3DXBONECOMBINATION pBoneComb;UINT iMatrixIndex;D3DXMATRIX matTemp;D3DCAPS9 d3dCaps;pd3dDevice->GetDeviceCaps( &d3dCaps );AttribIdPrev = UNUSED32;pBoneComb = reinterpret_cast( pMeshContainer->pBoneCombinationBuf->GetBufferPointer() );for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ ) // Draw using default vtx processing of the device (typically HW) { NumBlend = 0; for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i ) { if( pBoneComb[iAttrib].BoneId != UINT_MAX ) { NumBlend = i; } } if( d3dCaps.MaxVertexBlendMatrices >= NumBlend + 1 ) { for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i ) // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends { iMatrixIndex = pBoneComb[iAttrib].BoneId; if( iMatrixIndex != UINT_MAX ) { D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] ); V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) ); } } V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) ); if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) ) // lookup the material used for this subset of faces { V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) ); V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) ); AttribIdPrev = pBoneComb[iAttrib].AttribId; } V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) ); // draw the subset now that the correct material and matrices are loaded } }V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE ) );} // ROCKLAND NOTE:// this routine was written by rockland, not MS.// its the same as drawmeshcontainer3, but uses the current texture and material// bound to the fixed function pipeline instead of those specified by the mesh (if any).// this lets you change the textures and materials used to draw a skinned mesh - and // those textures and materials can be pooled!// note that this is only designd to work with skinned meshes that use the same texture for all subsets.//---------------------------------------------------------------------------------------------------------------------// Called to render a mesh in the hierarchy. skinned only, non-indexed. HW vertex processing only. // uses current material and texture//---------------------------------------------------------------------------------------------------------------------void DrawMeshContainer4( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase){HRESULT hr;D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;UINT NumBlend;UINT iAttrib;DWORD AttribIdPrev;LPD3DXBONECOMBINATION pBoneComb;UINT iMatrixIndex;D3DXMATRIX matTemp;D3DCAPS9 d3dCaps;pd3dDevice->GetDeviceCaps( &d3dCaps );AttribIdPrev = UNUSED32;pBoneComb = reinterpret_cast( pMeshContainer->pBoneCombinationBuf->GetBufferPointer() );for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ ) // Draw using default vtx processing of the device (typically HW) { NumBlend = 0; for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i ) { if( pBoneComb[iAttrib].BoneId != UINT_MAX ) { NumBlend = i; } } if( d3dCaps.MaxVertexBlendMatrices >= NumBlend + 1 ) { for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i ) // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends { iMatrixIndex = pBoneComb[iAttrib].BoneId; if( iMatrixIndex != UINT_MAX ) { D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] ); V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) ); } } V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) ); if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) ) // lookup the material used for this subset of faces {// V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) );// V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) ); AttribIdPrev = pBoneComb[iAttrib].AttribId; } V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) ); // draw the subset now that the correct material and matrices are loaded } }V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE ) );} // ROCKLAND NOTE:// this routine was written by rockland, not MS.// its the same as drawmeshcontainer4, but uses the meshcontainer`s texID, along with the current material// bound to the fixed function pipeline.// this lets you change the textures used to draw each mesh of a skinned mesh - and // those textures can be pooled!// note that this is only designd to work with skinned meshes that use the same texture for all subsets.//---------------------------------------------------------------------------------------------------------------------// Called to render a mesh in the hierarchy. skinned only, non-indexed. HW vertex processing only. // uses current material and texture//---------------------------------------------------------------------------------------------------------------------void DrawMeshContainer5( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase){HRESULT hr;D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;UINT NumBlend;UINT iAttrib;DWORD AttribIdPrev;LPD3DXBONECOMBINATION pBoneComb;UINT iMatrixIndex;D3DXMATRIX matTemp;D3DCAPS9 d3dCaps;pd3dDevice->GetDeviceCaps( &d3dCaps );AttribIdPrev = UNUSED32;pBoneComb = reinterpret_cast( pMeshContainer->pBoneCombinationBuf->GetBufferPointer() );for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ ) // Draw using default vtx processing of the device (typically HW) { NumBlend = 0; for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i ) { if( pBoneComb[iAttrib].BoneId != UINT_MAX ) { NumBlend = i; } } if( d3dCaps.MaxVertexBlendMatrices >= NumBlend + 1 ) { for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i ) // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends { iMatrixIndex = pBoneComb[iAttrib].BoneId; if( iMatrixIndex != UINT_MAX ) { D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] ); V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) ); } } V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) ); if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) ) // lookup the material used for this subset of faces {// V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) );// V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );Zsettex(pMeshContainer->texID); AttribIdPrev = pBoneComb[iAttrib].AttribId; } V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) ); // draw the subset now that the correct material and matrices are loaded } }V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE ) );} // ROCKLAND NOTE: use your choice of drawmeshcontainer, drawmeshcontainer2, // drawmeshcontainer3 , drawmeshcontainer4, or drawmeshcontainer5 in this routine.//--------------------------------------------------------------------------------------// Called to render a frame in the hierarchy//--------------------------------------------------------------------------------------void DrawFrame( IDirect3DDevice9* pd3dDevice, LPD3DXFRAME pFrame, D3DXMATRIX *g_matView ){LPD3DXMESHCONTAINER pMeshContainer;pMeshContainer = pFrame->pMeshContainer;while( pMeshContainer != NULL ) {// DrawMeshContainer( pd3dDevice, pMeshContainer, pFrame, g_matView );// DrawMeshContainer2( pd3dDevice, pMeshContainer);// DrawMeshContainer3( pd3dDevice, pMeshContainer);// drawmeshcontainer4 uses the current material and texture. set material and texture, then call draw_skinned_mesh.// DrawMeshContainer4( pd3dDevice, pMeshContainer);// drawmeshcontainer5 uses the current material and the meshcontainer`s texID. set material and texID`s, then call draw_skinned_mesh. DrawMeshContainer5( pd3dDevice, pMeshContainer); pMeshContainer = pMeshContainer->pNextMeshContainer; }if( pFrame->pFrameSibling != NULL ) { DrawFrame( pd3dDevice, pFrame->pFrameSibling, g_matView ); }if( pFrame->pFrameFirstChild != NULL ) { DrawFrame( pd3dDevice, pFrame->pFrameFirstChild, g_matView ); }} // ----------------------------- end LOW LEVEL MS CODE -----------------------
Sign in to follow this  


4 Comments


Recommended Comments

Note:

 

in the declaration of D3DXMESHCONTAINER_DERIVED,

 

int texID  is a texture ID variable added by Rockland.

 

any game specific members of a derived mesh container should go there.

 

 

 

send questions to rockland@rocklandsoftware.net

Share this comment


Link to comment

Note:  the code in these skinned mesh journal entries is from the .cpp output files generated by Rockland's CScript macro processor. since it contains both c++ code and c++ code generated by CScript, the formatting may be a little off.  I personally never look at the .cpp code, and code exclusively in CScript.  

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now