/****************************/
/*		OPENGL.C			*/
/* (c)2004 Pangea Software  */
/* By Brian Greenstone      */
/****************************/


/****************************/
/*    PROTOTYPES            */
/****************************/

static void OGL_DisposeFont(void);
static void InitOurState(void);
static void OGLMatrix4x4_Multiply_Float(const OGLMatrix4x4	*mA, const OGLMatrix4x4 *mB, OGLMatrix4x4	*result);
static void OGLMatrix4x4_Multiply_Altivec(const OGLMatrix4x4	*mA, const OGLMatrix4x4 *mB, OGLMatrix4x4	*result);
static void CalcStereoCameraFrustum(Boolean	isLeftCamera);
static void OffsetStereoCameraCoord(Boolean isLeftCamera);
static void SetCameraMatrices(void);
static void ColorBalanceRGBForAnaglyph(u_long *rr, u_long *gg, u_long *bb);
static void	ConvertTextureToColorAnaglyph(void *imageMemory, short width, short height);
static void	ConvertTextureToGrey(void *imageMemory, short width, short height);
static void BuildGreyCurve(void);


/**************************/
/*    EXTERNS             */
/**************************/

extern	Boolean			gAltiVec;
extern	WindowPtr		gGameWindow;
extern	short			gGameWindowWidth, gGameWindowHeight;
extern	float			gOneOverFramesPerSecond;
extern	PrefsType		gGamePrefs;


/**************************/
/*    CONSTANTS           */
/**************************/

#define	STATE_STACK_SIZE	20


#define	CAMERA_HITHER		1.0f
#define	CAMERA_YON			1000.0f
#define	CAMERA_FOV			60.0									// field of view of the camera (in degrees!)

#define	STEREO_FOCAL_LENGTH			7.0f							// distance from camera to screen
																	// anything closer will seem in front of the screen
																	// anything farther will seem behind the screen
																	
#define	STEREO_CAMERA_SEPARATION	(STEREO_FOCAL_LENGTH/30.0f)		// distance between left & right eye.
																	// general rule is to make this 1/30th of the focal length
#define	RED_RATIO_ADJ		.6f
#define	GREEN_RATIO_ADJ		.4f
#define	BLUE_RATIO_ADJ		.8f

/****************************/
/*    VARIABLES             */
/****************************/

AGLDrawable			gAGLWin;
AGLContext			gAGLContext = nil;

GLuint				gFontList;
int					gFrameCounter = 0;


			/* STATE STACK */
			
int			gStateStackIndex = 0;

Boolean		gStateStack_Lighting[STATE_STACK_SIZE];					
Boolean		gStateStack_CullFace[STATE_STACK_SIZE];					
Boolean		gStateStack_DepthTest[STATE_STACK_SIZE];			
Boolean		gStateStack_Texture2D[STATE_STACK_SIZE];
Boolean		gStateStack_Blend[STATE_STACK_SIZE];
Boolean		gStateStack_Fog[STATE_STACK_SIZE];
GLboolean	gStateStack_DepthMask[STATE_STACK_SIZE];
GLint		gStateStack_BlendDst[STATE_STACK_SIZE];
GLint		gStateStack_BlendSrc[STATE_STACK_SIZE];
OGLColorRGBA gStateStack_Color[STATE_STACK_SIZE];

Boolean			gMyState_Lighting;
Boolean			gMyState_Blend;
Boolean			gMyState_Fog;
Boolean			gMyState_Texture2D;
Boolean			gMyState_CullFace;
Boolean			gMyState_DepthTest;
Boolean			gMyState_DepthMask;
OGLColorRGBA	gMyState_Color;
GLenum			gMyState_BlendFuncS, gMyState_BlendFuncD;

			/* MATRICES */
			
OGLMatrix4x4	gViewToFrustumMatrix,gWorldToViewMatrix,gWorldToFrustumMatrix, gLocalToViewMatrix, gLocalToFrustumMatrix;


		/* CAMERA INFO */
		
OGLPoint3D	gOldCameraCoord;
		
OGLPoint3D	gCamera_Coord		= {0,0,13};
OGLPoint3D	gCamera_LookAt		= {0,0,0};
OGLVector3D	gCamera_UpVec		= {0,1,0};

static OGLPoint3D	gStereoCamera_Coord, gStereoCamera_LookAt;

Byte				gStereoPass;
static u_char		gAnaglyphGreyTable[255];



/****************** CREATE OPENGL DRAW CONTEXT *********************/

void OGL_CreateDrawContext(void)
{
GDHandle 		gdevice;
AGLPixelFormat 	fmt;
GLint          attribWindow[]	= {AGL_RGBA, AGL_DOUBLEBUFFER, AGL_DEPTH_SIZE, 32, AGL_ALL_RENDERERS, AGL_ACCELERATED, AGL_NO_RECOVERY, AGL_NONE};
GLint          attribFullScreen[] = {AGL_RGBA, AGL_FULLSCREEN, AGL_DOUBLEBUFFER, AGL_DEPTH_SIZE, 32, AGL_ALL_RENDERERS, AGL_ACCELERATED, AGL_NO_RECOVERY, AGL_NONE};

	gdevice = GetMainDevice();						// get the main display

			/***********************/
			/* CHOOSE PIXEL FORMAT */
			/***********************/

			/* PLAY IN WINDOW */
												
	if (gGamePrefs.playInWindow)
		fmt = aglChoosePixelFormat(&gdevice, 1, attribWindow);
		
			/* FULL-SCREEN */
	else
		fmt = aglChoosePixelFormat(&gdevice, 1, attribFullScreen);

	if ((fmt == nil) || (aglGetError() != AGL_NO_ERROR))
		DoFatalAlert("\pCannot make a draw context!");

			/**********************/
			/* CREATE AGL CONTEXT */
			/**********************/
			
	gAGLContext = aglCreateContext(fmt, nil);
	if ((gAGLContext == nil) || (aglGetError() != AGL_NO_ERROR))
		DoFatalAlert("\paglCreateContext failed!");
		
		
			/* PLAYING IN A WINDOW */
				
	if (gGamePrefs.playInWindow)
	{
		gAGLWin = (AGLDrawable)GetWindowPort(gGameWindow);
		aglSetDrawable(gAGLContext, gAGLWin);
	}
	
			/* PLAYING FULL-SCREEN */
	else
	{
		gAGLWin = nil;
		aglEnable(gAGLContext, AGL_FS_CAPTURE_SINGLE);		
		aglSetFullScreen(gAGLContext, 0, 0, 0, 0);	
	}	

			/* ACTIVATE THE CONTEXT */

	aglSetCurrentContext(gAGLContext);


			/* NO LONGER NEED PIXEL FORMAT */
			
	aglDestroyPixelFormat(fmt);
	
	
			/* INITIALIZE OUR OPENGL STATE */
				
	InitOurState();
	OGL_InitVertexArrayRanges();


	BuildGreyCurve();							// build the greyscale conversion table
}


