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


/****************************/
/*    EXTERNALS             */
/****************************/

#include "3dmath.h"

extern	MOMaterialObject	*gMostRecentMaterial;
extern	u_long			gGlobalMaterialFlags,gCurrentPickID;

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

static void OGL_Camera_SetPlacementAndUpdateMatricesForPicking(OGLSetupOutputType *setupInfo, float pickX, float pickY, float pickWidth, float pickHeight);

static void OGL_CreateDrawContext(OGLViewDefType *viewDefPtr, OGLSetupOutputType *outputDef, OGLSetupOutputType *sharedContext);
static void OGL_SetStyles(OGLSetupInputType *setupDefPtr);
static void OGL_CreateLights(OGLLightDefType *lightDefPtr, OGLSetupOutputType *outputDef);
static void OGL_InitFont(OGLSetupOutputType *setupInfo);
static void OGL_FreeFont(OGLSetupOutputType *setupInfo);


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

#define	STATE_STACK_SIZE	20



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

Boolean							gATIDriver				= false;
Boolean							gATITrueFormAvailable 	= false;


Boolean			gCanDo512 = true;


static GLuint 			gFontList;


OGLMatrix4x4	gViewToFrustumMatrix,gWorldToViewMatrix,gWorldToFrustumMatrix;
OGLMatrix4x4	gWorldToWindowMatrix,gFrustumToWindowMatrix;

float	gCurrentAspectRatio = 1;


Boolean		gStateStack_Lighting[STATE_STACK_SIZE];					
Boolean		gStateStack_CullFace[STATE_STACK_SIZE];					
Boolean		gStateStack_DepthTest[STATE_STACK_SIZE];			
Boolean		gStateStack_Normalize[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];
GLfloat		gStateStack_Color[STATE_STACK_SIZE][4];

int			gStateStackIndex = 0;

int			gPolysThisFrame;
int			gVRAMUsedThisFrame = 0;

Boolean		gMyState_Lighting;

Boolean		gIsPicking = false;
GLuint		gPickBuffer[PICK_BUFFER_SIZE];
int			gNumPickHits = 0;



/*********************** OGL: NEW VIEW DEF **********************/
//
// fills a view def structure with default values.
//

void OGL_NewViewDef(OGLSetupInputType *viewDef, WindowPtr window)
{
const OGLColorRGBA		clearColor = {0,0,.8,1};
const OGLPoint3D			cameraFrom = { 0, 200, 400 };
const OGLPoint3D			cameraTo = { 0, 0, 0 };
const OGLVector3D			cameraUp = { 0.0, 1.0, 0.0 };
const OGLColorRGBA			ambientColor = { .3, .3, .3, 1 };
const OGLColorRGBA			fillColor = { 1.0, 1.0, 1.0, 1 };
static OGLVector3D			fillDirection1 = { 1, 0, -1 };
static OGLVector3D			fillDirection2 = { -1, -.3, -.3 };


	OGLVector3D_Normalize(&fillDirection1, &fillDirection1);
	OGLVector3D_Normalize(&fillDirection2, &fillDirection2);

	viewDef->view.clearColor 		= clearColor;
	viewDef->view.clip.left 	= 0;
	viewDef->view.clip.right 	= 0;
	viewDef->view.clip.top 		= 0;
	viewDef->view.clip.bottom 	= 0;
	viewDef->view.clearBackBuffer = true;
	viewDef->view.window 		= window;

	viewDef->camera.useOrthoView	= true;						// default to ortho front view looking down z-axis
	viewDef->camera.orthoRot.x 		=
	viewDef->camera.orthoRot.y 		=
	viewDef->camera.orthoRot.z 		= 0;

	viewDef->camera.orthoScroll.x 	=
	viewDef->camera.orthoScroll.y 	= 0;
	
	
	
	viewDef->camera.from			= cameraFrom;
	viewDef->camera.to 				= cameraTo;
	viewDef->camera.up 				= cameraUp;
	viewDef->camera.hither 			= 10;
	viewDef->camera.yon 			= 4000;
	viewDef->camera.fov 			= 1.2;

	viewDef->styles.usePhong 		= false; 
	
	viewDef->styles.useFog			= false;
	viewDef->styles.fogStart		= viewDef->camera.yon * .5f;
	viewDef->styles.fogEnd			= viewDef->camera.yon;
	viewDef->styles.fogDensity		= 1.0;
	viewDef->styles.fogMode			= GL_LINEAR;

	viewDef->lights.ambientColor 	= ambientColor;
	viewDef->lights.numFillLights 	= 1;
	viewDef->lights.fillDirection[0] = fillDirection1;
	viewDef->lights.fillDirection[1] = fillDirection2;
	viewDef->lights.fillColor[0] 	= fillColor;
	viewDef->lights.fillColor[1] 	= fillColor;
}


/************** SETUP OGL WINDOW *******************/

