#include "../../Direct3D/Shader/RuShader_D3D_Particle.h"

#pragma unmanaged

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

CRuShader_Particle_Grass::CRuShader_Particle_Grass(CRuEngineD3D *engine)
:	CRuShader_Base(engine)
{
	m_override_Fog = FALSE;
	m_override_AlphaTest = TRUE;
	m_override_AlphaBlend = TRUE;

	// Setup vertex elements
	CRuVertexElement vertexElements[] =
	{
		{ 0,	0,	ruELEMENTTYPE_FLOAT3,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_POSITION,	0 },
		{ 0,	12,	ruELEMENTTYPE_D3DCOLOR,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_COLOR,		0 },
		RUVERTEXDECL_END()
	};

	// Construct vertex declaration
	m_vertexDecl = m_engine->CreateVertexDeclaration(vertexElements);


	// Vertex shader source
	char *vsSource = "\
																													\n\
	vs_1_1															; Vertex shader version 1.1						\n\
																													\n\
	dcl_position0		v0											; Declare position								\n\
	dcl_color			v2											; Declare diffuse								\n\
																													\n\
	; Generate sin(c27.z)																							\n\
	mov					r1.x,		c27.z							; Move t into r1.x								\n\
	mad					r0.x,		r1.x,		c30.w,		c30.y	; Bring argument into [-PI, PI]					\n\
	expp				r0.y,		r0.x																			\n\
	mad					r0.x,		r0.y,		c30.z,		-c30.x													\n\
	dst					r2.xy,		r0.x,		r0.x				; Generate 1, (r0.x)^2, ..., (r0.x)^6			\n\
	mul					r2.z,		r2.y,		r2.y																\n\
	mul					r2.w,		r2.y,		r2.z																\n\
	mul					r0,			r2,			r0.x				; Generate r0.x, (r0.x)^3, .., (r0.x)^7			\n\
	dp4					r3.x,		r0,			c32					; Compute sin(r0.x)								\n\
																													\n\
	; Calculate index to instance data																				\n\
	mul					r2.x,		v2.w,		c28.x				; Scale diffuse alpha by 256 to get an integer	\n\
	mad					r2.x,		r2.x,		c28.y,		c28.z	; Calculate address index for row 0 in r2.x		\n\
	add					r2.y,		r2.x,		c20.y				; Calculate address index for row 1 in r2.y		\n\
																													\n\
	; Transform position into homogeneous space																		\n\
	mov					r0,			c20.yyyy						; Move 1 into r0								\n\
	mov					r1,			c20.yyyy						; Move 1 into r1								\n\
	mov					r8,			c20.yyyy						; Move 1 into r8								\n\
																													\n\
	; Calculate position																							\n\
	mov					a0.x,		r2.x							; Copy row 0 index to index register			\n\
	mov					r8.xyz,		c[a0.x].xyz						; Copy particle origin to r8					\n\
	mul					r0.xyz,		v0,			c[a0.x].w			; Scale											\n\
	add					r0.xyz,		r0,			c[a0.x]				; Translate										\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
	mul					r1.xz,		r3.x,		c[a0.x].zw			; Calculate offset								\n\
	mul					r1.xz,		r1.xz,		v2.z				; Multiply offset by y modifier					\n\
	add					r0.xz,		r1.xz,		r0.xz				; Add to position								\n"
	"																												\n\
	m4x3				r1,			r0,			c3					; Transform vertex to view space				\n\
	m4x4				oPos,		r1,			c6					; Transform vertex to homogeneous space			\n\
																													\n\
	; Fog factor																									\n\
	sub					r3.x,		c25.z,		c25.y				; Fog End - Fog Start into r3.x					\n\
	rcp					r3.x,		r3.x							; Reciprocal of fog range into r0.x				\n\
	sub					r4.x,		r1.z,		c25.y				; Offset vertex z by fog start into r3.x		\n\
	mul					r3.x,		r4.x,		r3.x				; Divide z by fog range into r0					\n\
	sub					r3.x,		c20.y,		r3.x				; Invert fog factor								\n\
	max					r3.x,		r3.x,		c20.w				; Clamp to [0, 1]								\n\
	min					r3.x,		r3.x,		c20.y																\n\
	mov					oFog,		r3.x							; Output fog factor								\n"
	"																												\n\
	; Normal into r3																								\n\
	mov					r3,			c29																				\n\
																													\n\
	; Ambient lighting into r4																						\n\
	mov					r4,			c10																				\n\
																													\n\
	; Object-level spherical harmonics																				\n\
	add					r4,			c11,		r4					; Constant term									\n\
	mad					r4,			c12,		r3.x,		r4		; 1st-order terms								\n\
	mad					r4,			c13,		r3.y,		r4														\n\
	mad					r4,			c14,		r3.z,		r4														\n\
	mul					r5.x,		r3.x,		r3.z				; X * Z											\n\
	mul					r5.y,		r3.z,		r3.y				; Z * Y											\n\
	mul					r5.z,		r3.y,		r3.x				; Y * X											\n\
	mad					r4,			c15,		r5.x,		r4		; 2nd-order terms								\n\
	mad					r4,			c16,		r5.y,		r4														\n\
	mad					r4,			c17,		r5.z,		r4														\n\
	mul					r5.xyz,		r3,			r3																	\n\
	mul					r5.z,		r5.z,		c20.x																\n\
	sub					r5.z,		r5.z,		c20.y																\n\
	mad					r4,			c18,		r5.z,		r4														\n\
	sub					r5.x,		r5.x,		r5.y																\n\
	mad					r4,			c19,		r5.x,		r4														\n"
	"																												\n\
	; Diffuse color																									\n\
	mul					r4.w,		r1.z,		c28.w				; Divide z by fade distance into r4.w			\n\
	sub					r4.w,		c20.y,		r4.w				; Invert										\n\
	max					r4.w,		r4.w,		c20.w				; Clamp to [0, 1]								\n\
	min					r4.w,		r4.w,		c20.y																\n\
	mul					r4,			r4,			c26					; Multiply diffuse by global diffuse			\n\
	mov					oD0,		r4								; Set diffuse									\n\
																													\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
																													\n\
	; Calculate texture coordinates																					\n\
	mov					r3.xy,		c27.xy							; Move uv increments into r3					\n\
	mul					r1.xy,		c[a0.x].xy,	r3.xy				; Calculate subtexture uv origin				\n\
	mad					r0.xy,		v2.xy,		r3.xy,		r1.xy													\n\
	mov					oT0,		r0																				\n\
																													\n\
	m4x3				r1,			r8,			c3					; Transform vertex to view space				\n\
	m4x4				r8,			r1,			c6					; Transform vertex to homogeneous space into t1	\n\
	rcp					r0.x,		r8.w																			\n\
	mul					r8,			r8,			r0.x																\n\
;	add					r8.z,		r8.z,		c20.z																\n\
;	mov					r8.z,		c20.y								; Transform vertex to homogeneous space into t1	\n\
	mov					oT1,		r8								; Transform vertex to homogeneous space into t1	\n\
	";

	char *psSource = "\
																													\n\
	ps_2_0																											\n\
																													\n\
	; Declare texture samplers																						\n\
	dcl_2d				s0											; DIFFUSE										\n\
																													\n\
	; Declare texture coordinate registers																			\n\
	dcl					t0.xyzw										; Diffuse texture UV							\n\
	dcl					t1.xyzw										; Grass particle widget center					\n\
																													\n\
	dcl					v0.xyzw																						\n\
																													\n\
	texld				r0,			t0,			s0					; Sample DIFFUSE into r0						\n\
	mul					r0,			r0,			v0					; Modulate with diffuse color					\n\
	mov					oC0,		r0								; Move result into output						\n\
																													\n\
	; Modify output depth																							\n\
	;mov					oDepth,		t1.z																			\n\
	";

	// Assemble vertex shader
	m_vertexShader = RuD3DAssembleVertexShader(vsSource);

	// Assemble pixel shader
	m_pixelShader = RuD3DAssemblePixelShader(psSource);

	// Overrides
	m_override_ZWrite = TRUE;
}

