/****************************/
/*			TEXTURE.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;

#ifdef TARGET_OS_MAC
extern	FSSpec		gMyResourcesFolderFSSpec;
extern	GWorldPtr			gTempImageGWorld;
#endif


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

static void ConvertBufferFromARGBtoRGBA(u_long *buffer, int width, int height);
static int GetDisplayVRAM(void);
static void CalcMaxTextureSizeForHardware_Power2(int instanceNum, int vram, int *maxSizeVRAMH, int *maxSizeVRAMV);
static void CalcMaxTextureSizeForHardware_NonPower2(int instanceNum, int vram, int *maxSizeVRAMH, int *maxSizeVRAMV);
static void MakeBlackPixelsTransparent(u_long *pixels, int width, int height);


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


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

Ptr				gTempImageBuffer = nil;
Rect			gTempImageRect;




#pragma mark -


/********************* CALC MAX TEXTURE SIZE FOR HARDWARE ****************************/
//
// This code determines what the hardware specs are that the user has running.  The more
// VRAM there is, then the larger the textures can be.  But it also checks for the
// hardware's physical texture size limit, and also checks if hardware compression is
// supported.  
//

void CalcMaxTextureSizeForHardware(int instanceNum)
{
GLint	maxSizeHW;
int		vram, maxSizeVRAMH, maxSizeVRAMV, j;
static char			*s;


	s = (char *)glGetString(GL_EXTENSIONS);								// get extensions list

 		/**********************************************/
		/* SEE IF WE HAVE TEXTURE COMPRESSION SUPPORT */
 		/**********************************************/

	gMyInstances[instanceNum].compressTextures = false;					// assume no

	if (gPrefs.compressionMode != DRAGFRIC_MODE_NEVER)					// see if user forbids texture compression
	{
		if ((!gMyInstances[instanceNum].disableCompression)	|| (gPrefs.compressionMode == DRAGFRIC_MODE_ALWAYS))		// see want to try
		{
						
			if (strstr(s, "GL_ARB_texture_compression"))
			{
				GLint	*compFormats;
				GLint   numCompFormats;

//				printf("GL_ARB_texture_compression extension found\n");
				
					/* DO WE SUPPORT THE COMPRESSION METHOD THAT WE USE? */

				glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB, &numCompFormats);
				compFormats = malloc(numCompFormats * sizeof(GLint));
				glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS_ARB, compFormats);  
				
				for (j = 0; j < numCompFormats; j++)
				{
					if (compFormats[j] == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT)
					{
//						printf("We have the GL_COMPRESSED_RGBA_S3TC_DXT3_EXT compression format\n");		
						gMyInstances[instanceNum].compressTextures  = true;
						break;
					}
				}		
				
				free(compFormats);
			}
		}
	}
	
		/*************************************************/
		/* SEE IF WE HAVE NON-POWER OF 2 TEXTURE SUPPORT */
		/*************************************************/
		//
		// NOTE:   the OpenGL docs state that texture compression may not be compatible with non-power
		//			of 2 textures.  My test with a Radeon 9800 show that it works, but the textures are
		//			highly distorted.  So, we only allow non-p-2 textures if there's no compression.
		//

	gMyInstances[instanceNum].nonPower2 = false;					// assume no

	if (gPrefs.compressionMode != TEXTURE_COMP_MODE_ALWAYS)			// only allow non-power of 2 if no compression
	{
		if ((!gMyInstances[instanceNum].compressTextures) || gMyInstances[instanceNum].disableCompression ||
			(gPrefs.compressionMode == TEXTURE_COMP_MODE_NEVER))	
		{
			if (strstr(s, "GL_EXT_texture_rectangle"))
			{
				printf("GL_EXT_texture_rectangle extension found\n");
				gMyInstances[instanceNum].nonPower2 = true;	
			}
		}
	}

			/******************************************/
			/* SEE WHAT SIZE TEXTURES OPENGL SUPPORTS */
			/******************************************/
			
	if (gMyInstances[instanceNum].nonPower2)
		glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_EXT, &maxSizeHW);
	else
		glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSizeHW);

	vram = GetDisplayVRAM();


	if (gMyInstances[instanceNum].nonPower2)
		CalcMaxTextureSizeForHardware_NonPower2(instanceNum, vram, &maxSizeVRAMH, &maxSizeVRAMV);
	else
		CalcMaxTextureSizeForHardware_Power2(instanceNum, vram, &maxSizeVRAMH, &maxSizeVRAMV);


	
			/**************************/
			/* WHICH SIZE IS SMALLER? */
			/**************************/
			//
			// Make sure that the amount we can fit into VRAM
			// isn't larger than what the hardware maximum size
			// limit is.
			//
			
	if (maxSizeHW < maxSizeVRAMH)
		gMyInstances[instanceNum].maxTextureSizeH = maxSizeHW;
	else
		gMyInstances[instanceNum].maxTextureSizeH = maxSizeVRAMH;

	if (maxSizeHW < maxSizeVRAMV)
		gMyInstances[instanceNum].maxTextureSizeV = maxSizeHW;
	else
		gMyInstances[instanceNum].maxTextureSizeV = maxSizeVRAMV;


	printf("Max texture size we can afford = %d x %d\n", gMyInstances[instanceNum].maxTextureSizeH, gMyInstances[instanceNum].maxTextureSizeV);
}


