/////////////////////////////////////////////////
//
// File: "CD3DModel.cpp"
//
// Author: Jason Jurecka
//
// Creation Date: June 5, 2003
//
// Purpose: This is a wrapper for the DirectX functions
//	that work with models. This class when completed
//	will allow for loading, drawing, texturing, and
//	model animation.
//
// Note: This was written for DirectX version 9
/////////////////////////////////////////////////

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <d3dx9.h>
#include <d3dx9anim.h>
#include "CAllocateHierarchy.h"
#include "CD3DModel.h"
#include "DemoInterfaceC.h"
#include "FileHandleC.h"
#include "ImportableImageI.h"
#include "DX9Image.h"
#include <dxerr9.h>


using namespace PajaSystem;
using namespace PluginClass;
using namespace Import;
using namespace DX9ImagePlugin;


#define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } }
#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }

static
void
TRACE( const char* szFormat, ...  )
{
	char	szMsg[256];
	va_list	rList;
	va_start( rList, szFormat );
	_vsnprintf( szMsg, 255, szFormat, rList );
	va_end( rList );
	OutputDebugString( szMsg );
}


CD3DModelInstance::CD3DModelInstance( CD3DModel* pModel ) :
	m_pModel( pModel ),
	m_pAC( 0 )
{
	// Empty
}

CD3DModelInstance::~CD3DModelInstance()
{
	if( m_pModel )
		m_pModel->RemoveInstance( this );
}

HRESULT
CD3DModelInstance::Setup( LPD3DXANIMATIONCONTROLLER pAC )
{
	m_pAC = pAC;

/*
	// Start with all tracks disabled
  dwTracks = m_pAC->GetMaxNumTracks();
  for( i = 0; i < dwTracks; ++ i )
      m_pAC->SetTrackEnable( i, FALSE );
*/
	return S_OK;
}

void
CD3DModelInstance::Cleanup()
{
	SAFE_RELEASE( m_pAC );
	m_pModel = 0;
}

void
CD3DModelInstance::Draw( RenderContextS* pRC )
{
	if( m_pModel )
		m_pModel->Draw( pRC );
}

void
CD3DModelInstance::Update( double dTime, LPD3DXMATRIX pMatModel )
{
	//Set the time for animation
	if( m_pAC )
	{
		double	dCurTime = m_pAC->GetTime();
		if( dTime < dCurTime  )
			m_pAC->ResetTime();
		else
			dTime -= dCurTime;

		m_pAC->AdvanceTime( dTime, NULL );
	}

	//Update the frame hierarchy
	if( m_pModel )
	{
		m_pModel->UpdateMatrices( pMatModel );
	}
}

float
CD3DModelInstance::GetAnimMaxTime()
{
	if( m_pModel )
		return m_pModel->GetAnimMaxTime();
	return 0;
}

void
CD3DModelInstance::Release()
{
	delete this;
}


CD3DModel::CD3DModel( LPDIRECT3DDEVICE9 pD3DDevice ) :
	m_pd3dDevice( pD3DDevice ),
	m_pFrameRoot( NULL ),
	m_vCenter( 0.0f, 0.0f, 0.0f ),
	m_fRadius( 0.0f ),
	m_dwCurrentAnimation( 0 ),
	m_dwAnimationSetCount( 0 ),
	m_NumBoneMatricesMax( 0 ),
	m_pAnimController( NULL ),
	m_pBoneMatrices( NULL )
{
	// empty
}

CD3DModel::CD3DModel( const CD3DModel& )
{
	// empty
}