/***************** DISPOSE DRAW CONTEXT ***********************/
//
// Disposes of all data created by OGL_SetupWindow
//

void OGL_DisposeDrawContext(void)
{

	if (gAGLContext)
	{
			/* FREE THE FONT */
			
		OGL_DisposeFont();

			/* NUKE THE CONTEXT */

		aglSetCurrentContext(nil);						// make context not current
		aglSetDrawable(gAGLContext, nil);
		aglDestroyContext(gAGLContext);					// nuke the AGL context
		gAGLContext = nil;
	}
}



/******************* DRAW OPENGL SCENE *********************/
//
// Called from the main loop once each frame to draw the scene.
//

void OGL_DrawScene(void)
{

			/* MAKE OUR CONTEXT THE ACTIVE ONE */
			
  	aglSetCurrentContext(gAGLContext);

	OGL_UpdateVertexArrayRange();				// update VAR memory

			/*********************/
			/* CLEAR BACK BUFFER */
			/*********************/
	
	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);		// make sure clearing Red/Green/Blue channels
	glClear(GL_COLOR_BUFFER_BIT);							// clear back buffer
	

		/***********************************************/
		/* DRAW SCENE IN TWO PASSES (LEFT & RIGHT EYE) */
		/***********************************************/
			
	for (gStereoPass = 0; gStereoPass < 2; gStereoPass++)
	{
		glClear(GL_DEPTH_BUFFER_BIT);								// clear the z-buffer before each pass!
	
				/* SET COLOR MASK */
				
		if (gStereoPass == 0)
			glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);		// red-only for left eye
		else
			glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);		// green-blue for right eye	
		
		
			/* OFFSET CAMERA FOR LEFT/RIGHT EYE */
			
		OffsetStereoCameraCoord(gStereoPass == 0);
				
						
			/* SET FRUSTUM AND OTHER CAMERA MATRICES */
				
		SetCameraMatrices();
						
				/* DRAW SCENE */
				
		DrawSceneGeometry();						// draw stuff
	}
	
	
			/* END RENDER & SWAP THE BUFFER TO MAKE IT VISIBLE */
           		
	aglSwapBuffers(gAGLContext);
	
		
	gFrameCounter++;
}


#pragma mark _______ CAMERA __________

/************** OGL: SET CAMERA **********************/
//
// This sets the Projection and model view matrix.
//

static void SetCameraMatrices(void)
{	
			/**************************/
			/* INIT PROJECTION MATRIX */
			/**************************/
			
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();	

	CalcStereoCameraFrustum(gStereoPass == 0);

	
			/*************************/
			/* INIT MODELVIEW MATRIX */
			/*************************/
			
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(gStereoCamera_Coord.x, gStereoCamera_Coord.y, gStereoCamera_Coord.z,	
			gStereoCamera_LookAt.x, gStereoCamera_LookAt.y, gStereoCamera_LookAt.z,	
			gCamera_UpVec.x, gCamera_UpVec.y, gCamera_UpVec.z);


			/*******************************/
			/* GET VARIOUS CAMERA MATRICES */
			/*******************************/
			//
			// Here we extract some matrices from OpenGL and
			// calculate some others which most games need from time to time.
			//
			
	glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)&gWorldToViewMatrix);
	glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)&gLocalToViewMatrix);
	glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat *)&gViewToFrustumMatrix);
	
	OGLMatrix4x4_Multiply(&gLocalToViewMatrix, &gViewToFrustumMatrix, &gLocalToFrustumMatrix);
	OGLMatrix4x4_Multiply(&gWorldToViewMatrix, &gViewToFrustumMatrix, &gWorldToFrustumMatrix);


			/**************************************************/
			/* UPDATE THE OPENAL LISTENER TO MATCH OUR CAMERA */
			/**************************************************/
		
	if (gStereoPass == 0)										// only do this once per frame
	{
		OALVector3D	motion, aim;
		
		gOldCameraCoord = gCamera_Coord;						// set old camera coord to current coord
				
		motion.x = gCamera_Coord.x - gOldCameraCoord.x;			// calculate delta from previous position
		motion.y = gCamera_Coord.y - gOldCameraCoord.y;
		motion.z = gCamera_Coord.z - gOldCameraCoord.z;	

		aim.x = gCamera_LookAt.x - gCamera_Coord.x;			// calc camera aim vector
		aim.y = gCamera_LookAt.y - gCamera_Coord.y;
		aim.z = gCamera_LookAt.z - gCamera_Coord.z;	
		OGLVector3D_NormalizeFast(aim.x, aim.y, aim.z, (OGLVector3D *)&aim);
				
		OAL_SetListenerInfo((OALPoint3D *)&gCamera_Coord, &aim, (OALVector3D *)&gCamera_UpVec, &motion);
	}
}


/********************** CALC STEREO CAMERA FRUSTUM ******************/
//
// Sets the projection matrix for stereo rendering.
// glMatrixMode(GL_PROJECTION) must already be set upon entering this function.
//
// INPUT:	isLeftCamera = true if set for left camera, false for right camera
//

