#include "ThGlutRenderer.h"

using namespace Th;
//-------------------------------------------------------------

IMPLEMENT_IS_DERIVED_FROM(GlutRenderer)
IMPLEMENT_STATIC_RTTI_MEMBER(GlutRenderer, Renderer)
//-------------------------------------------------------------

GlutRenderer::GlutRenderer(int iWidth,int iHeight) : Renderer(iWidth, iHeight)
{ }
//-------------------------------------------------------------

GlutRenderer::GlutRenderer(int iWidth, int iHeight, const Color& rkBGColor)
		:
		Renderer(iWidth, iHeight)
{
	SetBackgroundColor(rkBGColor);
	glEnable(GL_DEPTH_TEST);
}
//-------------------------------------------------------------

void GlutRenderer::SetBackgroundColor(const Color& rkBackgroundColor)
{
	glClearColor(rkBackgroundColor.r, rkBackgroundColor.g, rkBackgroundColor.b, rkBackgroundColor.a);
}
//-------------------------------------------------------------

void GlutRenderer::RenderSphere(const Vector3& rfCenter, const Real& rfRadius)
{
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	
	glDisable(GL_TEXTURE_2D);
	glPushMatrix();
	glTranslatef(rfCenter.x,rfCenter.y, rfCenter.z);
	glutWireSphere(rfRadius, 10,10);
	glPopMatrix();
	
	glPopAttrib();
}
//-------------------------------------------------------------

void GlutRenderer::DisplayBackBuffer()
{
	glFlush();
	glutSwapBuffers();
}
//-------------------------------------------------------------

void GlutRenderer::ClearBuffers() 
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}
//-------------------------------------------------------------

void GlutRenderer::RenderPoint(const Vector3& rkPoint)
{
	glBegin(GL_POINTS);
	glVertex3fv(rkPoint.v);
	glEnd();
}
//-------------------------------------------------------------

void GlutRenderer::RenderLine(const Vector3& rkStart, const Vector3& rkEnd)
{
	glBegin(GL_LINES);
	glVertex3fv(rkStart.v);
	glVertex3fv(rkEnd.v);
	glEnd();
}
//-------------------------------------------------------------

void GlutRenderer::Render(const PointMesh* pkPointMesh)
{
	//Create model->world 4x4 matrix: remember, OpenGl stores 

	//the data in row-vector notation

	GLfloat fScale = pkPointMesh->WorldScale();
	const Vector3& kTrans = pkPointMesh->WorldTranslate();
	const Matrix3& kRot = pkPointMesh->WorldRotate();
	GLfloat afModelToWorld[16] = {
		fScale*kRot[0][0], fScale*kRot[1][0], fScale*kRot[2][0], 0.0f,
		fScale*kRot[0][1], fScale*kRot[1][1], fScale*kRot[2][1], 0.0f,
		fScale*kRot[0][1], fScale*kRot[1][2], fScale*kRot[2][2], 0.0f,
                kTrans.x,	   kTrans.y,	      kTrans.z         , 1.0f
	};
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glMultMatrixf(afModelToWorld);
	glBegin(GL_POINTS);
    const Vector3* const akVertices = pkPointMesh->Vertices();
	for (int iIndex = 0; iIndex < pkPointMesh->VertexCount(); iIndex++)
	{
		glVertex3fv(akVertices[iIndex].v);
	}
	glEnd();
	glPopMatrix();
}
//-------------------------------------------------------------