CRuShader_Particle_Grass::~CRuShader_Particle_Grass()
{
}

void CRuShader_Particle_Grass::BeginPass(INT32 passNumber)
{
	// Call base BeginPass to perform any additional setup
	CRuShader_Base::BeginPass(passNumber);

	if(passNumber == 0)
	{
		m_vertexDecl->Select();
		m_engine->SetVertexShader(m_vertexShader);
		m_engine->SetPixelShader(m_pixelShader);
//		m_engine->SetPixelShader(NULL);

//		m_engine->GetD3DDevice()->SetVertexShaderConstantF(0, m_shaderConstants, m_numBaseConstants);
		m_engine->GetD3DDevice()->SetVertexShaderConstantF(0, m_shaderConstants, 256);
		m_engine->GetD3DDevice()->SetPixelShaderConstantF(0, m_pixelShaderConstants, m_numBasePixelShaderConstants);

		m_engine->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
		m_engine->SetRenderState(D3DRS_ALPHAREF, 0x70);
		m_engine->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);

		m_engine->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
//		m_engine->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
//		m_engine->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

		m_engine->SetRenderState(D3DRS_BLENDFACTOR, 0x90909090);
		m_engine->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_BLENDFACTOR);
		m_engine->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVBLENDFACTOR);

//		m_engine->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MAX);

		m_engine->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);

		m_engine->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
		m_engine->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
/*
		m_engine->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
		m_engine->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
		m_engine->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);

		m_engine->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
		m_engine->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
		m_engine->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);

		m_engine->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
		m_engine->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
*/
		m_engine->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
		m_engine->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
		m_engine->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

		m_engine->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
		m_engine->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);

		m_engine->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	}
}

void CRuShader_Particle_Grass::EndPass(INT32 passNumber)
{
//	m_engine->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
	m_engine->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
}

void CRuShader_Particle_Grass::SetParameters(const CRuParameterCache *paramCache)
{
	DWORD param;

	CRuShader_Base::SetParameters(paramCache);

	if(paramCache->GetParameter(ruPARAMTYPE_GEOINSTANCE_S_COUNT, param))
	{
		m_numInstances = (UINT32) param;
		CRuMatrix4x4 *transform = (CRuMatrix4x4 *) param;

	}

	if(paramCache->GetParameter(ruPARAMTYPE_GEOINSTANCE_S_DATA, param))
	{
		float *instanceData = reinterpret_cast<float *>(param);

		// Number of constants = instance dataset + 24 constants for the per-draw data
		INT32 numConstants = 24 + m_numInstances * 8;
		INT32 constantOffset = 27 * 4;

		memcpy(&m_shaderConstants[constantOffset], instanceData, sizeof(float) * numConstants);
	}
}

void CRuShader_Particle_Grass::Instance(INT32 streamIndex, IRuMesh *mesh, UINT32 morphTargetIndex, void *buffer) const
{
	if(streamIndex == 0)
	{
		CRuVertex_Particle_Simple *vertices = (CRuVertex_Particle_Simple *) buffer;
		IRuMorphTarget *morphTarget = mesh->GetMorphTarget(morphTargetIndex);
		const DWORD *diffuse = morphTarget->GetDiffuse();

		if(morphTarget)
		{
			for(INT32 i = 0, j = 0; i < mesh->GetNumVertices(); i++)
			{
				vertices[i].x = morphTarget->GetPosition()[i].x;
				vertices[i].y = morphTarget->GetPosition()[i].y;
				vertices[i].z = morphTarget->GetPosition()[i].z;

				if(diffuse)
				{
					vertices[i].diffuse = diffuse[i];
				}
				else
				{
					vertices[i].diffuse = 0x00FFFFFF;
				}
			}
		}
	}
}

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