void OGL_SetupWindow(OGLSetupInputType *setupDefPtr, OGLSetupOutputType **outputHandle, OGLSetupOutputType *sharedContextParent)
{
OGLSetupOutputType	*outputPtr;
int					i;

			/* ALLOC MEMORY FOR OUTPUT DATA */

	outputPtr = (OGLSetupOutputType *)AllocPtr(sizeof(OGLSetupOutputType));
	if (outputPtr == nil)
		DoFatalAlert("\pOGL_SetupWindow: AllocPtr failed");

				/* INIT SHARED CONTEXTS LIST */
				
	outputPtr->numSharedContexts = 0;
	for (i = 0; i < MAX_SHARED_CONTEXT_CHILDREN; i++)
		outputPtr->childrenContexts[i] = nil;


				/* SETUP */

	OGL_CreateDrawContext(&setupDefPtr->view, outputPtr, sharedContextParent);	
	OGL_SetStyles(setupDefPtr);	
	OGL_CreateLights(&setupDefPtr->lights, outputPtr);


				/* PASS BACK INFO */
			
	outputPtr->window			= setupDefPtr->view.window;	
	outputPtr->clip 			= setupDefPtr->view.clip;
	outputPtr->hither 			= setupDefPtr->camera.hither;			// remember hither/yon
	outputPtr->yon 				= setupDefPtr->camera.yon;
	outputPtr->useFog 			= setupDefPtr->styles.useFog;
	outputPtr->clearBackBuffer 	= setupDefPtr->view.clearBackBuffer;
	
	outputPtr->isActive = true;											// it's now an active structure
	
	outputPtr->lightList = setupDefPtr->lights;							// copy lights

	if (setupDefPtr->camera.useOrthoView)
	{
		outputPtr->useOrthoView = true;
		outputPtr->orthoRot 	= setupDefPtr->camera.orthoRot;
		outputPtr->orthoScroll 	= setupDefPtr->camera.orthoScroll;
	}
	else
	{
		outputPtr->useOrthoView = false;
		outputPtr->fov = setupDefPtr->camera.fov;					// each camera will have its own fov so we can change it for special effects
		OGL_UpdateCameraFromTo(outputPtr, &setupDefPtr->camera.from, &setupDefPtr->camera.to);		
	}
		
	*outputHandle = outputPtr;											// return value to caller
}


/***************** OGL_DisposeWindowSetup ***********************/
//
// Disposes of all data created by OGL_SetupWindow
//

void OGL_DisposeWindowSetup(OGLSetupOutputType **dataHandle)
{
OGLSetupOutputType	*data;
int					i;

	data = *dataHandle;
	if (data == nil)												// see if this setup exists
		DoFatalAlert("\pOGL_DisposeWindowSetup: data == nil");

			/* NUKE ANY SHARED CONTEXT REFERENCES */
			
	if (data->parentContext)										// see if remove from parent's list
	{
		OGLSetupOutputType	*parent = data->parentContext;
	
		for (i = 0; i < parent->numSharedContexts; i++)
		{
			if (parent->childrenContexts[i] == data)						// if this is the one, zap it
			{
				for (; i < (MAX_SHARED_CONTEXT_CHILDREN-1); i++)			// shift list down
					parent->childrenContexts[i] = parent->childrenContexts[i+1];			
				parent->numSharedContexts--;
			}
		}
	}

				/* DETACH CHILDREN REFS */
	
	for (i = 0; i < data->numSharedContexts; i++)
	{
		OGLSetupOutputType	*child = data->childrenContexts[i];		
		child->parentContext = nil;		
	}
 	data->numSharedContexts = 0;
	
	
			/* KILL DEBUG FONT */
			
	OGL_FreeFont(data);

  	aglSetCurrentContext(nil);								// make context not current
   	aglSetDrawable(data->drawContext, nil);
	aglDestroyContext(data->drawContext);					// nuke the AGL context

	
		/* FREE MEMORY & NIL POINTER */
		
	data->isActive = false;									// now inactive
	DisposePtr((Ptr)data);
	*dataHandle = nil;
	
}




/**************** OGL: CREATE DRAW CONTEXT *********************/