/************ CALC MAX TEXTURE SIZE FOR HARDWARE : POWER OF 2 *******************/

static void CalcMaxTextureSizeForHardware_Power2(int instanceNum, int vram, int *maxSizeVRAMH, int *maxSizeVRAMV)
{

			/****************************************/
			/* CALC SIZE FOR SPHERE WITH 8 TEXTURES */
			/****************************************/
			//
			// Textures need to be smaller sometimes when we're doing a sphere
			// because we need 8 textures instead of 6, so they've all got to fit.
			//
			
	if (gMyInstances[instanceNum].doEquirectangular)
	{
				/* CALC MAX SIZE WITH COMPRESSION */
					
		if (gMyInstances[instanceNum].compressTextures)
		{
			if (vram > 0x8000000)								// >128MB
				*maxSizeVRAMH = *maxSizeVRAMV = 4096;
			else
			if (vram == 0x8000000)								// 128MB
			{
				*maxSizeVRAMH = 4096;
				*maxSizeVRAMV = 2048;
			}
			else
			if (vram >= 0x4000000)								// 64MB
				*maxSizeVRAMH = *maxSizeVRAMV = 2048;
			else
			if (vram >= 0x2000000)								// 32MB
			{
				*maxSizeVRAMH = 2048;
				*maxSizeVRAMV = 1024;
			}
			else
				*maxSizeVRAMH = *maxSizeVRAMV = 512;
		}
		
				/* CALC MAX SIZE WITHOUT COMPRESSION */
		else
		{
			
			if (vram > 0x8000000)								// >128MB
				*maxSizeVRAMH = *maxSizeVRAMV = 2048;
			else
			if (vram == 0x8000000)								// 128MB
			{
				*maxSizeVRAMH = 2048;
				*maxSizeVRAMV = 1024;
			}
			else
			if (vram >= 0x4000000)								// 64MB
				*maxSizeVRAMH = *maxSizeVRAMV = 1024;
			else
			if (vram >= 0x2000000)								// 32MB
			{
				*maxSizeVRAMH = 1024;
				*maxSizeVRAMV = 512;
			}
			else
			{
				*maxSizeVRAMH = 512;
				*maxSizeVRAMV = 256;
			}
		}
	}

			/**************************************/
			/* CALC SIZE FOR CUBE WITH 6 TEXTURES */
			/**************************************/

			/* CALC MAX SIZE WITH COMPRESSION */
				
	else		
	{	
		if (gMyInstances[instanceNum].compressTextures)
		{
			if (vram >= 0x8000000)								// 128MB+
				*maxSizeVRAMH = *maxSizeVRAMV = 4096;
			else
			if (vram >= 0x4000000)								// 64MB
				*maxSizeVRAMH = *maxSizeVRAMV = 2048;
			else
			if (vram >= 0x2000000)								// 32MB
			{
				*maxSizeVRAMH = 2048;
				*maxSizeVRAMV = 1024;
			}
			else
				*maxSizeVRAMH = *maxSizeVRAMV = 512;
		}
		
				/* CALC MAX SIZE WITHOUT COMPRESSION */
		else
		{
			
			if (vram >= 0x8000000)								// 128MB+
				*maxSizeVRAMH = *maxSizeVRAMV = 2048;
			else
			if (vram >= 0x4000000)								// 64MB
			{
				*maxSizeVRAMH = 2048;
				*maxSizeVRAMV = 1024;
			}
			else
			if (vram >= 0x2000000)								// 32MB
			{
				*maxSizeVRAMH = 1024;
				*maxSizeVRAMV = 512;
			}
			else
			{
				*maxSizeVRAMH = 512;
				*maxSizeVRAMV = 256;
			}
		}
	}
}


