www.pudn.com > DirectX source.zip > CMeshNode.cpp
/*!
@file : CMeshNode.cpp
Contains the implementation of class CMeshNode.
@author Sarmad Kh. Abdulla
*/
//------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
// Header files
#include "stdafx.h"
/*!
* Initialize the object.
*
* @param void :
*
* @return void :
*/
void CMeshNode::InitObject( void )
{
pD3DXSkinMesh = NULL;
pD3DXBlendedMesh = NULL;
rgiAdjacency = NULL;
aMaterial = NULL;
apD3DTexture = NULL;
apBoneMatrix = NULL;
aBoneOffsetMatrix = NULL;
pBoneNamesBuf = NULL;
pBoneCombinationBuf = NULL;
}
/*!
* Create the mesh object from an x file data object.
*
* @param pxofobj : The x file data object to read from.
* @param pd3ddevice : The D3D device.
*
* @return void :
*/
void CMeshNode::Create( LPDIRECTXFILEDATA pxofobj, LPDIRECT3DDEVICE8 pd3ddevice)
{
HRESULT result;
LPD3DXBUFFER pmaterialsbuf = NULL;
LPD3DXBUFFER padjacencybuf = NULL;
LPD3DXBUFFER pboneoffsetbuf = NULL;
DWORD dwnamelength;
UINT dwfacescount;
// get the name
result = pxofobj->GetName( NULL, &dwnamelength );
if( FAILED( result ) )
throw new CDXException( "Couldn't get x file data object's name", result );
if( dwnamelength > 0 )
{
char * pname = new char[dwnamelength];
SAFEPOINTER( pname );
result = pxofobj->GetName( pname, &dwnamelength );
if( FAILED( result ) )
throw new CDXException( "Couldn't get x file data object's name", result );
SetName( pname );
delete[] pname;
}
// load the mesh
result = D3DXLoadSkinMeshFromXof( pxofobj, D3DXMESH_MANAGED, pd3ddevice, &padjacencybuf,
&pmaterialsbuf, &dwMaterialCount, &pBoneNamesBuf, &pboneoffsetbuf, &pD3DXSkinMesh );
if( FAILED( result ) )
throw new CDXException( "Couldn't load skin mesh", result );
dwfacescount = pD3DXSkinMesh->GetNumFaces();
// Process skinning data
if( pD3DXSkinMesh->GetNumBones() )
{
// bones are available
apBoneMatrix = new D3DXMATRIX*[pD3DXSkinMesh->GetNumBones()];
SAFEPOINTER( apBoneMatrix );
aBoneOffsetMatrix = new D3DXMATRIX[pD3DXSkinMesh->GetNumBones()];
SAFEPOINTER( aBoneOffsetMatrix );
CopyMemory( aBoneOffsetMatrix, pboneoffsetbuf->GetBufferPointer(),
sizeof(D3DXMATRIX)*pD3DXSkinMesh->GetNumBones() );
LPDWORD padjacencyin = static_cast(padjacencybuf->GetBufferPointer());
rgiAdjacency = new DWORD[dwfacescount * 3];
SAFEPOINTER( rgiAdjacency );
CopyMemory( rgiAdjacency, padjacencyin, dwfacescount * 3 * sizeof(DWORD) );
GenerateMesh();
}
else
{
// no bones are found, blend the mesh and use it as a static mesh
pD3DXSkinMesh->GetOriginalMesh( &pD3DXBlendedMesh );
SAFERELEASE( pD3DXSkinMesh );
dwAttrCount = dwMaterialCount;
}
// check for materials
if( (pmaterialsbuf == NULL) || (dwMaterialCount == 0))
{
// no materials are found, create default material
aMaterial = new D3DMATERIAL8[1];
SAFEPOINTER( aMaterial );
apD3DTexture = new LPDIRECT3DTEXTURE8[1];
SAFEPOINTER( apD3DTexture );
memset( aMaterial, 0, sizeof(D3DMATERIAL8) );
aMaterial[0].Diffuse.r = 0.5f;
aMaterial[0].Diffuse.g = 0.5f;
aMaterial[0].Diffuse.b = 0.5f;
aMaterial[0].Specular = aMaterial[0].Diffuse;
apD3DTexture[0] = NULL;
dwAttrCount = dwMaterialCount = 1;
}
else
{
// load the materials
aMaterial = new D3DMATERIAL8[dwMaterialCount];
SAFEPOINTER( aMaterial );
apD3DTexture = new LPDIRECT3DTEXTURE8[dwMaterialCount];
SAFEPOINTER( apD3DTexture );
ZeroMemory( apD3DTexture, dwMaterialCount * sizeof(void*) );
LPD3DXMATERIAL axmaterial = (LPD3DXMATERIAL)pmaterialsbuf->GetBufferPointer();
for( int i = 0; i < dwMaterialCount; i++ )
{
aMaterial[i] = axmaterial[i].MatD3D;
if( axmaterial[i].pTextureFilename != NULL )
{
// load the texture
HRESULT result = D3DXCreateTextureFromFileEx( pd3ddevice,
axmaterial[i].pTextureFilename, D3DX_DEFAULT, D3DX_DEFAULT,
D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT,
D3DX_DEFAULT, 0, NULL, NULL, &apD3DTexture[i] );
if( FAILED( result ) )
throw new CFileException( axmaterial[i].pTextureFilename );
}
else
apD3DTexture[i] = NULL;
}
}
SAFERELEASE( padjacencybuf );
SAFERELEASE( pmaterialsbuf );
SAFERELEASE( pboneoffsetbuf );
}
/*!
* Generates the blended mesh to be used by the rendering function.
*
* @param void :
*
* @return void :
*/
void CMeshNode::GenerateMesh( void )
{
HRESULT result;
DWORD* padjacencyin;
DWORD dwfacecount = pD3DXSkinMesh->GetNumFaces();
// copy the adjacency buffer
padjacencyin = new DWORD[dwfacecount * 3];
SAFEPOINTER( padjacencyin );
memcpy( padjacencyin, rgiAdjacency, dwfacecount * 3 * sizeof(DWORD) );
SAFERELEASE( pD3DXBlendedMesh );
// blend the mesh
result = pD3DXSkinMesh->ConvertToBlendedMesh( D3DXMESH_WRITEONLY, padjacencyin,
rgiAdjacency, &dwAttrCount, &pBoneCombinationBuf, &pD3DXBlendedMesh );
delete[] padjacencyin;
if( FAILED(result) )
throw new CDXException( "Couldn't convert to blended mesh", result );
// calculate the max face influence count
if( (pD3DXBlendedMesh->GetFVF() & D3DFVF_POSITION_MASK) != D3DFVF_XYZ )
dwMaxFaceInfl = 1 + ((pD3DXBlendedMesh->GetFVF() & D3DFVF_POSITION_MASK) - D3DFVF_XYZRHW) / 2;
else
dwMaxFaceInfl = 1;
}
/*!
* Render the mesh to a device
*
* @param pd3ddevice : pointer to the device to render to.
*
* @return void :
*/
void CMeshNode::Render( LPDIRECT3DDEVICE8 pd3ddevice )
{
UINT ipattr;
// check if we have a skin mesh or a normal mesh
if (pD3DXSkinMesh)
{
// draw the skin mesh
// draw using non indexed blending
// assuming that the hardware can support the number of matrices
// get the bone combinations
LPD3DXBONECOMBINATION abonecombination;
abonecombination = (LPD3DXBONECOMBINATION)pBoneCombinationBuf->GetBufferPointer();
// enable vertex blending
pd3ddevice->SetRenderState( D3DRS_VERTEXBLEND, dwMaxFaceInfl - 1 );
for( ipattr = 0; ipattr < dwAttrCount; ipattr++ )
{
// set the matrices for this attribute range
for( DWORD i = 0; i < dwMaxFaceInfl; ++i )
{
DWORD matid = abonecombination[ipattr].BoneId[i];
if( matid != UINT_MAX && (ipattr == 0 || matid != abonecombination[ipattr - 1].BoneId[i]))
{
pd3ddevice->SetTransform( D3DTS_WORLDMATRIX(i), apBoneMatrix[matid] );
pd3ddevice->MultiplyTransform( D3DTS_WORLDMATRIX(i), &aBoneOffsetMatrix[matid]);
}
}
// check if the material is different than that of the previous attribute
if( ipattr == 0 || (abonecombination[ipattr].AttribId != abonecombination[ipattr - 1].AttribId))
{
// set the new material
pd3ddevice->SetMaterial( &(aMaterial[abonecombination[ipattr].AttribId]) );
pd3ddevice->SetTexture( 0, apD3DTexture[abonecombination[ipattr].AttribId] );
}
// render
pD3DXBlendedMesh->DrawSubset( ipattr );
}
// rendering is finished, set the vertex blending back to disabled mode
pd3ddevice->SetRenderState( D3DRS_VERTEXBLEND, 0 );
}
else // pD3DSkinMesh
{
// we don't have a skin mesh, so draw the normal mesh
for( ipattr = 0; ipattr < dwAttrCount; ipattr++ )
{
pd3ddevice->SetMaterial( &(aMaterial[ipattr]) );
pd3ddevice->SetTexture( 0, apD3DTexture[ipattr] );
pD3DXBlendedMesh->DrawSubset( ipattr );
}
}
}
/*!
* Destroy the object.
*
* @param void :
*
* @return void :
*/
void CMeshNode::Destroy( void )
{
SAFERELEASE( pD3DXSkinMesh );
SAFERELEASE( pD3DXBlendedMesh );
SAFEDELETEARRAY( aMaterial );
if( apD3DTexture )
{
for( WORD i = 0; i < dwMaterialCount; i++ )
if( apD3DTexture[i] )
apD3DTexture[i]->Release();
delete[] apD3DTexture;
apD3DTexture = NULL;
}
SAFEDELETEARRAY( apBoneMatrix );
SAFEDELETEARRAY( aBoneOffsetMatrix );
SAFEDELETEARRAY( rgiAdjacency );
SAFERELEASE( pBoneCombinationBuf );
SAFERELEASE( pBoneNamesBuf );
}
/*!
* Find the bones of this mesh object.
*
* @param prootframe : the root frame to start searching for frames
*
* @return void :
*/
void CMeshNode::FindBones( CFrameNode * prootframe )
{
if( !pD3DXSkinMesh ) return;
// get the buffer with the names of the bones
char ** abonename = static_cast(pBoneNamesBuf->GetBufferPointer());
for( DWORD i = 0; i < pD3DXSkinMesh->GetNumBones(); i++ )
{
// find a frame with the same name
CFrameNode * pframe = prootframe->FindFrame( abonename[i] );
// get a pointer to its world matrix
apBoneMatrix[i] = pframe->GetCombinedMatrix();
}
}