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

#pragma unmanaged

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

CRuShader_Base::CRuShader_Base(CRuEngineD3D *engine)
{
	// Store engine reference
	m_engine = engine;

	// Clear variables
	m_vertexDecl = NULL;
	m_vertexShader = NULL;
	m_pixelShader = NULL;
	m_vertexShaderConstantTable = NULL;
	m_pixelShaderConstantTable = NULL;
	m_numBaseConstants = 30;
	memset(m_shaderConstants, 0, sizeof(REAL) * 1024);
	m_numBasePixelShaderConstants = 2;
	memset(m_pixelShaderConstants, 0, sizeof(float) * 4 * 32);

	m_override_Fog = FALSE;
	m_override_AlphaTest = FALSE;
	m_override_AlphaBlend = FALSE;
	m_override_ZTest = FALSE;
	m_override_ZWrite = FALSE;

	m_2Sided = FALSE;
	m_faceted = FALSE;
	m_wire = FALSE;
	m_alphaTestEnabled = FALSE;
	m_alphaTestThreshold = 200;
	m_alphaTestMode = ruALPHATESTMODE_NONE;
	m_material_AlphaMode = ruALPHAMODE_NORMAL;
	m_material_Translucent = FALSE;
	m_material_ZTestMode = ruZTESTMODE_DEFAULT;
	m_material_ZWriteMode = ruZWRITEMODE_DEFAULT;

	m_fx_ReverseCull = FALSE;
	m_fx_StencilEnable = FALSE;
	m_fx_StencilID = 0;
}

CRuShader_Base::~CRuShader_Base()
{
	// Release resources
	ruSAFE_RELEASE(m_vertexDecl);
	ruSAFE_RELEASE(m_vertexShader);
	ruSAFE_RELEASE(m_pixelShader);
	ruSAFE_RELEASE(m_vertexShaderConstantTable);
	ruSAFE_RELEASE(m_pixelShaderConstantTable);
}

INT32 CRuShader_Base::GetShaderClass() const
{
	return 1000;
}

INT32 CRuShader_Base::GetShaderPriority() const
{
	return 1000;
}