static void CalcStereoCameraFrustum(Boolean	isLeftCamera)
{
	float	left, right, a, b, aspect;
	float	fov;
	
			/* DO FORMULA CALCULATIONS */
			
	fov = CAMERA_FOV / 180.0f * M_PI;				// convert degrees to radians
			
	aspect = (float)gGameWindowWidth/(float)gGameWindowHeight;

	a		= CAMERA_HITHER * tan(fov * .5f);
	b		= CAMERA_HITHER / STEREO_FOCAL_LENGTH;	

	if (isLeftCamera)									// left camera
	{
		left  = - aspect * a + (STEREO_CAMERA_SEPARATION * .5f) * b;
		right =   aspect * a + (STEREO_CAMERA_SEPARATION * .5f) * b;
	}
	else												// right camera
	{
		left  = - aspect * a - (STEREO_CAMERA_SEPARATION * .5f) * b;
		right =   aspect * a - (STEREO_CAMERA_SEPARATION * .5f) * b;
	}
	
	
			/* SET THE PROJECTION MATRIX FRUSTUM */
				
	glFrustum(left, right, -a, a, CAMERA_HITHER, CAMERA_YON);
}


/********************* OFFSET STEREO CAMERA COORD *************************/
//
// This offsets the camera for left/right eyes
//
// INPUT:  isLeftCamera = true if offset for the left eye or false for right eye
//
// OUTPUT:	gStereoCamera_Coord		= the offset camera coord
//			gStereoCamera_LookAt	= the offset camera lookat coord
//

static void OffsetStereoCameraCoord(Boolean isLeftCamera)
{
OGLVector3D	aim;
OGLVector3D	xaxis;
float		sep = STEREO_CAMERA_SEPARATION * .5;		// half separation is the amount to offset left/right

	if (isLeftCamera)									// negate separation for left eye
		sep = -sep;

			/* CALC CAMERA'S X-AXIS */
			
	aim.x = gCamera_LookAt.x - gCamera_Coord.x;
	aim.y = gCamera_LookAt.y - gCamera_Coord.y;
	aim.z = gCamera_LookAt.z - gCamera_Coord.z;
	OGLVector3D_Normalize(&aim, &aim);
	OGLVector3D_Cross(&aim, &gCamera_UpVec, &xaxis); 


				/* OFFSET CAMERA COORD */
				
	gStereoCamera_Coord.x = gCamera_Coord.x + (xaxis.x * sep);
	gStereoCamera_Coord.y = gCamera_Coord.y + (xaxis.y * sep);
	gStereoCamera_Coord.z = gCamera_Coord.z + (xaxis.z * sep);
	
				/* OFFSET CAMERA LOOKAT */
				
	gStereoCamera_LookAt.x = gCamera_LookAt.x + (xaxis.x * sep);
	gStereoCamera_LookAt.y = gCamera_LookAt.y + (xaxis.y * sep);
	gStereoCamera_LookAt.z = gCamera_LookAt.z + (xaxis.z * sep);
}





#pragma mark ______ AGL FONT FUNCTIONS _______

/****************** INIT OPENGL FONT ***********************/
//
// Initializes a font list that we can use for drawing text
// into a draw context for simple debug information.
//

void OGL_InitFont(void)
{	
	GLboolean	success;

	gFontList = glGenLists(256);
 
 	success = aglUseFont(gAGLContext,		// our draw context 
						kFontIDMonaco,		// font to use
						bold,				// style of font
						9,					// point size of font
						0,					// first character
						256,				// # characters
						gFontList);			// list to store font in


	if (!success)
		DoFatalAlert("\paglUseFont failed");
}


/******************* DRAW OPENGL STRING ***********************/
//
// Draws a pascal string to the 2D window coordinates specified by x,y.
//
// INPUT:	s = pascal string
//			x/y = screen coords (based on 640x480)
//

void OGL_DrawString(Str255 s, GLint x, GLint y)
{
	OGL_PushState();							// preserve the current state before changing it
	
			/* SET THE DRAWING MATRICES */

	glMatrixMode (GL_MODELVIEW);
	glLoadIdentity();
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, 640, 0, 480, -10.0, 10.0);

	
			/* SET SOME STATE VALUES */

	OGL_DisableLighting();
	OGL_DisableTexture2D();	
	OGL_SetColor4f (1,1,1,1);


		/* SET COORDINATE TO DRAW STRING */

	glRasterPos2i(x, 480-y);


			/* DRAW THE STRING */

	glListBase(gFontList);
	glCallLists(s[0], GL_UNSIGNED_BYTE, &s[1]);
	
	OGL_PopState();							// restore the state
}


/**************** DRAW INT WITH OPENGL FONT ********************/

void OGL_DrawInt(int f, GLint x, GLint y)
{
Str255	s;

	NumToString(f,s);
	OGL_DrawString(s,x,y);

}


/******************* DISPOSE OPENGL FONT ***********************/

static void OGL_DisposeFont(void)
{
	glDeleteLists(gFontList, 256);	
}

#pragma mark _____STATE FUNCTIONS_______


/************** INIT OUR STATE ******************/
//
// Called when an OpenGL draw context is created.  This will initialize our
// state and our cached info to the desired default values.
//

