/*!
	@file RuEntity_Particle_Grass.h

	Copyright (c) 2004-2005 All rights reserved
*/

#ifndef _RUENTITY_PARTICLE_GRASS_H_
#define _RUENTITY_PARTICLE_GRASS_H_

#include "../../Scene/Base/RuEntityBase.h"

#pragma unmanaged

// ************************************************************************************************************************************************************

class CRuParticle_Grass
{
public:
	CRuVector3								m_position;							//!< Position of the grass particle
	UINT16									m_textureIndex;						//!< Texture index
	UINT8									m_subTextureIndex;					//!< Sub-texture index
	UINT8									m_modelStyle;						//!< Mesh style number
	REAL									m_scale;							//!< Object scale
	REAL									m_swayMagnitude;					//!< Object swaying magnitude
};

// ************************************************************************************************************************************************************

class CRuGrassDatabase : public IRuObject
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	struct TextureDesc
	{
		char*								m_textureName;								//!< Resource name of the grass texture
		INT32								m_uSubdivision;								//!< Subdivision size along u
		INT32								m_vSubdivision;								//!< Subdivision size along v

		// Run-time
		IRuTexture*							m_texture;									//!< Instanced texture
	};

	BOOL									m_isDirty;
	CRuArrayList<TextureDesc>				m_textureList;

public:
											CRuGrassDatabase();
	virtual									~CRuGrassDatabase();

	// IRuObject serialization
	virtual BOOL							SerializeFrom(IRuStream *stream);
	virtual BOOL							SerializeTo(IRuStream *stream);

	INT32									GetNumTextures();
	INT32									AppendTexture();

	const char*								GetTextureName(INT32 index) const;
	INT32									GetUSubdivision(INT32 index) const;
	INT32									GetVSubdivision(INT32 index) const;
	BOOL									SetTextureName(INT32 index, const char *textureName);
	BOOL									SetUSubdivision(INT32 index, INT32 subdivision);
	BOOL									SetVSubdivision(INT32 index, INT32 subdivision);

	void									SetDirty(BOOL dirty);
	BOOL									IsDirty();

	IRuTexture*								GetTexture(INT32 index);
};

// ************************************************************************************************************************************************************

class CRuEntity_Particle_Grass : public IRuEntity_Renderable
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	class BatchDesc
	{
	public:
		INT32								m_numParticles;						//!< Number of particles in this batch
		REAL*								m_vsConstants;						//!< Vertex shader constants array
		CRuProxyMesh*						m_mesh;								//!< Proxy mesh
		CRuMaterial*						m_material;							//!< Material to use for the batch

	public:
		BatchDesc(INT32 maxParticlesPerBatch)
		{
			// Create constants array
			m_vsConstants = ruNEW REAL [8 * maxParticlesPerBatch + 24];

			// Create proxy mesh
			m_mesh = ruNEW CRuProxyMesh();

			// Create material and assign constants array to it
			m_material = ruNEW CRuMaterial();
			m_material->SetShader(g_ruResourceManager->GetShader("ruSHADER_PARTICLE_GRASS"));
			m_material->GetParameterBlock()->SetParameter(ruPARAMTYPE_GEOINSTANCE_S_COUNT, 0);
			m_material->GetParameterBlock()->SetParameter(ruPARAMTYPE_GEOINSTANCE_S_DATA, (DWORD) m_vsConstants);
		}

		~BatchDesc()
		{
			// Delete constants array
			delete[] m_vsConstants;

			// Release proxy mesh
			ruSAFE_RELEASE(m_mesh);

			// Release material
			ruSAFE_RELEASE(m_material);
		}
	};

	INT32									m_maxParticlesPerBatch;				//!< Maximum number of particles to draw per batch

	REAL									m_periodInterval;					//!< Length of time, in seconds, of one period (PI)
	REAL									m_t;

	CRuGrassDatabase*						m_grassDB;							//!< Grass DB to use when rendering grass
	CRuMesh*								m_mesh;								//!< Mesh
	CRuArrayList<BatchDesc *>				m_batches;							//!< Currently active batch list
	CRuStack<BatchDesc *>					m_freeBatchDescStack;				//!< Stack of unused batch descriptors

	CRuArrayList<UINT32>					m_sortCriterias[2];					//!< Sorting criterias
	CRuRadixSort							m_radixSorter;						//!< Radix sorter

public:
											CRuEntity_Particle_Grass();
	virtual									~CRuEntity_Particle_Grass();

	// Base entity interface
	virtual CRuEntity*						Clone(CRuEntity *clonedEntity = NULL);
	virtual BOOL							ResetStates();

	// Base interface
	virtual BOOL							Update(REAL elapsedTime);

	// IRuEntity_Renderable interface
	virtual INT32							GetNumMeshes() const;
	virtual BOOL							GetMesh(INT32 meshIndex, IRuMesh **meshOut, IRuMaterial **materialOut);

	void									SetGrassDatabase(CRuGrassDatabase *grassDB);
	void									SetParticleList(const CRuArrayList<CRuParticle_Grass> &particleList, BOOL calculateEntityBounds = FALSE);

protected:
	void									ClearBatches();
};

// ************************************************************************************************************************************************************

class CRuEntity_Particle_Grass_Optimized : public IRuEntity_Renderable
{
	ruRTTI_DECLARE
	ruHEAP_DECLARE
	ruCLASSGUID_DECLARE

protected:
	INT32									m_maxParticlesPerBatch;				//!< Maximum number of particles to draw per batch

	REAL									m_periodInterval;					//!< Length of time, in seconds, of one period (PI)
	REAL									m_t;

	CRuGrassDatabase*						m_grassDB;							//!< Grass DB to use when rendering grass
	CRuArrayList<CRuMesh *>					m_meshes;							//!< Meshes
	CRuArrayList<CRuMaterial *>				m_materials;						//!< Materials
	CRuArrayList<REAL *>					m_vsConstants;

	CRuArrayList<UINT32>					m_sortCriterias[2];					//!< Sorting criterias
	CRuRadixSort							m_radixSorter;						//!< Radix sorter

public:
											CRuEntity_Particle_Grass_Optimized();
	virtual									~CRuEntity_Particle_Grass_Optimized();

	// Base entity interface
	virtual CRuEntity*						Clone(CRuEntity *clonedEntity = NULL);
	virtual BOOL							ResetStates();

	// Base interface
	virtual BOOL							Update(REAL elapsedTime);

	// IRuEntity_Renderable interface
	virtual INT32							GetNumMeshes() const;
	virtual BOOL							GetMesh(INT32 meshIndex, IRuMesh **meshOut, IRuMaterial **materialOut);

	void									SetGrassDatabase(CRuGrassDatabase *grassDB);
	void									SetParticleList(const CRuArrayList<CRuParticle_Grass> &particleList, BOOL calculateEntityBounds = FALSE);

protected:
	void									FillVertexData(CRuMesh *mesh, INT32 numParticles);
	void									ClearBatches();
};

// ************************************************************************************************************************************************************

#pragma managed

#endif