CRuShader_Particle_Grass_Optimized::CRuShader_Particle_Grass_Optimized(CRuEngineD3D *engine)
:	CRuShader_Base(engine)
{
	m_override_Fog = FALSE;
	m_override_AlphaTest = TRUE;
	m_override_AlphaBlend = TRUE;

	// Setup vertex elements
	CRuVertexElement vertexElements[] =
	{
		{ 0,	0,	ruELEMENTTYPE_FLOAT3,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_POSITION,	0 },
		{ 0,	12,	ruELEMENTTYPE_D3DCOLOR,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_COLOR,		0 },
		{ 0,	16,	ruELEMENTTYPE_FLOAT4,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_BLENDWEIGHT,	0 },
		RUVERTEXDECL_END()
	};

	// Construct vertex declaration
	m_vertexDecl = m_engine->CreateVertexDeclaration(vertexElements);


	// Vertex shader source
	char *vsSource = "\
																													\n\
	vs_1_1															; Vertex shader version 1.1						\n\
																													\n\
	dcl_position0		v0											; Declare position								\n\
	dcl_color			v2											; Declare diffuse								\n\
	dcl_blendweight		v3											; Declare grass properties						\n\
																													\n\
	; Generate sin(c27.z)																							\n\
	mov					r1.x,		c27.z							; Move t into r1.x								\n\
	mad					r0.x,		r1.x,		c30.w,		c30.y	; Bring argument into [-PI, PI]					\n\
	expp				r0.y,		r0.x																			\n\
	mad					r0.x,		r0.y,		c30.z,		-c30.x													\n\
	dst					r2.xy,		r0.x,		r0.x				; Generate 1, (r0.x)^2, ..., (r0.x)^6			\n\
	mul					r2.z,		r2.y,		r2.y																\n\
	mul					r2.w,		r2.y,		r2.z																\n\
	mul					r0,			r2,			r0.x				; Generate r0.x, (r0.x)^3, .., (r0.x)^7			\n\
	dp4					r3.x,		r0,			c32					; Compute sin(r0.x)								\n\
																													\n\
	; Transform position into homogeneous space																		\n\
	mov					r0,			c20.yyyy						; Move 1 into r0								\n\
	mov					r1,			c20.yyyy						; Move 1 into r1								\n\
	mov					r8,			c20.yyyy						; Move 1 into r8								\n\
																													\n\
	; Calculate position																							\n\
	mov					r0,			v0								; Move position into r0							\n\
	mul					r1.xz,		r3.x,		v3.zw				; Calculate offset								\n\
	mul					r1.xz,		r1.xz,		v2.z				; Multiply offset by y modifier					\n\
	add					r0.xz,		r1.xz,		r0.xz				; Add to position								\n"
	"																												\n\
	m4x3				r1,			r0,			c3					; Transform vertex to view space				\n\
	m4x4				oPos,		r1,			c6					; Transform vertex to homogeneous space			\n\
																													\n\
	; Fog factor																									\n\
	sub					r3.x,		c25.z,		c25.y				; Fog End - Fog Start into r3.x					\n\
	rcp					r3.x,		r3.x							; Reciprocal of fog range into r0.x				\n\
	sub					r4.x,		r1.z,		c25.y				; Offset vertex z by fog start into r3.x		\n\
	mul					r3.x,		r4.x,		r3.x				; Divide z by fog range into r0					\n\
	sub					r3.x,		c20.y,		r3.x				; Invert fog factor								\n\
	max					r3.x,		r3.x,		c20.w				; Clamp to [0, 1]								\n\
	min					r3.x,		r3.x,		c20.y																\n\
	mov					oFog,		r3.x							; Output fog factor								\n"
	"																												\n\
	; Normal into r3																								\n\
	mov					r3,			c29																				\n\
																													\n\
	; Ambient lighting into r4																						\n\
	mov					r4,			c10																				\n\
																													\n\
	; Object-level spherical harmonics																				\n\
	add					r4,			c11,		r4					; Constant term									\n\
	mad					r4,			c12,		r3.x,		r4		; 1st-order terms								\n\
	mad					r4,			c13,		r3.y,		r4														\n\
	mad					r4,			c14,		r3.z,		r4														\n\
	mul					r5.x,		r3.x,		r3.z				; X * Z											\n\
	mul					r5.y,		r3.z,		r3.y				; Z * Y											\n\
	mul					r5.z,		r3.y,		r3.x				; Y * X											\n\
	mad					r4,			c15,		r5.x,		r4		; 2nd-order terms								\n\
	mad					r4,			c16,		r5.y,		r4														\n\
	mad					r4,			c17,		r5.z,		r4														\n\
	mul					r5.xyz,		r3,			r3																	\n\
	mul					r5.z,		r5.z,		c20.x																\n\
	sub					r5.z,		r5.z,		c20.y																\n\
	mad					r4,			c18,		r5.z,		r4														\n\
	sub					r5.x,		r5.x,		r5.y																\n\
	mad					r4,			c19,		r5.x,		r4														\n"
	"																												\n\
	; Diffuse color																									\n\
	mul					r4.w,		r1.z,		c28.w				; Divide z by fade distance into r4.w			\n\
	sub					r4.w,		c20.y,		r4.w				; Invert										\n\
	max					r4.w,		r4.w,		c20.w				; Clamp to [0, 1]								\n\
	min					r4.w,		r4.w,		c20.y																\n\
	mul					r4,			r4,			c26					; Multiply diffuse by global diffuse			\n\
	mov					oD0,		r4								; Set diffuse									\n\
																													\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
																													\n\
	; Calculate texture coordinates																					\n\
	mov					r3.xy,		c27.xy							; Move uv increments into r3					\n\
	mul					r1.xy,		v3.xy,		r3.xy				; Calculate subtexture uv origin				\n\
	mad					r0.xy,		v2.xy,		r3.xy,		r1.xy													\n\
	mov					oT0,		r0																				\n\
	";

	char *psSource = "\
																													\n\
	ps_2_0																											\n\
																													\n\
	; Declare texture samplers																						\n\
	dcl_2d				s0											; DIFFUSE										\n\
																													\n\
	; Declare texture coordinate registers																			\n\
	dcl					t0.xyzw										; Diffuse texture UV							\n\
	dcl					t1.xyzw										; Grass particle widget center					\n\
																													\n\
	dcl					v0.xyzw																						\n\
																													\n\
	texld				r0,			t0,			s0					; Sample DIFFUSE into r0						\n\
	mul					r0,			r0,			v0					; Modulate with diffuse color					\n\
	mov					oC0,		r0								; Move result into output						\n\
	";

	// Assemble vertex shader
	m_vertexShader = RuD3DAssembleVertexShader(vsSource);

	// Assemble pixel shader
	m_pixelShader = RuD3DAssemblePixelShader(psSource);

	// Overrides
	m_override_ZWrite = TRUE;
}

CRuShader_Particle_Grass_Optimized::~CRuShader_Particle_Grass_Optimized()
{
}

void CRuShader_Particle_Grass_Optimized::BeginPass(INT32 passNumber)
{
	// Call base BeginPass to perform any additional setup
	CRuShader_Base::BeginPass(passNumber);

	if(passNumber == 0)
	{
		m_vertexDecl->Select();
		m_engine->SetVertexShader(m_vertexShader);
		m_engine->SetPixelShader(m_pixelShader);
//		m_engine->SetPixelShader(NULL);

		m_engine->GetD3DDevice()->SetVertexShaderConstantF(0, m_shaderConstants, m_numBaseConstants + 6);
		m_engine->GetD3DDevice()->SetPixelShaderConstantF(0, m_pixelShaderConstants, m_numBasePixelShaderConstants);

		m_engine->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
		m_engine->SetRenderState(D3DRS_ALPHAREF, 0x70);
		m_engine->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);

		m_engine->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
//		m_engine->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
//		m_engine->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

		m_engine->SetRenderState(D3DRS_BLENDFACTOR, 0x90909090);
		m_engine->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_BLENDFACTOR);
		m_engine->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVBLENDFACTOR);

//		m_engine->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MAX);

		m_engine->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);

		m_engine->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
		m_engine->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
/*
		m_engine->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
		m_engine->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
		m_engine->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);

		m_engine->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
		m_engine->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
		m_engine->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);

		m_engine->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
		m_engine->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
*/
		m_engine->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
		m_engine->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
		m_engine->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

		m_engine->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
		m_engine->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);

		m_engine->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	}
}

void CRuShader_Particle_Grass_Optimized::EndPass(INT32 passNumber)
{
//	m_engine->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
	m_engine->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
}

void CRuShader_Particle_Grass_Optimized::SetParameters(const CRuParameterCache *paramCache)
{
	DWORD param;

	CRuShader_Base::SetParameters(paramCache);

	if(paramCache->GetParameter(ruPARAMTYPE_GEOINSTANCE_S_DATA, param))
	{
		float *instanceData = reinterpret_cast<float *>(param);

		// Number of constants = 24 constants for the per-draw data
		INT32 numConstants = 24;
		INT32 constantOffset = 27 * 4;

		memcpy(&m_shaderConstants[constantOffset], instanceData, sizeof(float) * numConstants);
	}
}