static void InitOurState(void)
{
static OGLColorRGBA ambientColor	= { .5, .5, .5, 1.0 };
static OGLVector3D	lightVec		= {.3, .6, 1};					// NOTE:  light vectors are actually inverted in OpenGL!  So make sure vector is opposite of direction we actually want!
static OGLColorRGBA lightamb		= { 0.0, 0.0, 0.0, 1.0 };
static OGLColorRGBA diffuseColor	= { 1.0, 1.0, 1.0, 1.0 };

		/****************************/
		/* SET PERMANENT STATE INFO */
		/****************************/

	glClearColor(0, 0, 0, 1.0);

	glEnable(GL_ALPHA_TEST);
	glAlphaFunc(GL_NOTEQUAL, 0);								// draw any pixel who's Alpha != 0

	glHint(GL_TRANSFORM_HINT_APPLE, GL_FASTEST);				// fastest transforms with minimal accuracy loss.

	glEnable(GL_RESCALE_NORMAL);								// do any vector scaling the fast way

	glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);	// use color material for faster color calls
	glEnable(GL_COLOR_MATERIAL);		


		/*************************/
		/* SET CACHED STATE INFO */
		/*************************/

		/* ENABLE LIGHTING */

	gMyState_Lighting = false;				// set state to false so that enable will set it to true
	OGL_EnableLighting();
	
		/* DISABLE FOG */
		
	gMyState_Fog = true;
	OGL_DisableFog();
		
		/* DISABLE TEXTURE2D */
			
	gMyState_Texture2D = true;
	OGL_DisableTexture2D();
	
		/* ENABLE BACKFACE CULLING */
			
	gMyState_CullFace = false;
	OGL_EnableCullFace();
	
		
		/* ENABLE Z-BUFFER READS */
		
	gMyState_DepthTest = false;
	OGL_EnableDepthTest();

		/* ENABLE Z-BUFFER WRITES */
		
	gMyState_DepthMask = false;
	OGL_DepthMask(true);
	
	
		/* TURN OFF BLENDING */
		
	gMyState_Blend = true;
	OGL_DisableBlend();
		
	gMyState_BlendFuncS = GL_ONE;			// even though blending is off, let's set the default blending mode
	gMyState_BlendFuncD = GL_ONE;
	
	
			/* SET COLOR */
			
	gMyState_Color.r =
	gMyState_Color.g =
	gMyState_Color.b = 1;
	gMyState_Color.a = 0;
	
	OGL_SetColor4f(1,1,1,1);
	
		
		/****************/
		/* SET LIGHTING */
		/****************/

			/* CREATE AMBIENT LIGHT */

	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (GLfloat *)&ambientColor);
	
	
			/* CREATE A DIRECTIONAL LIGHT */

	OGLVector3D_NormalizeFast(lightVec.x,
							lightVec.y,
							lightVec.z,
							&lightVec);							// make sure light vector is normalized!			
	glLightfv(GL_LIGHT0, GL_POSITION, (GLfloat *)&lightVec);	// set fill light direction vector
	
	glLightfv(GL_LIGHT0, GL_AMBIENT, (GLfloat *)&lightamb);		// set ambient color
	
	glLightfv(GL_LIGHT0, GL_DIFFUSE, (GLfloat *)&diffuseColor);	// set diffuse color

	glEnable(GL_LIGHT0);										// enable the light
	
		
}


/********************* PUSH STATE **************************/

void OGL_PushState(void)
{
int	i;

		/* PUSH MATRIES WITH OPENGL */

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();	

		/* SAVE STATE INFO */
	
	i = gStateStackIndex++;											// get stack index and increment
	
	if (i >= STATE_STACK_SIZE)
		DoFatalAlert("\pOGL_PushState: stack overflow");
	
	gStateStack_Lighting[i]		= gMyState_Lighting;					
	gStateStack_CullFace[i]		= gMyState_CullFace;					
	gStateStack_DepthTest[i]	= gMyState_DepthTest;		
	gStateStack_Texture2D[i]	= gMyState_Texture2D;		
	gStateStack_Fog[i]			= gMyState_Fog;		
	gStateStack_Blend[i]		= gMyState_Blend;		
	gStateStack_Color[i]		= gMyState_Color;
	gStateStack_DepthMask[i]	= gMyState_DepthMask;
	
	gStateStack_BlendSrc[i]		= gMyState_BlendFuncS;
	gStateStack_BlendDst[i]		= gMyState_BlendFuncD;
}


/********************* POP STATE **************************/

void OGL_PopState(void)
{
int		i;

		/* RETREIVE OPENGL MATRICES */
		
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	
		/* GET STATE INFO */
		
	i = --gStateStackIndex;												// dec stack index
		
	if (i < 0)
		DoFatalAlert("\pOGL_PopState: stack underflow!");

	if (gStateStack_Lighting[i])		// restore lighting
		OGL_EnableLighting();
	else
		OGL_DisableLighting();


	if (gStateStack_CullFace[i])		// restore backface culling
		OGL_EnableCullFace();
	else
		OGL_DisableCullFace();


	if (gStateStack_DepthTest[i])		// restore depth test
		OGL_EnableDepthTest();
	else
		OGL_DisableDepthTest();

	if (gStateStack_Texture2D[i])		// restore texture state
		OGL_EnableTexture2D();				
	else
		OGL_DisableTexture2D();				

	if (gStateStack_Blend[i])			// restore blending mode
		OGL_EnableBlend();
	else
		OGL_DisableBlend();

	if (gStateStack_Fog[i])				// restore fog state
		OGL_EnableFog();
	else
		OGL_DisableFog();

	OGL_DepthMask(gStateStack_DepthMask[i]);	// restore depth mask state

	OGL_BlendFunc(gStateStack_BlendSrc[i], gStateStack_BlendDst[i]);	// restore blending mode
	OGL_SetColor4fv(&gStateStack_Color[i]);	// restore color state

}


/******************* OGL ENABLE LIGHTING ****************************/

void OGL_EnableLighting(void)
{
	if (!gMyState_Lighting)
	{
		gMyState_Lighting = true;
		glEnable(GL_LIGHTING);	
	}
}


/******************* OGL DISABLE LIGHTING ****************************/

void OGL_DisableLighting(void)
{
	if (gMyState_Lighting)
	{
		gMyState_Lighting = false;
		glDisable(GL_LIGHTING);	
	}
}

/******************* OGL ENABLE DEPTH TEST ****************************/

void OGL_EnableDepthTest(void)
{
	if (!gMyState_DepthTest)
	{
		gMyState_DepthTest = true;
		glEnable(GL_DEPTH_TEST);	
	}
}


/******************* OGL DISABLE DEPTH TEST ****************************/

void OGL_DisableDepthTest(void)
{

	if (gMyState_DepthTest)
	{
		gMyState_DepthTest = false;
		glDisable(GL_DEPTH_TEST);	
	}
}




/********************** OGL ENABLE BLEND ********************/

void OGL_EnableBlend(void)
{
	if (!gMyState_Blend)
	{
		gMyState_Blend = true;
		glEnable(GL_BLEND);	
	}
}


/******************* OGL DISABLE BLEND ****************************/

void OGL_DisableBlend(void)
{
	if (gMyState_Blend)
	{
		gMyState_Blend = false;
		glDisable(GL_BLEND);	
	}
}


/********************** OGL ENABLE TEXTURE 2D ********************/