static void OGL_CreateDrawContext(OGLViewDefType *viewDefPtr, OGLSetupOutputType *outputDef, OGLSetupOutputType *sharedContext)
{
AGLPixelFormat 	fmt;
GLboolean      mkc,ok;
GLint          attrib[] = {AGL_RGBA, AGL_DOUBLEBUFFER, AGL_DEPTH_SIZE, 16, AGL_ALL_RENDERERS, AGL_ACCELERATED, AGL_NO_RECOVERY, AGL_NONE};
GLint          attrib2[] = {AGL_RGBA, AGL_DOUBLEBUFFER, AGL_DEPTH_SIZE, 16, AGL_ALL_RENDERERS, AGL_NONE};
GLint			maxTexSize;
static char			*s;
int				n;
AGLContext	parent;


	outputDef->aglDrawable = (AGLDrawable)GetWindowPort(viewDefPtr->window);
	
	if (outputDef->aglDrawable == nil)
		DoFatalAlert("\pOGL_CreateDrawContext: no window specified");


			/* CHOOSE PIXEL FORMAT */
			
	fmt = aglChoosePixelFormat(nil, 0, attrib);
	if ((fmt == NULL) || (aglGetError() != AGL_NO_ERROR))
	{
		fmt = aglChoosePixelFormat(nil, 0, attrib2);							// try being less stringent
		if ((fmt == NULL) || (aglGetError() != AGL_NO_ERROR))
		{
			DoFatalAlert("\paglChoosePixelFormat failed!  Check that your 3D accelerator is OpenGL compliant, installed properly, and that you have the latest drivers.");
		}
	}


			/* CREATE AGL CONTEXT & ATTACH TO WINDOW */
			
	if (sharedContext)
		parent = sharedContext->drawContext;
	else
		parent = nil;
			
	outputDef->drawContext = aglCreateContext(fmt, parent);
	if ((outputDef->drawContext == nil) || (aglGetError() != AGL_NO_ERROR))
		DoFatalAlert("\pOGL_CreateDrawContext: aglCreateContext failed!");
		
		
	ok = aglSetDrawable(outputDef->drawContext, outputDef->aglDrawable);
	if ((!ok) || (aglGetError() != AGL_NO_ERROR))
	{
		if (aglGetError() == AGL_BAD_ALLOC)
		{
			DoFatalAlert("\pNot enough VRAM for the selected video mode.  Please try again and select a different mode.");	
		}
		else
			DoFatalAlert("\pOGL_CreateDrawContext: aglSetDrawable failed!");
	}


			/* ACTIVATE CONTEXT */

	mkc = aglSetCurrentContext(outputDef->drawContext);
	if ((mkc == NULL) || (aglGetError() != AGL_NO_ERROR))
		return;


	outputDef->parentContext	= sharedContext;						// remember who's context we're sharing


			/* NO LONGER NEED PIXEL FORMAT */
			
	aglDestroyPixelFormat(fmt);


			/* CLEAR ALL BUFFERS TO BLACK */
			
	glClearColor(0,0,0,1);
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);		// clear buffer
	aglSwapBuffers(outputDef->drawContext);
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);		// clear buffer
	aglSwapBuffers(outputDef->drawContext);


				/* SET VARIOUS STATE INFO */


	glClearColor(viewDefPtr->clearColor.r, viewDefPtr->clearColor.g, viewDefPtr->clearColor.b, 1.0);
	glEnable(GL_DEPTH_TEST);								// use z-buffer
	
	{
		GLfloat	color[] = {1,1,1,1};									// set global material color to white
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
	}
	
	glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);	
	if (USE_GL_COLOR_MATERIAL)
		glEnable(GL_COLOR_MATERIAL);		
	else
		glDisable(GL_COLOR_MATERIAL);
  	glEnable(GL_NORMALIZE);
  	
  	  	 	
  	 	
 		/***************************/
		/* GET OPENGL CAPABILITIES */
 		/***************************/

		/* SEE IF RUNNING ON ATI */
				  			
	s = (char *)glGetString(GL_VENDOR);	
	if (strstr(s, "ATI Technologies Inc."))
		gATIDriver = true;
	else
		gATIDriver = false;

	s = (char *)glGetString(GL_EXTENSIONS);					// get extensions list
	
	  	 	
			/* INIT DEBUG FONT */
			
	OGL_InitFont(outputDef);


			/* SEE IF SUPPORT 512X512 */
			
	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
	if (maxTexSize < 512)
		gCanDo512 = false;
	else
		gCanDo512 = true;


			/* HANDLE CONTEXT SHARING */
			
	outputDef->parentContext = sharedContext;									// keep ptr to parent shared context (if any)
	if (sharedContext)
	{
		n = sharedContext->numSharedContexts;										// add this context to the parent's list of children contexts
		sharedContext->childrenContexts[n++] = outputDef;
	}
}



/**************** OGL: SET STYLES ****************/

static void OGL_SetStyles(OGLSetupInputType *setupDefPtr)
{
OGLStyleDefType *styleDefPtr = &setupDefPtr->styles;


	glEnable(GL_CULL_FACE);									// activate culling
	glCullFace(GL_BACK);
	glFrontFace(GL_CCW);									// CCW is front face
	glEnable(GL_DITHER);
	
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);		// set default blend func
	glDisable(GL_BLEND);									// but turn it off by default

	glHint(GL_TRANSFORM_HINT_APPLE, GL_FASTEST);
	glDisable(GL_RESCALE_NORMAL);

    glHint(GL_FOG_HINT, GL_NICEST);		// pixel accurate fog?


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


		/* SET FOG */

	glHint(GL_FOG_HINT, GL_FASTEST);

	if (styleDefPtr->useFog)
	{
		glFogi(GL_FOG_MODE, styleDefPtr->fogMode);
		glFogf(GL_FOG_DENSITY, styleDefPtr->fogDensity);
		glFogf(GL_FOG_START, styleDefPtr->fogStart);
		glFogf(GL_FOG_END, styleDefPtr->fogEnd);
		glFogfv(GL_FOG_COLOR, (float *)&setupDefPtr->view.clearColor);
		glEnable(GL_FOG);
	}
	else
		glDisable(GL_FOG);
}




/********************* OGL: CREATE LIGHTS ************************/
//
// NOTE:  The Projection matrix must be the identity or lights will be transformed.
//