/************ CALC MAX TEXTURE SIZE FOR HARDWARE : NON POWER OF 2 *******************/

static void CalcMaxTextureSizeForHardware_NonPower2(int instanceNum, int vram, int *maxSizeVRAMH, int *maxSizeVRAMV)
{

			/****************************************/
			/* CALC SIZE FOR SPHERE WITH 8 TEXTURES */
			/****************************************/
			//
			// Textures need to be smaller sometimes when we're doing a sphere
			// because we need 8 textures instead of 6, so they've all got to fit.
			//
			
	if (gMyInstances[instanceNum].doEquirectangular)
	{
		if (vram >= 0x10000000)								// >256MB
		{
			*maxSizeVRAMH = 3500;
			*maxSizeVRAMV = 2048;
		}
		else
		if (vram >= 0x8000000)								// 128MB
		{
			*maxSizeVRAMH = 2048;
			*maxSizeVRAMV = 1700;
		}
		else
		if (vram >= 0x4000000)								// 64MB
		{
			*maxSizeVRAMH = 1600;
			*maxSizeVRAMV = 1024;
		}
		else
		if (vram >= 0x2000000)								// 32MB
		{
			*maxSizeVRAMH = 1024;
			*maxSizeVRAMV = 512;
		}
		else
		{
			*maxSizeVRAMH = 512;
			*maxSizeVRAMV = 256;
		}
	}

			/**************************************/
			/* CALC SIZE FOR CUBE WITH 6 TEXTURES */
			/**************************************/
				
	else		
	{	
		if (vram >= 0x8000000)								// 128MB+
		{
			*maxSizeVRAMH = 2048;
			*maxSizeVRAMV = 2048;
		}
		else
		if (vram >= 0x4000000)								// 64MB
		{
			*maxSizeVRAMH = 2048;
			*maxSizeVRAMV = 1024;
		}
		else
		if (vram >= 0x2000000)								// 32MB
		{
			*maxSizeVRAMH = 1024;
			*maxSizeVRAMV = 900;
		}
		else
		{
			*maxSizeVRAMH = 512;
			*maxSizeVRAMV = 256;
		}
	}
}




/******************** GET DISPLAY VRAM ***********************/
//
// Check to see how much VRAM is on the main device.
//

