/****************************/
/*			CUBE.C			*/
/****************************/

/*	PangeaVR - 3D accelerated plug-in for viewing panoramas.

	©2005 Pangea Software, Inc.  All rights reserved.
	Pangea Software, PangeaVR and various logos are trademarks of Pangea Software, Inc.
	
	Written by Brian Greenstone,   brian@pangeasoft.net	
	
	This program and source code is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
	
	IMPORTANT:  The purpose in releasing this source code is to encourage others
				To port this program to other platforms, and to make improvments to it.
				However, to prevent things from getting out of control with lots of
				different versions of PangeaVR floating around the internet,
				Pangea Software, Inc. prohibits the public distribution of any versions
				of PangeaVR without prior approval from Pangea Software, Inc.
				
				Should you make any improvements to this source code and wish to
				have those changes rolled into an official release then you'll
				need to contact Pangea Software to have your modifications adapted.				
*/


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

extern	MyInstanceType	gMyInstances[];
extern	PrefsType				gPrefs;


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



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

#define	CUBE_SIZE			500.0f


#define	DRIFT_DECAY_SPEED	8.0f


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





/**************** INTERACTIVELY DRAG PANO ************************/
//
// Called when the user clicks the mouse in our window.  This keeps animating the cube until the user releases the mouse button.
// We call the Event loop to keep things going on in the background and to prevent the OS X beachball cursor from coming up.
//

void InteractivelyDragCube(int instanceNum)
{
Point	mousePt, newMousePt;
float	dx,dy;
float	oldRotX, oldRotY;
float	ndx, ndy;
float	zoomCoeff;
UInt32	modifiers;

#ifdef TARGET_OS_MAC
	GetMouse(&mousePt);
#else
	----- WINDOWS OR LINUX CODE GOES HERE -----
#endif		
	
	if (gMyInstances[instanceNum].autoPan)
	{
		gMyInstances[instanceNum].autoPan = false;				// as soon as user interacts, it kills any auto-panning
		gMyInstances[instanceNum].cubeRotDY *= .4;
	}

	while(Button())
	{
		oldRotX = gMyInstances[instanceNum].cubeRotX;
		oldRotY = gMyInstances[instanceNum].cubeRotY;
	
				/* DRAW SCENE */
				
		OGL_DrawScene(instanceNum);
		
				
				/* DO CUBE DRIFT / FRICTION */
					
		switch(gPrefs.dragFrictionMode)
		{
			case	TEXTURE_COMP_MODE_ALWAYS:
					UpdateCubeDrift(instanceNum, true);
					break;

			case	TEXTURE_COMP_MODE_NEVER:
					UpdateCubeDrift(instanceNum, false);
					break;

			default:
					UpdateCubeDrift(instanceNum, gMyInstances[instanceNum].doDragFriction);
					break;
		}


			/* GET MOUSE DELTAS FOR ROT DELTAS */
			
#ifdef TARGET_OS_MAC
		GetMouse(&newMousePt);
#else
		----- WINDOWS OR LINUX CODE GOES HERE -----
#endif	
	
		dx = newMousePt.h - mousePt.h;
		dy = newMousePt.v - mousePt.v;
		

				/***************/
				/* CALC DELTAS */
				/***************/

			/* CALC ZOOM COEFFICIENT SO ROTATE SENSITIVITY IS LESS AS WE ZOOM IN */
					
		zoomCoeff = 1.0f;

		if (gMyInstances[instanceNum].fov < DEFAULT_FOV)
		{				
			zoomCoeff -= (DEFAULT_FOV - gMyInstances[instanceNum].fov) * .011f;
			if (zoomCoeff < 0.01f)
				zoomCoeff = 0.01f;			
		}
						
						
					/* CALC IT */
					
		switch(gPrefs.dragFrictionMode)
		{
			case	TEXTURE_COMP_MODE_ALWAYS:
					break;

			case	TEXTURE_COMP_MODE_NEVER:								// if no drag friction, then lower the sensitivity
					zoomCoeff *= .2f;
					break;

			case	TEXTURE_COMP_MODE_HTML:
					if (!gMyInstances[instanceNum].doDragFriction)			// if no drag friction, then lower the sensitivity
						zoomCoeff *= .2f;
					break;
		}
					

		ndx = dy * MOUSE_SENSITIVITY * zoomCoeff;
		ndy = dx * MOUSE_SENSITIVITY * zoomCoeff;


					/* APPLY IT */
				
		gMyInstances[instanceNum].cubeRotDX += ndx;
		gMyInstances[instanceNum].cubeRotDY += ndy;

		mousePt = newMousePt;
		
		
				/* SEE IF ZOOM IN/OUT */
				
		gMyInstances[instanceNum].hiliteZoomIn = false;			// assume not
		gMyInstances[instanceNum].hiliteZoomOut = false;
				
		modifiers = GetCurrentEventKeyModifiers();
			
		if (modifiers & (1<<shiftKeyBit))						// shift == zoom in
		{
			gMyInstances[instanceNum].fov	-= gMyInstances[instanceNum].oneOverFPS * FOV_ZOOM_SPEED;
			if (gMyInstances[instanceNum].fov < MIN_FOV)
				gMyInstances[instanceNum].fov = MIN_FOV;
			gMyInstances[instanceNum].hiliteZoomIn = true;
		}
		else
		if (modifiers & (1<<controlKeyBit))							// ctrl == zoom out
		{
			gMyInstances[instanceNum].fov	+= gMyInstances[instanceNum].oneOverFPS * FOV_ZOOM_SPEED;
			if (gMyInstances[instanceNum].fov > MAX_FOV)
				gMyInstances[instanceNum].fov = MAX_FOV;
			gMyInstances[instanceNum].hiliteZoomOut = true;
		}

		
					/* MAINTENANCE */
			
#ifdef TARGET_OS_MAC
		{									
			EventRecord 	theEvent;		
			GetNextEvent(autoKeyMask, &theEvent);		// poll events to keep beachball from coming up
		}
#else
		----- WINDOWS OR LINUX CODE GOES HERE -----
#endif	
	}
	
	gMyInstances[instanceNum].hiliteZoomIn = false;	
	gMyInstances[instanceNum].hiliteZoomOut = false;
	
}