static void OGL_CreateLights(OGLLightDefType *lightDefPtr, OGLSetupOutputType *outputDef)
{
int		i;
GLfloat	ambient[4];

	OGL_EnableLighting(outputDef);

	
			/************************/
			/* CREATE AMBIENT LIGHT */
			/************************/

	ambient[0] = lightDefPtr->ambientColor.r;
	ambient[1] = lightDefPtr->ambientColor.g;
	ambient[2] = lightDefPtr->ambientColor.b;
	ambient[3] = 1;
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);			// set scene ambient light


			/**********************/
			/* CREATE FILL LIGHTS */
			/**********************/
			
	for (i=0; i < lightDefPtr->numFillLights; i++)
	{
		static GLfloat lightamb[4] = { 0.0, 0.0, 0.0, 1.0 };
		GLfloat lightVec[4];
		GLfloat	diffuse[4];
	
					/* SET FILL DIRECTION */
					
		OGLVector3D_Normalize(&lightDefPtr->fillDirection[i], &lightDefPtr->fillDirection[i]);
		lightVec[0] = -lightDefPtr->fillDirection[i].x;		// negate vector because OGL is stupid
		lightVec[1] = -lightDefPtr->fillDirection[i].y;
		lightVec[2] = -lightDefPtr->fillDirection[i].z;
		lightVec[3] = 0;									// when w==0, this is a directional light, if 1 then point light
		glLightfv(GL_LIGHT0+i, GL_POSITION, lightVec);

		
					/* SET COLOR */
					
		glLightfv(GL_LIGHT0+i, GL_AMBIENT, lightamb);
		
		diffuse[0] = lightDefPtr->fillColor[i].r;
		diffuse[1] = lightDefPtr->fillColor[i].g;
		diffuse[2] = lightDefPtr->fillColor[i].b;
		diffuse[3] = 1;
		
		glLightfv(GL_LIGHT0+i, GL_DIFFUSE, diffuse);
	
			
		glEnable(GL_LIGHT0+i);								// enable the light
	}	
	
}

#pragma mark -


/******************* OGL PICK SCENE *************************/

void OGL_PickScene(OGLSetupOutputType *setupInfo, void (*drawRoutine)(OGLSetupOutputType *),
					float pickX, float pickY,
					float pickWidth, float pickHeight)
{
int			x,y,w,h;

	if (setupInfo == nil)										// make sure it's legit
		DoFatalAlert("\pOGL_PickScene setupInfo == nil");
	if (!setupInfo->isActive)									
		DoFatalAlert("\pOGL_PickScene isActive == false");

  	aglSetCurrentContext(setupInfo->drawContext);								// make context not current


			/* LET'S GET INTO PICKING MODE */
			
	gIsPicking = true;
	gNumPickHits = 0;
	glSelectBuffer(PICK_BUFFER_SIZE, gPickBuffer);
	glRenderMode(GL_SELECT);
	glInitNames();
	gCurrentPickID = 0;
	glPushName(gCurrentPickID);

				/* SET VIEWPORT */

	OGL_GetCurrentViewport(setupInfo, &x, &y, &w, &h);
	glViewport(x,y, w, h);
	gCurrentAspectRatio = (float)w/(float)h;


			/* GET UPDATED GLOBAL COPIES OF THE VARIOUS MATRICES */

	if (setupInfo->useOrthoView)
	{		
		GLint		viewport[4];
		
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		
		viewport[0] = 0;
		viewport[1] = 0;
		viewport[2] = w;
		viewport[3] = h;
		gluPickMatrix(pickX, h - pickY, pickWidth, pickHeight, viewport);		// limit picking to this area
		
		glOrtho(setupInfo->orthoScroll.x, setupInfo->orthoScroll.x+w,
				setupInfo->orthoScroll.y+h, setupInfo->orthoScroll.y, -1000, 1000);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();	
		glRotatef(setupInfo->orthoRot.x, 1,0,0);
		glRotatef(setupInfo->orthoRot.y, 0,1,0);
		glRotatef(setupInfo->orthoRot.z, 0,0,1);
	}
	else
		OGL_Camera_SetPlacementAndUpdateMatricesForPicking(setupInfo, pickX, pickY, pickWidth, pickHeight);


			/* CALL INPUT DRAW FUNCTION */

	if (drawRoutine != nil)
		drawRoutine(setupInfo);


		/* CLEANUP & GET OUT OF PICKING MODE */
			
	glFlush();
	gNumPickHits = glRenderMode(GL_RENDER);
	gIsPicking = false;
}


/******************* OGL DRAW SCENE *********************/

void OGL_DrawScene(OGLSetupOutputType *setupInfo, void (*drawRoutine)(OGLSetupOutputType *))
{
int	x,y,w,h;

	if (setupInfo == nil)										// make sure it's legit
		DoFatalAlert("\pOGL_DrawScene setupInfo == nil");
	if (!setupInfo->isActive)									
		DoFatalAlert("\pOGL_DrawScene isActive == false");

  	aglSetCurrentContext(setupInfo->drawContext);					// make context active


			/* INIT SOME STUFF */
	
	gPolysThisFrame 	= 0;										// init poly counter
	gMostRecentMaterial = nil;
	gGlobalMaterialFlags = 0;			
	SetColor4f(1,1,1,1);
	glDisable(GL_TEXTURE_2D);

				/* CLEAR BUFFER */

	if (setupInfo->clearBackBuffer)
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	else
		glClear(GL_DEPTH_BUFFER_BIT);
	


				/* SET VIEWPORT */

	OGL_GetCurrentViewport(setupInfo, &x, &y, &w, &h);
	glViewport(x,y, w, h);
	gCurrentAspectRatio = (float)w/(float)h;


			/* GET UPDATED GLOBAL COPIES OF THE VARIOUS MATRICES */

	if (setupInfo->useOrthoView)
	{		
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(setupInfo->orthoScroll.x, setupInfo->orthoScroll.x+w,
				setupInfo->orthoScroll.y+h, setupInfo->orthoScroll.y, -1000, 1000);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();	
		glRotatef(setupInfo->orthoRot.x, 1,0,0);
		glRotatef(setupInfo->orthoRot.y, 0,1,0);
		glRotatef(setupInfo->orthoRot.z, 0,0,1);
	}
	else
		OGL_Camera_SetPlacementAndUpdateMatrices(setupInfo);


			/* CALL INPUT DRAW FUNCTION */

	if (drawRoutine != nil)
		drawRoutine(setupInfo);



            /**************/
			/* END RENDER */
			/**************/
           	
           /* SWAP THE BUFFS */
           		
	aglSwapBuffers(setupInfo->drawContext);					// end render loop

}