CD3DModel::~CD3DModel()
{
	for( std::vector<TextureHandleS*>::iterator TexIt = m_vecTextureHandles.begin(); TexIt != m_vecTextureHandles.end(); ++TexIt )
	{
		delete (*TexIt);
	}

	for( std::vector<CD3DModelInstance*>::iterator MoIt = m_vecInstances.begin(); MoIt != m_vecInstances.end(); ++MoIt )
	{
		(*MoIt)->Cleanup();
	}
	m_vecInstances.clear();


	//Delete Animation Controller
	SAFE_RELEASE( m_pAnimController );

	delete [] m_pBoneMatrices;
	m_pBoneMatrices = 0;

	//if there is a frame hierarchyo
	if( m_pFrameRoot )
	{
		//Allocation class
		CAllocateHierarchy	Alloc;
		D3DXFrameDestroy( m_pFrameRoot, &Alloc );
		m_pFrameRoot = NULL;
	}

	//Make the Device not point to the other device
	m_pd3dDevice = NULL;
}

CD3DModel&
CD3DModel::operator=( const CD3DModel& Mdl )
{
	return *this;
}

const D3DXVECTOR3&
CD3DModel::GetBoundingSphereCenter() const
{
	return m_vCenter;
}

float
CD3DModel::GetBoundingSphereRadius() const
{
	return m_fRadius;
}

DWORD
CD3DModel::GetCurrentAnimation() const
{
	return m_dwCurrentAnimation;
}

HRESULT
CD3DModel::LoadXFile( const char* strFileName )
{
	HRESULT			hr;
	CAllocateHierarchy	Alloc;

	hr = D3DXLoadMeshHierarchyFromX( strFileName, D3DXMESH_MANAGED, m_pd3dDevice, &Alloc, NULL, &m_pFrameRoot, &m_pAnimController );
	if( FAILED( hr ) )
		return hr;

	hr = SetupBoneMatrixPointers( m_pFrameRoot );
	if( FAILED( hr ) )
		return hr;

	hr = D3DXFrameCalculateBoundingSphere( m_pFrameRoot, &m_vCenter, &m_fRadius );
	if( FAILED( hr ) )
		return hr;

	return S_OK;
	if( m_pAnimController )
		m_dwAnimationSetCount = m_pAnimController->GetMaxNumAnimationSets();
}

HRESULT
CD3DModel::LoadXFile( BYTE* pData, UINT uiDataSize )
{
	HRESULT			hr;
	CAllocateHierarchy	Alloc;

	hr = D3DXLoadMeshHierarchyFromXInMemory( pData, uiDataSize, D3DXMESH_MANAGED, m_pd3dDevice, &Alloc, NULL, &m_pFrameRoot, &m_pAnimController );
	if( FAILED( hr ) )
		return hr;

	hr = SetupBoneMatrixPointers( m_pFrameRoot );
	if( FAILED( hr ) )
		return hr;

	hr = D3DXFrameCalculateBoundingSphere( m_pFrameRoot, &m_vCenter, &m_fRadius );
	if( FAILED( hr ) )
		return hr;

	return S_OK;
	if( m_pAnimController )
		m_dwAnimationSetCount = m_pAnimController->GetMaxNumAnimationSets();
}

HRESULT
CD3DModel::CreateInstance( CD3DModelInstance** ppAnimInstance )
{
	* ppAnimInstance = NULL;

	LPD3DXANIMATIONCONTROLLER	pNewAC = NULL;
	HRESULT hr = S_OK;
	CD3DModelInstance* pModelInst = NULL;

	// Clone the original AC.  This clone is what we will use to animate
	// this mesh; the original never gets used except to clone, since we
	// always need to be able to add another instance at any time.
	if( m_pAnimController )
	{
		hr = m_pAnimController->CloneAnimationController( m_pAnimController->GetMaxNumAnimationOutputs(),
			m_pAnimController->GetMaxNumAnimationSets(),
			m_pAnimController->GetMaxNumTracks(),
			m_pAnimController->GetMaxNumEvents(),
			&pNewAC );
	}

	if( SUCCEEDED( hr ) )
	{
		// create the new AI
		pModelInst = new CD3DModelInstance( this );
		if( pModelInst == NULL )
		{
			hr = E_OUTOFMEMORY;
			goto e_Exit;
		}

		// set it up
		hr = pModelInst->Setup( pNewAC );
		if( FAILED( hr ) )
			goto e_Exit;

		* ppAnimInstance = pModelInst;

		m_vecInstances.push_back( pModelInst );
	}

e_Exit:

	if( FAILED( hr ) )
	{
		if( pModelInst )
			pModelInst->Release();

		if( pNewAC )
			pNewAC->Release();
	}

	return hr;
}