void CRuShader_Particle_Grass_Optimized::Instance(INT32 streamIndex, IRuMesh *mesh, UINT32 morphTargetIndex, void *buffer) const
{
	if(streamIndex == 0)
	{
		CRuVertex_Particle_Grass_Optimized *vertices = (CRuVertex_Particle_Grass_Optimized *) buffer;
		IRuMorphTarget *morphTarget = mesh->GetMorphTarget(morphTargetIndex);
		const DWORD *diffuse = morphTarget->GetDiffuse();
		const REAL *grassProperties = morphTarget->GetBlendWeight();

		if(morphTarget)
		{
			for(INT32 i = 0, j = 0; i < mesh->GetNumVertices(); i++)
			{
				vertices[i].x = morphTarget->GetPosition()[i].x;
				vertices[i].y = morphTarget->GetPosition()[i].y;
				vertices[i].z = morphTarget->GetPosition()[i].z;
				vertices[i].diffuse = diffuse[i];

				vertices[i].w0 = grassProperties[j++];
				vertices[i].w1 = grassProperties[j++];
				vertices[i].w2 = grassProperties[j++];
				vertices[i].w3 = grassProperties[j++];
			}
		}
	}
}

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

CRuShader_Particle_Sprite::CRuShader_Particle_Sprite(CRuEngineD3D *engine)
:	CRuShader_Base(engine),
	m_orientation(ruAXIS_Z),
	m_billboardType(ruBILLBOARDTYPE_UNCONSTRAINED)
{
	// Setup vertex elements
	CRuVertexElement vertexElements[] =
	{
		{ 0,	0,	ruELEMENTTYPE_FLOAT3,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_POSITION,	0 },
		{ 0,	12,	ruELEMENTTYPE_D3DCOLOR,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_COLOR,		0 },
		RUVERTEXDECL_END()
	};

	// Construct vertex declaration
	m_vertexDecl = m_engine->CreateVertexDeclaration(vertexElements);

	// Vertex shader source
	char *vsSource_X = "\
																													\n\
	vs_1_1															; Vertex shader version 1.1						\n\
																													\n\
	dcl_position0		v0											; Declare position								\n\
	dcl_color			v2											; Declare diffuse								\n\
																													\n\
	; Calculate index to instance data																				\n\
	mul					r2.x,		v2.w,		c32.x				; Scale diffuse alpha by 256 to get an integer	\n\
	mad					r2.x,		r2.x,		c32.y,		c32.z	; Calculate address index for row 0 in r2.x		\n\
	add					r2.y,		r2.x,		c20.y				; Calculate address index for row 1 in r2.y		\n\
																													\n\
	; Transform position into homogeneous space																		\n\
	mov					r0,			c20.yyyy						; Move 1 into r0								\n\
	mov					r1,			c20.yyyy						; Move 1 into r1								\n\
	mov					r3,			c20.yyyy						; Move 1 into r1								\n\
	mov					r4,			c20.wwww						; Move 1 into r1								\n\
	mov					r5,			c20.wwww						; Move 1 into r1								\n\
																													\n\
	; Calculate t factors																							\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
	mov					r7.y,		c[a0.x].z						; Move t1 into r7.y								\n\
	sub					r7.x,		c20.y,		r7.y				; Calculate t0 into r7.x						\n\
	mov					r4.y,		c[a0.x].w						; Move cos(phi) into r4.y						\n\
	mov					r5.z,		c[a0.x].w						; Move cos(phi) into r5.z						\n\
																													\n\
	; Calculate position																							\n\
	mov					a0.x,		r2.x							; Copy row 0 index to index register			\n\
	mov					r4.z,		-c[a0.x].w						; Move -sin(phi) into r4.z						\n\
	mov					r5.y,		c[a0.x].w						; Move sin(phi) into r5.y						\n\
	mul					r1.x,		c31.z,		r7.x				; Calculate scale								\n\
	mad					r1.x,		c31.w,		r7.y,		r1.x													\n\
	mul					r0.xyz,		v0,			r1.x				; Scale											\n\
																													\n\
	dp3					r1.y,		r0.xyz,		r4.xyz				; Rotate point about view space z-axis			\n\
	dp3					r1.z,		r0.xyz,		r5.xyz				; Rotate point about view space z-axis			\n\
	mov					r1.x,		r0.x																			\n\
																													\n\
	m3x3				r3,			r1,			c27					; Billboard transform							\n\
	add					r0.xyz,		r3,			c[a0.x]				; Translate to world space						\n\
	m4x3				r1,			r0,			c3					; Transform vertex to view space				\n\
	m4x4				oPos,		r1,			c6					; Transform vertex to homogeneous space			\n"
	"																												\n\
	; Fog factor																									\n\
	mov					oFog,		c20.y							; Output fog factor								\n"
	"																												\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
																													\n\
	; Diffuse color																									\n\
	mov					r1.x,		c[a0.x].z						; Move t (end color factor) into r1.x			\n\
	mul					r0,			c35,		r1.x				; Blend end color								\n\
	sub					r1.x,		c20.y,		r1.x				; Invert t to get start color factor into r1.x	\n\
	mad					r4,			c34,		r1.x,		r0		; Blend start color								\n\
	mul					r4,			r4,			c26					; Multiply diffuse by global diffuse			\n\
	mov					oD0,		r4								; Move output									\n\
																													\n\
	; Calculate texture coordinates																					\n\
	mov					r3.xy,		c31.xy							; Move uv increments into r3					\n\
	mul					r1.xy,		c[a0.x].xy,	r3.xy				; Calculate subtexture uv origin				\n\
	mad					r0.xy,		v2.xy,		r3.xy,		r1.xy													\n\
	mov					oT0,		r0																				\n\
	";

	char *vsSource_Y = "\
																													\n\
	vs_1_1															; Vertex shader version 1.1						\n\
																													\n\
	dcl_position0		v0											; Declare position								\n\
	dcl_color			v2											; Declare diffuse								\n\
																													\n\
	; Calculate index to instance data																				\n\
	mul					r2.x,		v2.w,		c32.x				; Scale diffuse alpha by 256 to get an integer	\n\
	mad					r2.x,		r2.x,		c32.y,		c32.z	; Calculate address index for row 0 in r2.x		\n\
	add					r2.y,		r2.x,		c20.y				; Calculate address index for row 1 in r2.y		\n\
																													\n\
	; Transform position into homogeneous space																		\n\
	mov					r0,			c20.yyyy						; Move 1 into r0								\n\
	mov					r1,			c20.yyyy						; Move 1 into r1								\n\
	mov					r3,			c20.yyyy						; Move 1 into r1								\n\
	mov					r4,			c20.wwww						; Move 1 into r1								\n\
	mov					r5,			c20.wwww						; Move 1 into r1								\n\
																													\n\
	; Calculate t factors																							\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
	mov					r7.y,		c[a0.x].z						; Move t1 into r7.y								\n\
	sub					r7.x,		c20.y,		r7.y				; Calculate t0 into r7.x						\n\
	mov					r4.x,		c[a0.x].w						; Move cos(phi) into r4.x						\n\
	mov					r5.z,		c[a0.x].w						; Move cos(phi) into r5.y						\n\
																													\n\
	; Calculate position																							\n\
	mov					a0.x,		r2.x							; Copy row 0 index to index register			\n\
	mov					r4.z,		c[a0.x].w						; Move sin(phi) into r4.z						\n\
	mov					r5.x,		-c[a0.x].w						; Move -sin(phi) into r5.x						\n\
	mul					r1.x,		c31.z,		r7.x				; Calculate scale								\n\
	mad					r1.x,		c31.w,		r7.y,		r1.x													\n\
	mul					r0.xyz,		v0,			r1.x				; Scale											\n\
																													\n\
	dp3					r1.x,		r0.xyz,		r4.xyz				; Rotate point about view space z-axis			\n\
	dp3					r1.z,		r0.xyz,		r5.xyz				; Rotate point about view space z-axis			\n\
	mov					r1.y,		r0.y																			\n\
																													\n\
	m3x3				r3,			r1,			c27					; Billboard transform							\n\
	add					r0.xyz,		r3,			c[a0.x]				; Translate to world space						\n\
	m4x3				r1,			r0,			c3					; Transform vertex to view space				\n\
	m4x4				oPos,		r1,			c6					; Transform vertex to homogeneous space			\n"
	"																												\n\
	; Fog factor																									\n\
	mov					oFog,		c20.y							; Output fog factor								\n"
	"																												\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
																													\n\
	; Diffuse color																									\n\
	mov					r1.x,		c[a0.x].z						; Move t (end color factor) into r1.x			\n\
	mul					r0,			c35,		r1.x				; Blend end color								\n\
	sub					r1.x,		c20.y,		r1.x				; Invert t to get start color factor into r1.x	\n\
	mad					r4,			c34,		r1.x,		r0		; Blend start color								\n\
	mul					r4,			r4,			c26					; Multiply diffuse by global diffuse			\n\
	mov					oD0,		r4								; Move output									\n\
																													\n\
	; Calculate texture coordinates																					\n\
	mov					r3.xy,		c31.xy							; Move uv increments into r3					\n\
	mul					r1.xy,		c[a0.x].xy,	r3.xy				; Calculate subtexture uv origin				\n\
	mad					r0.xy,		v2.xy,		r3.xy,		r1.xy													\n\
	mov					oT0,		r0																				\n\
	";

	char *vsSource_Z = "\
																													\n\
	vs_1_1															; Vertex shader version 1.1						\n\
																													\n\
	dcl_position0		v0											; Declare position								\n\
	dcl_color			v2											; Declare diffuse								\n\
																													\n\
	; Calculate index to instance data																				\n\
	mul					r2.x,		v2.w,		c32.x				; Scale diffuse alpha by 256 to get an integer	\n\
	mad					r2.x,		r2.x,		c32.y,		c32.z	; Calculate address index for row 0 in r2.x		\n\
	add					r2.y,		r2.x,		c20.y				; Calculate address index for row 1 in r2.y		\n\
																													\n\
	; Transform position into homogeneous space																		\n\
	mov					r0,			c20.yyyy						; Move 1 into r0								\n\
	mov					r1,			c20.yyyy						; Move 1 into r1								\n\
	mov					r3,			c20.yyyy						; Move 1 into r1								\n\
	mov					r4,			c20.wwww						; Move 1 into r1								\n\
	mov					r5,			c20.wwww						; Move 1 into r1								\n\
																													\n\
	; Calculate t factors																							\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
	mov					r7.y,		c[a0.x].z						; Move t1 into r7.y								\n\
	sub					r7.x,		c20.y,		r7.y				; Calculate t0 into r7.x						\n\
	mov					r4.x,		c[a0.x].w						; Move cos(phi) into r4.x						\n\
	mov					r5.y,		c[a0.x].w						; Move cos(phi) into r5.y						\n\
																													\n\
	; Calculate position																							\n\
	mov					a0.x,		r2.x							; Copy row 0 index to index register			\n\
	mov					r4.y,		-c[a0.x].w						; Move -sin(phi) into r4.y						\n\
	mov					r5.x,		c[a0.x].w						; Move sin(phi) into r5.x						\n\
	mul					r1.x,		c31.z,		r7.x				; Calculate scale								\n\
	mad					r1.x,		c31.w,		r7.y,		r1.x													\n\
	mul					r0.xyz,		v0,			r1.x				; Scale											\n\
																													\n\
	dp3					r1.x,		r0.xyz,		r4.xyz				; Rotate point about view space z-axis			\n\
	dp3					r1.y,		r0.xyz,		r5.xyz				; Rotate point about view space z-axis			\n\
	mov					r1.z,		r0.z																			\n\
																													\n\
	m3x3				r3,			r1,			c27					; Billboard transform							\n\
	add					r0.xyz,		r3,			c[a0.x]				; Translate to world space						\n\
	m4x3				r1,			r0,			c3					; Transform vertex to view space				\n\
	m4x4				oPos,		r1,			c6					; Transform vertex to homogeneous space			\n"
	"																												\n\
	; Fog factor																									\n\
	mov					oFog,		c20.y							; Output fog factor								\n"
	"																												\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
																													\n\
	; Diffuse color																									\n\
	mov					r1.x,		c[a0.x].z						; Move t (end color factor) into r1.x			\n\
	mul					r0,			c35,		r1.x				; Blend end color								\n\
	sub					r1.x,		c20.y,		r1.x				; Invert t to get start color factor into r1.x	\n\
	mad					r4,			c34,		r1.x,		r0		; Blend start color								\n\
	mul					r4,			r4,			c26					; Multiply diffuse by global diffuse			\n\
	mov					oD0,		r4								; Move output									\n\
																													\n\
	; Calculate texture coordinates																					\n\
	mov					r3.xy,		c31.xy							; Move uv increments into r3					\n\
	mul					r1.xy,		c[a0.x].xy,	r3.xy				; Calculate subtexture uv origin				\n\
	mad					r0.xy,		v2.xy,		r3.xy,		r1.xy													\n\
	mov					oT0,		r0																				\n\
	";

	// Assemble vertex shader
	m_vertexShaders[0] = RuD3DAssembleVertexShader(vsSource_X);
	m_vertexShaders[1] = RuD3DAssembleVertexShader(vsSource_Y);
	m_vertexShaders[2] = RuD3DAssembleVertexShader(vsSource_Z);

	// Overrides
	m_override_ZWrite = TRUE;
}