/********************** OGL: GET CURRENT VIEWPORT ********************/
//
// Remember that with OpenGL, the bottom of the screen is y==0, so some of this code
// may look upside down.
//

void OGL_GetCurrentViewport(const OGLSetupOutputType *setupInfo, int *x, int *y, int *w, int *h)
{
int	t,b,l,r;
WindowPtr	window;
Rect		q;
int			winW,winH;
		
	window = setupInfo->window;
	GetPortBounds(GetWindowPort(window), &q);
	winW = q.right - q.left;
	winH = q.bottom - q.top;
		
	t = setupInfo->clip.top;
	b = setupInfo->clip.bottom;
	l = setupInfo->clip.left;
	r = setupInfo->clip.right;
				
	*x = l;
	*y = t;
	*w = winW-l-r;
	*h = winH-t-b;
}


#pragma mark -


/***************** OGL TEXTUREMAP LOAD **************************/
 
GLuint OGL_TextureMap_Load(const OGLSetupOutputType *setupInfo, void *imageMemory, int width, int height,
							GLint srcFormat,  GLint destFormat, GLint dataType)
{	
GLuint	textureName;
								
				
			/* GET A UNIQUE TEXTURE NAME & INITIALIZE IT */

	glGenTextures(1, &textureName);		
	if (OGL_CheckError(setupInfo))
		DoFatalAlert("\pOGL_TextureMap_Load: glGenTextures failed!");

	glBindTexture(GL_TEXTURE_2D, textureName);				// this is now the currently active texture
	if (OGL_CheckError(setupInfo))
		DoFatalAlert("\pOGL_TextureMap_Load: glBindTexture failed!");
			
			
				/* LOAD TEXTURE AND/OR MIPMAPS */
				
	if (0)
	{
		GLint	error; 
		
	    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		error = gluBuild2DMipmaps(GL_TEXTURE_2D,
							destFormat,								// format in OpenGL
							width,									// width in pixels
							height,									// height in pixels
							srcFormat,								// what my format is
							dataType,								// size of each r,g,b
							imageMemory);							// pointer to the actual texture pixels
							
		if (error)
		{
			DoAlert("\pOGL_TextureMap_Load: gluBuild2DMipmaps failed!");
			ShowSystemErr(error);
		}
	}
	else
	{				
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		glTexImage2D(GL_TEXTURE_2D,
					0,										// mipmap level
					destFormat,								// format in OpenGL
					width,									// width in pixels
					height,									// height in pixels
					0,										// border
					srcFormat,								// what my format is
					dataType,								// size of each r,g,b
					imageMemory);							// pointer to the actual texture pixels
	}
		
			/* SEE IF RAN OUT OF MEMORY WHILE COPYING TO OPENGL */

	if (OGL_CheckError(setupInfo))
		DoFatalAlert("\pOGL_TextureMap_Load: glTexImage2D failed!");
	
	
				/* SET THIS TEXTURE AS CURRENTLY ACTIVE FOR DRAWING */
	
	OGL_Texture_SetOpenGLTexture(setupInfo, 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(const OGLSetupOutputType *setupInfo, GLuint textureName)
{

	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	if (OGL_CheckError(setupInfo))
		DoFatalAlert("\pOGL_Texture_SetOpenGLTexture: glPixelStorei failed!");

	glBindTexture(GL_TEXTURE_2D, textureName);		
	if (OGL_CheckError(setupInfo))
		DoFatalAlert("\pOGL_Texture_SetOpenGLTexture: glBindTexture failed!");
			
	glEnable(GL_TEXTURE_2D);
}



#pragma mark -

/*************** OGL_MoveCameraFromTo ***************/

void OGL_MoveCameraFromTo(OGLSetupOutputType *setupInfo, float fromDX, float fromDY, float fromDZ, float toDX, float toDY, float toDZ)
{

			/* SET CAMERA COORDS */
			
	setupInfo->cameraPlacement.cameraLocation.x += fromDX;
	setupInfo->cameraPlacement.cameraLocation.y += fromDY;
	setupInfo->cameraPlacement.cameraLocation.z += fromDZ;

	setupInfo->cameraPlacement.pointOfInterest.x += toDX;
	setupInfo->cameraPlacement.pointOfInterest.y += toDY;
	setupInfo->cameraPlacement.pointOfInterest.z += toDZ;

}


/*************** OGL_MoveCameraFrom ***************/

void OGL_MoveCameraFrom(OGLSetupOutputType *setupInfo, float fromDX, float fromDY, float fromDZ)
{

			/* SET CAMERA COORDS */
			
	setupInfo->cameraPlacement.cameraLocation.x += fromDX;
	setupInfo->cameraPlacement.cameraLocation.y += fromDY;
	setupInfo->cameraPlacement.cameraLocation.z += fromDZ;

}



/*************** OGL_UpdateCameraFromTo ***************/

void OGL_UpdateCameraFromTo(OGLSetupOutputType *setupInfo, const OGLPoint3D *from, const OGLPoint3D *to)
{
static const OGLVector3D up = {0,1,0};
						
	setupInfo->cameraPlacement.upVector 		= up;
	setupInfo->cameraPlacement.cameraLocation 	= *from;
	setupInfo->cameraPlacement.pointOfInterest 	= *to;
	
}

/*************** OGL_UpdateCameraFromToUp ***************/

void OGL_UpdateCameraFromToUp(OGLSetupOutputType *setupInfo, OGLPoint3D *from, OGLPoint3D *to, OGLVector3D *up)
{			
			
	setupInfo->cameraPlacement.upVector 		= *up;
	setupInfo->cameraPlacement.cameraLocation 	= *from;
	setupInfo->cameraPlacement.pointOfInterest 	= *to;

}



/************** OGL: CAMERA SET PLACEMENT & UPDATE MATRICES **********************/
//
// This is called by OGL_DrawScene to initialize all of the view matrices,
// and to extract the current view matrices used for culling et.al.
//

void OGL_Camera_SetPlacementAndUpdateMatrices(OGLSetupOutputType *setupInfo)
{
float	aspect;
OGLCameraPlacement	*placement;
int		temp, w, h, i;
OGLLightDefType	*lights;

	OGL_GetCurrentViewport(setupInfo, &temp, &temp, &w, &h);
	aspect = (float)w/(float)h;
	
			/* INIT PROJECTION MATRIX */
			
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();	
	gluPerspective (OGLMath_RadiansToDegrees(setupInfo->fov),	// fov
					aspect,					// aspect
					setupInfo->hither,		// hither
					setupInfo->yon);		// yon
	
	
			/* INIT MODELVIEW MATRIX */
			
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	placement = &setupInfo->cameraPlacement;
	gluLookAt(placement->cameraLocation.x, placement->cameraLocation.y, placement->cameraLocation.z,	
			placement->pointOfInterest.x, placement->pointOfInterest.y, placement->pointOfInterest.z,	
			placement->upVector.x, placement->upVector.y, placement->upVector.z);


		/* UPDATE LIGHT POSITIONS */
		
	lights =  &setupInfo->lightList;						// point to light list
	for (i=0; i < lights->numFillLights; i++)
	{
		GLfloat lightVec[4];
	
		lightVec[0] = -lights->fillDirection[i].x;			// negate vector because OGL is stupid
		lightVec[1] = -lights->fillDirection[i].y;
		lightVec[2] = -lights->fillDirection[i].z;
		lightVec[3] = 0;									// when w==0, this is a directional light, if 1 then point light
		glLightfv(GL_LIGHT0+i, GL_POSITION, lightVec);	
	}


			/* GET VARIOUS CAMERA MATRICES */
			
	glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)&gWorldToViewMatrix);
	glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat *)&gViewToFrustumMatrix);
	OGLMatrix4x4_Multiply(&gWorldToViewMatrix, &gViewToFrustumMatrix, &gWorldToFrustumMatrix);

	OGLMatrix4x4_GetFrustumToWindow(setupInfo, &gFrustumToWindowMatrix);
	OGLMatrix4x4_Multiply(&gWorldToFrustumMatrix, &gFrustumToWindowMatrix, &gWorldToWindowMatrix);

}

