// draw draws the set of graphics primitives using the object's world
// transformation and reflected colour
//
void Graphic::draw(const iObject* object, int i) {

    // if graphic is not setup, set it up
    if (!isSetup) setup(object);

    if (isSetup) {
		#if   PIPELINE == FIXED_FUNCTION
		#elif PIPELINE == PROGRAMMABLE || PIPELINE == PROGRAMMABLE_EFFECT
		Display* disp = reinterpret_cast<Display*>(display);
		Matrix worldView = object->world() * disp->viewMatrix();
		Matrix worldViewProjection = worldView * disp->projectionMatrix();
		#endif
        #if   PIPELINE == FIXED_FUNCTION
        d3dd->SetTransform(D3DTS_WORLD, (D3DXMATRIX*)(&object->world()));
        d3dd->SetMaterial(&mat);
        if (!strcmp(technique, "opaque") || !strcmp(technique, "skybox"))
            d3dd->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
        else if (!strcmp(technique, "translucent"))
            d3dd->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
        vertexList->draw(i);
        #elif PIPELINE == PROGRAMMABLE
        LPD3DXCONSTANTTABLE vertexTable = (LPD3DXCONSTANTTABLE)uniformVS;
        LPD3DXCONSTANTTABLE fragmentTable = (LPD3DXCONSTANTTABLE)uniformFS;
        vertexTable->SetMatrix(d3dd, "world", (D3DXMATRIX*)&object->world());
        fragmentTable->SetFloatArray(d3dd, "material.ambient", (FLOAT*)&ambient, 4);
        fragmentTable->SetFloatArray(d3dd, "material.diffuse", (FLOAT*)&diffuse, 4);
        fragmentTable->SetFloatArray(d3dd, "material.specular", (FLOAT*)&specular, 4); 
        fragmentTable->SetFloat(d3dd, "material.power", (FLOAT)power);
        if (!strcmp(technique, "opaque") || !strcmp(technique, "skybox"))
            d3dd->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
        else if (!strcmp(technique, "translucent"))
            d3dd->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
        #elif PIPELINE == PROGRAMMABLE_EFFECT
        effect->SetInt(stagesHandle,      nStages);
        effect->SetMatrix(worldHandle,    (D3DXMATRIX*)&object->world());
        effect->SetVector(ambientHandle,  (D3DXVECTOR4*)&ambient);
        effect->SetVector(diffuseHandle,  (D3DXVECTOR4*)&diffuse);
        effect->SetVector(specularHandle, (D3DXVECTOR4*)&specular);
        effect->SetFloat(powerHandle,     power);
		effect->SetFloat(roughnessHandle, object->getRoughness());
		effect->SetFloat(refractionHandle,object->getIndexOfRefraction());
		effect->SetInt(algorithmHandle,   object->getAlgorithm());
		effect->SetMatrix(worldViewHandle, (D3DXMATRIX*)&worldView);
		effect->SetMatrix(clipMatrixHandle, (D3DXMATRIX*)&worldViewProjection);
        effect->CommitChanges();
        Matrix viewProjectionBak;
        effect->GetMatrix("viewProjection", (D3DXMATRIX*)&viewProjectionBak);
        effect->SetTechnique(techniqueHandle);
        unsigned nPasses;
        effect->Begin(&nPasses, 0);
        for (unsigned iPass = 0; iPass < nPasses; iPass++) {
            effect->BeginPass(iPass);
            vertexList->draw(i);
            effect->EndPass();
        }
        effect->End();
        #endif
    }
}
bool CD3D9HLSLMaterialRenderer::setVariable(bool vertexShader, const c8* name, 
											const f32* floats, int count)
{
	LPD3DXCONSTANTTABLE tbl = vertexShader ? VSConstantsTable : PSConstantsTable;
	if (!tbl)
		return false;

	// currently we only support top level parameters. 
	// Should be enough for the beginning. (TODO)

	D3DXHANDLE hndl = tbl->GetConstantByName(NULL, name);
	if (!hndl)
	{
		core::stringc s = "HLSL Variable to set not found: '";
		s += name;
		s += "'. Available variables are:";
		os::Printer::log(s.c_str(), ELL_WARNING);
		printHLSLVariables(tbl);
		return false;
	}

	HRESULT hr = tbl->SetFloatArray(pID3DDevice, hndl, floats, count);
	if (FAILED(hr))
	{
		os::Printer::log("Error setting float array for HLSL variable", ELL_WARNING);
		return false;
	}

	return true;
}
bool CD3D9HLSLMaterialRenderer::setVariable(bool vertexShader, s32 index,
					const f32* floats, int count)
{
	LPD3DXCONSTANTTABLE tbl = vertexShader ? VSConstantsTable : PSConstantsTable;
	if (index < 0 || !tbl)
		return false;

	// currently we only support top level parameters.
	// Should be enough for the beginning. (TODO)

	D3DXHANDLE hndl = tbl->GetConstant(NULL, index);
	if (!hndl)
		return false;

	D3DXCONSTANT_DESC Description;
	UINT ucount = 1;
    tbl->GetConstantDesc(hndl, &Description, &ucount);

	if(Description.RegisterSet != D3DXRS_SAMPLER)
	{
		HRESULT hr = tbl->SetFloatArray(pID3DDevice, hndl, floats, count);
		if (FAILED(hr))
		{
			os::Printer::log("Error setting float array for HLSL variable", ELL_WARNING);
			return false;
		}
	}

	return true;
}
// Function for pixel shader that just needs material colour
void PS_PlainColourFn( int method, CMatrix4x4* worldMatrix, CCamera* camera )
{
	// Ensure we have default render states
	SetDefaultStates();

	LPD3DXCONSTANTTABLE shaderConsts = renderMethods[method].pixelConsts;

	shaderConsts->SetFloatArray( g_pd3dDevice, "MaterialColour", (FLOAT*)&m_DiffuseColour, 3 );
}
// Pass data to pixel shaders that perform pixel lighting (2 point lights)
void PS_PixelLit2Fn( int method, CMatrix4x4* worldMatrix, CCamera* camera )
{
	// Ensure we have default render states
	SetDefaultStates();

	LPD3DXCONSTANTTABLE shaderConsts = renderMethods[method].pixelConsts;

	D3DXVECTOR3 cameraPos = ToD3DXVECTOR( camera->Position() );
	shaderConsts->SetFloatArray( g_pd3dDevice, "CameraPosition", (FLOAT*)&cameraPos, 3 );

	shaderConsts->SetFloatArray( g_pd3dDevice, "AmbientLight", (FLOAT*)&m_AmbientLight, 3 );
	shaderConsts->SetFloatArray( g_pd3dDevice, "Light1Position", (FLOAT*)&m_Lights[0]->GetPosition(), 3 );
	shaderConsts->SetFloatArray( g_pd3dDevice, "Light1Colour", (FLOAT*)&m_Lights[0]->GetColour(), 3 );
	shaderConsts->SetFloat( g_pd3dDevice, "Light1Brightness", m_Lights[0]->GetBrightness() );
	shaderConsts->SetFloatArray( g_pd3dDevice, "Light2Position", (FLOAT*)&m_Lights[1]->GetPosition(), 3 );
	shaderConsts->SetFloatArray( g_pd3dDevice, "Light2Colour", (FLOAT*)&m_Lights[1]->GetColour(), 3 );
	shaderConsts->SetFloat( g_pd3dDevice, "Light2Brightness", m_Lights[1]->GetBrightness() );

	shaderConsts->SetFloatArray( g_pd3dDevice, "MaterialColour", (FLOAT*)&m_DiffuseColour, 3 ); // If needed
	shaderConsts->SetFloatArray( g_pd3dDevice, "SpecularStrength", (FLOAT*)&m_SpecularColour, 3 ); // If needed
	shaderConsts->SetFloat( g_pd3dDevice, "SpecularPower", m_SpecularPower );
}
// Pass data to vertex shaders that perform vertex lighting (1 point light)
// Passes full range of data - some shaders don't need all of it. This
// reduces the number of these functions at the expense of redundancy
void VS_VertLit1Fn( int method, CMatrix4x4* worldMatrix, CCamera* camera )
{
	LPD3DXCONSTANTTABLE shaderConsts = renderMethods[method].vertexConsts;

	D3DXMATRIXA16 matViewProj = ToD3DXMATRIX( camera->GetViewProjMatrix() );
	shaderConsts->SetMatrix( g_pd3dDevice, "ViewProjMatrix", &matViewProj );

	D3DXMATRIX* matWorld = ToD3DXMATRIXPtr( worldMatrix );
	shaderConsts->SetMatrix( g_pd3dDevice, "WorldMatrix", matWorld );

	D3DXVECTOR3 cameraPos = ToD3DXVECTOR( camera->Position() );
	shaderConsts->SetFloatArray( g_pd3dDevice, "CameraPosition", (FLOAT*)&cameraPos, 3 ); // If needed

	shaderConsts->SetFloatArray( g_pd3dDevice, "AmbientLight", (FLOAT*)&m_AmbientLight, 3 );
	shaderConsts->SetFloatArray( g_pd3dDevice, "LightPosition", (FLOAT*)&m_Lights[0]->GetPosition(), 3 );
	shaderConsts->SetFloatArray( g_pd3dDevice, "LightColour", (FLOAT*)&m_Lights[0]->GetColour(), 3 );
	shaderConsts->SetFloat( g_pd3dDevice, "LightBrightness", m_Lights[0]->GetBrightness() );

	shaderConsts->SetFloatArray( g_pd3dDevice, "MaterialColour", (FLOAT*)&m_DiffuseColour, 3 ); // If needed
	shaderConsts->SetFloatArray( g_pd3dDevice, "SpecularStrength", (FLOAT*)&m_SpecularColour, 3 ); // If needed
	shaderConsts->SetFloat( g_pd3dDevice, "SpecularPower", m_SpecularPower ); // If needed
}