/****************************/
/*			SPHERE.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;
extern	short			gMovfRefNum;
extern	Ptr				gTempImageBuffer;
extern	Rect			gTempImageRect;

#ifdef TARGET_OS_MAC
extern	GWorldPtr			gTempImageGWorld;
#endif

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

static void DrawSphereSlice(int	instanceNum, double theta1,double theta2,double phi1,double phi2);
static void ChopEquirectangularIntoTextures(int instanceNum);


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



#define	SPHERE_RADIUS			500.0

#define	SPHERE_REZ				40


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


/******************* DRAW SPHERE SLICE **************************/
//
//  Draws a "slice" of a sphere.  
//
//  Partial spheres are created using theta1->theta2, phi1->phi2 in radians 0 < theta < 2pi, -pi/2 < phi < pi/2
//

static void DrawSphereSlice(int	instanceNum, double theta1,double theta2,double phi1,double phi2)
{
int			i,j;
double		t1,t2,t3;
Point3D		p;
const double		r = SPHERE_RADIUS;
const double		n = SPHERE_REZ;
float				u,v;

	for (j=0; j<n/2; j++)
	{
		t1 = phi1 + j * (phi2 - phi1) / (n/2);
		t2 = phi1 + (j + 1) * (phi2 - phi1) / (n/2);

		glBegin(GL_TRIANGLE_STRIP);

		for (i=0;i<=n;i++)
		{
			t3 = theta1 + i * (theta2 - theta1) / n;

			p.x = cos(t1) * cos(t3) * r;
			p.y = sin(t1) * r;
			p.z = cos(t1) * sin(t3) * r;
			
			u = i / (double)n;
			v = 1.0 - 2 * j / (double)n;			
			if (gMyInstances[instanceNum].nonPower2)
			{
				u *= gMyInstances[instanceNum].textureWidth;
				v *= gMyInstances[instanceNum].textureHeight;
			}
	
			glTexCoord2f(u, v);
			glVertex3f(p.x,p.y,p.z);

			p.x = cos(t2) * cos(t3) * r;
			p.y = sin(t2) * r;
			p.z = cos(t2) * sin(t3) * r;
			
			u = i / (double)n;
			v = 1.0 - 2*(j+1)/(double)n;
			if (gMyInstances[instanceNum].nonPower2)
			{
				u *= gMyInstances[instanceNum].textureWidth;
				v *= gMyInstances[instanceNum].textureHeight;
			}
						
			glTexCoord2f(u, v);
			glVertex3f(p.x,p.y,p.z);
		}
		
		glEnd();
	}
}


/******************* DRAW THE SPHERE *************************/

void DrawTheSphere(int instanceNum)
{	
int		i;
double	sliceTheta[8][4] =
{
	0, PI/2, 0, PI/2,
	PI/2, PI/2 * 2, 0, PI/2,
	PI/2*2, PI/2*3, 0, PI/2,
	PI/2*3, PI/2*4, 0, PI/2,
	0, PI/2, -PI/2, 0,
	PI/2, PI/2 * 2, -PI/2, 0,
	PI/2 * 2, PI/2 *3, -PI/2, 0,
	PI/2 * 3, PI/2 *4, -PI/2, 0,
};

	glDisable(GL_BLEND);	

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

	if (!gMyInstances[instanceNum].texturesLoaded) 
		return;
	
	glEnable(gMyInstances[instanceNum].nonPower2 ? GL_TEXTURE_RECTANGLE_EXT : GL_TEXTURE_2D);	
		
	
					/* FADE IN */
					
	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 THE QUAD SECTIONS */
			/**************************/
			//
			// We draw the sphere in 8 sections (4 on each top/bottom hemisphere) because
			// each has a different texturemap. 
			//
			
			
	for (i = 0; i < 8; i++)
	{
        glBindTexture (gMyInstances[instanceNum].nonPower2 ? GL_TEXTURE_RECTANGLE_EXT : GL_TEXTURE_2D,
						gMyInstances[instanceNum].textureNames[i]);

		DrawSphereSlice(instanceNum, sliceTheta[i][0], sliceTheta[i][1], sliceTheta[i][2], sliceTheta[i][3]);
	
	}
}


#pragma mark -


/******************* LOAD EQUIRECTANGULAR IMAGE *****************************/
//
// If the streamed file is a JPEG then this gets called.  Here we load the JPEG and
// chop it up into textures that we'll use when drawing our sphere.
//

