/** 
 * \file ModelStatic.cpp
 * \brief File containing CModelStatic class function implementation.
 * This class enables using static (as oposed to skinned, animated) 3D mesh. It loads x file and manages rendering.
 * 
 * \author Petar Bajic, MPE (C) All Rights Reserved, Homepage: www.mystic-peanut.com
 * \date January, 16 2007.
 */

#include "ModelStatic.h"

#pragma warning( disable : 4996 ) // disable deprecated warning 
#include <strsafe.h>
#pragma warning( default : 4996 )

//utility function for simple character conversion
void chartowchar(WCHAR* wstr, int size, char* str)
{
	for (int i=0; i<size; i++)
	if(str[i] != '\0')
	{
		wstr[i]= str[i];
	}
	else
	{
		wstr[i]= str[i];
		break;
	}
}

/**
 * \brief Standard constructor.
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
CModelStatic::CModelStatic()
{
	m_pMesh = NULL;
	m_pTexture = NULL;
	m_pMaterial = NULL;
	D3DXMatrixIdentity(&m_matWorld);
	m_Position = D3DXVECTOR3(0.0f,0.0f,0.0f);
}

/**
 * \brief Standard destructor.
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
CModelStatic::~CModelStatic()
{
	SAFE_RELEASE(m_pMesh);
	for (DWORD i=0; i<m_numMaterials; i++)
	{if(m_pTexture) SAFE_RELEASE(m_pTexture[i]);}
}

/**
 * \brief Load static mesh using simple old D3DXLoadMeshFromX function
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
bool CModelStatic::Load(LPDIRECT3DDEVICE9 pd3dDevice, WCHAR* strFilePath, WCHAR* strFileName)
{
	HRESULT hr;
	LPD3DXBUFFER materialBuffer;
	WCHAR temp_path[MAX_PATH];

	hr = D3DXLoadMeshFromX(strFileName, D3DXMESH_MANAGED,
				pd3dDevice, NULL, &materialBuffer,NULL, &m_numMaterials, &m_pMesh);
	if( FAILED(hr) || (m_pMesh == NULL) )
		return false;

	D3DXMATERIAL* d3dxMaterials = (D3DXMATERIAL*)materialBuffer->GetBufferPointer();
	m_pMaterial = new D3DMATERIAL9[m_numMaterials];
	m_pTexture = new LPDIRECT3DTEXTURE9[m_numMaterials];
	for (DWORD i=0; i<m_numMaterials; i++)
	{
		StringCchCopy( temp_path, MAX_PATH, strFilePath );
		// Copy the material
		m_pMaterial[i] = d3dxMaterials[i].MatD3D;
		// Set the ambient color for the material (D3DX does not do this)
		m_pMaterial[i].Ambient = m_pMaterial[i].Diffuse;
		// Create the texture if it exists - it may not
		m_pTexture[i] = NULL;
		if (d3dxMaterials[i].pTextureFilename)
		{
			WCHAR filename[MAX_PATH];
			chartowchar(filename, MAX_PATH, d3dxMaterials[i].pTextureFilename);
			StringCchCat(temp_path, MAX_PATH, filename);
			HRESULT hrk = D3DXCreateTextureFromFileW(pd3dDevice, temp_path, &m_pTexture[i]);
			if (hrk == S_OK)
				filename[0] = '\0';
		}
	}
	materialBuffer->Release();
	//m_Alive = true;
	return true;
}

/**
 * \brief Render function renders static model.
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
void CModelStatic::OnRender(LPDIRECT3DDEVICE9 pd3dDevice)
{
	if (m_pMesh)
	{
		//Turn on D3D lighting, so we can see materials color
		pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
		
		//Set world matrix (model position)
		pd3dDevice->SetTransform( D3DTS_WORLD, &m_matWorld );

		/* When rendering our subsets if any of them are transparent (contain alpha information) 
		you should render them last after rendering the ones without alpha. This is required for 
		the blending to work correctly. So you could have two loops, the first one renders all 
		subsets where the material has a diffuse alpha component of 1.0f (no transparency) and 
		the second one renders all those with an alpha component less than 1.0f (some transparency).*/
		
		for (DWORD i=0; i<m_numMaterials; i++)
		{

		//Use alpha from texture
		pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
		pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
		pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
		pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
		pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

			pd3dDevice->SetMaterial(&m_pMaterial[i]);
			pd3dDevice->SetTexture(0, m_pTexture[i]);

			// Draw the mesh subset
			m_pMesh->DrawSubset(i);
		}

		pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
	}
}

/**
 * \brief Move function doesn't have much work to do for static 3D mesh! It just positions him in the world.
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
void CModelStatic::OnFrameMove(LPDIRECT3DDEVICE9 pd3dDevice, float rotation, D3DXVECTOR3 position)
{
	D3DXMATRIXA16 matTrans;
	D3DXMatrixRotationY(&m_matWorld, rotation);
	if((m_Position.x!=0.0f) && (m_Position.y!=0.0f) && (m_Position.z!=0.0f))
	{
		D3DXMatrixTranslation( &matTrans, m_Position.x, m_Position.y, m_Position.z );
	}
	else
	{
		D3DXMatrixTranslation( &matTrans, position.x, position.y, position.z );
	}
	D3DXMatrixMultiply(&m_matWorld, &m_matWorld, &matTrans);
}