CRuShader_Particle_Sprite::~CRuShader_Particle_Sprite()
{
	for(INT32 i = 0; i < 3; ++i)
	{
		ruSAFE_RELEASE(m_vertexShaders[i]);
	}
}

void CRuShader_Particle_Sprite::BeginPass(INT32 passNumber)
{
	// Call base BeginPass to perform any additional setup
	CRuShader_Base::BeginPass(passNumber);

	if(passNumber == 0)
	{
		m_vertexDecl->Select();

		switch(m_orientation)
		{
			case ruAXIS_X:
				m_engine->SetVertexShader(m_vertexShaders[0]);
				break;
			case ruAXIS_Y:
				m_engine->SetVertexShader(m_vertexShaders[1]);
				break;
			default:
			case ruAXIS_Z:
				m_engine->SetVertexShader(m_vertexShaders[2]);
				break;
		}

		m_engine->SetPixelShader(NULL);
//		m_engine->GetD3DDevice()->SetVertexShaderConstantF(0, m_shaderConstants, m_numBaseConstants);
		m_engine->GetD3DDevice()->SetVertexShaderConstantF(0, m_shaderConstants, 256);

		m_engine->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);

		m_engine->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

		switch(m_alphaMode)
		{
			case ruALPHAMODE_NORMAL:
				{
					m_engine->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
					m_engine->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
				}

				break;
			case ruALPHAMODE_ADD:
				{
					m_engine->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
					m_engine->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
				}

				break;
			case ruALPHAMODE_SUBTRACT:
				{
					m_engine->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
					m_engine->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
				}

				break;
		}

		m_engine->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
		m_engine->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);

		m_engine->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
		m_engine->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);

		m_engine->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
		m_engine->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
		m_engine->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);

		m_engine->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
		m_engine->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
		m_engine->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);

		m_engine->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
		m_engine->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);

		m_engine->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
		m_engine->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
		m_engine->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
	}
}