void OGL_EnableTexture2D(void)
{
	if (!gMyState_Texture2D)
	{
		gMyState_Texture2D = true;
		glEnable(GL_TEXTURE_2D);	
	}
}


/******************* OGL DISABLE TEXTURE 2D ****************************/

void OGL_DisableTexture2D(void)
{
	if (gMyState_Texture2D)
	{
		gMyState_Texture2D = false;
		glDisable(GL_TEXTURE_2D);	
	}
}


/********************** OGL SET COLOR 4fV ********************/

void OGL_SetColor4fv(OGLColorRGBA *color)
{
	if ((color->r != gMyState_Color.r) || 
		(color->g != gMyState_Color.g) ||
		(color->b != gMyState_Color.b) ||
		(color->a != gMyState_Color.a))
	{

		glColor4fv((GLfloat *)color);
		
		gMyState_Color = *color;
	}
}


/********************** OGL SET COLOR 4f ********************/

void OGL_SetColor4f(float r, float g, float b, float a)
{
	if ((r != gMyState_Color.r) || 
		(g != gMyState_Color.g) ||
		(b != gMyState_Color.b) ||
		(a != gMyState_Color.a))
	{
	
		glColor4f(r, g, b, a);
	
		gMyState_Color.r = r;
		gMyState_Color.g = g;
		gMyState_Color.b = b;
		gMyState_Color.a = a;
	}
}


/********************** OGL ENABLE CULL FACE ********************/

void OGL_EnableCullFace(void)
{
	if (!gMyState_CullFace)
	{
		gMyState_CullFace = true;
		glEnable(GL_CULL_FACE);		
	}
}


/******************* OGL DISABLE CULL FACE ****************************/

void OGL_DisableCullFace(void)
{

	if (gMyState_CullFace)
	{
		gMyState_CullFace = false;
		glDisable(GL_CULL_FACE);	
	}
}


/********************** OGL ENABLE FOG ********************/

void OGL_EnableFog(void)
{

	if (!gMyState_Fog)
	{
		gMyState_Fog = true;
		glEnable(GL_FOG);	
	}
}


/******************* OGL DISABLE FOG ****************************/

void OGL_DisableFog(void)
{

	if (gMyState_Fog)
	{
		gMyState_Fog = false;
		glDisable(GL_FOG);	
	}
}


/********************** OGL BLEND FUNC ********************/

void OGL_BlendFunc(GLenum sfactor, GLenum dfactor)
{
	if ((sfactor != gMyState_BlendFuncS) || (dfactor != gMyState_BlendFuncD))
	{
		glBlendFunc(sfactor, dfactor);

		gMyState_BlendFuncS = sfactor;
		gMyState_BlendFuncD = dfactor;
	}
}


/******************* OGL DEPTH MASK ****************************/
//
// Turns the z-buffer on/off.
//
// INPUT: useZbuffer = true if want depth mask turned on.
//

void OGL_DepthMask(Boolean useZbuffer)
{
	if (gMyState_DepthMask != useZbuffer)
	{
		glDepthMask(useZbuffer);
		gMyState_DepthMask = useZbuffer;
	}
}



#pragma mark _______ 3D MATH FUNCTIONS _______

/****************** OGL MATRIX 4X4 MULTIPLY ********************/

void OGLMatrix4x4_Multiply(const OGLMatrix4x4	*mA, const OGLMatrix4x4 *mB, OGLMatrix4x4	*result)
{
	if (gAltiVec)
		OGLMatrix4x4_Multiply_Altivec(mA, mB, result);
	else
		OGLMatrix4x4_Multiply_Float(mA,mB, result);
}


/****************** OGL MATRIX 4X4 MULTIPLY FLOAT ********************/

static void OGLMatrix4x4_Multiply_Float(const OGLMatrix4x4	*mA, const OGLMatrix4x4 *mB, OGLMatrix4x4	*result)
{
	float	b00, b01, b02, b03;
	float	b10, b11, b12, b13;
	float	b20, b21, b22, b23;
	float	b30, b31, b32, b33;
	float	ax0, ax1, ax2, ax3;
	
	b00 = mB->value[M00];	b01 = mB->value[M10];	b02 = mB->value[M20];	b03 = mB->value[M30];
	b10 = mB->value[M01];	b11 = mB->value[M11];	b12 = mB->value[M21];	b13 = mB->value[M31];
	b20 = mB->value[M02];	b21 = mB->value[M12];	b22 = mB->value[M22];	b23 = mB->value[M32];
	b30 = mB->value[M03];	b31 = mB->value[M13];	b32 = mB->value[M23];	b33 = mB->value[M33];
	
	ax0 = mA->value[M00];	ax1 = mA->value[M10];
	ax2 = mA->value[M20];	ax3 = mA->value[M30];
	
	result->value[M00] = ax0*b00 + ax1*b10 + ax2*b20 + ax3*b30;
	result->value[M10] = ax0*b01 + ax1*b11 + ax2*b21 + ax3*b31;
	result->value[M20] = ax0*b02 + ax1*b12 + ax2*b22 + ax3*b32;
	result->value[M30] = ax0*b03 + ax1*b13 + ax2*b23 + ax3*b33;
	
	ax0 = mA->value[M01];	ax1 = mA->value[M11];
	ax2 = mA->value[M21];	ax3 = mA->value[M31];
	
	result->value[M01] = ax0*b00 + ax1*b10 + ax2*b20 + ax3*b30;
	result->value[M11] = ax0*b01 + ax1*b11 + ax2*b21 + ax3*b31;
	result->value[M21] = ax0*b02 + ax1*b12 + ax2*b22 + ax3*b32;
	result->value[M31] = ax0*b03 + ax1*b13 + ax2*b23 + ax3*b33;
	
	ax0 = mA->value[M02];	ax1 = mA->value[M12];
	ax2 = mA->value[M22];	ax3 = mA->value[M32];
	
	result->value[M02] = ax0*b00 + ax1*b10 + ax2*b20 + ax3*b30;
	result->value[M12] = ax0*b01 + ax1*b11 + ax2*b21 + ax3*b31;
	result->value[M22] = ax0*b02 + ax1*b12 + ax2*b22 + ax3*b32;
	result->value[M32] = ax0*b03 + ax1*b13 + ax2*b23 + ax3*b33;
	
	ax0 = mA->value[M03];	ax1 = mA->value[M13];
	ax2 = mA->value[M23];	ax3 = mA->value[M33];
	
	result->value[M03] = ax0*b00 + ax1*b10 + ax2*b20 + ax3*b30;
	result->value[M13] = ax0*b01 + ax1*b11 + ax2*b21 + ax3*b31;
	result->value[M23] = ax0*b02 + ax1*b12 + ax2*b22 + ax3*b32;
	result->value[M33] = ax0*b03 + ax1*b13 + ax2*b23 + ax3*b33;
}