/************** OGL: CAMERA SET PLACEMENT & UPDATE MATRICES FOR PICKING **********************/
//
// This is called by OGL_PickScene to initialize all of the view matrices,
// and to extract the current view matrices used for culling et.al.
//

static void OGL_Camera_SetPlacementAndUpdateMatricesForPicking(OGLSetupOutputType *setupInfo, float pickX, float pickY, float pickWidth, float pickHeight)
{
float				aspect;
OGLCameraPlacement	*placement;
int					temp, w, h;
GLint				viewport[4];

	OGL_GetCurrentViewport(setupInfo, &temp, &temp, &w, &h);
	aspect = (float)w/(float)h;
	
			/* INIT PROJECTION MATRIX */
			
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();	
	
	viewport[0] = 0;
	viewport[1] = 0;
	viewport[2] = w;
	viewport[3] = h;
	gluPickMatrix(pickX, h - pickY, pickWidth, pickHeight, viewport);		// limit picking to this area
	
	gluPerspective (OGLMath_RadiansToDegrees(setupInfo->fov),	// fov
					aspect,					// aspect
					setupInfo->hither,		// hither
					setupInfo->yon);		// yon
	
	
			/* INIT MODELVIEW MATRIX */
			
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	placement = &setupInfo->cameraPlacement;
	gluLookAt(placement->cameraLocation.x, placement->cameraLocation.y, placement->cameraLocation.z,	
			placement->pointOfInterest.x, placement->pointOfInterest.y, placement->pointOfInterest.z,	
			placement->upVector.x, placement->upVector.y, placement->upVector.z);


			/* GET VARIOUS CAMERA MATRICES */
			
	glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)&gWorldToViewMatrix);
	glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat *)&gViewToFrustumMatrix);
	OGLMatrix4x4_Multiply(&gWorldToViewMatrix, &gViewToFrustumMatrix, &gWorldToFrustumMatrix);

	OGLMatrix4x4_GetFrustumToWindow(setupInfo, &gFrustumToWindowMatrix);
	OGLMatrix4x4_Multiply(&gWorldToFrustumMatrix, &gFrustumToWindowMatrix, &gWorldToWindowMatrix);

}