void CRuShader_Particle_Sprite::EndPass(INT32 passNumber)
{
	m_engine->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
}

void CRuShader_Particle_Sprite::SetParameters(const CRuParameterCache *paramCache)
{
	DWORD param;

	CRuShader_Base::SetParameters(paramCache);

	if(paramCache->GetParameter(ruPARAMTYPE_GEOINSTANCE_S_COUNT, param))
	{
		m_numInstances = (UINT32) param;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_GEOINSTANCE_S_DATA, param))
	{
		float *instanceData = reinterpret_cast<float *>(param);

		// Number of constants = instance dataset + 24 constants for the per-draw data
		INT32 numConstants = 24 + m_numInstances * 8;
		INT32 constantOffset = 31 * 4;

		memcpy(&m_shaderConstants[constantOffset], instanceData, sizeof(float) * numConstants);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_DWORD_0, param))
	{
		m_orientation = static_cast<RuOrthogonalAxis>(param);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_FX_BILLBOARD, param))
	{
		m_billboardType = static_cast<RuBillboardType>(param);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_TRANSFORM_VIEW, param))
	{
		// Setup billboard transform
		CRuMatrix4x4 billboardTransform;

		switch(m_billboardType)
		{
			default:
			case ruBILLBOARDTYPE_NONE:
				billboardTransform.SetToIdentity();
				break;

			case ruBILLBOARDTYPE_UNCONSTRAINED:
				billboardTransform.SetToBillboard(*((CRuMatrix4x4 *) param), CRuVector3(0.0f, 0.0f, 1.0f), CRuVector3(0.0f, 1.0f, 0.0f));
				break;

			case ruBILLBOARDTYPE_AXIAL:
				billboardTransform.SetToAxialBillboard(*((CRuMatrix4x4 *) param), CRuVector3(0.0f, 0.0f, 1.0f), CRuVector3(0.0f, 1.0f, 0.0f));
				break;
		}

		// Copy billboard transform to shader constants
		INT32 uvTransformOffset = 27 * 4;

		for(UINT32 i = 0, k = 0; i < 4; ++i)
		{
			for(UINT32 j = 0; j < 4; ++j, ++k)
			{
				m_shaderConstants[k + uvTransformOffset] = billboardTransform.m_elements[j][i];
			}
		}
	}
}

void CRuShader_Particle_Sprite::Instance(INT32 streamIndex, IRuMesh *mesh, UINT32 morphTargetIndex, void *buffer) const
{
	if(streamIndex == 0)
	{
		CRuVertex_Particle_Simple *vertices = (CRuVertex_Particle_Simple *) buffer;
		IRuMorphTarget *morphTarget = mesh->GetMorphTarget(morphTargetIndex);
		const DWORD *diffuse = morphTarget->GetDiffuse();

		if(morphTarget)
		{
			for(INT32 i = 0, j = 0; i < mesh->GetNumVertices(); i++)
			{
				vertices[i].x = morphTarget->GetPosition()[i].x;
				vertices[i].y = morphTarget->GetPosition()[i].y;
				vertices[i].z = morphTarget->GetPosition()[i].z;

				if(diffuse)
				{
					vertices[i].diffuse = diffuse[i];
				}
				else
				{
					vertices[i].diffuse = 0x00FFFFFF;
				}
			}
		}
	}
}

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

