#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(float) * 4 * 256);
	m_numBasePixelShaderConstants = 2;
	memset(m_pixelShaderConstants, 0, sizeof(float) * 4 * 32);
	m_alphaMode = ruALPHAMODE_NORMAL;

	m_override_Fog = FALSE;
	m_override_AlphaTest = FALSE;
	m_override_AlphaBlend = FALSE;
	m_override_ZWrite = FALSE;
	m_force_AlphaBlend = FALSE;
}

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);
}

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;

	// 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:
				{
					// Setup global alpha blends
					if(m_force_AlphaBlend == FALSE && m_shaderConstants[107] >= 1.0f)
					{
						m_engine->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);

						if(m_override_ZWrite == FALSE)
						{
							m_engine->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
						}
					}
					else
					{
						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;
						}

						if(m_override_ZWrite == FALSE)
						{
							m_engine->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
						}
					}
				}

				break;

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

					if(m_override_ZWrite == FALSE)
					{
						m_engine->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
					}
				}

				break;

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

					if(m_override_ZWrite == FALSE)
					{
						m_engine->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
					}
				}

				break;
		}
	}

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

void CRuShader_Base::EndPass(INT32 passNumber)
{
}

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

	// Reset morph targets
	m_morphTarget0 = 0;
	m_morphTarget1 = 0;

	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_POS, 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_LIGHT_SELF, param))
	{
		INT32 selfIllumOffset = 92;
		float selfIllumination = RuCastDWORDToFloat(param);

		m_shaderConstants[selfIllumOffset] = selfIllumination;
	}

	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_GLOBAL_ALPHAMODE, param))
	{
		m_alphaMode = (RuAlphaMode) param;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_GLOBAL_ALPHA, param))
	{
		INT32 globalAlphaOffset = 107;
		REAL globalAlpha = RuCastDWORDToFloat(param);

		m_shaderConstants[globalAlphaOffset] = globalAlpha;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_GLOBAL_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 (DEPRECATED)
	if(paramCache->GetParameter(ruPARAMTYPE_TWEEN_FACTOR, param))
	{
		INT32 tweenFactorOffset = 103;
		float tweenFactor = RuCastDWORDToFloat(param);

		m_shaderConstants[tweenFactorOffset] = tweenFactor;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_TWEEN_MORPHTARGET_0, param))
	{
		m_morphTarget0 = (INT32) param;
	}

	if(paramCache->GetParameter(ruPARAMTYPE_TWEEN_MORPHTARGET_1, param))
	{
		m_morphTarget1 = (INT32) param;
	}

	// Geometry and material settings
	if(paramCache->GetParameter(ruPARAMTYPE_GEOMETRY_2SIDED, param))
	{
		m_2Sided = static_cast<BOOL>(param);
	}

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

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

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

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

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

	// Raw 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);
	}
}

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

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);
}

CRuShader_Static_Base::~CRuShader_Static_Base()
{
}

