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


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

static void DrawSceneGeometry(void);
static void InitOurState(void);


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

extern	Boolean						gPlayInWindow, gAltiVec;
extern	WindowPtr					gGameWindow;


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

#define	STATE_STACK_SIZE	20


#define	CAMERA_HITHER		10.0f
#define	CAMERA_YON			1000.0f
#define	CAMERA_FOV			100.0			// field of view of the camera (in degrees)

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

AGLDrawable			gAGLWin;
AGLContext			gAGLContext = nil;

GLuint				gFontList;
static				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;



/****************** 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 (gPlayInWindow)
		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 (gPlayInWindow)
	{
		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();

}

#pragma mark -

/******************* 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);
	
			/* CLEAR THE DRAW BUFFER & Z-BUFFER */
				
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	
		
				/* DO OUR GEOMETRY DRAWING HERE */
				
	DrawSceneGeometry();
				

			/* END RENDER & SWAP THE BUFFER TO MAKE IT VISIBLE */
           		
	aglSwapBuffers(gAGLContext);	
}



/***************** DRAW SCENE GEOMETRY *******************/
//
// Called from DrawOpenGLScene() when it's time to submit our
// geometry data to OpenGL.
//

static void DrawSceneGeometry(void)
{
static float	x = 0;
static float	dx = .1f;

			/* DRAW A FRAME COUNTER IN THE UPPER-LEFT CORNER */
			
	OGL_DrawString("\pframe #:", 20, 20);
	OGL_DrawInt(gFrameCounter++, 70, 20);
	
			
			/* DRAW MESSAGE IN OPPOSITE CORNER */
			
	OGL_DrawString("\pClick the mouse to Exit", 500, 440);
		
	
			/***********************/
			/* DRAW A GREEN SQUARE */
			/***********************/
		
			/* INIT DESIRED MATRIX & STATE */
			
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-100, 100, -100, 100, -10.0, 10.0);
		
	OGL_DisableLighting();	
		
	OGL_SetColor4f(0,1,0,1);
	
	
			/* DRAW IT */
			
	glBegin(GL_QUADS);
	glVertex2f(x-25, -25);
	glVertex2f(x+25, -25);
	glVertex2f(x+25, 25);
	glVertex2f(x-25, 25);
	glEnd();
	
			/* MOVE X FOR NEXT FRAME */
			
	x += dx;
	if ((x > 100.0f) || (x < -100.0f))
		dx = -dx;
}



#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);

}


#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)
{
		/****************************/
		/* SET PERMANENT STATE INFO */
		/****************************/

	glClearColor(0,0,.5, 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);
}


/********************* 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;
	}
}
