CRuShader_Particle_SpriteEx::CRuShader_Particle_SpriteEx(CRuEngineD3D *engine)
:	CRuShader_Base(engine),
	m_orientation(ruAXIS_Z),
	m_billboardType(ruBILLBOARDTYPE_UNCONSTRAINED)
{
	// Setup vertex elements
	CRuVertexElement vertexElements[] =
	{
		{ 0,	0,	ruELEMENTTYPE_FLOAT3,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_POSITION,	0 },
		{ 0,	12,	ruELEMENTTYPE_D3DCOLOR,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_COLOR,		0 },
		RUVERTEXDECL_END()
	};

	// Construct vertex declaration
	m_vertexDecl = m_engine->CreateVertexDeclaration(vertexElements);

	// Vertex shader source
	char *vsSource_X = "\
																													\n\
	vs_1_1															; Vertex shader version 1.1						\n\
																													\n\
	dcl_position0		v0											; Declare position								\n\
	dcl_color			v2											; Declare diffuse								\n\
																													\n\
	; Calculate index to instance data																				\n\
	mul					r2.x,		v2.w,		c32.x				; Scale diffuse alpha by 256 to get an integer	\n\
	mad					r2.x,		r2.x,		c32.y,		c32.z	; Calculate address index for row 0 in r2.x		\n\
	add					r2.y,		r2.x,		c20.y				; Calculate address index for row 1 in r2.y		\n\
	add					r2.z,		r2.y,		c20.y				; Calculate address index for row 2 in r2.z		\n\
																													\n\
	; Transform position into homogeneous space																		\n\
	mov					r0,			c20.yyyy						; Move 1 into r0								\n\
	mov					r1,			c20.yyyy						; Move 1 into r1								\n\
	mov					r3,			c20.yyyy						; Move 1 into r1								\n\
	mov					r4,			c20.wwww						; Move 1 into r1								\n\
	mov					r5,			c20.wwww						; Move 1 into r1								\n\
																													\n\
	; Calculate t factors																							\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
	mov					r4.y,		c[a0.x].w						; Move cos(phi) into r4.y						\n\
	mov					r4.z,		-c[a0.x].z						; Move -sin(phi) into r4.z						\n\
	mov					r5.y,		c[a0.x].z						; Move sin(phi) into r5.y						\n\
	mov					r5.z,		c[a0.x].w						; Move cos(phi) into r5.z						\n\
																													\n\
	; Calculate position																							\n\
	mov					a0.x,		r2.x							; Copy row 0 index to index register			\n\
	mul					r0.xyz,		v0,			c[a0.x].w			; Scale											\n\
																													\n\
	mov					r1.x,		r0.x																			\n\
	dp3					r1.y,		r0.xyz,		r4.xyz				; Rotate point about view space z-axis			\n\
	dp3					r1.z,		r0.xyz,		r5.xyz				; Rotate point about view space z-axis			\n\
																													\n\
	m3x3				r3,			r1,			c27					; Billboard transform							\n\
	mov					r4.xyz,		c[a0.x]							; Move particle position into r4				\n\
	mov					r4.w,		c20.y							; Move 1 into r4.w								\n\
	m4x3				r0.xyz,		r4,			c0					; Transform particle position to world space	\n\
	add					r0.xyz,		r3,			r0					; Translate particle vertex to world space		\n\
	m4x3				r1,			r0,			c3					; Transform vertex to view space				\n\
	m4x4				oPos,		r1,			c6					; Transform vertex to homogeneous space			\n"
	"																												\n\
	; Fog factor																									\n\
	mov					oFog,		c20.y							; Output fog factor								\n"
	"																												\n\
	; Diffuse color																									\n\
	mov					a0.x,		r2.z							; Copy row 2 index to index register			\n\
	mov					r4,			c[a0.x]							; Move particle color into r4					\n\
	mul					r4,			r4,			c26					; Multiply diffuse by global diffuse			\n\
	mov					oD0,		r4								; Move output									\n\
																													\n\
	; Calculate texture coordinates																					\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
	mov					r3.xy,		c31.xy							; Move uv increments into r3					\n\
	mul					r1.xy,		c[a0.x].xy,	r3.xy				; Calculate subtexture uv origin				\n\
	mad					r0.xy,		v2.xy,		r3.xy,		r1.xy													\n\
	mov					oT0,		r0																				\n\
	";

	char *vsSource_Y = "\
																													\n\
	vs_1_1															; Vertex shader version 1.1						\n\
																													\n\
	dcl_position0		v0											; Declare position								\n\
	dcl_color			v2											; Declare diffuse								\n\
																													\n\
	; Calculate index to instance data																				\n\
	mul					r2.x,		v2.w,		c32.x				; Scale diffuse alpha by 256 to get an integer	\n\
	mad					r2.x,		r2.x,		c32.y,		c32.z	; Calculate address index for row 0 in r2.x		\n\
	add					r2.y,		r2.x,		c20.y				; Calculate address index for row 1 in r2.y		\n\
	add					r2.z,		r2.y,		c20.y				; Calculate address index for row 2 in r2.z		\n\
																													\n\
	; Transform position into homogeneous space																		\n\
	mov					r0,			c20.yyyy						; Move 1 into r0								\n\
	mov					r1,			c20.yyyy						; Move 1 into r1								\n\
	mov					r3,			c20.yyyy						; Move 1 into r1								\n\
	mov					r4,			c20.wwww						; Move 1 into r1								\n\
	mov					r5,			c20.wwww						; Move 1 into r1								\n\
																													\n\
	; Calculate t factors																							\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
	mov					r4.x,		c[a0.x].w						; Move cos(phi) into r4.x						\n\
	mov					r4.z,		c[a0.x].z						; Move sin(phi) into r4.z						\n\
	mov					r5.z,		c[a0.x].w						; Move cos(phi) into r5.y						\n\
	mov					r5.x,		-c[a0.x].z						; Move -sin(phi) into r5.x						\n\
																													\n\
	; Calculate position																							\n\
	mov					a0.x,		r2.x							; Copy row 0 index to index register			\n\
	mul					r0.xyz,		v0,			c[a0.x].w			; Scale											\n\
																													\n\
	dp3					r1.x,		r0.xyz,		r4.xyz				; Rotate point about view space z-axis			\n\
	mov					r1.y,		r0.y																			\n\
	dp3					r1.z,		r0.xyz,		r5.xyz				; Rotate point about view space z-axis			\n\
																													\n\
	m3x3				r3,			r1,			c27					; Billboard transform							\n\
	mov					r4.xyz,		c[a0.x]							; Move particle position into r4				\n\
	mov					r4.w,		c20.y							; Move 1 into r4.w								\n\
	m4x3				r0.xyz,		r4,			c0					; Transform particle position to world space	\n\
	add					r0.xyz,		r3,			r0					; Translate particle vertex to world space		\n\
	m4x3				r1,			r0,			c3					; Transform vertex to view space				\n\
	m4x4				oPos,		r1,			c6					; Transform vertex to homogeneous space			\n"
	"																												\n\
	; Fog factor																									\n\
	mov					oFog,		c20.y							; Output fog factor								\n"
	"																												\n\
	; Diffuse color																									\n\
	mov					a0.x,		r2.z							; Copy row 2 index to index register			\n\
	mov					r4,			c[a0.x]							; Move particle color into r4					\n\
	mul					r4,			r4,			c26					; Multiply diffuse by global diffuse			\n\
	mov					oD0,		r4								; Move output									\n\
																													\n\
	; Calculate texture coordinates																					\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
	mov					r3.xy,		c31.xy							; Move uv increments into r3					\n\
	mul					r1.xy,		c[a0.x].xy,	r3.xy				; Calculate subtexture uv origin				\n\
	mad					r0.xy,		v2.xy,		r3.xy,		r1.xy													\n\
	mov					oT0,		r0																				\n\
	";

	char *vsSource_Z = "\
																													\n\
	vs_1_1															; Vertex shader version 1.1						\n\
																													\n\
	dcl_position0		v0											; Declare position								\n\
	dcl_color			v2											; Declare diffuse								\n\
																													\n\
	; Calculate index to instance data																				\n\
	mul					r2.x,		v2.w,		c32.x				; Scale diffuse alpha by 256 to get an integer	\n\
	mad					r2.x,		r2.x,		c32.y,		c32.z	; Calculate address index for row 0 in r2.x		\n\
	add					r2.y,		r2.x,		c20.y				; Calculate address index for row 1 in r2.y		\n\
	add					r2.z,		r2.y,		c20.y				; Calculate address index for row 2 in r2.z		\n\
																													\n\
	; Transform position into homogeneous space																		\n\
	mov					r0,			c20.yyyy						; Move 1 into r0								\n\
	mov					r1,			c20.yyyy						; Move 1 into r1								\n\
	mov					r3,			c20.yyyy						; Move 1 into r1								\n\
	mov					r4,			c20.wwww						; Move 1 into r1								\n\
	mov					r5,			c20.wwww						; Move 1 into r1								\n\
																													\n\
	; Calculate t factors																							\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
	mov					r4.x,		c[a0.x].w						; Move cos(phi) into r4.x						\n\
	mov					r4.y,		-c[a0.x].z						; Move -sin(phi) into r4.y						\n\
	mov					r5.x,		c[a0.x].z						; Move sin(phi) into r5.x						\n\
	mov					r5.y,		c[a0.x].w						; Move cos(phi) into r5.y						\n\
																													\n\
	; Calculate position																							\n\
	mov					a0.x,		r2.x							; Copy row 0 index to index register			\n\
	mul					r0.xyz,		v0,			c[a0.x].w			; Scale											\n\
																													\n\
	dp3					r1.x,		r0.xyz,		r4.xyz				; Rotate point about view space z-axis			\n\
	dp3					r1.y,		r0.xyz,		r5.xyz				; Rotate point about view space z-axis			\n\
	mov					r1.z,		r0.z																			\n\
																													\n\
	m3x3				r3,			r1,			c27					; Billboard transform							\n\
	mov					r4.xyz,		c[a0.x]							; Move particle position into r4				\n\
	mov					r4.w,		c20.y							; Move 1 into r4.w								\n\
	m4x3				r0.xyz,		r4,			c0					; Transform particle position to world space	\n\
	add					r0.xyz,		r3,			r0					; Translate particle vertex to world space		\n\
	m4x3				r1,			r0,			c3					; Transform vertex to view space				\n\
	m4x4				oPos,		r1,			c6					; Transform vertex to homogeneous space			\n"
	"																												\n\
	; Fog factor																									\n\
	mov					oFog,		c20.y							; Output fog factor								\n"
	"																												\n\
	; Diffuse color																									\n\
	mov					a0.x,		r2.z							; Copy row 2 index to index register			\n\
	mov					r4,			c[a0.x]							; Move particle color into r4					\n\
	mul					r4,			r4,			c26					; Multiply diffuse by global diffuse			\n\
	mov					oD0,		r4								; Move output									\n\
																													\n\
	; Calculate texture coordinates																					\n\
	mov					a0.x,		r2.y							; Copy row 1 index to index register			\n\
	mov					r3.xy,		c31.xy							; Move uv increments into r3					\n\
	mul					r1.xy,		c[a0.x].xy,	r3.xy				; Calculate subtexture uv origin				\n\
	mad					r0.xy,		v2.xy,		r3.xy,		r1.xy													\n\
	mov					oT0,		r0																				\n\
	";

	// Assemble vertex shader
	m_vertexShaders[0] = RuD3DAssembleVertexShader(vsSource_X);
	m_vertexShaders[1] = RuD3DAssembleVertexShader(vsSource_Y);
	m_vertexShaders[2] = RuD3DAssembleVertexShader(vsSource_Z);

	// Overrides
	m_override_ZWrite = TRUE;
}

