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

/*  Render.c                                                                            */

/*                                                                                      */

/*  Author: George McBay (gfm@my-deja.com)                                              */

/*	Jet3D Modifications: Christopher Plymire (chrisjp@eudoramail.com)					*/

/*																						*/

/*  Description: Polygon rasterization functions for OpenGL driver                      */

/*                                                                                      */

/*  The contents of this file are subject to the Genesis3D Public License               */

/*  Version 1.01 (the "License"); you may not use this file except in                   */

/*  compliance with the License. You may obtain a copy of the License at                */

/*  http://www.genesis3d.com                                                            */

/*                                                                                      */

/*  Software distributed under the License is distributed on an "AS IS"                 */

/*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See                */

/*  the License for the specific language governing rights and limitations              */

/*  under the License.                                                                  */

/*                                                                                      */

/*                                                                                      */

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



#define USE_PCACHE 1





//#include <windows.h>

#include <GL/gl.h>



#include "JetDirect.h"

#include "Render.h"

#include "OGLDrv.h"

#include "OglMisc.h"

#include "THandle.h"

#include "BeSpec.h"



#ifdef USE_PCACHE

#include "PCache.h"

#endif



DRV_RENDER_MODE		RenderMode = RENDER_NONE;

uint32				Render_HardwareFlags = 0;



GLuint				boundTexture = 0;  // Currently bound OpenGL texture object

GLuint				boundTexture2 = 0; // Currently bound OpenGL tex object on second TMU



GLuint				decalTexObj = 0;



extern JetDirectWindow* pDirectGLWindow;



// Render a world polygon without multitexture support.  This will do two-polygon draws,

// one with the regular texture, then another with the lightmap texture.  Clearly we hope

// we don't have to use this function (if multitexture is supported in hardware and OpenGL

// ICD, we won't have to), but its here just in case.



//jeBoolean DRIVERCC Render_WorldPoly(jeTLVertex *Pnts, int32 NumPoints, jeRDriver_Layer *Layers, int32 NumLayers, void *LMapCBContext, uint32 Flags);



void Render_WorldPolyRegular(jeTLVertex *Pnts, int32 NumPoints,jeRDriver_Layer *Layers, int32 NumLayers, 

							 void *LMapCBContext,

							 GLfloat shiftU, GLfloat shiftV, 

							 GLfloat scaleU, GLfloat scaleV,

							 GLubyte alpha)

{

	jeTLVertex *pPnt;

	GLfloat zRecip;

	GLfloat tu, tv;

	GLint	i;



	if(NumLayers > 1)

	{

		glDepthMask(GL_FALSE);

	}



	pPnt = Pnts;



	glBegin(GL_TRIANGLE_FAN);	



	for(i = 0; i < NumPoints; i++)

	{	

		zRecip = 1.0f / pPnt->z;   



		tu = (pPnt->u * scaleU + shiftU);

		tv = (pPnt->v * scaleV + shiftV);



		glColor4ub((GLubyte)pPnt->r, (GLubyte)pPnt->g, (GLubyte)pPnt->b, alpha);



		glTexCoord4f(tu * Layers[0].THandle->InvScale * zRecip, 

			tv * Layers[0].THandle->InvScale * zRecip, 0.0f, zRecip);

	

		glVertex3f(pPnt->x, pPnt->y, -1.0f + zRecip);

		

		pPnt++;

	}



	glEnd(); 



	if(NumLayers > 1)

	{

		glDepthMask(GL_TRUE);

		

		glBlendFunc(GL_DST_COLOR,GL_ZERO);



		pPnt = Pnts;



		if(boundTexture != Layers[1].THandle->TextureID)

		{

			glBindTexture(GL_TEXTURE_2D, Layers[1].THandle->TextureID);

			boundTexture = Layers[1].THandle->TextureID;

		}



		if(Layers[1].THandle->Flags & THANDLE_UPDATE)

		{

			THandle_Update(Layers[1].THandle);

		}



		shiftU = Layers[1].ShiftU;//(GLfloat)LInfo->MinU - 8.0f;

		shiftV = Layers[1].ShiftV;//(GLfloat)LInfo->MinV - 8.0f;



		glColor4ub(255, 255, 255, 255);



	    glBegin(GL_TRIANGLE_FAN);	

		

		for(i = 0; i < NumPoints; i++)

		{	

			zRecip = 1.0f / pPnt->z;   



			tu = pPnt->u - shiftU;

			tv = pPnt->v - shiftV;



			glTexCoord4f(tu * Layers[1].THandle->InvScale * zRecip, 

				tv * Layers[1].THandle->InvScale * zRecip, 0.0f, zRecip);



			glVertex3f(pPnt->x, pPnt->y, -1.0f + zRecip);

			

			pPnt++;

		}

		

		glEnd(); 



		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	}

}