/****************** OGL MATRIX 4X4 MULTIPLY ALTIVEC ********************/

static void OGLMatrix4x4_Multiply_Altivec(const OGLMatrix4x4	*mA, const OGLMatrix4x4 *mB, OGLMatrix4x4	*result)
{
	vector float A1,A2,A3,A4, B1, B2, B3, B4;
	vector float zeroF = (vector float) vec_splat_u32(0);
	vector float C1, C2, C3, C4;		
	
	/* LOAD MATRIX A */		
	
	A1 = vec_ld(  0, (float*) mA );
	A2 = vec_ld( 16, (float*) mA );
	A3 = vec_ld( 32, (float*) mA );
	A4 = vec_ld( 48, (float*) mA );
	B1 = vec_ld(  0, (float*) mB );
	B2 = vec_ld( 16, (float*) mB );
	B3 = vec_ld( 32, (float*) mB );
	B4 = vec_ld( 48, (float*) mB );
	
	//Do the first scalar x vector multiply for each row
	C1 = vec_madd( vec_splat( A1, 0 ), B1, zeroF );
	C2 = vec_madd( vec_splat( A2, 0 ), B1, zeroF );
	C3 = vec_madd( vec_splat( A3, 0 ), B1, zeroF );
	C4 = vec_madd( vec_splat( A4, 0 ), B1, zeroF );
	
	//Accumulate in the second scalar x vector multiply for each row
	C1 = vec_madd( vec_splat( A1, 1 ), B2, C1 );
	C2 = vec_madd( vec_splat( A2, 1 ), B2, C2 );
	C3 = vec_madd( vec_splat( A3, 1 ), B2, C3 );
	C4 = vec_madd( vec_splat( A4, 1 ), B2, C4 );
	
	//Accumulate in the third scalar x vector multiply for each row
	C1 = vec_madd( vec_splat( A1, 2 ), B3, C1 );
	C2 = vec_madd( vec_splat( A2, 2 ), B3, C2 );
	C3 = vec_madd( vec_splat( A3, 2 ), B3, C3 );
	C4 = vec_madd( vec_splat( A4, 2 ), B3, C4 );
	
	//Accumulate in the fourth scalar x vector multiply for each row
	C1 = vec_madd( vec_splat( A1, 3 ), B4, C1 );
	C2 = vec_madd( vec_splat( A2, 3 ), B4, C2 );
	C3 = vec_madd( vec_splat( A3, 3 ), B4, C3 );
	C4 = vec_madd( vec_splat( A4, 3 ), B4, C4 );
	
	vec_st( C1,  0, (float*) result );
	vec_st( C2, 16, (float*) result );
	vec_st( C3, 32, (float*) result );
	vec_st( C4, 48, (float*) result );		
}


/****************** NORMALIZE VECTOR FAST **********************/
//
// Uses frsqrte and Newton-Rhapson refinement to quickly
// normalize the input vector.
//
// INPUT:  x,y,z = vector to normalize
//
// OUTPUT:	outV = normalized vector (estimate)
//

void OGLVector3D_NormalizeFast(float x, float y, float z, OGLVector3D *outV)
{
float	len;
float	isqrt, temp1, temp2;		
	
			/* CALC LENGTH OF INPUT VECTOR */

	len = (x * x) + (y * y) + (z * z);
	
		/* CALCULATE 1.0 / SQRT(len) ESTIMATE */

	isqrt = __frsqrte (len);				


		/* REFINE THE ESTIMATE WITH NEWTON-RHAPSON */

	temp1 = len * -.5f;						
	temp2 = isqrt * isqrt;				
	temp1 *= isqrt;						 
	isqrt *= (float)(3.0/2.0);			 
	len = isqrt + temp1 * temp2;	


			/* RETURN NORMALIZED VECTOR */

	outV->x = x * len;
	outV->y = y * len;
	outV->z = z * len;
}

/****************** VECTOR 3D CROSS *********************/

void OGLVector3D_Cross(const OGLVector3D *v1, const OGLVector3D	*v2, OGLVector3D *result)
{
OGLVector3D			s1, s2, temp;
const OGLVector3D	*s1Ptr, *s2Ptr;
	
	if (v1 == result)
	{
		s1 = *v1;
		s1Ptr = &s1;
	}
	else
	{
		s1Ptr = v1;
	}	
	
	if (v2 == result)
	{
		s2 = *v2;
		s2Ptr = &s2;
	}
	else
	{
		s2Ptr = v2;
	}

	temp.x = s1Ptr->y * s2Ptr->z - s2Ptr->y * s1Ptr->z;
	temp.y = s2Ptr->x * s1Ptr->z - s1Ptr->x * s2Ptr->z;
	temp.z = s1Ptr->x * s2Ptr->y - s2Ptr->x * s1Ptr->y;

	OGLVector3D_Normalize(&temp, result);
}

/************** VECTOR 3D NORMALIZE *****************/

void OGLVector3D_Normalize(const OGLVector3D *vector3D, OGLVector3D	*result)
{
float length, oneOverLength;

	length = (vector3D->x * vector3D->x) +
			 (vector3D->y * vector3D->y) +
			 (vector3D->z * vector3D->z);

	length = sqrt(length);
				   		 
     //  Check for zero-length vector

    if (length <= 0.0001)
    {
       	result->x = 
       	result->y = 
       	result->z = 0;
    }
    else
    {
    	oneOverLength = 1.0f/length;
	
		result->x = vector3D->x * oneOverLength;
		result->y = vector3D->y * oneOverLength;
		result->z = vector3D->z * oneOverLength;
	}
}