void CRuShader_Static_Base::Instance(INT32 streamIndex, IRuMesh *mesh, UINT32 morphTargetIndex, void *buffer) const
{
	if(streamIndex == 0)
	{
		CRuVertex_Static *vertices = (CRuVertex_Static *) buffer;
		IRuMorphTarget *morphTarget = mesh->GetMorphTarget(morphTargetIndex);
		const float *texCoords = morphTarget->GetTextureCoordinate(ruTEXCHANNEL_DIFFUSEMAP);

		if(morphTarget)
		{
			const CRuVector3 *position = morphTarget->GetPosition();
			const CRuVector3 *normal = morphTarget->GetNormal();

			if(normal && texCoords)
			{
				for(INT32 i = 0, j = 0, numVertices = mesh->GetNumVertices(); i < numVertices; i++)
				{
					vertices[i].x = position[i].x;
					vertices[i].y = position[i].y;
					vertices[i].z = position[i].z;

					vertices[i].nx = normal[i].x;
					vertices[i].ny = normal[i].y;
					vertices[i].nz = normal[i].z;

					vertices[i].u = texCoords[j++];
					vertices[i].v = texCoords[j++];
				}
			}
			else if(normal)
			{
				for(INT32 i = 0, j = 0, numVertices = mesh->GetNumVertices(); i < numVertices; i++)
				{
					vertices[i].x = position[i].x;
					vertices[i].y = position[i].y;
					vertices[i].z = position[i].z;

					vertices[i].nx = normal[i].x;
					vertices[i].ny = normal[i].y;
					vertices[i].nz = normal[i].z;

					vertices[i].u = 0.0f;
					vertices[i].v = 0.0f;
				}
			}
			else if(texCoords)
			{
				for(INT32 i = 0, j = 0, numVertices = mesh->GetNumVertices(); i < numVertices; i++)
				{
					vertices[i].x = position[i].x;
					vertices[i].y = position[i].y;
					vertices[i].z = position[i].z;

					vertices[i].nx = 0.0f;
					vertices[i].ny = 0.0f;
					vertices[i].nz = 0.0f;

					vertices[i].u = texCoords[j++];
					vertices[i].v = texCoords[j++];
				}
			}
			else
			{
				for(INT32 i = 0, j = 0, numVertices = mesh->GetNumVertices(); i < numVertices; i++)
				{
					vertices[i].x = position[i].x;
					vertices[i].y = position[i].y;
					vertices[i].z = position[i].z;

					vertices[i].nx = 0.0f;
					vertices[i].ny = 0.0f;
					vertices[i].nz = 0.0f;

					vertices[i].u = 0.0f;
					vertices[i].v = 0.0f;
				}
			}
		}
	}
}

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

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()
	};

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

	m_maxSubFrames = (m_engine->GetD3DDeviceCaps().MaxVertexShaderConst - m_numBaseConstants) / 3;
}

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 skinPaletteCoefficientOffset = 27 * 4;
		INT32 skinConstantsOffset = m_numBaseConstants * 4;

		// Setup coefficients used in indexed palette skinning
		m_shaderConstants[skinPaletteCoefficientOffset++] = (float) m_numBaseConstants;
		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::SetParameters(const CRuParameterCache *paramCache)
{
	DWORD param;

	// Chain to parent class
	CRuShader_Base::SetParameters(paramCache);

	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;
	}
}

void CRuShader_Skin_Base::Instance(INT32 streamIndex, IRuMesh *mesh, UINT32 morphTargetIndex, void *buffer) const
{
	if(streamIndex == 0)
	{
		CRuVertex_Skin *vertices = (CRuVertex_Skin *) buffer;

		IRuMorphTarget *morphTarget = mesh->GetMorphTarget(morphTargetIndex);
		const UINT16 *blendIndices = morphTarget->GetBlendIndex();
		const float *blendWeights = morphTarget->GetBlendWeight();
		const float *texCoords = morphTarget->GetTextureCoordinate(ruTEXCHANNEL_DIFFUSEMAP);

		if(morphTarget)
		{
			for(INT32 i = 0, j = 0, k = 0, l = 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].nx = morphTarget->GetNormal()[i].x;
				vertices[i].ny = morphTarget->GetNormal()[i].y;
				vertices[i].nz = morphTarget->GetNormal()[i].z;

				if(blendIndices)
				{
					vertices[i].i0 = blendIndices[k++];
					vertices[i].i1 = blendIndices[k++];
					vertices[i].i2 = blendIndices[k++];
					vertices[i].i3 = blendIndices[k++];
				}
				else
				{
					vertices[i].i0 = 0;
					vertices[i].i1 = 0;
					vertices[i].i2 = 0;
					vertices[i].i3 = 0;
				}

				if(blendWeights)
				{
					vertices[i].w0 = blendWeights[l++];
					vertices[i].w1 = blendWeights[l++];
					vertices[i].w2 = blendWeights[l++];
					vertices[i].w3 = blendWeights[l++];
				}
				else
				{
					vertices[i].w0 = 1.0f;
					vertices[i].w1 = 0.0f;
					vertices[i].w2 = 0.0f;
					vertices[i].w3 = 0.0f;
				}

				if(texCoords)
				{
					vertices[i].u = texCoords[j++];
					vertices[i].v = texCoords[j++];
				}
				else
				{
					vertices[i].u = 0.0f;
					vertices[i].v = 0.0f;
				}
			}
		}
	}
}

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

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