// Render a world polygon using multiple TMUs to map the regular texture and lightmap in one pass 

void Render_WorldPolyMultitexture(jeTLVertex *Pnts, int32 NumPoints, jeRDriver_Layer* Layers, int32 NumLayers,

								  void *LMapCBContext,

								  GLfloat shiftU, GLfloat shiftV, 

								  GLfloat scaleU, GLfloat scaleV,

								  GLubyte alpha)

{

	jeTLVertex *pPnt;

	GLfloat zRecip;

	GLfloat tu, tv, lu, lv;

	GLfloat shiftU2, shiftV2;

	GLint	i;

	



	if(NumLayers > 1)

	{

		glActiveTextureARB(GL_TEXTURE1_ARB);

		glEnable(GL_TEXTURE_2D);



		if(boundTexture2 != Layers[1].THandle->TextureID)

		{

			glBindTexture(GL_TEXTURE_2D,Layers[1].THandle->TextureID);

			boundTexture2 = Layers[1].THandle->TextureID;

		}



		if(Layers[1].THandle->Flags & THANDLE_UPDATE)

		{

			THandle_Update(Layers[1].THandle);

		}



		shiftU2 = (GLfloat)Layers[1].ShiftU;//MinU - 8.0f;

		shiftV2 = (GLfloat)Layers[1].ShiftV;//(GLfloat)LInfo->MinV - 8.0f;

	}





	pPnt = Pnts;



	glBegin(GL_TRIANGLE_FAN);	



	for(i = 0; i < NumPoints; i++)

	{	

		zRecip = 1.0f / pPnt->z;   



		tu = (pPnt->u * scaleU + shiftU);

		tv = (pPnt->v * scaleV + shiftV);



		glColor4ub((GLubyte)pPnt->r, (GLubyte)pPnt->g, (GLubyte)pPnt->b, alpha);



		glMultiTexCoord4fARB(GL_TEXTURE0_ARB, tu * Layers->THandle->InvScale * zRecip, 

			tv * Layers->THandle->InvScale * zRecip, 0.0f, zRecip);





		if(NumLayers > 1)

		{

			lu = pPnt->u - shiftU2;

			lv = pPnt->v - shiftV2;



			glMultiTexCoord4fARB(GL_TEXTURE1_ARB, lu * Layers[1].THandle->InvScale * zRecip, 

				lv * Layers[1].THandle->InvScale * zRecip, 0.0f, zRecip);

		}





		glVertex3f(pPnt->x, pPnt->y, -1.0f + zRecip);

		

		pPnt++;

	}



	glEnd(); 



	if( NumLayers > 1)

	{

		glDisable(GL_TEXTURE_2D);

		glActiveTextureARB(GL_TEXTURE0_ARB);

	}



#if 0

	if(LInfo != NULL)

	{

		glDisable(GL_TEXTURE_2D);

		glActiveTextureARB(GL_TEXTURE0_ARB);

	}

#endif

} 



// Entry point to render a world polygon, does some setup and then passed off control

// to a specific WorldPoly function (see above) depending upon whether multitexture is on or

// off



jeBoolean DRIVERCC Render_WorldPoly(jeTLVertex *Pnts, int32 NumPoints, jeRDriver_Layer *Layers, int32 NumLayers, void *LMapCBContext, uint32 Flags)