void
CD3DModel::RemoveInstance( CD3DModelInstance* pAnimInstance )
{
	for( std::vector<CD3DModelInstance*>::iterator MoIt = m_vecInstances.begin(); MoIt != m_vecInstances.end(); ++MoIt )
	{
		if( (*MoIt) == pAnimInstance )
		{
			m_vecInstances.erase( MoIt );
			return;
		}
	}
}

HRESULT
CD3DModel::SetupBoneMatrixPointersOnMesh( LPD3DXMESHCONTAINER pMeshContainerBase )
{
	UINT	iBone, cBones;
	D3DXFRAME_DERIVED*	pFrame;

	D3DXMESHCONTAINER_DERIVED*	pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pMeshContainerBase;

	// if there is a skinmesh, then setup the bone matrices
	if( pMeshContainer->pSkinInfo != NULL )
	{
		cBones = pMeshContainer->pSkinInfo->GetNumBones();

		pMeshContainer->ppBoneMatrixPtrs = new D3DXMATRIX*[cBones];
		if( pMeshContainer->ppBoneMatrixPtrs == NULL )
			return E_OUTOFMEMORY;

		for( iBone = 0; iBone < cBones; iBone++ )
		{
			pFrame = (D3DXFRAME_DERIVED*)D3DXFrameFind( m_pFrameRoot, pMeshContainer->pSkinInfo->GetBoneName( iBone ) );
			if( pFrame == NULL )
				return E_FAIL;

			pMeshContainer->ppBoneMatrixPtrs[iBone] = &pFrame->CombinedTransformationMatrix;
		}

		// allocate a buffer for bone matrices, but only if another mesh has not allocated one of the same size or larger
		if( pMeshContainer->pSkinInfo->GetNumBones() > m_NumBoneMatricesMax )
		{
			m_NumBoneMatricesMax = pMeshContainer->pSkinInfo->GetNumBones();

			// Allocate space for blend matrices
			delete [] m_pBoneMatrices; 
			m_pBoneMatrices = new D3DXMATRIX[m_NumBoneMatricesMax];
			if( m_pBoneMatrices == NULL )
				return E_OUTOFMEMORY;
		}
	}

	return S_OK;
}

HRESULT
CD3DModel::SetupBoneMatrixPointers( LPD3DXFRAME pFrame )
{
	HRESULT hr;

	if( pFrame->pMeshContainer != NULL )
	{
		hr = SetupBoneMatrixPointersOnMesh( pFrame->pMeshContainer );
		if( FAILED( hr ) )
			return hr;
	}

	if( pFrame->pFrameSibling != NULL )
	{
		hr = SetupBoneMatrixPointers( pFrame->pFrameSibling );
		if( FAILED( hr ) )
			return hr;
	}

	if( pFrame->pFrameFirstChild != NULL )
	{
		hr = SetupBoneMatrixPointers( pFrame->pFrameFirstChild );
		if( FAILED( hr ) )
			return hr;
	}

	return S_OK;
}

void
CD3DModel::SetCurrentAnimation( DWORD dwAnimationFlag )
{
	// If the animation is not one that we are already using
	//	and the passed in flag is not bigger than the number of animations
	if( dwAnimationFlag != m_dwCurrentAnimation && dwAnimationFlag < m_dwAnimationSetCount )
	{ 
		m_dwCurrentAnimation = dwAnimationFlag;
		LPD3DXANIMATIONSET	pAnimSet = NULL;
		m_pAnimController->GetAnimationSet( m_dwCurrentAnimation, &pAnimSet );
		m_pAnimController->SetTrackAnimationSet( 0, pAnimSet );
		SAFE_RELEASE( pAnimSet )
	}
}