void CRuShader_Base::BeginPass(INT32 passNumber)
{
	// Setup SH9 + zero + 0.5 constants
	m_shaderConstants[80] = 3.0f;
	m_shaderConstants[81] = 1.0f;
	m_shaderConstants[82] = 0.5f;
	m_shaderConstants[83] = 0.0f;

	m_shaderConstants[23 * 4 + 1] = 0.92f;

	m_pixelShaderConstants[0] = 1.0f;
	m_pixelShaderConstants[1] = 0.5f;
	m_pixelShaderConstants[2] = 0.0f;
	m_pixelShaderConstants[3] = 0.0f;

	// Calculate effective material alpha
	m_material_EffectiveAlpha = m_material_Alpha * m_material_AlphaModifier;

	// Set effective material alpha
	INT32 globalAlphaOffset = 107;
	m_shaderConstants[globalAlphaOffset] = m_material_EffectiveAlpha;

	// Material is translucent if the global alpha is less than one, or if the material was already marked translucent
	m_material_Translucent = (m_shaderConstants[107] < 1.0f) || m_material_Translucent;

	// Select default source blend mode
	D3DBLEND defaultSrcBlendMode = (m_material_AlphaMode == ruALPHAMODE_PREMULTIPLIED_NORMAL || m_material_AlphaMode == ruALPHAMODE_PREMULTIPLIED_ADD) ? D3DBLEND_ONE : D3DBLEND_SRCALPHA;

	// Setup fog
	if(m_override_Fog == FALSE)
	{
		m_engine->SetRenderState(D3DRS_FOGENABLE, TRUE);
	}

	if(m_override_AlphaBlend == FALSE)
	{
		switch(m_material_AlphaMode)
		{
			case ruALPHAMODE_NORMAL:
			case ruALPHAMODE_PREMULTIPLIED_NORMAL:
				{
					// Material translucency is off
					if(m_material_Translucent == FALSE && m_material_AlphaMode == ruALPHAMODE_NORMAL)
					{
						// Disable blending and enable Z write
						m_engine->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
						m_engine->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);

						SetZTestMode(D3DCMP_LESSEQUAL);
						SetZWriteMode(TRUE);
					}
					// Material translucency is on
					else
					{
						m_engine->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
						m_engine->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);

						m_engine->SetRenderState(D3DRS_SRCBLEND, defaultSrcBlendMode);
						m_engine->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

						SetZTestMode(D3DCMP_LESSEQUAL);
						SetZWriteMode((m_material_Translucent == FALSE && m_material_AlphaMode == ruALPHAMODE_PREMULTIPLIED_NORMAL) ? TRUE : FALSE);
					}
				}

				break;

			case ruALPHAMODE_ADD:
			case ruALPHAMODE_PREMULTIPLIED_ADD:
				{
					m_engine->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
					m_engine->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
					m_engine->SetRenderState(D3DRS_SRCBLEND, defaultSrcBlendMode);
					m_engine->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

					SetZTestMode(D3DCMP_LESSEQUAL);
					SetZWriteMode(FALSE);
				}

				break;

			case ruALPHAMODE_SUBTRACT:
				{
					m_engine->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
					m_engine->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_REVSUBTRACT);
					m_engine->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
					m_engine->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

					SetZTestMode(D3DCMP_LESSEQUAL);
					SetZWriteMode(FALSE);
				}

				break;
		}
	}
	else
	{
		SetZTestMode(D3DCMP_LESSEQUAL);
	}

	if(m_override_AlphaTest == FALSE)
	{
		// Setup alpha test states
		m_engine->SetRenderState(D3DRS_ALPHATESTENABLE, m_alphaTestEnabled);

		if(m_alphaTestEnabled)
		{
			// Modify alpha test threshold based on material alpha?
			INT32 modifiedAlphaThreshold = (BYTE) (m_alphaTestThreshold * m_material_EffectiveAlpha);

			m_engine->SetRenderState(D3DRS_ALPHAREF, modifiedAlphaThreshold);

			switch(m_alphaTestMode)
			{
				case ruALPHATESTMODE_NONE:
					m_engine->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_ALWAYS);
					break;

				case ruALPHATESTMODE_GREATER:
					m_engine->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);
					break;

				case ruALPHATESTMODE_GREATEREQUAL:
					m_engine->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);
					break;

				case ruALPHATESTMODE_EQUAL:
					m_engine->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_EQUAL);
					break;

				case ruALPHATESTMODE_LESSEQUAL:
					m_engine->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_LESSEQUAL);
					break;

				case ruALPHATESTMODE_LESS:
					m_engine->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_LESS);
					break;

				case ruALPHATESTMODE_NOTEQUAL:
					m_engine->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_NOTEQUAL);
					break;
			}
		}
	}

	if(m_2Sided)
	{
		m_engine->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	}
	else
	{
		if(m_fx_ReverseCull)
		{
			m_engine->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
		}
		else
		{
			m_engine->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
		}
	}

	if(m_fx_StencilEnable)
	{
		// Enable stencil testing
		m_engine->SetRenderState(D3DRS_STENCILENABLE, TRUE);
		m_engine->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
		m_engine->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);
		m_engine->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);
		m_engine->SetRenderState(D3DRS_STENCILREF, m_fx_StencilID);
		m_engine->SetRenderState(D3DRS_STENCILMASK, 0xFFFFFFFF);
		m_engine->SetRenderState(D3DRS_STENCILWRITEMASK, 0xFFFFFFFF);
		m_engine->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
	}
	else
	{
		m_engine->SetRenderState(D3DRS_STENCILENABLE, FALSE);
	}

	m_engine->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE);
}

void CRuShader_Base::EndPass(INT32 passNumber)
{
}