#pragma mark -


/**************** OGL BUFFER TO GWORLD ***********************/

GWorldPtr OGL_BufferToGWorld(Ptr buffer, int width, int height, int bytesPerPixel)
{
Rect			r;
GWorldPtr		gworld;
PixMapHandle	gworldPixmap;
long			gworldRowBytes,x,y,pixmapRowbytes;
Ptr				gworldPixelPtr;
unsigned long	*pix32Src,*pix32Dest;
unsigned short	*pix16Src,*pix16Dest;
OSErr			iErr;
long			pixelSize;

			/* CREATE GWORLD TO DRAW INTO */
	
	switch(bytesPerPixel)
	{
		case	2:
				pixelSize = 16;
				break;
				
		case	4:
				pixelSize = 32;
				break;
	}
	
	SetRect(&r,0,0,width,height);
	iErr = NewGWorld(&gworld,pixelSize, &r, nil, nil, 0);
	if (iErr)
		DoFatalAlert("\pOGL_BufferToGWorld: NewGWorld failed!");

	DoLockPixels(gworld);

	gworldPixmap = GetGWorldPixMap(gworld);
	LockPixels(gworldPixmap);
	
	gworldRowBytes = (**gworldPixmap).rowBytes & 0x3fff;					// get GWorld's rowbytes
	gworldPixelPtr = GetPixBaseAddr(gworldPixmap);							// get ptr to pixels

	pixmapRowbytes = width * bytesPerPixel;
	

			/* WRITE DATA INTO GWORLD */
			
	switch(pixelSize)
	{
		case	32:
				pix32Src = (unsigned long *)buffer;							// get 32bit pointers
				pix32Dest = (unsigned long *)gworldPixelPtr;
				for (y = 0; y <  height; y++)
				{
					for (x = 0; x < width; x++)
						pix32Dest[x] = pix32Src[x];
				
					pix32Dest += gworldRowBytes/4;							// next dest row
					pix32Src += pixmapRowbytes/4;
				}
				break;

		case	16:
				pix16Src = (unsigned short *)buffer;						// get 16bit pointers
				pix16Dest = (unsigned short *)gworldPixelPtr;
				for (y = 0; y <  height; y++)
				{
					for (x = 0; x < width; x++)
						pix16Dest[x] = pix16Src[x];
				
					pix16Dest += gworldRowBytes/2;							// next dest row
					pix16Src += pixmapRowbytes/2;
				}
				break;
				
				
		default:
				DoFatalAlert("\pOGL_BufferToGWorld: Only 32/16 bit textures supported right now.");

	}

	return(gworld);	
}


/******************** OGL: CHECK ERROR ********************/

GLenum OGL_CheckError(const OGLSetupOutputType *setupInfo)
{
GLenum	err;


	err = glGetError();
	if (err != GL_NO_ERROR)
	{
		switch(err)
		{
			case	GL_INVALID_ENUM:
					DoAlert("\pOGL_CheckError: GL_INVALID_ENUM");
					DoFatalAlert("\pThis typically means that you have a very old version of OpenGL installed.  Install OpenGL 1.2.1 or later.");
					break;

			case	GL_INVALID_VALUE:
					DoAlert("\pOGL_CheckError: GL_INVALID_VALUE");
					break;

			case	GL_INVALID_OPERATION:
					DoAlert("\pOGL_CheckError: GL_INVALID_OPERATION");
					break;

			case	GL_STACK_OVERFLOW:
					DoAlert("\pOGL_CheckError: GL_STACK_OVERFLOW");
					break;

			case	GL_STACK_UNDERFLOW:
					DoAlert("\pOGL_CheckError: GL_STACK_UNDERFLOW");
					break;

			case	GL_OUT_OF_MEMORY:
					DoAlert("\pOGL_CheckError: GL_OUT_OF_MEMORY  (increase your Virtual Memory setting!)");
					break;	
					
			default:
					DoAlert("\pOGL_CheckError: some other error");
		}		
	}
	
	return(err);
}


#pragma mark -


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

