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(); 
	} 
}