double
CD3DModel::GetCurrentAnimationPerioid()
{
	double	dPerioid = 0;
	LPD3DXANIMATIONSET	pAnimSet = NULL;
	m_pAnimController->GetAnimationSet( m_dwCurrentAnimation, &pAnimSet );
	if( pAnimSet )
		dPerioid = pAnimSet->GetPeriod();
	SAFE_RELEASE( pAnimSet );
	return dPerioid;
}

//////////////////////////////////////////////////////////////////////////
// Draw Functions
//////////////////////////////////////////////////////////////////////////

void CD3DModel::Draw( RenderContextS* pRC )
{
	DrawFrame( m_pFrameRoot, pRC );
}

void CD3DModel::DrawFrame( LPD3DXFRAME pFrame, RenderContextS* pRC )
{
	LPD3DXMESHCONTAINER	pMeshContainer;

	pMeshContainer = pFrame->pMeshContainer;
	while( pMeshContainer != NULL )
	{
		DrawMeshContainer( pMeshContainer, pFrame, pRC );

		pMeshContainer = pMeshContainer->pNextMeshContainer;
	}

	if( pFrame->pFrameSibling != NULL )
	{
		DrawFrame( pFrame->pFrameSibling, pRC );
	}

	if( pFrame->pFrameFirstChild != NULL )
	{
		DrawFrame( pFrame->pFrameFirstChild, pRC );
	}
}