{

#ifdef USE_PCACHE



if( ! PCache_InsertWorldPoly(Pnts,NumPoints,Layers,NumLayers,LMapCBContext,Flags) )

	return JE_FALSE;



return JE_TRUE;



#else



	jeRDriver_LMapCBInfo	LMapCBInfo;

	GLfloat	shiftU, shiftV, scaleU, scaleV;

	jeTLVertex *pPnt = Pnts;

	GLubyte alpha;

	bool	Dynamic = 0;





	if(Flags & JE_RENDER_FLAG_ALPHA)

	{

		alpha = (GLubyte)Pnts->a;

	}

	else

	{

		alpha = 255;

	}





	shiftU = Layers->ShiftU;//TexInfo->ShiftU;

	shiftV = Layers->ShiftV;//TexInfo->ShiftV;

	scaleU = 1.0f / Layers->ScaleU; //1.0f / TexInfo->DrawScaleU;

	scaleV = 1.0f / Layers->ScaleV; //1.0f / TexInfo->DrawScaleV;



	if(boundTexture != Layers[0].THandle->TextureID)

	{

		glBindTexture(GL_TEXTURE_2D, Layers[0].THandle->TextureID);

		boundTexture = Layers[0].THandle->TextureID;

	}



	if(Layers[0].THandle->Flags & THANDLE_UPDATE)

	{

		THandle_Update(Layers[0].THandle);

	}



	if( NumLayers > 1) // lightmap poly.

	{

		// Call the engine to setup the lightmap..

		OGLDRV.SetupLightmap(&LMapCBInfo, LMapCBContext);



		if(LMapCBInfo.Dynamic || Layers[1].THandle->Flags & THANDLE_UPDATE_LM)

		{

			THandle_DownloadLightmap(Layers[1].THandle, &LMapCBInfo);

			

			if(Dynamic)

			{

				Layers[1].THandle->Flags |= THANDLE_UPDATE_LM;

			}

			else

			{

				Layers[1].THandle->Flags &= ~THANDLE_UPDATE_LM;

			} 

		}

	}



	if(multitexture) 

	{

		// Hooray!!

		Render_WorldPolyMultitexture(Pnts, NumPoints, Layers,NumLayers ,LMapCBContext, shiftU, shiftV,

			scaleU, scaleV, alpha);

	}

	else

	{

		// Hiss! Boo!

		Render_WorldPolyRegular(Pnts, NumPoints, Layers,NumLayers,LMapCBContext, shiftU, shiftV,

			scaleU, scaleV, alpha);

	}



	OGLDRV.NumRenderedPolys++; 



	return JE_TRUE;



#endif

}





// Render a generic plain ol' polygon using Gouraud smooth shading and no texture map.

jeBoolean DRIVERCC Render_GouraudPoly(jeTLVertex *Pnts, int32 NumPoints, uint32 Flags)

{

#ifdef USE_PCACHE



if( ! PCache_InsertGouraudPoly(Pnts,NumPoints,Flags) )

	return JE_FALSE;



	return JE_TRUE;



#else



	GLint i;

	GLfloat zRecip;

	jeTLVertex *pPnt = Pnts;

	GLubyte alpha;



	if(Flags & JE_RENDER_FLAG_ALPHA)

	{

		alpha = (GLubyte)Pnts->a;

	}

	else

	{

		alpha = 255;

	}



	glDisable(GL_TEXTURE_2D);



 	glBegin(GL_TRIANGLE_FAN);



	for(i = 0; i < NumPoints; i++)

	{

		zRecip = 1.0f / pPnt->z;   

		

		glColor4ub((GLubyte)pPnt->r, (GLubyte)pPnt->g, (GLubyte)pPnt->b, alpha);



		glVertex3f(pPnt->x, pPnt->y, -1.0f + zRecip);



		pPnt++;

	}



	glEnd(); 



	glEnable(GL_TEXTURE_2D); 



	OGLDRV.NumRenderedPolys++; 



	return JE_TRUE;

#endif



}





// Render a non-world polygon (such as on an actor).  

//jeBoolean DRIVERCC Render_MiscTexturePoly(jeTLVertex *Pnts, int32 NumPoints, 

//										  geRDriver_THandle *THandle, uint32 Flags)

jeBoolean DRIVERCC Render_MiscTexturePoly(jeTLVertex *Pnts, int32 NumPoints, jeRDriver_Layer *Layers, int32 NumLayers, uint32 Flags)

