#include "stdafx.h"													// for precompiled headers (has to be in first place)
#include "3dtrianglebunch.h"								// C3DTriangleBunch
#include <assert.h>													// assert()
#include "3DMaterial.h"											// C3DMaterial
#include "3DRenderer.h"											// POLYBUMPVERTEX
#include "Error.h"													// Error,Message
#include "3DTools.h"												// Build3DCone

#include "TangentSpaceCalculation.h"				// CTangentSpaceCalculation

// constructor
C3DTriangleBunch::C3DTriangleBunch( void )
{
	m_IVertexBuffer=0;
	m_IIndexBuffer=0;
	m_pMaterial=0;
	m_localIndex=0;
	m_PrimitiveCount=0;
	m_minlocalVertex=0;
	m_maxlocalVertex=0;
}

// destructor
C3DTriangleBunch::~C3DTriangleBunch( void )
{
}



// to calculate the tangent base from one triangle only
class COneTriangleInputProxy
{
public:

	//! /return 0..
	DWORD GetTriangleCount( void ) const
	{
		return(1);
	}

	//! /param indwTriNo 0..
	//! /param outdwPos
	//! /param outdwNorm
	//! /param outdwUV
	void GetTriangleIndices( const DWORD indwTriNo, DWORD outdwPos[3], DWORD outdwNorm[3], DWORD outdwUV[3] ) const
	{
		assert(indwTriNo==0);

		outdwNorm[0]=0;
		outdwNorm[1]=0;
		outdwNorm[2]=0;
		outdwPos[0]=0;
		outdwPos[1]=1;
		outdwPos[2]=2;
		outdwUV[0]=0;
		outdwUV[1]=1;
		outdwUV[2]=2;
	}

	//! /param indwPos 0..
	//! /param outfPos
	void GetPos( const DWORD indwPos, float outfPos[3] ) const
	{
		assert(indwPos<3);
		outfPos[0]=m_VertexPos[indwPos].x;
		outfPos[1]=m_VertexPos[indwPos].y;
		outfPos[2]=m_VertexPos[indwPos].z;
	}

	//! /param indwPos 0..
	//! /param outfUV 
	void GetUV( const DWORD indwPos, float outfUV[2] ) const
	{
		assert(indwPos<3);
		outfUV[0]=m_U[indwPos];
		outfUV[1]=m_V[indwPos];
	}


	D3DXVECTOR3		m_VertexPos[3];			//!<
	float					m_U[3];							//!<
	float					m_V[3];							//!<
};