void CRuShader_Base::SetDefaultParameters()
{
	m_fx_ReverseCull = FALSE;
	m_fx_StencilEnable = FALSE;
	m_fx_StencilID = 0;
}

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

	if(paramCache->GetParameter(ruPARAMTYPE_TRANSFORM_WORLD, param))
	{
		CRuMatrix4x4 *transform = (CRuMatrix4x4 *) param;
		
		for(UINT32 i = 0, k = 0; i < 3; ++i)
		{
			for(UINT32 j = 0; j < 4; ++j, ++k)
			{
				m_shaderConstants[k] = transform->m_elements[j][i];
			}
		}
	}

	if(paramCache->GetParameter(ruPARAMTYPE_TRANSFORM_VIEW, param))
	{
		CRuMatrix4x4 *transform = (CRuMatrix4x4 *) param;

		for(UINT32 i = 0, k = 12; i < 3; ++i)
		{
			for(UINT32 j = 0; j < 4; ++j, ++k)
			{
				m_shaderConstants[k] = transform->m_elements[j][i];
			}
		}
	}

	if(paramCache->GetParameter(ruPARAMTYPE_TRANSFORM_PROJECTION, param))
	{
		CRuMatrix4x4 *transform = (CRuMatrix4x4 *) param;

		for(UINT32 i = 0, k = 24; i < 4; ++i)
		{
			for(UINT32 j = 0; j < 4; ++j, ++k)
			{
				m_shaderConstants[k] = transform->m_elements[j][i];
			}
		}
	}

	if(paramCache->GetParameter(ruPARAMTYPE_LIGHT_AMBIENT, param))
	{
		INT32 ambientOffset = 40;
		CRuColor4 *ambient = (CRuColor4 *) param;
		if(ambient)
		{
			m_shaderConstants[ambientOffset++] = ambient->m_r;
			m_shaderConstants[ambientOffset++] = ambient->m_g;
			m_shaderConstants[ambientOffset++] = ambient->m_b;
			m_shaderConstants[ambientOffset++] = ambient->m_a;
		}
		else
		{
			for(INT32 i = 0; i < 4; ++i)
			{
				m_shaderConstants[ambientOffset++] = 0.0f;
			}
		}
	}

	if(paramCache->GetParameter(ruPARAMTYPE_LIGHT_OBJ_SH9, param))
	{
		INT32 sh9Offset = 44;
		CRuColor4 *shCoefficients = (CRuColor4 *) param;
		if(shCoefficients)
		{
			for(INT32 i = 0; i < 9; ++i)
			{
				m_shaderConstants[sh9Offset++] = shCoefficients[i].m_r;
				m_shaderConstants[sh9Offset++] = shCoefficients[i].m_g;
				m_shaderConstants[sh9Offset++] = shCoefficients[i].m_b;
				m_shaderConstants[sh9Offset++] = shCoefficients[i].m_a;
			}
		}
		else
		{
			for(INT32 i = 0; i < 36; ++i)
			{
				m_shaderConstants[sh9Offset++] = 0.0f;
			}
		}
	}

	if(paramCache->GetParameter(ruPARAMTYPE_LIGHT_SPECULAR_DIR, param))
	{
		INT32 specularOffset = 84;
		CRuVector3 *specularDir = (CRuVector3 *) param;
		if(specularDir)
		{
			m_shaderConstants[specularOffset++] = specularDir->m_x;
			m_shaderConstants[specularOffset++] = specularDir->m_y;
			m_shaderConstants[specularOffset++] = specularDir->m_z;
			m_shaderConstants[specularOffset++] = 1.0f;
		}
		else
		{
			for(INT32 i = 0; i < 4; ++i)
			{
				m_shaderConstants[specularOffset++] = 0.0f;
			}
		}
	}

	if(paramCache->GetParameter(ruPARAMTYPE_LIGHT_SPECULAR_COLOR, param))
	{
		INT32 specularOffset = 88;
		CRuColor4 specularColor(static_cast<RUCOLOR>(param));

		m_shaderConstants[specularOffset++] = specularColor.m_r;
		m_shaderConstants[specularOffset++] = specularColor.m_g;
		m_shaderConstants[specularOffset++] = specularColor.m_b;
		m_shaderConstants[specularOffset++] = specularColor.m_a;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_FOG_CONSTANT, param))
	{
		INT32 fogConstantOffset = 100;
		float fogConstant = RuCastDWORDToFloat(param);

		m_shaderConstants[fogConstantOffset] = fogConstant;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_FOG_START, param))
	{
		INT32 fogStartOffset = 101;
		float fogStart = RuCastDWORDToFloat(param);

		m_shaderConstants[fogStartOffset] = fogStart;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_FOG_END, param))
	{
		INT32 fogEndOffset = 102;
		float fogEnd = RuCastDWORDToFloat(param);

		m_shaderConstants[fogEndOffset] = fogEnd;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_FOG_COLOR, param))
	{
		CRuColor4 fogColor(static_cast<RUCOLOR>(param));

		m_engine->SetRenderState(D3DRS_FOGCOLOR, param);

		INT32 fogColorOffset = 4;
		m_pixelShaderConstants[fogColorOffset++] = fogColor.m_r;
		m_pixelShaderConstants[fogColorOffset++] = fogColor.m_g;
		m_pixelShaderConstants[fogColorOffset++] = fogColor.m_b;
		m_pixelShaderConstants[fogColorOffset++] = fogColor.m_a;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_MATERIAL_DIFFUSE, param))
	{
		INT32 globalDiffuseOffset = 104;
		RUCOLOR globalDiffuse = static_cast<RUCOLOR>(param);

		m_shaderConstants[globalDiffuseOffset++] = ((globalDiffuse >> 16) & 0xFF) / 255.0f;
		m_shaderConstants[globalDiffuseOffset++] = ((globalDiffuse >>  8) & 0xFF) / 255.0f;
		m_shaderConstants[globalDiffuseOffset++] = ((globalDiffuse >>  0) & 0xFF) / 255.0f;
	}

	// Morphing tween factor
	if(paramCache->GetParameter(ruPARAMTYPE_TWEEN_FACTOR, param))
	{
		INT32 tweenFactorOffset = 103;
		REAL tweenFactor = RuCastDWORDToFloat(param);

		m_shaderConstants[tweenFactorOffset] = tweenFactor;
	}

	// Material settings
	if(paramCache->GetParameter(ruPARAMTYPE_MATERIAL_SELFILLUMINATION, param))
	{
		INT32 selfIllumOffset = 92;
		REAL selfIllumination = RuCastDWORDToFloat(param);

		m_shaderConstants[selfIllumOffset] = selfIllumination;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_MATERIAL_2SIDED, param))
	{
		m_2Sided = static_cast<BOOL>(param);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_MATERIAL_FACETED, param))
	{
		m_faceted = static_cast<BOOL>(param);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_MATERIAL_WIRE, param))
	{
		m_wire = static_cast<BOOL>(param);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_MATERIAL_ALPHATESTMODE, param))
	{
		m_alphaTestMode = static_cast<RuAlphaTestMode>(param);
		m_alphaTestEnabled = m_alphaTestMode != ruALPHATESTMODE_NONE;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_MATERIAL_ALPHATESTVALUE, param))
	{
		m_alphaTestThreshold = static_cast<UINT32>(param);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_MATERIAL_ALPHAMODE, param))
	{
		m_material_AlphaMode = static_cast<RuAlphaMode>(param);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_MATERIAL_TRANSLUCENT, param))
	{
		m_material_Translucent = param;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_MATERIAL_ZTESTMODE, param))
	{
		m_material_ZTestMode = static_cast<RuZTestMode>(param);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_MATERIAL_ZWRITEMODE, param))
	{
		m_material_ZWriteMode = static_cast<RuZWriteMode>(param);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_MATERIAL_ALPHA, param))
	{
		m_material_Alpha = RuCastDWORDToFloat(param);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_MATERIAL_ALPHAMODIFIER, param))
	{
		m_material_AlphaModifier = RuCastDWORDToFloat(param);
	}

	// Raw vertex shader data
	INT32 rawConstOffset = 0;
	INT32 rawConstCount = 0;

	if(paramCache->GetParameter(ruPARAMTYPE_RAW_OFFSET, param))
	{
		rawConstOffset = param;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_RAW_COUNT, param))
	{
		rawConstCount = param;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_RAW_DATA, param))
	{
		REAL *rawData = reinterpret_cast<REAL *>(param);

		memcpy(&m_shaderConstants[rawConstOffset * 4], rawData, sizeof(REAL) * rawConstCount * 4);
	}

	// Raw pixel shader data
	INT32 rawPSConstOffset = 0;
	INT32 rawPSConstCount = 0;

	if(paramCache->GetParameter(ruPARAMTYPE_RAW_PS_OFFSET, param))
	{
		rawPSConstOffset = param;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_RAW_PS_COUNT, param))
	{
		rawPSConstCount = param;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_RAW_PS_DATA, param))
	{
		REAL *rawData = reinterpret_cast<REAL *>(param);

		memcpy(&m_pixelShaderConstants[rawPSConstOffset * 4], rawData, sizeof(REAL) * rawPSConstCount * 4);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_FX_REVERSECULL, param))
	{
		m_fx_ReverseCull = param;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_SYSTEM_STENCILENABLE, param))
	{
		m_fx_StencilEnable = static_cast<BOOL>(param);
	}

	if(paramCache->GetParameter(ruPARAMTYPE_SYSTEM_STENCILID, param))
	{
		m_fx_StencilID = static_cast<UINT8>(param);
	}
}