static int GetDisplayVRAM(void)
{
long			vram;

#ifdef TARGET_OS_MAC

	io_service_t	port;
	CFTypeRef		classCode;
	static	CGDirectDisplayID	displayID;

	displayID = CGMainDisplayID();						// get Main Device

	port = CGDisplayIOServicePort(displayID);
	classCode = IORegistryEntryCreateCFProperty(port, CFSTR(kIOFBMemorySizeKey), kCFAllocatorDefault, kNilOptions);
	
	if (CFGetTypeID(classCode) == CFNumberGetTypeID())
		CFNumberGetValue(classCode, kCFNumberSInt32Type, &vram);
	else
		vram = 0x2000000;								// if failed, then just assume 32MB VRAM to be safe
	

#else

	----- WINDOWS OR LINUX CODE GOES HERE -----

#endif


	printf("Amount of VRAM = 0x%x\n", (int)vram);
	
	return(vram);
}


/********************* CALC ALLOWED TEXTURE SIZE **************************/
//
// This is called when a cube face texture has been loaded and we need to see what
// size we want to re-scale it to.
//
// INPUT:  sourceSize = rect of the loaded image / JPEG
//
// OUTPUT:	allowedSize = rect of the size to scale it to.
//

void	CalcAllowedTextureSize(int instanceNum, Rect *sourceSize, Rect *allowedSize)
{
int		sourceWidth = sourceSize->right;
int		sourceHeight = sourceSize->bottom;
int		allowedHeight, allowedWidth;



			/* IS THE SOURCE IMAGE WIDTH LARGER THAN WHAT WE ALLOW? */
			
	if (sourceWidth > gMyInstances[instanceNum].maxTextureSizeH)
	{
		allowedWidth = gMyInstances[instanceNum].maxTextureSizeH;	// yes, so just set allowed size to our max allowed size
	}
	else															// nope, texture is smaller than we can afford
	{
		if (gMyInstances[instanceNum].nonPower2)					// if we're using non-power-2 textures, then just leave size as-is
		{
			allowedWidth = sourceWidth;
		}
		else														// otherwise, round to best power-2 texture
		{
			if (sourceWidth <= 256)
				allowedWidth = 256;
			else
			if (sourceWidth <= 512)
				allowedWidth = 512;
			else
			if (sourceWidth <= 1024)
				allowedWidth = 1024;
			else
			if (sourceWidth <= 2048)
				allowedWidth = 2048;
			else
				allowedWidth = 4096;
		}
	}


			/* IS THE SOURCE IMAGE HEIGHT LARGER THAN WHAT WE ALLOW? */


	if (sourceHeight > gMyInstances[instanceNum].maxTextureSizeV)
		allowedHeight = gMyInstances[instanceNum].maxTextureSizeV;
	else															// nope, texture is smaller than we can afford
	{
		if (gMyInstances[instanceNum].nonPower2)					// if we're using non-power-2 textures, then just leave size as-is
		{
			allowedHeight = sourceHeight;
		}
		else														// otherwise, round to best power-2 texture
		{
		
			if (sourceHeight <= 256)
				allowedHeight = 256;
			else
			if (sourceHeight <= 512)
				allowedHeight = 512;
			else
			if (sourceHeight <= 1024)
				allowedHeight = 1024;
			else
			if (sourceHeight <= 2048)
				allowedHeight = 2048;
			else
				allowedHeight = 4096;
		}
	}

		

	allowedSize->left = 0;
	allowedSize->right = allowedWidth;
	allowedSize->top = 0;
	allowedSize->bottom = allowedHeight;

	gMyInstances[instanceNum].textureWidth = allowedWidth;
	gMyInstances[instanceNum].textureHeight = allowedHeight;

	printf("Texture being converted to %d x %d\n", allowedWidth, allowedHeight);
//	printf("VRAM used = %d\n", allowedWidth*allowedWidth*4);

}

#pragma mark -

	
	
/********************** MAKE TEXTURE FROM BUFFER ****************************/
//
// Takes the input buffer and creates an OpenGL texture from it.
//
// INPUT:	buffer = ptr to the pixel buffer.
//			width/height = dimensions of the buffer
//			hasAlpha = true if we want to use the alpha bits
//			compressIt = true if want to do hardware compression.
//