/******************* UPDATE CUBE DRIFT ***********************/
//
// This is the code that keeps the cube spinning for a second after the user stops manipulating it.
//

void UpdateCubeDrift(int	instanceNum, Boolean doFriction)
{
float	fps = gMyInstances[instanceNum].oneOverFPS;

	gMyInstances[instanceNum].cubeRotX += gMyInstances[instanceNum].cubeRotDX * fps;
	gMyInstances[instanceNum].cubeRotY += gMyInstances[instanceNum].cubeRotDY * fps;	
	
			/* PIN MIN/MAX TILT */
			
	if (gMyInstances[instanceNum].cubeRotX < gMyInstances[instanceNum].minRotX)
		gMyInstances[instanceNum].cubeRotX = gMyInstances[instanceNum].minRotX;
	else
	if (gMyInstances[instanceNum].cubeRotX > gMyInstances[instanceNum].maxRotX)
		gMyInstances[instanceNum].cubeRotX = gMyInstances[instanceNum].maxRotX;

	if (doFriction && (!gMyInstances[instanceNum].autoPan))
	{
		float	decay = 1.0 - (fps * DRIFT_DECAY_SPEED);

		if (decay < 0.0f)
			decay = 0;
	
		gMyInstances[instanceNum].cubeRotDX *= decay;
		gMyInstances[instanceNum].cubeRotDY *= decay;
	}
}



/******************* DRAW THE CUBE *************************/