void
CD3DModel::DrawMeshContainer( LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase, RenderContextS* pRC )
{
	D3DXMESHCONTAINER_DERIVED*	pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pMeshContainerBase;
	D3DXFRAME_DERIVED*					pFrame = (D3DXFRAME_DERIVED*)pFrameBase;
	UINT	iMaterial;
	UINT	iAttrib;
	LPD3DXBONECOMBINATION	pBoneComb;

	UINT	iMatrixIndex;
	UINT	iPaletteEntry;
	D3DXMATRIX	matTemp;

	// first check for skinning
	if( pMeshContainer->pSkinInfo != NULL )
	{
		pRC->pEffect->SetTechnique( "tBone" );

		pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>( pMeshContainer->pBoneCombinationBuf->GetBufferPointer() );
		for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )
		{ 
			// first calculate all the world matrices
			for( iPaletteEntry = 0; iPaletteEntry < pMeshContainer->NumPaletteEntries; ++iPaletteEntry )
			{
				iMatrixIndex = pBoneComb[iAttrib].BoneId[iPaletteEntry];
				if( iMatrixIndex != UINT_MAX )
				{
					D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex], pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
					D3DXMatrixMultiply( &m_pBoneMatrices[iPaletteEntry], &matTemp, pRC->pMatView );
				}
			}
			pRC->pEffect->SetMatrixArray( "mWorldMatrixArray", m_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 );

			// setup the material of the mesh subset - REMEMBER to use the original pre-skinning attribute id to get the correct material id
			DWORD	dwTexID = pMeshContainer->pTextures[pBoneComb[iAttrib].AttribId];
			if( dwTexID != 0 )
			{
				TextureHandleS*	pHandle = m_vecTextureHandles[dwTexID - 1];
				if( pHandle->pTexHandle )
				{
					ImportableImageI*	pTex = (ImportableImageI*)pHandle->pTexHandle->get_importable();
					if( pTex )
					{
						pTex->eval_state( pRC->iTime );
						pTex->bind_texture( pRC->pDevice, 0, IMAGE_WRAP | IMAGE_LINEAR );
					}

					// Read back the D3D texture interface.
					IDirect3DBaseTexture9*	pD3DTex = 0;
					m_pd3dDevice->GetTexture( 0, &pD3DTex );

					HRESULT	hr;
					if( FAILED( hr = pRC->pEffect->SetTexture( "tBaseTex", pD3DTex ) ) )
						TRACE( "set base texture failed %s\n", DXGetErrorString9( hr ) );

					if( pD3DTex )
						pD3DTex->Release();

					// set material color properties 
					D3DXVECTOR4	White( 1, 1, 1, 1 );
					pRC->pEffect->SetVector( "MaterialDiffuse", &White );
				}
				else
					m_pd3dDevice->SetTexture( 0, NULL );
			}
			else
			{
				m_pd3dDevice->SetTexture( 0, NULL );
				// set material color properties 
				pRC->pEffect->SetVector( "MaterialDiffuse", (D3DXVECTOR4*)&(pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Diffuse) );
			}
			pRC->pEffect->SetVector( "MaterialAmbient", (D3DXVECTOR4*)&ambEmm );


			// Set CurNumBones to select the correct vertex shader for the number of bones
			pRC->pEffect->SetInt( "CurNumBones", pMeshContainer->NumInfl - 1 );

			// Start the effect now all parameters have been updated
			UINT numPasses;
			pRC->pEffect->Begin( &numPasses, D3DXFX_DONOTSAVESTATE );
			for( UINT iPass = 0; iPass < numPasses; iPass++ )
			{
				pRC->pEffect->Pass( iPass );
				// draw the subset with the current world matrix palette and material state
				pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib );
			}

			pRC->pEffect->End();        

			m_pd3dDevice->SetVertexShader( NULL );
		}
	}
	else
	{
		bool	bUseBump = false;

		for( iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++ )
		{
			DWORD	dwTexID = pMeshContainer->pTextures[iMaterial];
			if( dwTexID != 0 )
			{
				TextureHandleS*	pHandle = m_vecTextureHandles[dwTexID - 1];
				if( pHandle->pBumpTexHandle )
					bUseBump = true;
			}
		}

		HRESULT	hr;

		if( bUseBump )
		{
			if( FAILED( hr = pRC->pEffect->SetTechnique( "tBump" ) ) )
				TRACE( "set tech 'tBump' failed %s\n", DXGetErrorString9( hr ) );
		}
		else
		{
			if( FAILED( hr = pRC->pEffect->SetTechnique( "tBasic" ) ) )
				TRACE( "set tech 'tBasic' failed %s\n", DXGetErrorString9( hr ) );
		}

		// calculate object space camera position.
		D3DXVECTOR3	CamPos;
		CamPos.x = 0;
		CamPos.y = 0;
		CamPos.z = 0;

		D3DXMatrixMultiply( &matTemp, &pFrame->CombinedTransformationMatrix, pRC->pMatView );

		D3DXMATRIX	matInvObj;
		D3DXMatrixInverse( &matInvObj, NULL, &matTemp );

		D3DXVECTOR4	objCamPos;
		D3DXVec3Transform( &objCamPos, &CamPos, &matInvObj );

		pRC->pEffect->SetVector( "eyePosObj", &objCamPos );

		pRC->pEffect->SetMatrixArray( "mWorldMatrixArray", &matTemp, 1 );

		for( iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++ )
		{
			// Sum of all ambient and emissive contribution
			D3DXCOLOR	color1(pMeshContainer->pMaterials[iMaterial].MatD3D.Ambient);
			D3DXCOLOR	color2(.25, .25, .25, 1.0);
			D3DXCOLOR	ambEmm;
			D3DXColorModulate( &ambEmm, &color1, &color2 );
			ambEmm += D3DXCOLOR( pMeshContainer->pMaterials[iMaterial].MatD3D.Emissive );

			// set material color properties 
			pRC->pEffect->SetVector( "MaterialAmbient", (D3DXVECTOR4*)&ambEmm );

			DWORD	dwTexID = pMeshContainer->pTextures[iMaterial];
			if( dwTexID != 0 )
			{
				TextureHandleS*	pHandle = m_vecTextureHandles[dwTexID - 1];
				if( pHandle->pTexHandle )
				{
					ImportableImageI*	pTex = (ImportableImageI*)pHandle->pTexHandle->get_importable();
					if( pTex )
					{
						pTex->eval_state( pRC->iTime );
						pTex->bind_texture( pRC->pDevice, 0, IMAGE_WRAP | IMAGE_LINEAR );

						// Read back the D3D texture interface.
						IDirect3DBaseTexture9*	pD3DTex = 0;
						m_pd3dDevice->GetTexture( 0, &pD3DTex );

						if( FAILED( hr = pRC->pEffect->SetTexture( "tBaseTex", pD3DTex ) ) )
							TRACE( "set base texture failed %s\n", DXGetErrorString9( hr ) );

						if( pD3DTex )
							pD3DTex->Release();

						D3DXVECTOR4	White( 1, 1, 1, 1 );
						pRC->pEffect->SetVector( "MaterialDiffuse", &White );
					}
					else
					{
						m_pd3dDevice->SetTexture( 0, NULL );
						if( FAILED( hr = pRC->pEffect->SetTexture( "tBaseTex", NULL ) ) )
							TRACE( "set bump texture failed %s\n", DXGetErrorString9( hr ) );
					}
				}
				else
				{
					if( FAILED( hr = pRC->pEffect->SetTexture( "tBaseTex", NULL ) ) )
						TRACE( "set bump texture failed %s\n", DXGetErrorString9( hr ) );
					m_pd3dDevice->SetTexture( 0, NULL );
					pRC->pEffect->SetVector( "MaterialDiffuse", (D3DXVECTOR4*)&(pMeshContainer->pMaterials[iMaterial].MatD3D.Diffuse) );
				}

				if( bUseBump && pHandle->pBumpTexHandle )
				{
					ImportableImageI*	pTex = (ImportableImageI*)pHandle->pBumpTexHandle->get_importable();
					if( pTex )
					{
						pTex->eval_state( pRC->iTime );
						pTex->bind_texture( pRC->pDevice, 1, IMAGE_WRAP | IMAGE_LINEAR );

						IDirect3DBaseTexture9*	pD3DTex = 0;
						m_pd3dDevice->GetTexture( 1, &pD3DTex );

						if( FAILED( hr = pRC->pEffect->SetTexture( "tBumpTex", pD3DTex ) ) )
							TRACE( "set bump texture failed %s\n", DXGetErrorString9( hr ) );

						if( pD3DTex )
							pD3DTex->Release();
					}
					else
					{
						m_pd3dDevice->SetTexture( 1, NULL );
						if( FAILED( hr = pRC->pEffect->SetTexture( "tBumpTex", NULL ) ) )
							TRACE( "set bump texture failed %s\n", DXGetErrorString9( hr ) );
					}
				}
				else
				{
					m_pd3dDevice->SetTexture( 1, NULL );
					if( FAILED( hr = pRC->pEffect->SetTexture( "tBumpTex", NULL ) ) )
						TRACE( "set bump texture failed %s\n", DXGetErrorString9( hr ) );
				}
			}
			else
			{
				m_pd3dDevice->SetTexture( 0, NULL );
				m_pd3dDevice->SetTexture( 1, NULL );

				if( FAILED( hr = pRC->pEffect->SetTexture( "tBaseTex", NULL ) ) )
					TRACE( "set bump texture failed %s\n", DXGetErrorString9( hr ) );
				if( FAILED( hr = pRC->pEffect->SetTexture( "tBumpTex", NULL ) ) )
					TRACE( "set bump texture failed %s\n", DXGetErrorString9( hr ) );

				pRC->pEffect->SetVector( "MaterialDiffuse", (D3DXVECTOR4*)&(pMeshContainer->pMaterials[iMaterial].MatD3D.Diffuse) );
			}


			// Start the effect now all parameters have been updated
			UINT numPasses;
			pRC->pEffect->Begin( &numPasses, D3DXFX_DONOTSAVESTATE );
			for( UINT iPass = 0; iPass < numPasses; iPass++ )
			{
				pRC->pEffect->Pass( iPass );
				// draw the subset with the current world matrix palette and material state
				pMeshContainer->MeshData.pMesh->DrawSubset( iMaterial );
			}

			pRC->pEffect->End();        

			m_pd3dDevice->SetVertexShader( NULL );
		}
	}
}