void GlutRenderer::Render(const MD2Object* pkMD2Object)
{
	static int siFrame = 0;
	static float sfTime = 0.0f;
	static int siAnimTime = 30;

	PlaceInWorld(pkMD2Object);
	const MD2Object::MD2Header& rkHeader = pkMD2Object->Header();
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	PlaceInWorld(pkMD2Object);
	const Vector3* akVertices = pkMD2Object->Vertices();
	static int iCurrFrame = 0;
	if (siFrame >= siAnimTime)
	{
		iCurrFrame++;
		siFrame = 0;
		sfTime = 0.0f;
	}
	if (iCurrFrame >= rkHeader.iNumFrames-1)
		iCurrFrame = 0;

	//Get Triangles & Texture coords

	const MD2Object::MD2Triangle* akTriangle = pkMD2Object->Triangles();
	const MD2Object::MD2TextureCoord* akTexture = pkMD2Object->TextureCoords();

	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	
	//Render triangles

	glBegin(GL_TRIANGLES);
	for (int iIndex = 0; iIndex < rkHeader.iNumTriangles; iIndex++)
	{
		int iLookUp1 = akTriangle[iIndex].iVertexIndex[0]+iCurrFrame*rkHeader.iNumVertices;
		int iLookUp2 = akTriangle[iIndex].iVertexIndex[0]+(iCurrFrame+1)*rkHeader.iNumVertices;
		glTexCoord2f(((float)akTexture[akTriangle[iIndex].iTextureIndex[0]].sU) / (float)rkHeader.iSkinWidth,
					 ((float)akTexture[akTriangle[iIndex].iTextureIndex[0]].sV) / (float)rkHeader.iSkinHeight);
		glVertex3f(	(1.0f-sfTime)*akVertices[iLookUp1].x + sfTime*akVertices[iLookUp2].x,
					(1.0f-sfTime)*akVertices[iLookUp1].y + sfTime*akVertices[iLookUp2].y,
					(1.0f-sfTime)*akVertices[iLookUp1].z + sfTime*akVertices[iLookUp2].z);

		iLookUp1 = akTriangle[iIndex].iVertexIndex[1]+iCurrFrame*rkHeader.iNumVertices;
		iLookUp2 = akTriangle[iIndex].iVertexIndex[1]+(iCurrFrame+1)*rkHeader.iNumVertices;
		glTexCoord2f(((float)akTexture[akTriangle[iIndex].iTextureIndex[1]].sU) / (float)rkHeader.iSkinWidth,
					 ((float)akTexture[akTriangle[iIndex].iTextureIndex[1]].sV) / (float)rkHeader.iSkinHeight);
		glVertex3f(	(1.0f-sfTime)*akVertices[iLookUp1].x + sfTime*akVertices[iLookUp2].x,
					(1.0f-sfTime)*akVertices[iLookUp1].y + sfTime*akVertices[iLookUp2].y,
					(1.0f-sfTime)*akVertices[iLookUp1].z + sfTime*akVertices[iLookUp2].z);


		iLookUp1 = akTriangle[iIndex].iVertexIndex[2]+iCurrFrame*rkHeader.iNumVertices;
		iLookUp2 = akTriangle[iIndex].iVertexIndex[2]+(iCurrFrame+1)*rkHeader.iNumVertices;
		glTexCoord2f(((float)akTexture[akTriangle[iIndex].iTextureIndex[2]].sU) / (float)rkHeader.iSkinWidth,
					 ((float)akTexture[akTriangle[iIndex].iTextureIndex[2]].sV) / (float)rkHeader.iSkinHeight);
		glVertex3f(	(1.0f-sfTime)*akVertices[iLookUp1].x + sfTime*akVertices[iLookUp2].x,
					(1.0f-sfTime)*akVertices[iLookUp1].y + sfTime*akVertices[iLookUp2].y,
					(1.0f-sfTime)*akVertices[iLookUp1].z + sfTime*akVertices[iLookUp2].z);
	}
	glEnd();

	glPopMatrix();

	siFrame++;
	sfTime += 1.0f/(float)siAnimTime;
}
//-------------------------------------------------------------

void GlutRenderer::PlaceInWorld(const Geometry* pkObject)
{
	assert(pkObject != NULL);
	GLfloat fScale = pkObject->WorldScale();
	const Vector3& kTrans = pkObject->WorldTranslate();
	const Matrix3& kRot = pkObject->WorldRotate();
	GLfloat afModelToWorld[16] = {
		fScale*kRot[0][0], fScale*kRot[1][0], fScale*kRot[2][0], 0.0f,
		fScale*kRot[0][1], fScale*kRot[1][1], fScale*kRot[2][1], 0.0f,
		fScale*kRot[0][1], fScale*kRot[1][2], fScale*kRot[2][2], 0.0f,
                kTrans.x,	   kTrans.y,	      kTrans.z         , 1.0f
	};
	glMultMatrixf(afModelToWorld);
}
//-------------------------------------------------------------