void CRuShader_Base::SetZTestMode(D3DCMPFUNC defaultValue)
{
	if(m_override_ZTest == FALSE)
	{
		switch(m_material_ZTestMode)
		{
			case ruZTESTMODE_DEFAULT:
				m_engine->SetRenderState(D3DRS_ZFUNC, defaultValue);
				break;

			case ruZTESTMODE_ALWAYS:
				m_engine->SetRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS);
				break;

			case ruZTESTMODE_GREATER:
				m_engine->SetRenderState(D3DRS_ZFUNC, D3DCMP_GREATER);
				break;

			case ruZTESTMODE_GREATEREQUAL:
				m_engine->SetRenderState(D3DRS_ZFUNC, D3DCMP_GREATEREQUAL);
				break;

			case ruZTESTMODE_EQUAL:
				m_engine->SetRenderState(D3DRS_ZFUNC, D3DCMP_EQUAL);
				break;

			case ruZTESTMODE_LESSEQUAL:
				m_engine->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
				break;

			case ruZTESTMODE_LESS:
				m_engine->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS);
				break;

			case ruZTESTMODE_NOTEQUAL:
				m_engine->SetRenderState(D3DRS_ZFUNC, D3DCMP_NOTEQUAL);
				break;
		}
	}
}

void CRuShader_Base::SetZWriteMode(BOOL defaultValue)
{
	if(m_override_ZWrite == FALSE)
	{
		switch(m_material_ZWriteMode)
		{
			case ruZWRITEMODE_DEFAULT:
				m_engine->SetRenderState(D3DRS_ZWRITEENABLE, defaultValue);
				break;

			case ruZWRITEMODE_DISABLE:
				m_engine->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
				break;

			case ruZWRITEMODE_ENABLE:
				m_engine->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
				break;
		}
	}
}

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

