//////////
//
//	File:		VR3DTexture.c
//
//	Contains:	Support for adding a QuickTime movie or a picture as a texture on a QD3D object.
//
//	Written by:	Tim Monroe
//				Parts modeled on BoxMoov code by Rick Evans and Robert Dierkes.
//
//	Copyright:	© 1996-1998 by Apple Computer, Inc., all rights reserved.
//
//	Change History (most recent first):
//
//	   <10>	 	03/28/00	rtm		changed ((**myPixMap).rowBytes & 0x3fff) to QTGetPixMapHandleRowBytes(myPixMap)
//	   <9>	 	03/22/00	rtm		made changes to get things running under CarbonLib
//	   <8>	 	04/21/98	rtm		changed all occurrences of gw->portPixMap to GetGWorldPixMap(gw),
//									to prevent crashes reported on Windows
//	   <7>	 	03/09/98	rtm		changed PICT-specific code to use the graphics importer routines;
//									now we can open any image file format supported by QuickTime;
//									incorporated code previously in the file VR3DObjects.c, in the routines
//									VR3DObjects_LoadEmbeddedMovie and VR3DObjects_LoopEmbeddedMovie
//	   <6>	 	01/28/98	rtm		changed NewGWorld calls to QTNewGWorld
//	   <5>	 	01/27/98	rtm		made minor changes to VR3DTexture_Delete
//	   <4>	 	05/02/97	rtm		added fMediaHandler field to textures, for movies with sound
//	   <3>	 	01/14/97	rtm		added support for PICT textures
//	   <2>	 	12/17/96	rtm		modified VR3DTexture_AddToGroup to replace a texture shader
//	   <1>	 	12/16/96	rtm		first file; revised to personal coding style
//	   
//////////

#if QD3D_AVAIL


//////////
//
// header files
//	   
//////////

#include "VR3DTexture.h"


//////////
//
// VR3DTexture_New
// Create a new texture from a QuickTime movie or PICT file.
//
//////////