GLint MakeTextureFromBuffer(Boolean nonPower2, Ptr buffer, int width, int height, Boolean hasAlpha, Boolean compressIt)
{
#pragma unused (hasAlpha)
GLint		textureName, internalFormat;
	
				/************************************/
				/* CONVERT THE IMAGE INTO A TEXTURE */
				/************************************/
	
			/* GET A UNIQUE TEXTURE NAME & INITIALIZE IT */

	glGenTextures(1, &textureName);		

	glBindTexture (nonPower2 ? GL_TEXTURE_RECTANGLE_EXT : GL_TEXTURE_2D,	// this is now the currently active texture
					textureName);
	
	
	glTexParameterf(nonPower2 ? GL_TEXTURE_RECTANGLE_EXT : GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameterf(nonPower2 ? GL_TEXTURE_RECTANGLE_EXT : GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			
	
	glTexParameterf(nonPower2 ? GL_TEXTURE_RECTANGLE_EXT : GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);	// clamp the edges
	glTexParameterf(nonPower2 ? GL_TEXTURE_RECTANGLE_EXT : GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);	
	
	
	glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, 1);		// TELL OPENGL TO USE OUR COPY OF IT (NO DUPLICATE IN OGL)
	
					/**********************************/
					/* CREATE AN UNCOMPRESSED TEXTURE */
					/**********************************/
					
	if (!compressIt)
	{
		glTexImage2D(nonPower2 ? GL_TEXTURE_RECTANGLE_EXT : 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
						buffer);								// pointer to the actual texture pixels

	}
	
					/*******************************/
					/* CREATE A COMPRESSED TEXTURE */
					/*******************************/
	
	else
	{
		ConvertBufferFromARGBtoRGBA((u_long *)buffer, width, height);		// convert source to RGBA format

		
					/* CHOOSE A COMPRESSION FORMAT */
					//
					// Important!  There seems to be a bug when using GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
					// which would cause some 2048x2048 textures to appear with a black grid pattern on them.
					//
					
//		if (hasAlpha)											// alpha textures need a different compression format
//			internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
//		else
			internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
		

					/* CREATE COMPRESSED TEXTURE */
					
		glTexImage2D(nonPower2 ? GL_TEXTURE_RECTANGLE_EXT : GL_TEXTURE_2D,
						0,										// mipmap level
						internalFormat,							// format in OpenGL
						width,									// width in pixels
						height,									// height in pixels
						0,										// border
						GL_RGBA,								// what my format is
						GL_UNSIGNED_BYTE,						// size of each r,g,b
						buffer);								// pointer to the actual texture pixels


	}

	return(textureName);
}


/********************* CONVERT BUFFER FROM ARGB TO RGBA ********************************/
//
// Converts the pixel buffer from Mac-happy ARGB to OpenGL-friendly RGBA
//

static void ConvertBufferFromARGBtoRGBA(u_long *buffer, int width, int height)
{
int		x,y;
u_long	pixel, a;

	for (y = 0; y < height; y++)
	{
		for (x = 0; x < width; x++)
		{
			pixel = *buffer;
			
			a = (pixel & 0xff000000) >> 24;
			pixel <<= 8;
			pixel &= 0xffffff00;
			pixel |= a;
		
			*buffer = pixel;
			buffer++;
		}
	}
}


#pragma mark -

/******************* LOAD SPRITES FOR INSTANCE ***********************/
//
// Loads all the sprites needed for this instance and converts them to OpenGL textures.
//

void LoadSpritesForInstance(int i)
{
OSErr		iErr;
short		j;
const char	*filenames[NUM_SPRITES] =
{
	"ui_bar.tif",
	"zoomin_off.tif",
	"zoomin_on.tif",
	"zoomout_off.tif",
	"zoomout_on.tif",
	"minimize.tif",
	"maximize.tif",
	"logo.tif",
	"prefs_off.tif",
	"prefs_on.tif",
};

	for (j = 0; j < NUM_SPRITES; j++)
	{
#ifdef TARGET_OS_MAC
		c2pstrcpy(gMyResourcesFolderFSSpec.name, filenames[j]);
		
		iErr = FSMakeFSSpec(gMyResourcesFolderFSSpec.vRefNum,				// do this just to verify the file exists
					gMyResourcesFolderFSSpec.parID,
					gMyResourcesFolderFSSpec.name,				
					&gMyResourcesFolderFSSpec);
		if (iErr)
		{
			printf("LoadSpritesForInstance: file not found %d - ", iErr);
			printf(filenames[j]);
			printf("\n");
			continue;
		}
	
		iErr =  DrawTIFFIntoBuffer_Mac(&gMyResourcesFolderFSSpec, &gMyInstances[i].sprites[j].buffer);
		if (iErr)
			printf("LoadSpritesForInstance: DrawTIFFIntoBuffer failed\n");

#else
		iErr =  DrawTIFFIntoBuffer_Windows(&filenames[j], &gMyInstances[i].sprites[j].buffer);
		if (iErr)
			printf("LoadSpritesForInstance: DrawTIFFIntoBuffer failed\n");
#endif		


		gMyInstances[i].sprites[j].width = gTempImageRect.right;
		gMyInstances[i].sprites[j].height = gTempImageRect.bottom;


		gMyInstances[i].sprites[j].aspectRatio = (float)gMyInstances[i].sprites[j].height /
													(float)gMyInstances[i].sprites[j].width;

		gMyInstances[i].sprites[j].textureName = MakeTextureFromBuffer(false,
																		gMyInstances[i].sprites[j].buffer,
																		gMyInstances[i].sprites[j].width,
																		gMyInstances[i].sprites[j].height,
																		true,
																		gMyInstances[i].compressTextures);

	}			

}


#pragma mark -



/**************************** LOAD THUMBNAIL **********************************/

void LoadThumbnail(int instanceNum, const char *urlPath)
{
int		urlLen, j, q;
char	thumbURL[1024];
Handle	jpegData;

	if (gMyInstances[instanceNum].thumbnailLoaded)							// check if thumbnail already loaded
		return;

	if (urlPath[0] == 0x00)													// if no URL path specified yet, then bail
		return;

	if (!gMyInstances[instanceNum].drawContext)								// there must be a draw context to show thumbnails!
	{
		printf("Cannot show thumbnail because OpenGL draw context not created yet\n");
		return;
	}
	
	printf("LOADING THUMBNAIL-------\n");
	
			/********************************************/
			/* CREATE URL STRING FOR THE THUMBNAIL FILE */
			/********************************************/

			/* FIND LAST "/" IN URL */
	
	urlLen = strlen(urlPath);										// get length of URL string	
	while(urlPath[urlLen] != '/')									// are we at the / ?
	{
		urlLen--;
		if (urlLen < 0)											// something went wrong
		{
			printf("NPP_NewStream: no / in thumbnail URL\n");
fail:
			gMyInstances[instanceNum].thumbnailFilename[0] = 0x00;		// nix the url so that we don't try to load the file ever again
			return;
		}
	}
	
		/* COPY PATH TO NEW STRING */
		
	for (j = 0; j <= urlLen; j++)
		thumbURL[j] = urlPath[j];
		
		/* APPEND THE THUMBNAIL FILENAME */
				
	q = 0;
	while(gMyInstances[instanceNum].thumbnailFilename[q] != 0x00)
		thumbURL[j++] = gMyInstances[instanceNum].thumbnailFilename[q++];
	
	thumbURL[j] = 0x00;
	
	printf("URL to thumbnail = ");
	printf(thumbURL);
	printf("\n");
	

				/*******************************/
				/* DOWNLOAD THE THUMBNAIL FILE */
				/*******************************/

#ifdef TARGET_OS_MAC				
				
				/* ALLOCATE HANDLE TO RECIEVE THE JPEG DATA */
				
	jpegData = NewHandle(1);									// this gets resized by URLSimpleDownload()	
	
				/* DOWNLOAD IT */
				
	if (DownloadURL(thumbURL, jpegData))
	{
		DisposeHandle(jpegData);
		printf("Error downloading thumbnail!\n");
		return;
	}

			/****************************/
			/* CONVERT IT INTO A SPRITE */
			/****************************/

			/* DRAW JPEG INTO BUFFER */
				
	if (DrawJPEGFromRAMIntoBuffer_Mac(*jpegData, GetHandleSize(jpegData), &gTempImageGWorld, &gMyInstances[instanceNum].thumbnailPixels))
	{
		DisposeHandle(jpegData);
		goto fail;
	}
	DisposeHandle(jpegData);										// dont need JPEG anymore
	DisposeGWorld(gTempImageGWorld);								// don't need the gworld for anything
#else

		------- WINDOWS CODE GOES HERE --------

#endif
	

	
			/* VERIFY THAT IT'S THE RIGHT SIZE */
			//
			// We've hard-wired this so make life easier.  Must be 256x256, 256x128, 512x512, or 512x256
			//
				
	if (((gTempImageRect.right == 256) && (gTempImageRect.bottom == 256)) ||
		((gTempImageRect.right == 512) && (gTempImageRect.bottom == 512)))
	{
		gMyInstances[instanceNum].thumbShape = 1;	
	}
	else
	if (((gTempImageRect.right == 256) && (gTempImageRect.bottom == 128)) ||
		((gTempImageRect.right == 512) && (gTempImageRect.bottom == 256)))
	{
		gMyInstances[instanceNum].thumbShape = 2;	
	}
	else
	{
		printf("Thumbnail image is not 512x512, 512x256, 256x256 or 256x128, so we can't use it!\n");
		goto fail;
	}
	
	
		/* MAKE BLACK PIXELS TRANSPARENT */
		
	MakeBlackPixelsTransparent((u_long *)gMyInstances[instanceNum].thumbnailPixels, gTempImageRect.right, gTempImageRect.bottom);

	

		/* MAKE INTO AN OPENGL TEXTURE */

	gMyInstances[instanceNum].thumbnailTexture = MakeTextureFromBuffer(false,
																	gMyInstances[instanceNum].thumbnailPixels,
																	gTempImageRect.right,
																	gTempImageRect.bottom,
																	false,
																	false);
	
	
	printf("Thumbnail loaded.\n");
	
	gMyInstances[instanceNum].thumbnailLoaded = true;
	
}


/********************* MAKE BLACK PIXELS TRANSPARET *************************/
//
// Clears the alpha of any pixels that's black
//
// This is working from JPEG images, so compression artifacts cause problems here.
// That's why we check black with some leeway.
//

static void MakeBlackPixelsTransparent(u_long *pixels, int width, int height)
{
int	x,y;
u_long	pixel, r, g, b;

	for (y = 0; y < height; y++)
	{
		for (x = 0; x < width; x++) 
		{
			pixel = *pixels;										// get pixel
			
			r = (pixel & 0x00ff0000) >> 16;							// get RGB components
			g = (pixel & 0x0000ff00) >> 8;
			b = pixel & 0x000000ff; 
			
			if ((r < 0x03) && (g < 0x03) && (b < 0x03))				// is black?
				*pixels = *pixels & 0x00ffffff;						// zap alpha
			else
			if ((r < 0x07) && (g < 0x07) && (b < 0x07))				// is almost black?
			{
				*pixels = *pixels & 0x00ffffff;						// alpha partial
				*pixels |= 0x60000000;
			}
			
			pixels++;
		}
	}
}







