Jump to content
  • Advertisement
Sign in to follow this  
  • entries
    45
  • comments
    44
  • views
    50937

Skinned mesh code part 1 - Low level Microsoft code

Sign in to follow this  
Norman Barrows

1544 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
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!