/** 
 * \file Forest.cpp
 * \brief This file renders forest in scenography. There is 3D forest (tree models) 
 * and 2D forest (tree bilboards).
 * 
 * \author Petar Bajic, MPE (C) All Rights Reserved, Homepage: www.mystic-peanut.com
 * \date January, 16 2007.
 */

#include "Forest.h"

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

void chartowchar(WCHAR* wstr, int size, char* str);
float GetRandomFloat(float lowBound, float highBound);

float m_z[20];
float m_x[20];
float m_x2[20];
float m_s[20];
float m_r[20];

/**
 * \brief Standard constructor for 2D forest.
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
CForest::CForest()
{
	m_pMesh = NULL;
	m_pTexture = NULL;
	m_pMaterial = NULL;
}

/**
 * \brief Standard constructor for 3D forest.
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
C3DForest::C3DForest()
{
	m_p3DTreeMesh = NULL;
	m_p3DTreeTexture = NULL;
	m_p3DTreeMaterial = NULL;
}

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

/**
 * \brief Standard destructor for 3D forest.
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
C3DForest::~C3DForest()
{
	SAFE_RELEASE(m_p3DTreeMesh);
	for (DWORD i=0; i<m_3DTreeNumMaterials; i++)
	{if(m_p3DTreeTexture) SAFE_RELEASE(m_p3DTreeTexture[i]);}
}

/**
 * \brief Load static tree mesh that is basicly 2D poligon with picture of a tree on it!
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
bool CForest::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();

	for(int j=0;j<20;j++)
	{
		// get random float in [0, 1] interval
		m_z[j] = GetRandomFloat(130.0f, 150.0f);
		m_x[j] = GetRandomFloat(-170.0f, 170.0f);
		m_x2[j] = GetRandomFloat(-70.0f, 70.0f);
		m_s[j] = GetRandomFloat(5.0f, 15.0f);
		m_r[j] = GetRandomFloat(0.0f, 6.0f);
	}

	return true;
}

/**
 * \brief Renders 2D forest on the hill. Trees are just bilboards layedout in one line.
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
void CForest::OnRender(LPDIRECT3DDEVICE9 pd3dDevice)
{
	if (m_pMesh)
	{
		//Turn on D3D lighting, so we can see materials color
		pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
		//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);

		//Render 10 trees in row
		for(int j=0;j<10;j++)
		{
			D3DXMATRIXA16 mat,smat,rmat,tmat;
			D3DXMatrixRotationY(&rmat, -D3DX_PI/2);
			D3DXMatrixScaling(&smat, m_s[j], m_s[j], m_s[j]);
			//trees are all in one row but each one is 1 point closer to player (by z coordinate)
			//because transparency flickering is present when bilboards are touching
			D3DXMatrixTranslation( &tmat, m_x[j], 20, (float)(140-j) ); 
			D3DXMatrixMultiply(&mat, &rmat, &smat);
			D3DXMatrixMultiply(&mat, &mat, &tmat);
			//Set world matrix (model position)
			pd3dDevice->SetTransform( D3DTS_WORLD, &mat );

			/* 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++)
			{
				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 Load static tree 3D mesh.
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
bool C3DForest::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_3DTreeNumMaterials, &m_p3DTreeMesh);
	if( FAILED(hr) || (m_p3DTreeMesh == NULL) )
		return false;

	D3DXMATERIAL* d3dxMaterials = (D3DXMATERIAL*)materialBuffer->GetBufferPointer();
	m_p3DTreeMaterial = new D3DMATERIAL9[m_3DTreeNumMaterials];
	m_p3DTreeTexture = new LPDIRECT3DTEXTURE9[m_3DTreeNumMaterials];
	for (DWORD i=0; i<m_3DTreeNumMaterials; i++)
	{
		StringCchCopy( temp_path, MAX_PATH, strFilePath );
		// Copy the material
		m_p3DTreeMaterial[i] = d3dxMaterials[i].MatD3D;
		// Set the ambient color for the material (D3DX does not do this)
		m_p3DTreeMaterial[i].Ambient = m_p3DTreeMaterial[i].Diffuse;
		// Create the texture if it exists - it may not
		m_p3DTreeTexture[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_p3DTreeTexture[i]);
			if (hrk == S_OK)
				filename[0] = '\0';
		}
	}
	materialBuffer->Release();
	return true;
}

/**
 * \brief Renders 3D forest (tall cut-off trees with bushes on an trunk)
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
void C3DForest::OnRender(LPDIRECT3DDEVICE9 pd3dDevice)
{
	if (m_p3DTreeMesh)
	{
		pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
		//Render 5 trees in first row (z == 50)
		for(int j=0;j<5;j++)
		{
			D3DXMATRIXA16 mat,smat,rmat,tmat;
			D3DXMatrixRotationY(&rmat, m_r[j]);
			D3DXMatrixScaling(&smat, 1.0f, 1.0f, 1.0f);
			D3DXMatrixTranslation( &tmat, m_x2[j], 0, (float)(50) );
			D3DXMatrixMultiply(&mat, &rmat, &smat);
			D3DXMatrixMultiply(&mat, &mat, &tmat);
			//Set world matrix (model position)
			pd3dDevice->SetTransform( D3DTS_WORLD, &mat );

			for (DWORD i=0; i<m_3DTreeNumMaterials; i++)
			{
				pd3dDevice->SetMaterial(&m_p3DTreeMaterial[i]);
				pd3dDevice->SetTexture(0, m_p3DTreeTexture[i]);

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

		//Render 5 trees in second row (z == 60)
		for(int j=5;j<10;j++)
		{
			D3DXMATRIXA16 mat,smat,rmat,tmat;
			D3DXMatrixRotationY(&rmat, m_r[j]);
			D3DXMatrixScaling(&smat, 1.0f, 1.0f, 1.0f);
			D3DXMatrixTranslation( &tmat, m_x2[j], 0, (float)(60) );
			D3DXMatrixMultiply(&mat, &rmat, &smat);
			D3DXMatrixMultiply(&mat, &mat, &tmat);
			//Set world matrix (model position)
			pd3dDevice->SetTransform( D3DTS_WORLD, &mat );

			for (DWORD i=0; i<m_3DTreeNumMaterials; i++)
			{
				pd3dDevice->SetMaterial(&m_p3DTreeMaterial[i]);
				pd3dDevice->SetTexture(0, m_p3DTreeTexture[i]);

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