void
CD3DModel::Update( double dTime, LPD3DXMATRIX pMatModel )
{
	//Set the time for animation
	if( m_pAnimController )
	{
		double	dCurTime = m_pAnimController->GetTime();
		if( dTime < dCurTime  )
			m_pAnimController->ResetTime();
		else
			dTime -= dCurTime;

		m_pAnimController->AdvanceTime( dTime, NULL );
	}

	//Update the frame hierarchy
	if( m_pFrameRoot )
	{
		UpdateFrameMatrices( m_pFrameRoot, pMatModel );
	}
}

void
CD3DModel::UpdateMatrices( LPD3DXMATRIX pMatModel )
{
	//Update the frame hierarchy
	if( m_pFrameRoot )
	{
		UpdateFrameMatrices( m_pFrameRoot, pMatModel );
	}
}

void
CD3DModel::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 );
	}
}


int
CD3DModel::GetTextureCount()
{
	return (int)m_vecTextureHandles.size();
}

FileHandleC*
CD3DModel::GetTextureHandle( int i )
{
	if( i >= 0 && i < (int)m_vecTextureHandles.size() && m_vecTextureHandles[i] )
		return m_vecTextureHandles[i]->pTexHandle;
	return 0;
}

const std::string&
CD3DModel::GetTextureName( int i )
{
	static std::string	strEmpty;
	if( i >= 0 && i < (int)m_vecTextureHandles.size() && m_vecTextureHandles[i] )
		return m_vecTextureHandles[i]->strTexName;
	return strEmpty;
}