CRuShader_Particle_SpriteEx::~CRuShader_Particle_SpriteEx()
{
	for(INT32 i = 0; i < 3; ++i)
	{
		ruSAFE_RELEASE(m_vertexShaders[i]);
	}
}

void CRuShader_Particle_SpriteEx::BeginPass(INT32 passNumber)
{
	// Call base BeginPass to perform any additional setup
	CRuShader_Base::BeginPass(passNumber);

	if(passNumber == 0)
	{
		m_vertexDecl->Select();

		switch(m_orientation)
		{
			case ruAXIS_X:
				m_engine->SetVertexShader(m_vertexShaders[0]);
				break;
			case ruAXIS_Y:
				m_engine->SetVertexShader(m_vertexShaders[1]);
				break;
			default:
			case ruAXIS_Z:
				m_engine->SetVertexShader(m_vertexShaders[2]);
				break;
		}

		// Setup billboard transform
		CRuMatrix4x4 billboardTransform;

		if(m_viewTransform)
		{
			switch(m_billboardType)
			{
				default:
				case ruBILLBOARDTYPE_NONE:
					billboardTransform.SetToIdentity();
					break;

				case ruBILLBOARDTYPE_UNCONSTRAINED:
					billboardTransform.SetToBillboard(*((CRuMatrix4x4 *) m_viewTransform), CRuVector3(0.0f, 0.0f, 1.0f), CRuVector3(0.0f, 1.0f, 0.0f));
					break;

				case ruBILLBOARDTYPE_AXIAL:
					billboardTransform.SetToAxialBillboard(*((CRuMatrix4x4 *) m_viewTransform), CRuVector3(0.0f, 0.0f, 1.0f), CRuVector3(0.0f, 1.0f, 0.0f));
					break;
			}
		}
		else
		{
			billboardTransform.SetToIdentity();
		}

		// Copy billboard transform to shader constants
		INT32 uvTransformOffset = 27 * 4;

		for(UINT32 i = 0, k = 0; i < 4; ++i)
		{
			for(UINT32 j = 0; j < 4; ++j, ++k)
			{
				m_shaderConstants[k + uvTransformOffset] = billboardTransform.m_elements[j][i];
			}
		}

		m_engine->SetPixelShader(NULL);
//		m_engine->GetD3DDevice()->SetVertexShaderConstantF(0, m_shaderConstants, m_numBaseConstants);
		m_engine->GetD3DDevice()->SetVertexShaderConstantF(0, m_shaderConstants, 256);

		m_engine->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);

		m_engine->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

		switch(m_alphaMode)
		{
			case ruALPHAMODE_NORMAL:
				{
					m_engine->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
					m_engine->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
				}

				break;
			case ruALPHAMODE_ADD:
				{
					m_engine->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
					m_engine->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
				}

				break;
			case ruALPHAMODE_SUBTRACT:
				{
					m_engine->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
					m_engine->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
				}

				break;
		}

		m_engine->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
		m_engine->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);

		m_engine->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
		m_engine->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);

		m_engine->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
		m_engine->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
		m_engine->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);

		m_engine->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
		m_engine->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
		m_engine->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);

		m_engine->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
		m_engine->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);

		m_engine->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
		m_engine->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
		m_engine->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
	}
}

void CRuShader_Particle_SpriteEx::EndPass(INT32 passNumber)
{
	m_engine->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
}

void CRuShader_Particle_SpriteEx::SetParameters(const CRuParameterCache *paramCache)
{
	DWORD param;

	CRuShader_Base::SetParameters(paramCache);

	if(paramCache->GetParameter(ruPARAMTYPE_GEOINSTANCE_S_COUNT, param))
	{
		m_numInstances = (UINT32) param;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_GEOINSTANCE_S_DATA, param))
	{
		float *instanceData = reinterpret_cast<float *>(param);

		// Number of constants = instance dataset + 24 constants for the per-draw data
		INT32 numConstants = 24 + m_numInstances * 12;
		INT32 constantOffset = 31 * 4;

		memcpy(&m_shaderConstants[constantOffset], instanceData, sizeof(float) * numConstants);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_DWORD_0, param))
	{
		m_orientation = static_cast<RuOrthogonalAxis>(param);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_FX_BILLBOARD, param))
	{
		m_billboardType = static_cast<RuBillboardType>(param);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_TRANSFORM_VIEW, param))
	{
		m_viewTransform = reinterpret_cast<CRuMatrix4x4 *>(param);
	}
}

void CRuShader_Particle_SpriteEx::Instance(INT32 streamIndex, IRuMesh *mesh, UINT32 morphTargetIndex, void *buffer) const
{
	if(streamIndex == 0)
	{
		CRuVertex_Particle_Simple *vertices = (CRuVertex_Particle_Simple *) buffer;
		IRuMorphTarget *morphTarget = mesh->GetMorphTarget(morphTargetIndex);
		const DWORD *diffuse = morphTarget->GetDiffuse();

		if(morphTarget)
		{
			for(INT32 i = 0, j = 0; i < mesh->GetNumVertices(); i++)
			{
				vertices[i].x = morphTarget->GetPosition()[i].x;
				vertices[i].y = morphTarget->GetPosition()[i].y;
				vertices[i].z = morphTarget->GetPosition()[i].z;

				if(diffuse)
				{
					vertices[i].diffuse = diffuse[i];
				}
				else
				{
					vertices[i].diffuse = 0x00FFFFFF;
				}
			}
		}
	}
}

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

#pragma managed