TextureHdl VR3DTexture_New (char *thePathName, Boolean isTextureMovie)
{
	FSSpec						myFSSpec;
	unsigned long				myPictMapAddr;
	unsigned long 				myPictRowBytes;
	GWorldPtr 					myGWorld;
	PixMapHandle 				myPixMap;
	GDHandle					myOldGD;
	GWorldPtr					myOldGW;
	Rect						myBounds;
	TQ3StoragePixmap			*myStrgPMapPtr;
	TextureHdl					myTexture = NULL;
	GraphicsImportComponent		myImporter = NULL;
	short						myMovieFileRef;
	QDErr						myErr = noErr;

	myTexture = (TextureHdl)NewHandleClear(sizeof(Texture));
	if (myTexture == NULL)
		return(myTexture);
		
	HLock((Handle)myTexture);

	// save the current port
	GetGWorld(&myOldGW, &myOldGD);
		
	FileUtils_MakeFSSpecForPathName(0, 0L, thePathName, &myFSSpec);

	if (isTextureMovie) {
		// open the movie and store its address in our data structure
		myErr = OpenMovieFile(&myFSSpec, &myMovieFileRef, fsRdPerm);
		if (myErr != noErr)
			goto bail;

		myErr = NewMovieFromFile(&(**myTexture).fMovie, myMovieFileRef, NULL, (StringPtr)NULL, newMovieActive, NULL);
		if (myErr != noErr)
			goto bail;

		if (myMovieFileRef != 0)
			CloseMovieFile(myMovieFileRef);
			
		SetMovieMatrix((**myTexture).fMovie, NULL);
		MacSetRect(&myBounds, 0, 0, 128, 128);
		SetMovieBox((**myTexture).fMovie, &myBounds);

		// get the sound media handler for movies with sound
		if (QTUtils_IsMediaTypeInMovie((**myTexture).fMovie, SoundMediaType) || QTUtils_IsMediaTypeInMovie((**myTexture).fMovie, MusicMediaType))
			(**myTexture).fMediaHandler = QTUtils_GetSoundMediaHandler((**myTexture).fMovie);
		
		// create a new offscreen graphics world (into which we will draw the movie)
		myErr = QTNewGWorld(&myGWorld, kOffscreenPixelType, &myBounds, NULL, NULL, kICMTempThenAppMemory);
		if (myErr != noErr) {
			StopMovie((**myTexture).fMovie);
			DisposeMovie((**myTexture).fMovie);
			(**myTexture).fMovie = NULL;
			goto bail;
		}
	} else {
		// get a graphics importer for the image file
		myErr = GetGraphicsImporterForFile(&myFSSpec, &myImporter);
		if (myErr != noErr)
			goto bail;
		
		// determine the natural size of the image
		myErr = GraphicsImportGetNaturalBounds(myImporter, &myBounds);
		if (myErr != noErr)
			goto bail;
			
		// create a new offscreen graphics world (into which we will draw the image)
		myErr = QTNewGWorld(&myGWorld, kOffscreenPixelType, &myBounds, NULL, NULL, kICMTempThenAppMemory);
		if (myErr != noErr)
			goto bail;
	}

	myErr = noErr;

	myPixMap = GetGWorldPixMap(myGWorld);
	if (myPixMap != NULL) {
		LockPixels(myPixMap);
		myPictMapAddr = (unsigned long)GetPixBaseAddr(myPixMap);
		
		// get the offset, in bytes, from one row of pixels to the next
		myPictRowBytes = (unsigned long)QTGetPixMapHandleRowBytes(myPixMap);
		SetGWorld(myGWorld, NULL);

		// create a storage object associated with the new offscreen graphics world
		myStrgPMapPtr = &(**myTexture).fStoragePixmap;
		myStrgPMapPtr->image = Q3MemoryStorage_NewBuffer((void *)myPictMapAddr,
														myPictRowBytes * myBounds.bottom,
														myPictRowBytes * myBounds.bottom);
		myStrgPMapPtr->width		= myBounds.right;
		myStrgPMapPtr->height		= myBounds.bottom;
		myStrgPMapPtr->rowBytes		= myPictRowBytes;
		myStrgPMapPtr->pixelSize	= (**myPixMap).pixelSize;
		myStrgPMapPtr->pixelType	= ((**myPixMap).pixelSize == 16) ? kQ3PixelTypeRGB16 : kQ3PixelTypeRGB32;
		myStrgPMapPtr->bitOrder		= kQ3EndianBig;
		myStrgPMapPtr->byteOrder	= kQ3EndianBig;
	}
	

	if (isTextureMovie) {
		TimeBase	myTimeBase;
		
		// start playing the movie in a loop
		SetMovieGWorld((**myTexture).fMovie, myGWorld, GetGWorldDevice(myGWorld));
		GoToBeginningOfMovie((**myTexture).fMovie);

		// draw the movie first
		MoviesTask((**myTexture).fMovie, 0);

		// throw the movie into loop mode
		myTimeBase = GetMovieTimeBase((**myTexture).fMovie);
		SetTimeBaseFlags(myTimeBase, GetTimeBaseFlags(myTimeBase) | loopTimeBase);

		// start playing the movie
		VRMoov_StartMovie((**myTexture).fMovie);		
	} else {
		// draw the picture into the offscreen graphics world
		GraphicsImportSetGWorld(myImporter, myGWorld, NULL);
		GraphicsImportSetBoundsRect(myImporter, &myBounds);
		GraphicsImportDraw(myImporter);
	}

	(**myTexture).fpGWorld = myGWorld;

bail:
	SetGWorld(myOldGW, myOldGD);
	HUnlock((Handle)myTexture);

	if (myImporter != NULL)
		CloseComponent(myImporter);

	if (myErr != noErr) {
		if (myTexture != NULL)
			DisposeHandle((Handle)myTexture);
		myTexture = NULL;
	}
		
	return(myTexture);
}


//////////
//
// VR3DTexture_AddToGroup
// Create a new texture shader based on the pixmap storage data and add it to the group.
//
//////////