void LoadEquirectangularImage(int instanceNum)
{
Ptr		jpegData;
OSErr	iErr;
long	count = gMyInstances[instanceNum].srcFileSize;


				/* SEE WHAT SIZE TEXTURE THE HARDWARE SUPPORTS */
				//
				// Note:  we do this here because when we get here we know that we
				//		have an OpenGL context and we know if we're doing a cube or sphere.
				//
				
	CalcMaxTextureSizeForHardware(instanceNum);




			/* ALLOCATE BUFFER TO HOLD JPEG DATA */
			
	jpegData = malloc(count);


			/*****************/
			/* OPEN THE FILE */
			/*****************/
	
#ifdef TARGET_OS_MAC
	iErr = FSpOpenDF(&gMyInstances[instanceNum].dataFileSpec,fsRdPerm, &gMovfRefNum);
	if (iErr)
	{
		printf("LoadEquirectangularImage:  FSpOpenDF failed!  %d\n", iErr);
		return;
	}


				/**********************/
				/* READ THE JPEG DATA */
				/**********************/

	iErr = FSRead(gMovfRefNum, &count, jpegData);
	if (iErr)
	{
		printf("LoadEquirectangularImage:  FSRead failed!  %d\n", iErr);
		return;
	}	

	FSClose(gMovfRefNum);						


			/*************************/
			/* DRAW JPEG INTO BUFFER */
			/*************************/

	iErr = DrawJPEGFromRAMIntoBuffer_Mac(jpegData, count, &gTempImageGWorld, &gTempImageBuffer);
	if (iErr)
	{
		printf("LoadEquirectangularImage:  DrawJPEGFromRAMIntoBuffer failed!\n"); 
		return;
	}


#else
	
	/* Note for PC and Linux programmers:  The pathname specified below comes from the Browser.  It's probably a full path, and I don't
	   know how fopen() responds to full paths by default.  You should test this and be sure that fopen is successful.  If not, 
	   then you'll have to do whatever you normally do on Windows to make paths work
	*/

	gFile = fopen (gTheDataFilePath, "wb");
	if  (!gFile)           
	{
		printf("fopen() failed!\n");
		return;
	}
	
	fread(jpegData, count, 1, gFile);		
	if (ferror(gFile))
	{
		printf("LoadEquirectangularImage:  fread() failed!\n");
		return;
	}	
	
	fclose(gFile);
	
	iErr = DrawJPEGFromRAMIntoBuffer_Windows(jpegData, count, &gTempImageBuffer);
	if (iErr)
	{
		printf("LoadEquirectangularImage:  DrawJPEGFromRAMIntoBuffer_Windows failed!\n"); 
		return;
	}
	
			
#endif		


			/***********************************/
			/* CHOP UP THE IMAGE INTO TEXTURES */
			/***********************************/

	ChopEquirectangularIntoTextures(instanceNum);


				/* FREE THE MASTER IMAGE */
				
	free(jpegData);

}



/******************* CHOP EQUIRECTANGULAR INTO TEXTURES ************************/
//
// HI-rez equirectangular images are too big to fit into a single OpenGL texture since
// most video cards have a max texture size of 2048x2048.  We always chop up the texture into 
// 8 segments:  4 for the top hemisphere, and 4 for the bottom hemisphere.
//
// The eq image is currently in gTempImageBuffer
//

static void ChopEquirectangularIntoTextures(int instanceNum)
{
Rect		r, srcRect;
OSErr		iErr;
int			row,col,i;
int			sectionWidth, sectionHeight;
GLint		textureName;
#ifdef TARGET_OS_MAC
GWorldPtr	gworld;
#endif
Ptr			buff;

				/* CALC SIZE OF CHOP SECTIONS TO EXTRACT */
				
	sectionWidth = gTempImageRect.right / 4;
	sectionHeight = gTempImageRect.bottom / 2;
	
	printf("section size = %d x %d\n", sectionWidth, sectionHeight);


				/* CALC SIZE TO RE-SIZE THOSE CHOPPED SECTIONS TO */
				
	r.left = r.top = 0;
	r.right = sectionWidth;
	r.bottom = sectionHeight;
	CalcAllowedTextureSize(instanceNum, &r, &r);
		

				/**************************************/
				/* CHOP THE BIG IMAGE INTO 8 SECTIONS */
				/**************************************/

	i = 0;
	for (row = 0; row < 2; row++)
	{
		for (col = 0; col < 4; col++)
		{		
			gMyInstances[instanceNum].decodeStep = i;					// tell thermometer what to display
			OGL_DrawScene(instanceNum);
			
					
						/* ALLOCATE BUFFER FOR TEXTURE */
						
			buff = malloc((int)r.right * (int)r.bottom * 4);
			if (buff == nil)
			{
				printf("ChopEquirectangularIntoTextures: malloc failed!\n");
				return;
			}
			gMyInstances[instanceNum].textureBuffers[i] = buff;
			 
			
#ifdef TARGET_OS_MAC
			
						/* CREATE GWORLD FROM BUFFER */
			
			iErr = QTNewGWorldFromPtr(&gworld, k32ARGBPixelFormat, &r, nil, nil, 0,	buff, (int)r.right * 4);
			if (iErr)
			{
				printf("ChopEquirectangularIntoTextures: NewGWorld failed.  %d\n", iErr);
				return;
			}
			

						/* COPY SECTION INTO NEW BUFFER */
						
			srcRect.left	= sectionWidth * col;							// set rect of section to extract from master image
			srcRect.right	= srcRect.left + sectionWidth;
			srcRect.top		= sectionHeight * row;
			srcRect.bottom	= srcRect.top + sectionHeight;
						
			CopyPixelsFromBufferToBuffer_Mac(gTempImageGWorld, gworld, &srcRect, &r);	// copy into new buffer
			
			DisposeGWorld(gworld);											// dont need the gworld wrapper anymore
#else

		---- WINDOWS / LINUX CODE GOES HERE ----

#endif			
			
						/* CONVERT BUFFER TO OPENGL TEXTURE */
			
			textureName = MakeTextureFromBuffer(gMyInstances[instanceNum].nonPower2,
												buff, r.right, r.bottom, false,
												gMyInstances[instanceNum].compressTextures);
			
			gMyInstances[instanceNum].textureNames[i] = textureName;
			
			i++;
		}
	}



	gMyInstances[instanceNum].drawDecodingBar = false;

	gMyInstances[instanceNum].texturesLoaded = true;


}