/******************* CALC VECTOR LENGTH ********************/

float OGLVector3D_Length(OGLVector3D *v)
{
	float	d;

	d = (v->x * v->x) + (v->y * v->y) + (v->z * v->z);
	
	return(sqrt(d));
}


/************* MATRIX 4X4 SET IDENTITY ******************/

void OGLMatrix4x4_SetIdentity(OGLMatrix4x4 *m)
{
	m->value[M00] = 1;
	m->value[M11] = 1;
	m->value[M22] = 1;
	m->value[M33] = 1;

	m->value[M01] = 0;
	m->value[M02] = 0;
	m->value[M03] = 0;
	m->value[M10] = 0;
	m->value[M12] = 0;
	m->value[M13] = 0;
	m->value[M20] = 0;
	m->value[M21] = 0;
	m->value[M23] = 0;
	m->value[M30] = 0;
	m->value[M31] = 0;
	m->value[M32] = 0;
}

/**************** GLMATRIX4X4: SETSCALE ********************/

void OGLMatrix4x4_SetScale(OGLMatrix4x4 *m, float x, float y, float z)
{
	m->value[M00] = x;	
	m->value[M11] = y;	
	m->value[M22] = z;	
	m->value[M33] = 1;	

	m->value[M01] = 0;	
	m->value[M02] = 0;	
	m->value[M03] = 0;	

	m->value[M10] = 0;	
	m->value[M12] = 0;	
	m->value[M13] = 0;	

	m->value[M20] = 0;	
	m->value[M21] = 0;	
	m->value[M23] = 0;	

	m->value[M30] = 0;	
	m->value[M31] = 0;	
	m->value[M32] = 0;	
}


/**************** GLMATRIX4X4: SET ROTATE X ******************/

void OGLMatrix4x4_SetRotate_X(OGLMatrix4x4	*m, float angle)
{
float s = sin(angle);
float c = cos(angle);

	OGLMatrix4x4_SetIdentity(m);

	m->value[M11] = c;
	m->value[M12] = -s;
	m->value[M21] = s;
	m->value[M22] = c;
}


/**************** GLMATRIX4X4: SET ROTATE Y ******************/

void OGLMatrix4x4_SetRotate_Y(OGLMatrix4x4	*m, float angle)
{
float s = sin(angle);
float c = cos(angle);
	
	OGLMatrix4x4_SetIdentity(m);
	
	m->value[M00] = c;
	m->value[M02] = s;
	m->value[M20] = -s;
	m->value[M22] = c;
}


/**************** GLMATRIX4X4: SET ROTATE Z ******************/

void OGLMatrix4x4_SetRotate_Z(OGLMatrix4x4	*m, float angle)
{
float s = sin(angle);
float c = cos(angle);
	
	OGLMatrix4x4_SetIdentity(m);
	
	m->value[M00] = c;
	m->value[M01] = -s;
	m->value[M10] = s;
	m->value[M11] = c;
}

/******************* SET QUICK XYZ-ROTATION MATRIX ************************/
//
// Does a quick precomputation to calculate an XYZ rotation matrix
//

void OGLMatrix4x4_SetRotate_XYZ(OGLMatrix4x4 *m, float rx, float ry, float rz)
{
float	sx,cx,sy,sz,cy,cz,sxsy,cxsy;

	sx = sin(rx);
	sy = sin(ry);
	sz = sin(rz);
	cx = cos(rx);
	cy = cos(ry);
	cz = cos(rz);
	
	sxsy = sx*sy;
	cxsy = cx*sy;
	
	m->value[M00] = cy*cz;					m->value[M10] = cy*sz; 				m->value[M20] = -sy; 	m->value[M30] = 0;
	m->value[M01] = (sxsy*cz)+(cx*-sz);		m->value[M11] = (sxsy*sz)+(cx*cz);	m->value[M21] = sx*cy;	m->value[M31] = 0;
	m->value[M02] = (cxsy*cz)+(-sx*-sz);	m->value[M12] = (cxsy*sz)+(-sx*cz);	m->value[M22] = cx*cy;	m->value[M32] = 0;
	m->value[M03] = 0;						m->value[M13] = 0;					m->value[M23] = 0;		m->value[M33] = 1;
}


/*************** GL MATRIX 4X4: SET ROTATE ABOUT POINT ******************/

void OGLMatrix4x4_SetRotateAboutPoint(OGLMatrix4x4 *matrix4x4, const OGLPoint3D	*origin,
									float xAngle, float yAngle, float zAngle)
{
OGLMatrix4x4		negTransM, rotM;
	
	OGLMatrix4x4_SetTranslate(&negTransM, -origin->x, -origin->y, -origin->z);
	OGLMatrix4x4_SetTranslate(matrix4x4, origin->x, origin->y, origin->z);

	OGLMatrix4x4_SetRotate_XYZ(&rotM, xAngle, yAngle, zAngle);
		
	OGLMatrix4x4_Multiply(&rotM, matrix4x4, matrix4x4);
	OGLMatrix4x4_Multiply(&negTransM, matrix4x4, matrix4x4);
}

/************** MATRIX4X4 SET TRANSLATE ******************/

void OGLMatrix4x4_SetTranslate(OGLMatrix4x4 *m, float x, float y, float z)
{
	m->value[M03] = x;
	m->value[M13] = y;
	m->value[M23] = z;

	m->value[M00] = 1;
	m->value[M11] = 1;
	m->value[M22] = 1;
	m->value[M33] = 1;

	m->value[M01] = 0;
	m->value[M02] = 0;
	m->value[M10] = 0;
	m->value[M12] = 0;
	m->value[M20] = 0;
	m->value[M21] = 0;
	m->value[M30] = 0;
	m->value[M31] = 0;
	m->value[M32] = 0;
}



#pragma mark ____ TEXTURE FUNCTIONS _____


/***************** OGL TEXTUREMAP LOAD **************************/
//
// INPUT:
//			textureInRAM = true if OpenGL is to use the texture directly from imageMemory.
//							In this case we are in control of the texture, and must remember to delete it later
//