{

#ifdef USE_PCACHE



if( ! PCache_InsertMiscPoly(Pnts,NumPoints,Layers,NumLayers,Flags) )

	return JE_FALSE;



return JE_TRUE;



#else



	GLint i;

	GLfloat zRecip;

	jeTLVertex *pPnt = Pnts;

	GLubyte alpha;



	if(Flags & JE_RENDER_FLAG_ALPHA)

	{

		alpha = (GLubyte)Pnts->a;

	}

	else

	{

		alpha = 255;

	}



	if(boundTexture != Layers[0].THandle->TextureID)

	{

		glBindTexture(GL_TEXTURE_2D, Layers[0].THandle->TextureID);

		boundTexture = Layers[0].THandle->TextureID;

	}



	if(Layers[0].THandle->Flags & THANDLE_UPDATE)

	{

		THandle_Update(Layers[0].THandle);

	}



	if (Flags & JE_RENDER_FLAG_NO_ZTEST)		// We are assuming that this is not going to change all that much

		glDisable(GL_DEPTH_TEST);



	if (Flags & JE_RENDER_FLAG_NO_ZWRITE)	// We are assuming that this is not going to change all that much

		glDepthMask(0);



	glBegin(GL_TRIANGLE_FAN);



	for(i = 0; i < NumPoints; i++)

	{

		zRecip = 1.0f / pPnt->z;                    



		glColor4ub((GLubyte)pPnt->r, (GLubyte)pPnt->g, (GLubyte)pPnt->b, alpha);



		glTexCoord4f(pPnt->u * zRecip, pPnt->v * zRecip, 0.0f, zRecip);



		glVertex3f(pPnt->x, pPnt->y, -1.0f + zRecip);



		pPnt++;

	}



	glEnd();  





	if (Flags & JE_RENDER_FLAG_NO_ZTEST)		// We are assuming that this is not going to change all that much

		glEnable(GL_DEPTH_TEST);



	if (Flags & JE_RENDER_FLAG_NO_ZWRITE)	// We are assuming that this is not going to change all that much

		glDepthMask(1);



	return JE_TRUE;

#endif

}





// Render a 2D decal graphic...

// Notes: unfortunately traditional thru-the-API framebuffer access for OpenGL is 

//        ridculously slow with most ICDs.

//        So I've implemented decals as clamped textures on polygons.

//        This works rather well... usually... and it is fast.  However, it has drawbacks....

//        1) Extra use of texture ram to hold decals

//        2) May look shoddy under some situations on cards with 256x256 texture maximums 

//           (3DFX Voodoo springs to mind) 

//           Reason: If they only support 256x256, large decals like the GTest bitmap font may 

//           be sampled down to 256x256 and then restreched when drawing.  I haven't tested this 

//           against such cards, due to lack of any access to one, but my guess is the resulting 

//           image wont look so hot.

//

// In the future, may want to add platform-specific framebuffer access (GDI, X11, etc) as 

// allowed.  But...for Windows, DirectDraw isn't supported, and to the best of my knowledge, 

// GDI is only reliably supported when not using a double-buffer.  Joy.



#include <stdio.h>

jeBoolean DRIVERCC DrawDecal(jeRDriver_THandle *THandle, RECT *SRect, int32 x, int32 y)