FileHandleC*
CD3DModel::GetBumpTextureHandle( int i )
{
	if( i >= 0 && i < (int)m_vecTextureHandles.size() && m_vecTextureHandles[i] )
		return m_vecTextureHandles[i]->pBumpTexHandle;
	return 0;
}

const std::string&
CD3DModel::GetBumpTextureName( int i )
{
	static std::string	strEmpty;
	if( i >= 0 && i < (int)m_vecTextureHandles.size() && m_vecTextureHandles[i] )
		return m_vecTextureHandles[i]->strBumpTexName;
	return strEmpty;
}

void
CD3DModel::SetTextureCount( int iCount )
{
	m_vecTextureHandles.resize( iCount );
	for( int i = 0; i < iCount; i++ )
	{
		TextureHandleS*	pHandle = new TextureHandleS;
		pHandle->pTexHandle = 0;
		pHandle->pBumpTexHandle = 0;
		m_vecTextureHandles[i] = pHandle;
	}
}

void
CD3DModel::SetTextureHandle( int i, Import::FileHandleC* pHandle )
{
	if( i >= 0 && i < (int)m_vecTextureHandles.size() && m_vecTextureHandles[i] )
		m_vecTextureHandles[i]->pTexHandle = pHandle;
}

void
CD3DModel::SetBumpTextureHandle( int i, Import::FileHandleC* pHandle )
{
	if( i >= 0 && i < (int)m_vecTextureHandles.size() && m_vecTextureHandles[i] )
		m_vecTextureHandles[i]->pBumpTexHandle = pHandle;
}

void
CD3DModel::SetTextureName( int i, const std::string& strName )
{
	if( i >= 0 && i < (int)m_vecTextureHandles.size() && m_vecTextureHandles[i] )
		m_vecTextureHandles[i]->strTexName = strName;
}

void
CD3DModel::SetBumpTextureName( int i, const std::string& strName )
{
	if( i >= 0 && i < (int)m_vecTextureHandles.size() && m_vecTextureHandles[i] )
		m_vecTextureHandles[i]->strBumpTexName = strName;
}