CRuShader_Static_Base::CRuShader_Static_Base(CRuEngineD3D *engine)
:	CRuShader_Base(engine)
{
	// Setup vertex elements
	CRuVertexElement vertexElements[] =
	{
		{ 0,	0,	ruELEMENTTYPE_FLOAT3,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_POSITION,	0 },
		{ 0,	12,	ruELEMENTTYPE_FLOAT3,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_NORMAL,		0 },
		{ 0,	24,	ruELEMENTTYPE_FLOAT2,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_TEXCOORD,	0 },
		RUVERTEXDECL_END()
	};

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

	// Alter base constant count
	m_numBaseConstants = 31;

	// Set identity matrix to identity
	m_identityMatrix.SetToIdentity();
}

CRuShader_Static_Base::~CRuShader_Static_Base()
{
}

void CRuShader_Static_Base::SetDefaultParameters()
{
	CRuShader_Base::SetDefaultParameters();

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

void CRuShader_Static_Base::SetParameters(const CRuParameterCache *paramCache)
{
	CRuShader_Base::SetParameters(paramCache);

	DWORD param;

	if(paramCache->GetParameter(ruPARAMTYPE_TEXTURE_TRANSFORM_0, param))
	{
		INT32 uvTransformOffset = 108;

		CRuMatrix4x4 *transform = (CRuMatrix4x4 *) param;

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

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

CRuShader_Static_Colored_Base::CRuShader_Static_Colored_Base(CRuEngineD3D *engine)
:	CRuShader_Base(engine)
{
	// Setup vertex elements
	CRuVertexElement vertexElements[] =
	{
		{ 0,	0,	ruELEMENTTYPE_FLOAT3,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_POSITION,	0 },
		{ 0,	12,	ruELEMENTTYPE_UBYTE4,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_NORMAL,		0 },
		{ 0,	16,	ruELEMENTTYPE_D3DCOLOR,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_COLOR,		0 },
		{ 0,	20,	ruELEMENTTYPE_D3DCOLOR,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_COLOR,		1 },
		{ 0,	24,	ruELEMENTTYPE_FLOAT2,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_TEXCOORD,	0 },
//		{ 0,	12,	ruELEMENTTYPE_FLOAT3,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_NORMAL,		0 },
//		{ 0,	24,	ruELEMENTTYPE_D3DCOLOR,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_COLOR,		0 },
//		{ 0,	28,	ruELEMENTTYPE_FLOAT2,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_TEXCOORD,	0 },
		RUVERTEXDECL_END()
	};

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

	// Alter base constant count
	m_numBaseConstants = 31;

	// Set identity matrix to identity
	m_identityMatrix.SetToIdentity();
}

CRuShader_Static_Colored_Base::~CRuShader_Static_Colored_Base()
{
}

void CRuShader_Static_Colored_Base::SetDefaultParameters()
{
	CRuShader_Base::SetDefaultParameters();

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

void CRuShader_Static_Colored_Base::SetParameters(const CRuParameterCache *paramCache)
{
	CRuShader_Base::SetParameters(paramCache);

	DWORD param;

	if(paramCache->GetParameter(ruPARAMTYPE_TEXTURE_TRANSFORM_0, param))
	{
		INT32 uvTransformOffset = 108;

		CRuMatrix4x4 *transform = (CRuMatrix4x4 *) param;

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

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

CRuShader_Skin_Base::CRuShader_Skin_Base(CRuEngineD3D *engine)
:	CRuShader_Base(engine),
	m_maxSubFrames(0),
	m_numSubFrames(0),
	m_skinTransforms(NULL),
	m_indexMap(NULL)
{
/*
	// Setup vertex elements
	CRuVertexElement vertexElements[] =
	{
		{ 0,	0,	ruELEMENTTYPE_FLOAT3,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_POSITION,		0 },
		{ 0,	12,	ruELEMENTTYPE_FLOAT3,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_NORMAL,			0 },
		{ 0,	24,	ruELEMENTTYPE_FLOAT4,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_BLENDINDICES,	0 },
		{ 0,	40,	ruELEMENTTYPE_FLOAT4,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_BLENDWEIGHT,		0 },
		{ 0,	56,	ruELEMENTTYPE_FLOAT2,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_TEXCOORD,		0 },
		RUVERTEXDECL_END()
	};
*/
	// Setup vertex elements
	CRuVertexElement vertexElements[] =
	{
		{ 0,	0,	ruELEMENTTYPE_FLOAT3,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_POSITION,		0 },
		{ 0,	12,	ruELEMENTTYPE_UBYTE4,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_NORMAL,			0 },
		{ 0,	16,	ruELEMENTTYPE_D3DCOLOR,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_COLOR,			0 },
		{ 0,	20,	ruELEMENTTYPE_UBYTE4,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_BLENDINDICES,	0 },
		{ 0,	24,	ruELEMENTTYPE_UBYTE4,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_BLENDWEIGHT,		0 },
		{ 0,	28,	ruELEMENTTYPE_SHORT2N,		ruELEMENTMETHOD_DEFAULT,	ruELEMENTUSAGE_TEXCOORD,		0 },
		RUVERTEXDECL_END()
	};

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

	// Alter base constant count
	m_numBaseConstants = 32;

	// Calculate max bone count
	m_maxSubFrames = (m_engine->GetD3DDeviceCaps().MaxVertexShaderConst - m_numBaseConstants) / 3;

	// Set identity matrix to identity
	m_identityMatrix.SetToIdentity();
}

CRuShader_Skin_Base::~CRuShader_Skin_Base()
{
}

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

	if(passNumber == 0)
	{
		INT32 skinPaletteStart = 32;
//		INT32 skinPaletteCoefficientOffset = 27 * 4;
		INT32 skinConstantsOffset = skinPaletteStart * 4;
/*
		// Setup coefficients used in indexed palette skinning
		m_shaderConstants[skinPaletteCoefficientOffset++] = static_cast<REAL>(skinPaletteStart);
		m_shaderConstants[skinPaletteCoefficientOffset++] = 3.0f;
		m_shaderConstants[skinPaletteCoefficientOffset++] = 1.0f;
		m_shaderConstants[skinPaletteCoefficientOffset++] = 0.0f;
*/
		if(m_skinTransforms)
		{
			// Setup skin transform constants
			if(m_indexMap)
			{
				for(INT32 n = 0, k = skinConstantsOffset; n < m_numSubFrames; ++n)
				{
					// Iterate through each column
					for(INT32 i = 0; i < 3; ++i)
					{
						// Iterate through each row
						for(INT32 j = 0; j < 4; ++j, ++k)
						{
							m_shaderConstants[k] = m_skinTransforms[m_indexMap[n]].m_elements[j][i];
						}
					}
				}
			}
			else
			{
				// Index map is NULL, so copy skin transforms directly into shader constants
				for(INT32 n = 0, k = skinConstantsOffset; n < m_numSubFrames; ++n)
				{
					// Iterate through each column
					for(INT32 i = 0; i < 3; ++i)
					{
						// Iterate through each row
						for(INT32 j = 0; j < 4; ++j, ++k)
						{
							m_shaderConstants[k] = m_skinTransforms[n].m_elements[j][i];
						}
					}
				}
			}
		}
		else
		{
			// No skin transform is supplied, set everything to identity
			memset(&m_shaderConstants[skinConstantsOffset], 0, sizeof(float) * m_maxSubFrames * 12);
			for(INT32 n = 0, k = skinConstantsOffset; n < m_maxSubFrames; ++n)
			{
				// Iterate through each register row
				for(INT32 i = 0; i < 3; ++i, k += 5)
				{
					m_shaderConstants[k] = 1.0f;
				}

				k -= 3;
			}

			m_numSubFrames = m_maxSubFrames;
		}
	}
}

void CRuShader_Skin_Base::SetDefaultParameters()
{
	CRuShader_Base::SetDefaultParameters();

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

void CRuShader_Skin_Base::SetParameters(const CRuParameterCache *paramCache)
{
	// Chain to parent class
	CRuShader_Base::SetParameters(paramCache);

	DWORD param;

	if(paramCache->GetParameter(ruPARAMTYPE_TEXTURE_TRANSFORM_0, param))
	{
		INT32 uvTransformOffset = 108;

		CRuMatrix4x4 *transform = (CRuMatrix4x4 *) param;

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

	if(paramCache->GetParameter(ruPARAMTYPE_SKIN_NODECOUNT, param))
	{
		m_numSubFrames = (INT32) param;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_SKIN_TRANSFORMS, param))
	{
		m_skinTransforms = (CRuMatrix4x4 *) param;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_SKIN_INDEXMAP, param))
	{
		m_indexMap = (UINT16 *) param;
	}
}

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

IDirect3DVertexShader9 *RuD3DAssembleVertexShader(const char *vsString)
{
	IDirect3DVertexShader9 *vertexShader = NULL;

	LPD3DXBUFFER assembledShader;
	LPD3DXBUFFER errorMessages;
	if(D3DXAssembleShader(vsString, strlen(vsString), NULL, NULL, 0, &assembledShader,	&errorMessages) != D3D_OK)
	{
		MessageBox(NULL, "Error assembling VS", "", MB_OK);

		if(assembledShader)
			assembledShader->Release();
		if(errorMessages)
		{
			MessageBox(NULL, (LPCSTR) errorMessages->GetBufferPointer(), "", MB_OK);
			errorMessages->Release();
		}

		return NULL;
	}

	// Create shader
	if(g_ruEngineD3D->GetD3DDevice()->CreateVertexShader((DWORD *) assembledShader->GetBufferPointer(), &vertexShader) != D3D_OK)
	{
	}

	if(assembledShader)
		assembledShader->Release();
	if(errorMessages)
		errorMessages->Release();

	return vertexShader;
}

IDirect3DPixelShader9 *RuD3DAssemblePixelShader(const char *psString)
{
	IDirect3DPixelShader9 *pixelShader = NULL;

	LPD3DXBUFFER assembledShader;
	LPD3DXBUFFER errorMessages;
	if(D3DXAssembleShader(psString, strlen(psString), NULL, NULL, 0, &assembledShader,	&errorMessages) != D3D_OK)
	{
		if(assembledShader)
			assembledShader->Release();
		if(errorMessages)
		{
			MessageBox(NULL, (LPCSTR) errorMessages->GetBufferPointer(), "", MB_OK);
			errorMessages->Release();
		}

		return NULL;
	}

	// Create shader
	if(g_ruEngineD3D->GetD3DDevice()->CreatePixelShader((DWORD *) assembledShader->GetBufferPointer(), &pixelShader) != D3D_OK)
	{
		// Handle error here
	}

	if(assembledShader)
		assembledShader->Release();
	if(errorMessages)
		errorMessages->Release();

	return pixelShader;
}

IDirect3DVertexShader9 *RuD3DCompileVertexShader(const char *vsString, const char *entryFunction, const char *profile, ID3DXConstantTable **constantTableOut)
{
	IDirect3DVertexShader9 *vertexShader = NULL;

	LPD3DXBUFFER compiledShader;
	LPD3DXBUFFER errorMessages;
	if(D3DXCompileShader(vsString, strlen(vsString), NULL, NULL, entryFunction, profile, 0, &compiledShader, &errorMessages, constantTableOut) != D3D_OK)
	{
		MessageBox(NULL, "Error assembling VS", "", MB_OK);

		if(compiledShader)
			compiledShader->Release();
		if(errorMessages)
		{
			MessageBox(NULL, (LPCSTR) errorMessages->GetBufferPointer(), "", MB_OK);
			errorMessages->Release();
		}

		return NULL;
	}

	// Create shader
	if(g_ruEngineD3D->GetD3DDevice()->CreateVertexShader((DWORD *) compiledShader->GetBufferPointer(), &vertexShader) != D3D_OK)
	{
	}

	if(compiledShader)
		compiledShader->Release();
	if(errorMessages)
		errorMessages->Release();

	return vertexShader;
}

IDirect3DPixelShader9 *RuD3DCompilePixelShader(const char *psString, const char *entryFunction, const char *profile, ID3DXConstantTable **constantTableOut)
{
	IDirect3DPixelShader9 *pixelShader = NULL;

	LPD3DXBUFFER compiledShader;
	LPD3DXBUFFER errorMessages;
	if(D3DXCompileShader(psString, strlen(psString), NULL, NULL, entryFunction, profile, 0, &compiledShader, &errorMessages, constantTableOut) != D3D_OK)
	{
		if(compiledShader)
			compiledShader->Release();
		if(errorMessages)
		{
			MessageBox(NULL, (LPCSTR) errorMessages->GetBufferPointer(), "", MB_OK);
			errorMessages->Release();
		}

		return NULL;
	}

	// Create shader
	if(g_ruEngineD3D->GetD3DDevice()->CreatePixelShader((DWORD *) compiledShader->GetBufferPointer(), &pixelShader) != D3D_OK)
	{
		// Handle error here
	}

	if(compiledShader)
		compiledShader->Release();
	if(errorMessages)
		errorMessages->Release();

	return pixelShader;
}

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

#pragma managed