GLuint OGL_TextureMap_Load(void *imageMemory, int width, int height, Boolean textureInRAM)
{	
GLuint	textureName;


			/* DO ANAGLYPH COLOR BALANCING ON THIS TEXTURE */
			
	if (gGamePrefs.greyscale)
		ConvertTextureToGrey(imageMemory, width, height);
	else
		ConvertTextureToColorAnaglyph(imageMemory, width, height);

			/* GET A UNIQUE TEXTURE NAME & INITIALIZE IT */

	glGenTextures(1, &textureName);		

	glBindTexture(GL_TEXTURE_2D, textureName);				// this is now the currently active texture
			
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	if (textureInRAM)
		glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, 1);

	glTexImage2D(GL_TEXTURE_2D,
				0,										// mipmap level
				GL_RGBA8,								// format in OpenGL
				width,									// width in pixels
				height,									// height in pixels
				0,										// border
				GL_BGRA,								// what my format is
				GL_UNSIGNED_INT_8_8_8_8_REV,			// size of each r,g,b
				imageMemory);							// pointer to the actual texture pixels
		
	
				/* SET THIS TEXTURE AS CURRENTLY ACTIVE FOR DRAWING */
	
	OGL_Texture_SetOpenGLTexture(textureName);
	
	return(textureName);
}

/****************** OGL: TEXTURE SET OPENGL TEXTURE **************************/
//
// Sets the current OpenGL texture using glBindTexture et.al. so any textured triangles will use it.
//
 
void OGL_Texture_SetOpenGLTexture(GLuint textureName)
{
	glBindTexture(GL_TEXTURE_2D, textureName);		

	OGL_EnableTexture2D();				
}


/******************* COLOR BALANCE RGB FOR ANAGLYPH *********************/

static void ColorBalanceRGBForAnaglyph(u_long *rr, u_long *gg, u_long *bb)
{
long	r,g,b;					// 8-bit color component values
float	fr,fg,fb;				// float color component values
float	d;
float   lumR, lumGB, ratio;

			/* GET UNBALANCED INPUT RGB VALUES */
			
	fr = r = *rr;							// get both integer and float versions
	fg = g = *gg;
	fb = b = *bb;
	
				
				/* CALC LUMINOSITY OF RED AND CYAN EYES */
				//
				// The NTSC luminance calculation is = .299r + .587g + .114b
				//
				
	lumR	= fr * .299f;					// red luminosity = r*.299
	lumGB	= fg * .587f + fb * .114f;		// cyan luminosity = g*.587 + b*.114

	
		/* BALANCE RED */
		
	ratio = lumGB / lumR;					// calc ratio of cyan to red
	d = fr * ratio * RED_RATIO_ADJ;			// d = adjusted red value
	if (d > fr)								// only adjust up, not down
	{
		r = d;
		if (r > 0xff)						// make sure doesn't overflow 8-bits
			r = 0xff;	
	}
		

		/* BALANCE GREEN */
		
	ratio = lumR / lumGB;					// calc ratio of red to cyan
	d = fg * ratio * GREEN_RATIO_ADJ;		// d = adjusted green value
	if (d > fg)								
	{
		g = d;
		if (g > 0xff)
			g = 0xff;		
	}

	
		/* BALANCE BLUE */
		
	d = fb * ratio * BLUE_RATIO_ADJ;		// d = adjusted blue value
	if (d > fb)								
	{
		b = d;
		if (b > 0xff)						
			b = 0xff;		
	}

	*rr = r;								// return the new RGB values
	*gg = g;
	*bb = b;	
}


/******************** CONVERT TEXTURE TO COLOR ANAGLYPH **********************/

static void	ConvertTextureToColorAnaglyph(void *imageMemory, short width, short height)
{
long	x,y;
u_long	r,g,b;
u_long	a;

	u_long	*pix32 = (u_long *)imageMemory;
	for (y = 0; y < height; y++)
	{
		for (x = 0; x < width; x++)
		{					
			a = ((pix32[x] >> 24) & 0xff);
			r = ((pix32[x] >> 16) & 0xff);
			g = ((pix32[x] >> 8) & 0xff);
			b = ((pix32[x] >> 0) & 0xff);
	
			ColorBalanceRGBForAnaglyph(&r, &g, &b);
									
			pix32[x] = (a << 24) | (r << 16) | (g << 8) | b;
			
		}
		pix32 += width;
	}
}


/******************** CONVERT TEXTURE TO GREY **********************/
//
// The NTSC luminance standard  = .299r + .587g + .114b
//

static void	ConvertTextureToGrey(void *imageMemory, short width, short height)
{
long	x,y;
float	r,g,b;
u_long	a,lum;
u_long	*pix32 = (u_long *)imageMemory;

	for (y = 0; y < height; y++)
	{
		for (x = 0; x < width; x++)
		{	
				/* CALC LUMINANCE OF THE RGB CHANNELS */
					
			r = (float)((pix32[x] >> 16) & 0xff) / 255.0f * .299f;
			g = (float)((pix32[x] >> 8) & 0xff) / 255.0f * .586f;
			b = (float)(pix32[x] & 0xff) / 255.0f * .114f;
			a = (pix32[x] >> 24) & 0xff;
		
			/* CALC LUMINANCE OF THIS PIXEL & CONVERT TO 8-BIT COMPONENT VALUE */
															
			lum = (r + g + b) * 255.0f;									
			if (lum > 0xff)
				lum = 0xff;
				
				
				/* CONVERT THRU OUR BRIGHTNESS CURVE */
				
			lum = gAnaglyphGreyTable[lum];
			
		
				/* SAVE LUMINOSITY INTO RGB CHANNELS TO MAKE GREY */
						
			pix32[x] = (a << 24) | (lum << 16) | (lum << 8) | lum;
			
		}
		pix32 += width;
	}
	
}

/*************** BUILD GREY CURVE ***************/
//
// This makes an intensity curve to brighten things up, but sometimes
// it washes them out.
//

static void BuildGreyCurve(void)
{
short	i;
float	f;
		
	f = 0;
	for (i = 0; i < 255; i++)
	{
		gAnaglyphGreyTable[i] = sin(f) * 255.0f;
		f += (M_PI/2.0) / 255.0f;
	}
	
}