void
CD3DModel::LoadTextures( DemoInterfaceC* pInterface )
{
	LoadFrameTextures( m_pFrameRoot, pInterface );
}

void
CD3DModel::LoadFrameTextures( LPD3DXFRAME pFrame, DemoInterfaceC* pInterface )
{
	D3DXMESHCONTAINER_DERIVED*	pMeshContainer;

	pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pFrame->pMeshContainer;
	while( pMeshContainer != NULL )
	{
		for( UINT i = 0; i < pMeshContainer->NumMaterials; i++ )
		{
			if( pMeshContainer->pMaterials[i].pTextureFilename != NULL )
			{
				TextureHandleS*	pHandle = new TextureHandleS;
				pHandle->strTexName = pMeshContainer->pMaterials[i].pTextureFilename;

				pHandle->pTexHandle = pInterface->request_import( pInterface->get_absolute_path( pHandle->strTexName.c_str() ), SUPERCLASS_IMAGE, NULL_CLASSID );

				// Check if the bumpmap exists.
				char path_buffer[_MAX_PATH];
				char drive[_MAX_DRIVE];
				char dir[_MAX_DIR];
				char fname[_MAX_FNAME];
				char ext[_MAX_EXT];
				_splitpath( pMeshContainer->pMaterials[i].pTextureFilename, drive, dir, fname, ext );
				strcat( fname, "_bump" );
				_makepath( path_buffer, drive, dir, fname, ext );

				pHandle->strBumpTexName = path_buffer;
				pHandle->pBumpTexHandle = pInterface->request_import( pInterface->get_absolute_path( pHandle->strBumpTexName.c_str() ), SUPERCLASS_IMAGE, NULL_CLASSID );

				m_vecTextureHandles.push_back( pHandle );
				// the zero index means, no texture, we have base one here.
				pMeshContainer->pTextures[i] = (DWORD)m_vecTextureHandles.size();
			}
		}

		pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pMeshContainer->pNextMeshContainer;
	}

	if( pFrame->pFrameSibling != NULL )
	{
		LoadFrameTextures( pFrame->pFrameSibling, pInterface );
	}

	if( pFrame->pFrameFirstChild != NULL )
	{
		LoadFrameTextures( pFrame->pFrameFirstChild, pInterface );
	}
}

void
CD3DModel::FixTextures()
{
	FixFrameTextures( m_pFrameRoot );
}

void
CD3DModel::FixFrameTextures( LPD3DXFRAME pFrame )
{
	D3DXMESHCONTAINER_DERIVED*	pMeshContainer;

	pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pFrame->pMeshContainer;
	while( pMeshContainer != NULL )
	{
		for( UINT i = 0; i < pMeshContainer->NumMaterials; i++ )
		{
			if( pMeshContainer->pMaterials[i].pTextureFilename != NULL )
			{
				// Find texture form the set.
				for( int j = 0; j < (int)m_vecTextureHandles.size(); j++ )
				{
					TextureHandleS*	pHandle = m_vecTextureHandles[j];
					if( !pHandle )
						continue;
					if( pHandle->strTexName.compare( pMeshContainer->pMaterials[i].pTextureFilename ) == 0 )
					{
						pMeshContainer->pTextures[i] = j + 1;
						break;
					}
				}
			}
		}

		pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pMeshContainer->pNextMeshContainer;
	}

	if( pFrame->pFrameSibling != NULL )
	{
		FixFrameTextures( pFrame->pFrameSibling );
	}

	if( pFrame->pFrameFirstChild != NULL )
	{
		FixFrameTextures( pFrame->pFrameFirstChild );
	}
}

float
CD3DModel::GetAnimMaxTime()
{
	LPD3DXANIMATIONSET	pSet = 0;
	m_pAnimController->GetAnimationSet( 0, &pSet );
	if( pSet )
		return pSet->GetPeriod();
	return 0;
}