{



	printf("Draw decal\n");

	

	RECT tmpRect, *srcRect;

	GLint width, height;

	GLfloat uClamp, vClamp;

	GLfloat uDiff = 1.0f, vDiff = 1.0f;



	if(!SRect)

	{

		tmpRect.left = 0;

		tmpRect.right = THandle->Width;

		tmpRect.top = 0;

		tmpRect.bottom = THandle->Height;



		srcRect = &tmpRect;

		width = (THandle->Width);

		height = (THandle->Height);

	}

	else

	{

		srcRect = SRect;

		width = (srcRect->right - srcRect->left);

		height = (srcRect->bottom - srcRect->top);

	}

	

	if(x + width <= 0 || y + height <= 0 || x >= ClientWindow.Width || y >= ClientWindow.Height)

	{

		return JE_TRUE;

	}

	

	if(x + width >= (ClientWindow.Width - 1))

	{

		srcRect->right -= ((x + width) - (ClientWindow.Width - 1));

	}



	if(y + height >= (ClientWindow.Height - 1))

	{

		srcRect->bottom -= ((y + height) - (ClientWindow.Height - 1));

	}



	if(x < 0)

	{

		srcRect->left += -x;

		x = 0;

	}

	

	if(y < 0)

	{

		srcRect->top += -y;

		y = 0;

	}



	if(boundTexture != THandle->TextureID)

	{

		glBindTexture(GL_TEXTURE_2D, THandle->TextureID);

		boundTexture = THandle->TextureID;

	}



	if(THandle->Flags & THANDLE_UPDATE)

	{

		THandle_Update(THandle);

	}

	

	glDisable(GL_DEPTH_TEST);

	

	glColor4f(1.0, 1.0, 1.0, 1.0);

	

	glShadeModel(GL_FLAT);

		

	if(THandle->Data[1] == NULL)

	{

		uClamp = width / (GLfloat)THandle->PaddedWidth;

		vClamp = height / (GLfloat)THandle->PaddedHeight;

		

		glMatrixMode(GL_TEXTURE); 

		glPushMatrix();

		glLoadIdentity();

		glTranslatef(srcRect->left / (GLfloat)THandle->PaddedWidth, 

			srcRect->top / (GLfloat)THandle->PaddedHeight, 0.0f);

		

		glBegin(GL_QUADS);

		

		glTexCoord2f(0.0f, 0.0);

		glVertex2i(x, y);

		

		glTexCoord2f(uClamp, 0.0f);

		glVertex2i(x + width, y);

		

		glTexCoord2f(uClamp, vClamp);

		glVertex2i(x + width, y + height);

		

		glTexCoord2f(0.0f, vClamp);

		glVertex2i(x, y + height);

		

		glEnd();  

		

		glPopMatrix();

		glMatrixMode(GL_MODELVIEW);

	}

	else

	{

	

		glPixelStorei(GL_UNPACK_ROW_LENGTH, THandle->Width);

		glPixelStorei(GL_UNPACK_SKIP_PIXELS, srcRect->left);

		glPixelStorei(GL_UNPACK_SKIP_ROWS, srcRect->top);



		glPixelZoom(1.0, -1.0);



		if(width <= maxTextureSize && height <= maxTextureSize &&

			width == SnapToPower2(width) && height == SnapToPower2(height))

		{

			// Last chance to avoid the dreaded glDrawPixels...Yes, this is faster

			// than glDrawPixels on the ICD's I've tested.  Go figure.

			// (Could add a more complex texture upload/clamp system for

			//  width/heights other than powers of 2, but for now,

			//  this is enough complexity...Sorry, 3DFX)



			if(decalTexObj == 0)

			{

				glGenTextures(1, &decalTexObj);

			}



			glBindTexture(GL_TEXTURE_2D, decalTexObj);

			boundTexture = decalTexObj;



			glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 

					0, GL_RGBA, GL_UNSIGNED_BYTE, THandle->Data[1]); 



			glBegin(GL_QUADS);

			

			glTexCoord2f(0.0f, 0.0);

			glVertex2i(x, y);

			

			glTexCoord2f(1.0f, 0.0f);

			glVertex2i(x + width, y);

			

			glTexCoord2f(1.0f, 1.0f);

			glVertex2i(x + width, y + height);

			

			glTexCoord2f(0.0f, 1.0f);

			glVertex2i(x, y + height);

			

			glEnd();

		}

		else

		{

			glPixelZoom(1.0, -1.0);



			glRasterPos2i(x, y);

			glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, THandle->Data[1]);

		}



		glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);

		glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);

		glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);

	} 

	

	glShadeModel(GL_SMOOTH);

	

	glEnable(GL_DEPTH_TEST); 

	

	return JE_TRUE; 

}





jeBoolean DRIVERCC BeginScene(jeBoolean Clear, jeBoolean ClearZ, RECT *WorldRect)

{



	pDirectGLWindow->MakeCurrent();



	if(Clear)

	{

		glClear(GL_COLOR_BUFFER_BIT);

	}

	

	if(ClearZ)

	{

		glClear(GL_DEPTH_BUFFER_BIT);

	}



	OGLDRV.NumRenderedPolys = 0;



#ifdef USE_PCACHE

	if (!PCache_BeginBatch())

		return JE_FALSE;

#endif



	return JE_TRUE;

}





jeBoolean DRIVERCC EndScene(void)

{	





#ifdef USE_PCACHE

	if (!PCache_EndBatch())

		return JE_FALSE;

#endif



	FlipGLBuffers();



	pDirectGLWindow->ReleaseCurrent();

	

	return JE_TRUE;

}