TQ3Status VR3DTexture_AddToGroup (TextureHdl theTexture, TQ3GroupObject theGroup)
{
	TQ3Status			myStatus = kQ3Failure;
	TQ3StoragePixmap	myStoragePixmap;
	TQ3TextureObject	myTextureObject;
	
	if (theTexture == NULL || theGroup == NULL)
		return(myStatus);
	
	myStoragePixmap = (**theTexture).fStoragePixmap;
	myTextureObject = Q3PixmapTexture_New(&myStoragePixmap);
	if (myTextureObject != NULL) {

		TQ3ShaderObject		myTextureShader;
		
		myTextureShader = Q3TextureShader_New(myTextureObject);
		Q3Object_Dispose(myTextureObject);
		
		if (myTextureShader != NULL) {
			TQ3GroupPosition	myPosition = NULL;
			
			// look for an existing texture shader in the group
			Q3Group_GetFirstPositionOfType(theGroup, kQ3SurfaceShaderTypeTexture, &myPosition);
			if (myPosition != NULL) {
			
				// there is an existing texture shader; just replace it
				Q3Group_SetPositionObject(theGroup, myPosition, myTextureShader);
			} else {
			
				// there is no existing texture shader; add one to the group
				Q3Group_GetFirstPosition(theGroup, &myPosition);
				Q3Group_AddObjectBefore(theGroup, myPosition, myTextureShader);
			}
			
			Q3Object_Dispose(myTextureShader);
			myStatus = kQ3Success;
		}
	}

	return(myStatus);
}


//////////
//
// VR3DTexture_Delete
// Deallocate the texture.
//
//////////

Boolean	VR3DTexture_Delete (TextureHdl theTexture)
{
	PixMapHandle		myPixMap;

	if (theTexture == NULL)
		return(false);

	HLock((Handle)theTexture);

	if ((**theTexture).fMovie != NULL) {
		StopMovie((**theTexture).fMovie);
		DisposeMovie((**theTexture).fMovie);
		(**theTexture).fMovie = NULL;
	}

	if ((**theTexture).fpGWorld == NULL) {
		myPixMap = GetGWorldPixMap((**theTexture).fpGWorld);
		if (myPixMap != NULL)
			UnlockPixels(myPixMap);
	
		DisposeGWorld((**theTexture).fpGWorld);
		(**theTexture).fpGWorld = NULL;
	}

	if ((**theTexture).fStoragePixmap.image != NULL) {
		Q3Object_Dispose((**theTexture).fStoragePixmap.image);
		(**theTexture).fStoragePixmap.image = NULL;
	}

	HUnlock((Handle)theTexture);
	DisposeHandle((Handle)theTexture);
	
	return(true);
}


//////////
//
// VR3DTexture_NextFrame
// Advance the texture's movie to the next frame.
//
//////////

Boolean VR3DTexture_NextFrame (TextureHdl theTexture)
{
	TQ3StoragePixmap	*myStrgPMapPtr;
	long				mySize;
	TQ3Status			myStatus;
	unsigned long		myPictMapAddr;
	PixMapHandle		myPixMap;

	// if fpGWorld is non-NULL, then the fMovie is non-NULL and the movie needs updating
	if ((**theTexture).fpGWorld == NULL)
		return(false);

	HLock((Handle)theTexture);

	if ((**theTexture).fMovie != NULL)
		MoviesTask((**theTexture).fMovie, 0);	// draw the next movie frame

	myPixMap = GetGWorldPixMap((**theTexture).fpGWorld);
	myStrgPMapPtr = &(**theTexture).fStoragePixmap;
	mySize = myStrgPMapPtr->height * myStrgPMapPtr->rowBytes;
	myPictMapAddr = (unsigned long)GetPixBaseAddr(myPixMap);

	// tell QD3D the buffer changed
	myStatus = Q3MemoryStorage_SetBuffer(myStrgPMapPtr->image, (void *)myPictMapAddr, mySize, mySize);

	HUnlock((Handle)theTexture);
	
	return(myStatus == kQ3Success);
}

#endif	// if QD3D_AVAIL