// render tangent space vectors (this could be a major slowdown)
void C3DTriangleBunch::RenderDebugHelper( C3DRenderer &inRenderer, float infScale )
{
	bool bTriBaseVectors=inRenderer.GetShowTriBaseSpace();
	bool bVertexBaseVectors=inRenderer.GetShowVertexBaseSpace();
	bool bNormals=inRenderer.GetShowNormals();

	assert(bTriBaseVectors || bNormals || bVertexBaseVectors);


	const int _ARROW_CY_SIDES=3;
	POLYBUMPVERTEX array[_ARROW_CY_SIDES*2+2];

	HRESULT hRes;
	IDirect3DDevice8 *pDev=inRenderer.GetDirectXDevice();				assert(pDev);

	DWORD inoutdwBackBufferLayers=0;

	pDev->SetRenderState(D3DRS_ZFUNC,D3DCMP_LESS);
	pDev->SetRenderState(D3DRS_ALPHABLENDENABLE,FALSE);
	pDev->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ONE);
	pDev->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ZERO);
	pDev->SetPixelShader(0);
	pDev->SetTexture(0,0);
	pDev->SetTexture(1,0);
	pDev->SetTexture(3,0);
	pDev->SetRenderState(D3DRS_SPECULARENABLE,FALSE);
	pDev->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TFACTOR);
	pDev->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_SELECTARG1);
	pDev->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_DISABLE);

	pDev->SetVertexShader(inRenderer.m_ShaderManager.m_dwVS_Standard);


	

	POLYBUMPVERTEX *VertexBufferPtr=0;

	hRes=m_IVertexBuffer->Lock(m_minlocalVertex*sizeof(POLYBUMPVERTEX),
																				(m_maxlocalVertex-m_minlocalVertex)*sizeof(POLYBUMPVERTEX),
																				(BYTE **)(&VertexBufferPtr),D3DLOCK_READONLY);
	if(FAILED(hRes))
	{
		Error.AddDirectX("Lock VertexBuffer (LV) failed",hRes);
		return;
	}

	
	const float fLengthScale=5.0f*infScale;
	const float fOutScale=0.2f*infScale;
	const float fInScale=fOutScale*0.1f*infScale;

	// for every vertex
	for(DWORD i=0;i<m_maxlocalVertex-m_minlocalVertex;i++)
	{
		D3DXVECTOR3 vTo,vFrom;
		D3DXVECTOR3 vOverSurfacePos=VertexBufferPtr[i].pos+VertexBufferPtr[i].normal*infScale*0.2f;

		if(bVertexBaseVectors)
		{
			pDev->SetRenderState(D3DRS_TEXTUREFACTOR,D3DXCOLOR(1,0,0,0));						// red = binormal
			vTo=vOverSurfacePos;
			vFrom=vOverSurfacePos+VertexBufferPtr[i].binormal*fLengthScale;
			Build3DCone(array,_ARROW_CY_SIDES,fOutScale,fInScale,vFrom,vTo);
			pDev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,_ARROW_CY_SIDES*2,array,sizeof(POLYBUMPVERTEX));

			pDev->SetRenderState(D3DRS_TEXTUREFACTOR,D3DXCOLOR(0,1,0,0));						// green = tangent
			vTo=vOverSurfacePos;
			vFrom=vOverSurfacePos+VertexBufferPtr[i].tangent*fLengthScale;
			Build3DCone(array,_ARROW_CY_SIDES,fOutScale,fInScale,vFrom,vTo);
			pDev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,_ARROW_CY_SIDES*2,array,sizeof(POLYBUMPVERTEX));

			pDev->SetRenderState(D3DRS_TEXTUREFACTOR,D3DXCOLOR(0,0,1,0));						// blue = tnormal
			vTo=vOverSurfacePos;
			vFrom=vOverSurfacePos+VertexBufferPtr[i].tnormal*fLengthScale;
			Build3DCone(array,_ARROW_CY_SIDES,fOutScale,fInScale,vFrom,vTo);
			pDev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,_ARROW_CY_SIDES*2,array,sizeof(POLYBUMPVERTEX));
		}

		if(bNormals)
		{
			pDev->SetRenderState(D3DRS_TEXTUREFACTOR,D3DXCOLOR(0.5f,0.5f,0.5f,0));	// grey = normal
			vTo=vOverSurfacePos;
			vFrom=vOverSurfacePos+VertexBufferPtr[i].normal*fLengthScale*2.0f;
			Build3DCone(array,_ARROW_CY_SIDES,fOutScale,fInScale,vFrom,vTo);
			pDev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,_ARROW_CY_SIDES*2,array,sizeof(POLYBUMPVERTEX));
		}
	}

	if(bTriBaseVectors)
	{
		WORD *IndexBufferPtr=0;

		hRes=m_IIndexBuffer->Lock(m_localIndex*3,m_PrimitiveCount*sizeof(WORD),(BYTE **)(&IndexBufferPtr),D3DLOCK_READONLY);
		if(FAILED(hRes))
		{
			m_IVertexBuffer->Unlock();
			Error.AddDirectX("Lock IndexBuffer failed",hRes);
			return;
		}

		for(DWORD i=0;i<m_PrimitiveCount;i++)
		{
			D3DXVECTOR3 vTo,vFrom;
			WORD a=(*IndexBufferPtr++) - (WORD)m_minlocalVertex;
			WORD b=(*IndexBufferPtr++) - (WORD)m_minlocalVertex;
			WORD c=(*IndexBufferPtr++) - (WORD)m_minlocalVertex;

			D3DXVECTOR3 midpos = (VertexBufferPtr[a].pos + VertexBufferPtr[b].pos + VertexBufferPtr[c].pos)*(1.0f/3.0f);

			CTangentSpaceCalculation<COneTriangleInputProxy> tangents;
			COneTriangleInputProxy Input;

			Input.m_VertexPos[0]=VertexBufferPtr[a].pos;
			Input.m_VertexPos[1]=VertexBufferPtr[b].pos;
			Input.m_VertexPos[2]=VertexBufferPtr[c].pos;
			Input.m_U[0]=VertexBufferPtr[a].tu;
			Input.m_U[1]=VertexBufferPtr[b].tu;
			Input.m_U[2]=VertexBufferPtr[c].tu;
			Input.m_V[0]=VertexBufferPtr[a].tv;
			Input.m_V[1]=VertexBufferPtr[b].tv;
			Input.m_V[2]=VertexBufferPtr[c].tv;

			// calculate the base matrices
			tangents.CalculateTangentSpace(Input);
//			assert(tangents.GetBaseCount()==3);

			D3DXVECTOR3 vU,vV,vN;
			tangents.GetBase(0,vU,vV,vN);

			D3DXVECTOR3 vOverSurfacePos=midpos+vN*infScale*0.2f;

			pDev->SetRenderState(D3DRS_TEXTUREFACTOR,D3DXCOLOR(1,0,0,0));						// red = binormal
			vTo=vOverSurfacePos;
			vFrom=vOverSurfacePos+vU*fLengthScale;
			Build3DCone(array,_ARROW_CY_SIDES,fOutScale,fInScale,vFrom,vTo);
			pDev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,_ARROW_CY_SIDES*2,array,sizeof(POLYBUMPVERTEX));

			pDev->SetRenderState(D3DRS_TEXTUREFACTOR,D3DXCOLOR(0,1,0,0));						// green = tangent
			vTo=vOverSurfacePos;
			vFrom=vOverSurfacePos+vV*fLengthScale;
			Build3DCone(array,_ARROW_CY_SIDES,fOutScale,fInScale,vFrom,vTo);
			pDev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,_ARROW_CY_SIDES*2,array,sizeof(POLYBUMPVERTEX));

			pDev->SetRenderState(D3DRS_TEXTUREFACTOR,D3DXCOLOR(0,0,1,0));						// blue = tnormal
			vTo=vOverSurfacePos;
			vFrom=vOverSurfacePos+vN*fLengthScale;
			Build3DCone(array,_ARROW_CY_SIDES,fOutScale,fInScale,vFrom,vTo);
			pDev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,_ARROW_CY_SIDES*2,array,sizeof(POLYBUMPVERTEX));
		}

		m_IIndexBuffer->Unlock();
	}

	m_IVertexBuffer->Unlock();
}