void OGL_PushState(const OGLSetupOutputType *setupInfo)
{
int	i;

		/* PUSH MATRIES WITH OPENGL */

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

	glMatrixMode(GL_MODELVIEW);										// in my code, I keep modelview matrix as the currently active one all the time.
	

		/* SAVE OTHER 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] = glIsEnabled(GL_CULL_FACE);					
	gStateStack_DepthTest[i] = glIsEnabled(GL_DEPTH_TEST);		
	gStateStack_Normalize[i] = glIsEnabled(GL_NORMALIZE);		
	gStateStack_Texture2D[i] = glIsEnabled(GL_TEXTURE_2D);		
	gStateStack_Fog[i] 		= glIsEnabled(GL_FOG);		
	gStateStack_Blend[i] 	= glIsEnabled(GL_BLEND);	
	
	glGetFloatv(GL_CURRENT_COLOR, &gStateStack_Color[i][0]);
	
	glGetIntegerv(GL_BLEND_SRC, &gStateStack_BlendSrc[i]);	
	glGetIntegerv(GL_BLEND_DST, &gStateStack_BlendDst[i]);	
	glGetBooleanv(GL_DEPTH_WRITEMASK, &gStateStack_DepthMask[i]);	
}


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

void OGL_PopState(const OGLSetupOutputType *setupInfo)
{
int		i;

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

	if (gStateStack_Lighting[i])
		OGL_EnableLighting(setupInfo);
	else
		OGL_DisableLighting(setupInfo);


	if (gStateStack_CullFace[i])
		glEnable(GL_CULL_FACE);
	else
		glDisable(GL_CULL_FACE);


	if (gStateStack_DepthTest[i])
		glEnable(GL_DEPTH_TEST);
	else
		glDisable(GL_DEPTH_TEST);

	if (gStateStack_Normalize[i])
		glEnable(GL_NORMALIZE);
	else
		glDisable(GL_NORMALIZE);

	if (gStateStack_Texture2D[i])
		glEnable(GL_TEXTURE_2D);
	else
		glDisable(GL_TEXTURE_2D);

	if (gStateStack_Blend[i])
		glEnable(GL_BLEND);
	else
		glDisable(GL_BLEND);

	if (gStateStack_Fog[i])
		glEnable(GL_FOG);
	else
		glDisable(GL_FOG);

	glDepthMask(gStateStack_DepthMask[i]);
	glBlendFunc(gStateStack_BlendSrc[i], gStateStack_BlendDst[i]);

	glColor4fv(&gStateStack_Color[i][0]);

}


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

void OGL_EnableLighting(const OGLSetupOutputType *setupInfo)
{

	if (gIsPicking)
		return;

	gMyState_Lighting = true;
	glEnable(GL_LIGHTING);	
}

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

void OGL_DisableLighting(const OGLSetupOutputType *setupInfo)
{

	if (gIsPicking)
		return;

	gMyState_Lighting = false;
	glDisable(GL_LIGHTING);	
}


#pragma mark -

/************************** OGL_INIT FONT **************************/

static void OGL_InitFont(OGLSetupOutputType *setupInfo)
{
	
	gFontList = glGenLists(256);
 
    if (!aglUseFont(setupInfo->drawContext, kFontIDMonaco, bold, 9, 0, 256, gFontList))
		DoFatalAlert("\pOGL_InitFont: aglUseFont failed");
}


/******************* OGL_FREE FONT ***********************/

static void OGL_FreeFont(OGLSetupOutputType *setupInfo)
{

	glDeleteLists(gFontList, 256);
	
}

/**************** OGL_DRAW STRING ********************/

void OGL_DrawString(OGLSetupOutputType *setupInfo, Str255 s, GLint x, GLint y)
{


	OGL_PushState(setupInfo);

	glMatrixMode (GL_MODELVIEW);
	glLoadIdentity();
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, 640, 0, 480, -10.0, 10.0);
	
	glDisable(GL_LIGHTING);
	
	glRasterPos2i(x, 480-y);

	glListBase(gFontList);
	glCallLists(s[0], GL_UNSIGNED_BYTE, &s[1]);
	
	OGL_PopState(setupInfo);
	
}

/**************** OGL_DRAW FLOAT ********************/

void OGL_DrawFloat(OGLSetupOutputType *setupInfo, float f, GLint x, GLint y)
{

Str255	s;

	FloatToString(f,s);
	OGL_DrawString(setupInfo, s,x,y);
	
}



/**************** OGL_DRAW INT ********************/

void OGL_DrawInt(OGLSetupOutputType *setupInfo, int f, GLint x, GLint y)
{

Str255	s;

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

}

#pragma mark -


/********************* OGL:  CHECK RENDERER **********************/
//
// Returns: true if renderer for the requested device complies, false otherwise
//

Boolean OGL_CheckRenderer (GDHandle hGD)
{
AGLRendererInfo info, head_info;
GLint 			dAccel = 0;
Boolean			gotit = false;

			/**********************/
			/* GET FIRST RENDERER */
			/**********************/
			
	head_info = aglQueryRendererInfo(&hGD, 1);
	if(!head_info)
	{
		DoAlert("\pCheckRenderer: aglQueryRendererInfo failed");
		DoFatalAlert("\pThis problem occurs if you have run the faulty MacOS 9.2.1 updater.  To fix, simply delete all Nvidia extensions and reboot.");
	}

		/*******************************************/
		/* SEE IF THERE IS AN ACCELERATED RENDERER */
		/*******************************************/

	info = head_info;
			
	while (info)
	{
		aglDescribeRenderer(info, AGL_ACCELERATED, &dAccel);
		
				/* GOT THE ACCELERATED RENDERER */
				
		if (dAccel)
		{
			gotit = true;
			
					
			break;
		}
		
		
				/* TRY NEXT ONE */
				
		info = aglNextRendererInfo(info);
	}



			/***********/
			/* CLEANUP */
			/***********/
			
	aglDestroyRendererInfo(head_info);

	return(gotit);
}