void DrawTheCube(int instanceNum)
{	
int		i,p;
Boolean	np2 = gMyInstances[instanceNum].nonPower2;
static	const Point3D	coords[6][4] =
{
	-CUBE_SIZE/2,  CUBE_SIZE/2, -CUBE_SIZE/2,			// back
	-CUBE_SIZE/2, -CUBE_SIZE/2, -CUBE_SIZE/2,
	CUBE_SIZE/2,  -CUBE_SIZE/2, -CUBE_SIZE/2,
	CUBE_SIZE/2,  CUBE_SIZE/2,  -CUBE_SIZE/2,
	
	CUBE_SIZE/2,  CUBE_SIZE/2, -CUBE_SIZE/2,			// right
	CUBE_SIZE/2, -CUBE_SIZE/2, -CUBE_SIZE/2,
	CUBE_SIZE/2,  -CUBE_SIZE/2, CUBE_SIZE/2,
	CUBE_SIZE/2,  CUBE_SIZE/2,  CUBE_SIZE/2,	

	-CUBE_SIZE/2,  CUBE_SIZE/2, CUBE_SIZE/2,			// front
	-CUBE_SIZE/2, -CUBE_SIZE/2, CUBE_SIZE/2,
	CUBE_SIZE/2,  -CUBE_SIZE/2, CUBE_SIZE/2,
	CUBE_SIZE/2,  CUBE_SIZE/2,  CUBE_SIZE/2,	

	-CUBE_SIZE/2,  CUBE_SIZE/2, -CUBE_SIZE/2,			// left
	-CUBE_SIZE/2, -CUBE_SIZE/2, -CUBE_SIZE/2,
	-CUBE_SIZE/2,  -CUBE_SIZE/2, CUBE_SIZE/2,
	-CUBE_SIZE/2,  CUBE_SIZE/2,  CUBE_SIZE/2,	

	-CUBE_SIZE/2,  CUBE_SIZE/2, -CUBE_SIZE/2,			// top
	-CUBE_SIZE/2,  CUBE_SIZE/2, CUBE_SIZE/2,
	CUBE_SIZE/2,  CUBE_SIZE/2, CUBE_SIZE/2,
	CUBE_SIZE/2,  CUBE_SIZE/2,  -CUBE_SIZE/2,	

	-CUBE_SIZE/2,  -CUBE_SIZE/2, -CUBE_SIZE/2,			// bottom
	-CUBE_SIZE/2,  -CUBE_SIZE/2, CUBE_SIZE/2,
	CUBE_SIZE/2,  -CUBE_SIZE/2, CUBE_SIZE/2,
	CUBE_SIZE/2,  -CUBE_SIZE/2,  -CUBE_SIZE/2
};

static const TextureUV	uvs[6][4] =
{
	0,0,	0,1,	1,1,	1,0,								// back
	0,0,	0,1,	1,1,	1,0,								// right
	1,0,	1,1,	0,1,	0,0,								// front
	1,0,	1,1,	0,1,	0,0,								// left
	0,1,	0,0,	1,0,	1,1,								// top
	0,0,	0,1,	1,1,	1,0									// bottom
};

	glDisable(GL_BLEND);	

	glRotatef(gMyInstances[instanceNum].cubeRotX, 1, 0, 0);
	glRotatef(gMyInstances[instanceNum].cubeRotY, 0, 1, 0);

	if (gMyInstances[instanceNum].texturesLoaded) 
		glEnable(np2 ? GL_TEXTURE_RECTANGLE_EXT : GL_TEXTURE_2D);
	else
		return;	
			
					/* FADE IN CUBE */
					
	gMyInstances[instanceNum].cubeFade += gMyInstances[instanceNum].oneOverFPS * CUBE_FADE_SPEED;
	if (gMyInstances[instanceNum].cubeFade > 1.0f)
	{
		gMyInstances[instanceNum].cubeFade = 1.0f;
	}

	if (gMyInstances[instanceNum].cubeFade > .3f)
	{
		gMyInstances[instanceNum].loadingFade -= gMyInstances[instanceNum].oneOverFPS * 1.9;
		if (gMyInstances[instanceNum].loadingFade < 0.0f)
			gMyInstances[instanceNum].loadingFade = 0.0f;
	}
	
	glColor3f(gMyInstances[instanceNum].cubeFade,gMyInstances[instanceNum].cubeFade,gMyInstances[instanceNum].cubeFade);


				/* DRAW CUBE FACES */
				
	for (i = 0; i < 6; i++)
	{
        glBindTexture (np2 ? GL_TEXTURE_RECTANGLE_EXT : GL_TEXTURE_2D,	gMyInstances[instanceNum].textureNames[i]);
				
		glBegin(GL_QUADS);
		for (p = 0; p < 4; p++)
		{
			GLfloat	u,v;
			
			u = uvs[i][p].u;
			v = uvs[i][p].v;
			if (np2)
			{
				u *= gMyInstances[instanceNum].textureWidth;
				v *= gMyInstances[instanceNum].textureHeight;
			}
			
			glTexCoord2f(u, v);
			glVertex3fv((GLfloat *)&coords[i][p]);
		}
		glEnd();	
	}
	

}





