//////////
//
//	File:		VR3DObjects.c
//
//	Contains:	QuickDraw 3D support for QuickTime VR movies.
//
//	Written by:	Tim Monroe
//				Parts modeled on BoxMoov code by Nick Thompson, Rick Evans, and Robert Dierkes
//				and on code in QD3D book (by yours truly!).
//
//	Copyright:	© 1996-1999 by Apple Computer, Inc., all rights reserved.
//
//	Change History (most recent first):
//
//	   <37>	 	03/28/00	rtm		changed ((**myPixMap).rowBytes & 0x3fff) to QTGetPixMapHandleRowBytes(myPixMap)
//	   <36>	 	07/29/99	rtm		set polyhedronAttributeSet to NULL in VR3DObjects_EnlistRectangle; now
//									the Set3DObjColor command works on rectangles as well as other objects
//	   <35>	 	09/28/98	rtm		added Q3InteractiveRenderer_ calls to VR3DObjects_CreateView
//	   <34>	 	04/22/98	rtm		used GetGWorldPixMap instead of gw->portPixMap in VR3DObjects_PrescreenRoutine
//	   <33>	 	03/18/98	rtm		minor cleanup to make things a little more structured
//	   <32>	 	03/09/98	rtm		removed VR3DObjects_GetEmbeddedPicture; moved code into VR3DTexture.c from
//									VR3DObjects_LoadEmbeddedMovie and VR3DObjects_LoopEmbeddedMovie
//	   <31>	 	02/19/98	rtm		changed default chroma key color to prevent transparency problems
//	   <30>	 	01/28/98	rtm		changed NewGWorld calls to QTNewGWorld
//	   <29>	 	01/27/98	rtm		fixed pixel depth problems: QD3D's GWorlds should all be 32-bit
//	   <28>	 	10/13/97	rtm		added VR3DObjects_CreateDefaultGroup call to VR3DObjects_GetModelFromFile
//									to provide default lighting for 3DMF files
//	   <27>	 	10/03/97	rtm		removed UPDATE_GWORLD_BROKEN symbol
//	   <26>	 	09/23/97	rtm		added endian adjustment to VR3DObjects_GetEmbeddedPicture
//	   <25>	 	09/15/97	rtm		added UPDATE_GWORLD_BROKEN symbol; should be removed later
//									when UpdateGWorld is fixed
//	   <24>	 	09/12/97	rtm		added Windows support to VR3DObjects_GetModelFromFile
//	   <23>	 	07/17/97	rtm		added VR3DObjects_DoIdle
//	   <22>	 	05/05/97	rtm		more work on geometry specifications;
//									added Set calls for styles, colors, etc.
//	   <21>	 	05/04/97	rtm		reworked geometry specifications; added Enlist_ functions
//	   <20>	 	05/02/97	rtm		made sound in texture movies directional
//	   <19>	 	04/10/97	rtm		added call to Q3View_Sync to prevent tearing
//	   <18>	 	04/02/97	rtm		copied file for integration with VRScript; removed support for object nodes
//	   <17>	 	02/21/97	rtm		added support for multi-node, mixed-type movies;
//									fixed window resizing for object nodes
//	   <16>	 	02/10/97	rtm		fixed up-vector; fixed pan/tilt granularity in object nodes
//									(now QD3D camera doesn't update until object view changes)
//	   <15>	 	02/04/97	rtm		begun implementing support for object nodes
//	   <14>	 	01/24/97	rtm		slight adjustment to camera-setting algorithm
//	   <13>	 	01/23/97	rtm		reworked camera-setting algorithm; now it works fine!
//									(replaced VR3DObjects_RotateCamera[XY] with VR3DObjects_SetCamera)
//	   <12>	 	01/20/97	rtm		various small tweaks; nothing major 
//	   <11>	 	01/17/97	rtm		further work on tilting; still unable to fix it entirely 
//	   <10>	 	01/16/97	rtm		added VR3DObjects_SetCameraAspectRatio 
//									and VR3DObjects_UpdateDrawContext to handle window resizing
//	   <9>	 	01/14/97	rtm		added polyhedron object to VR3DObjects_CreateModel; added static texture support
//	   <8>	 	01/13/97	rtm		added some object types to VR3DObjects_CreateModel
//	   <7>	 	01/10/97	rtm		added VR3DObjects_MCActionFilterProc (later merged into ApplicationMCActionFilterProc)
//	   <6>	 	01/09/97	rtm		fixed jittering on pan/tilt/zoom; fixed VR3DObjects_RotateCameraX for correct tilting
//	   <5>	 	01/03/97	rtm		added animation (that is, rotation) support for 3D objects
//	   <4>	 	12/23/96	rtm		further work on 3D overlay; fixed some jitters
//	   <3>	 	12/20/96	rtm		added zooming support; implemented 3D overlay instead of 3D test window
//	   <2>	 	12/16/96	rtm		added 3D test window, intercept procedure
//									added QuickTime movie texture mapping support
//	   <1>	 	12/13/96	rtm		first file
//	   
//
// This file provides functions to display 3D objects at specific locations in a panorama.
//
// Our strategy: QuickDraw 3D is a "slave" to VR: whenever the VR environment changes,
// we change the QuickDraw 3D camera accordingly and render a new image to our view.
// All this occurs in the prescreen buffer imaging complete procedure.
//
//////////

// TODO:
//	+ add lighting calls (create new light, move light, set light color/intensity etc.)

#if QD3D_AVAIL

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

#include "VR3DObjects.h"


//////////
//
// global variables
//	   
//////////

extern Boolean			gHasQuickDraw3D;							// is QuickDraw 3D available?
extern Boolean			gHasQuickDraw3D15;							// is QuickDraw 3D version 1.5 (or greater) available?


//////////
//
// constants
//	   
//////////

const TQ3Point3D		kCameraOrigin = {0.0, 0.0, 0.0};
const TQ3Point3D		kPointOfInterest = {0.0, 0.0, -1000.0};
const TQ3ColorRGB		k3DObjColor = {0.5, 0.5, 0.5};				// default 3D object color
const RGBColor			k3DChromaColor = {0x1111, 0x2222, 0x3333};	// the default chroma key color


//////////
//
// VR3DObjects_Init
// Initialize for QuickDraw 3D.
//
//////////

void VR3DObjects_Init (void)
{
	// make sure that QuickDraw 3D is available
	gHasQuickDraw3D = VR3DObjects_IsQD3DAvailable() ? true : false;
	
	// now perform any initialization
	if (gHasQuickDraw3D) {
		unsigned long		myMajor, myMinor;
		
		Q3Initialize();
		
		// see which version of QD3D is installed; version 1.5 provides some useful new geometries
		Q3GetVersion(&myMajor, &myMinor);
		gHasQuickDraw3D15 = ((myMajor >= 0x0001) && (myMinor >= 0x0005));
	}
}


//////////
//
// VR3DObjects_Stop
// Clean up for QuickDraw 3D.
//
//////////

void VR3DObjects_Stop (void)
{
	// perform any QD3D-related shutdown operations
	if (gHasQuickDraw3D)
		Q3Exit();
}


//////////
//
// VR3DObjects_IsQD3DAvailable
// Is QuickDraw 3D available in the present operating environment?
//
//////////

Boolean VR3DObjects_IsQD3DAvailable (void)
{
#if TARGET_OS_MAC
	return((Ptr)Q3Initialize != (Ptr)kUnresolvedCFragSymbolAddress);
#endif

#if TARGET_OS_WIN32
	return(true);
#endif

	// the following code should work cross-platform, but currently it returns -5551 (gestaltUndefSelectorErr) on Windows
//	Boolean 	myQD3DAvail = false;
//	long		myAttrs;
//	OSErr 		myErr = noErr;
//
//	myErr = Gestalt(gestaltQD3D, &myAttrs);
//	if (myErr == noErr)
//		if (myAttrs & (1L << gestaltQD3DPresent))
//			myQD3DAvail = true;
//
//	return(myQD3DAvail);
}


//////////
//
// VR3DObjects_InitWindowData
// Initialize the window-specific 3D data.
//
//////////

void VR3DObjects_InitWindowData (WindowObject theWindowObject)
{
	ApplicationDataHdl	myAppData;
	Rect				myRect;
	OSErr				myErr = noErr;
	
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData != NULL) {
	
		// lock the application data handle
		HLock((Handle)myAppData);
		
		// get the size of the destination window
		if ((**theWindowObject).fMovie != NULL)
			GetMovieBox((**theWindowObject).fMovie, &myRect);
				
		// create an offscreen GWorld that is the size of the window area
		// (this GWorld is our draw context buffer)
		myErr = QTNewGWorld(&(**myAppData).fQD3DDCGWorld, kOffscreenPixelType, &myRect, NULL, NULL, kICMTempThenAppMemory);
		if (myErr != noErr)
			goto bail;

		// create a new view attached to the offscreen GWorld
		(**myAppData).fView = VR3DObjects_CreateView((**myAppData).fQD3DDCGWorld);
		
		// set the FOV vertical orientation flag to ON
		(**myAppData).fQD3DFOVIsVert = true;

		// set the default 3D chroma key color
		(**myAppData).fQD3DKeyColor = k3DChromaColor;

		// initialize QD3D camera's aspect ratio								 
		VR3DObjects_SetCameraAspectRatio(theWindowObject);								 

		// initialize QD3D camera's point-of-interest and FOV
		VR3DObjects_SetCamera(theWindowObject);
		
bail:
		// unlock the application data handle
		HUnlock((Handle)myAppData);
	}
}


//////////
//
// VR3DObjects_DumpWindowData
// Dump the window-specific 3D data.
//
//////////

void VR3DObjects_DumpWindowData (WindowObject theWindowObject)
{
	ApplicationDataHdl	myAppData;
	
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData != NULL) {
	
		// dispose of the view object
		if ((**myAppData).fView)
			Q3Object_Dispose((**myAppData).fView);

		// dispose of GWorld used by this window object
		if ((**myAppData).fQD3DDCGWorld != NULL)
			DisposeGWorld((**myAppData).fQD3DDCGWorld);
	}
}


//////////
//
// VR3DObjects_DoIdle
// Do any 3D-related processing that can or should occur at idle time.
// Returns true if the caller should call QTVRUpdate, false otherwise.
//
//////////

Boolean VR3DObjects_DoIdle (WindowObject theWindowObject)
{
	ApplicationDataHdl		myAppData;
	VRScript3DObjPtr		myPointer;
	Boolean					myNeedUpdate = false;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return(myNeedUpdate);
	
	// if any currently visible 3D object is animated or has a movie texture,
	// we need to update window
	myPointer = (VRScript3DObjPtr)(**myAppData).fListArray[kVREntry_QD3DObject];
	while ((myPointer != NULL) && !myNeedUpdate) {
		if (myPointer->fModelIsVisible)
			if ((myPointer->fModelIsAnimated) || (myPointer->fTextureIsMovie))
				myNeedUpdate = true;
		
		myPointer = myPointer->fNextEntry;
	}

	return(myNeedUpdate);
}
		
				
//////////
//
// VR3DObjects_EnlistBox
// Add a box to the list of 3D objects.
//
//////////

void VR3DObjects_EnlistBox (WindowObject theWindowObject, UInt32 theEntryID, float theX, float theY, float theZ, float theXSize, float theYSize, float theZSize, UInt32 theOptions)
{
	TQ3BoxData				myBoxData;
	TQ3GeometryObject		myObject;
	TQ3GroupObject			myGroup;
	
	myBoxData.boxAttributeSet = NULL;
	myBoxData.faceAttributeSet = NULL;
	Q3Point3D_Set(&myBoxData.origin, theXSize / -2.0, theYSize / -2.0, theZSize / -2.0);
	Q3Vector3D_Set(&myBoxData.orientation, 0.0, theYSize, 0.0);
	Q3Vector3D_Set(&myBoxData.majorAxis, 0.0, 0.0, theZSize);	
	Q3Vector3D_Set(&myBoxData.minorAxis, theXSize, 0.0, 0.0);	
	myObject = Q3Box_New(&myBoxData);	
	if (myObject != NULL) {
		myGroup = VR3DObjects_CreateDefaultGroup(myObject);
		if (myGroup != NULL)
			VRScript_Enlist3DObject(theWindowObject, myGroup, theEntryID, theX, theY, theZ, theOptions);
	}
}


//////////
//
// VR3DObjects_EnlistCone
// Add a cone to the list of 3D objects.
//
//////////

void VR3DObjects_EnlistCone (WindowObject theWindowObject, UInt32 theEntryID, float theX, float theY, float theZ, float theMajRad, float theMinRad, float theHeight, UInt32 theOptions)
{
	TQ3ConeData				myConeData;
	TQ3GeometryObject		myObject;
	TQ3GroupObject			myGroup;
	
	myConeData.coneAttributeSet = NULL;
	myConeData.interiorAttributeSet = NULL;
	myConeData.faceAttributeSet = NULL;
	myConeData.bottomAttributeSet = NULL;
	myConeData.caps = kQ3EndCapMaskBottom;
	Q3Point3D_Set(&myConeData.origin, 0.0, theHeight / -2.0, 0.0);
	Q3Vector3D_Set(&myConeData.orientation, 0.0, theHeight, 0.0);
	Q3Vector3D_Set(&myConeData.majorRadius, 0.0, 0.0, theMajRad);	
	Q3Vector3D_Set(&myConeData.minorRadius, theMinRad, 0.0, 0.0);	
	myObject = Q3Cone_New(&myConeData);

	if (myObject != NULL) {
		myGroup = VR3DObjects_CreateDefaultGroup(myObject);
		if (myGroup != NULL) {
			VR3DObjects_SetSubdivisionStyle(myGroup, 20);	// set a finer subdivision style
			VRScript_Enlist3DObject(theWindowObject, myGroup, theEntryID, theX, theY, theZ, theOptions);
		}
	}
}


//////////
//
// VR3DObjects_EnlistCylinder
// Add a cylinder to the list of 3D objects.
//
//////////

void VR3DObjects_EnlistCylinder (WindowObject theWindowObject, UInt32 theEntryID, float theX, float theY, float theZ, float theMajRad, float theMinRad, float theHeight, UInt32 theOptions)
{
	TQ3CylinderData			myCylinderData;
	TQ3GeometryObject		myObject;
	TQ3GroupObject			myGroup;
	
	myCylinderData.cylinderAttributeSet = NULL;
	myCylinderData.interiorAttributeSet = NULL;
	myCylinderData.topAttributeSet = NULL;
	myCylinderData.faceAttributeSet = NULL;
	myCylinderData.bottomAttributeSet = NULL;
	myCylinderData.caps = kQ3EndCapMaskTop | kQ3EndCapMaskBottom;
	Q3Point3D_Set(&myCylinderData.origin, 0.0, theHeight / -2.0, 0.0);
	Q3Vector3D_Set(&myCylinderData.orientation, 0.0, theHeight, 0.0);
	Q3Vector3D_Set(&myCylinderData.majorRadius, 0.0, 0.0, theMajRad);	
	Q3Vector3D_Set(&myCylinderData.minorRadius, theMinRad, 0.0, 0.0);	
	myObject = Q3Cylinder_New(&myCylinderData);
	
	if (myObject != NULL) {
		myGroup = VR3DObjects_CreateDefaultGroup(myObject);
		if (myGroup != NULL) {
			VR3DObjects_SetSubdivisionStyle(myGroup, 20);	// set a finer subdivision style
			VRScript_Enlist3DObject(theWindowObject, myGroup, theEntryID, theX, theY, theZ, theOptions);
		}
	}
}


//////////
//
// VR3DObjects_EnlistEllipsoid
// Add an ellipsoid to the list of 3D objects.
//
//////////

void VR3DObjects_EnlistEllipsoid (WindowObject theWindowObject, UInt32 theEntryID, float theX, float theY, float theZ, float theMajRad, float theMinRad, float theHeight, UInt32 theOptions)
{
	TQ3EllipsoidData		myEllipsoidData;
	TQ3GeometryObject		myObject;
	TQ3GroupObject			myGroup;
	
	myEllipsoidData.ellipsoidAttributeSet = NULL;
	myEllipsoidData.interiorAttributeSet = NULL;
	myEllipsoidData.caps = 0L;
	Q3Point3D_Set(&myEllipsoidData.origin, 0.0, 0.0, 0.0);
	Q3Vector3D_Set(&myEllipsoidData.orientation, 0.0, theHeight, 0.0);
	Q3Vector3D_Set(&myEllipsoidData.majorRadius, 0.0, 0.0, theMajRad);	
	Q3Vector3D_Set(&myEllipsoidData.minorRadius, theMinRad, 0.0, 0.0);	
	myObject = Q3Ellipsoid_New(&myEllipsoidData);

	if (myObject != NULL) {
		myGroup = VR3DObjects_CreateDefaultGroup(myObject);
		if (myGroup != NULL) {
			VR3DObjects_SetSubdivisionStyle(myGroup, 20);	// set a finer subdivision style
			VRScript_Enlist3DObject(theWindowObject, myGroup, theEntryID, theX, theY, theZ, theOptions);
		}
	}
}


//////////
//
// VR3DObjects_EnlistTorus
// Add a torus to the list of 3D objects.
//
//////////

void VR3DObjects_EnlistTorus (WindowObject theWindowObject, UInt32 theEntryID, float theX, float theY, float theZ, float theMajRad, float theMinRad, float theHeight, float theRatio, UInt32 theOptions)
{
	TQ3TorusData			myTorusData;
	TQ3GeometryObject		myObject;
	TQ3GroupObject			myGroup;
	
	myTorusData.torusAttributeSet = NULL;
	myTorusData.interiorAttributeSet = NULL;
	myTorusData.caps = 0L;
	myTorusData.ratio = theRatio;
	Q3Point3D_Set(&myTorusData.origin, 0.0, 0.0, 0.0);
	Q3Vector3D_Set(&myTorusData.orientation, 0.0, theHeight, 0.0);
	Q3Vector3D_Set(&myTorusData.majorRadius, 0.0, 0.0, theMajRad);	
	Q3Vector3D_Set(&myTorusData.minorRadius, theMinRad, 0.0, 0.0);	
	myObject = Q3Torus_New(&myTorusData);

	if (myObject != NULL) {
		myGroup = VR3DObjects_CreateDefaultGroup(myObject);
		if (myGroup != NULL) {
			VR3DObjects_SetSubdivisionStyle(myGroup, 20);	// set a finer subdivision style
			VRScript_Enlist3DObject(theWindowObject, myGroup, theEntryID, theX, theY, theZ, theOptions);
		}
	}
}


//////////
//
// VR3DObjects_EnlistRectangle
// Add a rectangle to the list of 3D objects.
// This is intended to serve as a "screen" for texture-mapped movies or pictures.
//
//////////

void VR3DObjects_EnlistRectangle (WindowObject theWindowObject, UInt32 theEntryID, float theX, float theY, float theZ,
									float theX1, float theY1, float theZ1, 
									float theX2, float theY2, float theZ2, 
									float theX3, float theY3, float theZ3, 
									float theX4, float theY4, float theZ4, UInt32 theOptions)
{
	TQ3PolyhedronData			myPolyData;
	static TQ3Vertex3D			myVertices[4];
	static TQ3Param2D			myUVParams[4] = {
									{0.0, 0.0},
									{0.0, 1.0},
									{1.0, 1.0},
									{1.0, 0.0}
								};
	
	TQ3PolyhedronTriangleData	myTriangles[2] = {
									{ { 0, 1, 3 }, kQ3PolyhedronEdge01 | kQ3PolyhedronEdge20, NULL },
									{ { 1, 2, 3 }, kQ3PolyhedronEdge01 | kQ3PolyhedronEdge12, NULL }
								};

	unsigned long				myIndex;
	TQ3GeometryObject			myObject;
	TQ3GroupObject				myGroup;
	
	// set up vertices, edges, and triangular faces
	Q3Point3D_Set(&myVertices[0].point, theX1, theY1, theZ1);
	Q3Point3D_Set(&myVertices[1].point, theX2, theY2, theZ2);
	Q3Point3D_Set(&myVertices[2].point, theX3, theY3, theZ3);
	Q3Point3D_Set(&myVertices[3].point, theX4, theY4, theZ4);
	myVertices[0].attributeSet = NULL;
	myVertices[1].attributeSet = NULL;
	myVertices[2].attributeSet = NULL;
	myVertices[3].attributeSet = NULL;
	myPolyData.numVertices = 4;
	myPolyData.vertices = myVertices;	
	myPolyData.numEdges = 0;
	myPolyData.edges = NULL;
	myPolyData.numTriangles	= 2;
	myPolyData.triangles = myTriangles;
	
	// add uv parameterization to the polyhedron
	for (myIndex = 0; myIndex < myPolyData.numVertices; myIndex++) {					
		myVertices[myIndex].attributeSet = Q3AttributeSet_New();
		Q3AttributeSet_Add(myVertices[myIndex].attributeSet, kQ3AttributeTypeSurfaceUV, &myUVParams[myIndex]);
	}
	
	myPolyData.polyhedronAttributeSet = NULL;		// inherit the attribute set from the current state
	myObject = Q3Polyhedron_New(&myPolyData);

	if (myObject != NULL) {
		myGroup = VR3DObjects_CreateDefaultGroup(myObject);
		if (myGroup != NULL)
			VRScript_Enlist3DObject(theWindowObject, myGroup, theEntryID, theX, theY, theZ, theOptions);
	}
}


//////////
//
// VR3DObjects_Enlist3DMFFile
// Open a 3DMF file and add it to the list of 3D objects.
//
//////////

void VR3DObjects_Enlist3DMFFile (WindowObject theWindowObject, UInt32 theEntryID, float theX, float theY, float theZ, UInt32 theOptions, char *thePathName)
{
	TQ3GroupObject			myGroup;
	
	myGroup = VR3DObjects_GetModelFromFile(thePathName);
	if (myGroup != NULL) 
		VRScript_Enlist3DObject(theWindowObject, myGroup, theEntryID, theX, theY, theZ, theOptions);
}


//////////
//
// VR3DObjects_CreateDefaultGroup
// Return a default model containing the specified object.
//
//////////

TQ3GroupObject VR3DObjects_CreateDefaultGroup (TQ3GeometryObject theObject)
{
	TQ3GroupObject			myGroup;
	
	// create a new display group
	myGroup = Q3DisplayGroup_New();
	if (myGroup != NULL) {
		TQ3ShaderObject			myIlluminationShader;
		
		// create an illumination shader	
		myIlluminationShader = Q3PhongIllumination_New();
		if (myIlluminationShader != NULL) {
			TQ3AttributeSet			myAttributeSet;

			// add the illumination shader to our display group	
			Q3Group_AddObject(myGroup, myIlluminationShader);
			
			// add the default diffuse color to the group
			myAttributeSet = Q3AttributeSet_New();
			Q3AttributeSet_Add(myAttributeSet, kQ3AttributeTypeDiffuseColor, &k3DObjColor);
			Q3Group_AddObject(myGroup, myAttributeSet);

			// add the geometry object to the group
			Q3Group_AddObject(myGroup, theObject);
			
			// dispose of the objects we've added to the group
			if (myIlluminationShader != NULL) 
				Q3Object_Dispose(myIlluminationShader);	
		}
	}
	
	if (theObject != NULL) 
		Q3Object_Dispose(theObject);
	
	return(myGroup);
}


//////////
//
// VR3DObjects_CreateView
// Return a view object for the QD3D objects in our model.
//
//////////

TQ3ViewObject VR3DObjects_CreateView (GWorldPtr theGWorld)
{
	TQ3Status				myStatus;
	TQ3ViewObject			myView;
	TQ3DrawContextObject	myDrawContext;
	TQ3RendererObject		myRenderer;
	TQ3CameraObject			myCamera;
	TQ3GroupObject			myLights;
	
	// create a new view object
	myView = Q3View_New();
	if (myView == NULL)
		goto bail;
	
	// create and set draw context
	myDrawContext = VR3DObjects_CreateDrawContext(theGWorld);
	if (myDrawContext == NULL)
		goto bail;
		
	myStatus = Q3View_SetDrawContext(myView, myDrawContext);
	if (myStatus == kQ3Failure)
		goto bail;

	Q3Object_Dispose(myDrawContext);
	
	// create and set renderer
	myRenderer = Q3Renderer_NewFromType(kQ3RendererTypeInteractive);
	if (myRenderer == NULL) {
		myRenderer = Q3Renderer_NewFromType(kQ3RendererTypeWireFrame);
	} else {
		// these two lines allow us to use the best possible renderer,
		// including hardware if it is installed.
		Q3InteractiveRenderer_SetDoubleBufferBypass(myRenderer, kQ3True);						
		Q3InteractiveRenderer_SetPreferences(myRenderer, kQAVendor_BestChoice, 0);
	}
		
	if (myRenderer == NULL) 
		goto bail;
	
	myStatus = Q3View_SetRenderer(myView, myRenderer);
	if (myStatus == kQ3Failure)
		goto bail;
		
	Q3Object_Dispose(myRenderer);
	
	// create and set camera
	myCamera = VR3DObjects_CreateCamera(theGWorld);
	if (myCamera == NULL)
		goto bail;
		
	myStatus = Q3View_SetCamera(myView, myCamera);
	if (myStatus == kQ3Failure)
		goto bail;

	Q3Object_Dispose(myCamera);
	
	// create and set lights
	myLights = VR3DObjects_CreateLights();
	if (myLights == NULL)
		goto bail;
		
	myStatus = Q3View_SetLightGroup(myView, myLights);
	if (myStatus == kQ3Failure)
		goto bail;

	Q3Object_Dispose(myLights);
		
	return(myView);
	
bail:
	// if any of the above creations failed, don't return a view
	return(NULL);
}


//////////
//
// VR3DObjects_CreateLights
// Return a light group object for the QD3D objects in our model.
// For displaying embedded movies, we need just the ambient light.
//
//////////

TQ3GroupObject VR3DObjects_CreateLights (void)
{
	TQ3GroupPosition			myGroupPosition;
	TQ3GroupObject				myLightList;
	TQ3LightData				myLightData;
	TQ3PointLightData			myPointLightData;
	TQ3DirectionalLightData		myDirectionalLightData;
	TQ3LightObject				myAmbientLight, myPointLight, myFillLight;
	TQ3Point3D					myPointLocation =	{-10.0, 0.0, 10.0};
	TQ3Vector3D					myFillDirection =	{10.0, 0.0, 10.0};
	TQ3ColorRGB					myWhiteLight =		{1.0F, 1.0F, 1.0F};
	
	// create the ambient light
	myLightData.isOn = kQ3True;
	myLightData.color = myWhiteLight;
	myLightData.brightness = 1.0F;
	myAmbientLight = Q3AmbientLight_New(&myLightData);
	if (myAmbientLight == NULL)
		goto bail;
	
	// create a point light
	myLightData.brightness = 1.0F;
	myPointLightData.lightData = myLightData;
	myPointLightData.castsShadows = kQ3False;
	myPointLightData.attenuation = kQ3AttenuationTypeNone;
	myPointLightData.location = myPointLocation;
	myPointLight = Q3PointLight_New(&myPointLightData);
	if (myPointLight == NULL)
		goto bail;

	// create a directional light for fill
	myLightData.brightness = 0.2F;
	myDirectionalLightData.lightData = myLightData;
	myDirectionalLightData.castsShadows = kQ3False;
	myDirectionalLightData.direction = myFillDirection;
	myFillLight = Q3DirectionalLight_New(&myDirectionalLightData);
	if (myFillLight == NULL)
		goto bail;

	// create a light group and add each of the lights to the group
	myLightList = Q3LightGroup_New();
	if (myLightList == NULL)
		goto bail;
	myGroupPosition = Q3Group_AddObject(myLightList, myAmbientLight);
	if (myGroupPosition == 0)
		goto bail;
	myGroupPosition = Q3Group_AddObject(myLightList, myPointLight);
	if (myGroupPosition == 0)
		goto bail;
	myGroupPosition = Q3Group_AddObject(myLightList, myFillLight);
	if (myGroupPosition == 0)
		goto bail;

	Q3Object_Dispose(myAmbientLight);
	Q3Object_Dispose(myPointLight);
	Q3Object_Dispose(myFillLight);

	return(myLightList);
	
bail:
	// if any of the above creations failed, don't return a light group
	return(NULL);
}


//////////
//
// VR3DObjects_CreateDrawContext
// Return a draw context object for the QD3D objects in our model.
//
//////////

TQ3DrawContextObject VR3DObjects_CreateDrawContext (GWorldPtr theGWorld)
{
	TQ3DrawContextObject		myDrawContext;
	TQ3PixmapDrawContextData	myPixMapDrawContextData;
	TQ3DrawContextData			myDrawContextData;
	PixMapHandle 				myPixMap;
	Rect						myRect;
	TQ3ColorARGB				myClearColor;
	float						myFactor = 0xffff;
	
	if (theGWorld == NULL)
		goto bail;
		
	// set the background color;
	// note that RGBColor is defined in the range 0-65535,
	// while TQ3ColorARGB is defined in the range 0.0-1.0; hence the division....
	myClearColor.a = 0.0;
	myClearColor.r = k3DChromaColor.red / myFactor;
	myClearColor.g = k3DChromaColor.green / myFactor;
	myClearColor.b = k3DChromaColor.blue / myFactor;
	
	// fill in draw context data
	myDrawContextData.clearImageMethod = kQ3ClearMethodWithColor;
	myDrawContextData.clearImageColor = myClearColor;
	myDrawContextData.paneState = kQ3False;
	myDrawContextData.maskState = kQ3False;
	myDrawContextData.doubleBufferState = kQ3False;
 
	myPixMapDrawContextData.drawContextData = myDrawContextData;
	
	// the pixmap must remain locked in memory for as long as it exists
	myPixMap = GetGWorldPixMap(theGWorld);
	LockPixels(myPixMap);

	myRect = theGWorld->portRect;
	
	myPixMapDrawContextData.pixmap.width = myRect.right - myRect.left;
	myPixMapDrawContextData.pixmap.height = myRect.bottom - myRect.top;
	myPixMapDrawContextData.pixmap.rowBytes = (unsigned long)QTGetPixMapHandleRowBytes(myPixMap)
	myPixMapDrawContextData.pixmap.pixelType = kQ3PixelTypeRGB32;
	myPixMapDrawContextData.pixmap.pixelSize = 32;
	myPixMapDrawContextData.pixmap.bitOrder = kQ3EndianBig;
	myPixMapDrawContextData.pixmap.byteOrder = kQ3EndianBig;
	myPixMapDrawContextData.pixmap.image = GetPixBaseAddr(myPixMap);
	
	// create draw context and return it
	myDrawContext = Q3PixmapDrawContext_New(&myPixMapDrawContextData);

bail:
	return(myDrawContext);
}


//////////
//
// VR3DObjects_CreateCamera
// Return a camera object for the QD3D objects in our model.
// Note that some of the initial values are wrong and will soon be reset (in InitApplicationWindowObject).
//
//////////

TQ3CameraObject VR3DObjects_CreateCamera (CGrafPtr thePort)
{
	TQ3CameraObject					myCamera;
	TQ3ViewAngleAspectCameraData	myCameraData;
	
	TQ3Point3D 						myFrom 		= kCameraOrigin;
	TQ3Point3D 						myTo 		= kPointOfInterest;
	TQ3Vector3D 					myUp 		= {0.0, 1.0, 0.0};

	float 							myFOV		= 1.00;				// initial FOV really depends on QTVR instance, so we reset this later
	float 							myHither	= (float)0.01;
	float 							myYon 		= 1000.0;
	
	myCameraData.cameraData.placement.cameraLocation = myFrom;
	myCameraData.cameraData.placement.pointOfInterest = myTo;
	myCameraData.cameraData.placement.upVector = myUp;

	myCameraData.cameraData.range.hither = myHither;
	myCameraData.cameraData.range.yon = myYon;

	// the default camera view port
	myCameraData.cameraData.viewPort.origin.x = -1.0F;
	myCameraData.cameraData.viewPort.origin.y = 1.0F;
	myCameraData.cameraData.viewPort.width = 2.0F;
	myCameraData.cameraData.viewPort.height = 2.0F;
	
	// some default FOV and aspect ratio values
	myCameraData.fov = myFOV;
	myCameraData.aspectRatioXToY =
		(float)(thePort->portRect.right - thePort->portRect.left) / 
		(float)(thePort->portRect.bottom - thePort->portRect.top);
		
	myCamera = Q3ViewAngleAspectCamera_New(&myCameraData);

	return(myCamera);
}


//////////
//
// VR3DObjects_GetModelAttributeSet
// Get the attribute set of a 3D model.
//
//////////

TQ3AttributeSet VR3DObjects_GetModelAttributeSet (VRScript3DObjPtr thePointer)
{
	TQ3AttributeSet		myAttributeSet = NULL;
	
	if ((thePointer != NULL) && (thePointer->fModel != NULL)) {
		TQ3GroupPosition	myPosition;
		
		// get the position of the attribute set in the group
		Q3Group_GetFirstPositionOfType(thePointer->fModel, kQ3SetTypeAttribute, &myPosition);
		if (myPosition != 0)		// get the attribute set
			Q3Group_GetPositionObject(thePointer->fModel, myPosition, &myAttributeSet);
	}
	
	return(myAttributeSet);
}


//////////
//
// VR3DObjects_SetSubdivisionStyle
// Create a subdivision style and add it to the specified group.
//
//////////

void VR3DObjects_SetSubdivisionStyle (TQ3GroupObject theGroup, short theNumDivisions)
{
	TQ3SubdivisionStyleData		myStyleData;
	TQ3StyleObject				myStyleObject;
	TQ3GroupPosition			myPosition;
	
	// get the position of the geometry object in the group
	Q3Group_GetFirstPositionOfType(theGroup, kQ3ShapeTypeGeometry, &myPosition);
	
	// set a subdivision style
	myStyleData.method = kQ3SubdivisionMethodConstant;
	myStyleData.c1 = theNumDivisions;
	myStyleData.c2 = theNumDivisions;
	myStyleObject = Q3SubdivisionStyle_New(&myStyleData);
	Q3Group_AddObjectBefore(theGroup, myPosition, myStyleObject);
	Q3Object_Dispose(myStyleObject);
}


//////////
//
// VR3DObjects_SetColor
// Set the diffuse color of a 3D object.
//
//////////

void VR3DObjects_SetColor (WindowObject theWindowObject, UInt32 theEntryID, float theRed, float theGreen, float theBlue, UInt32 theOptions)
{
#pragma unused(theOptions)
	VRScript3DObjPtr	myPointer;
	TQ3AttributeSet		myAttributeSet;
	
	myPointer = (VRScript3DObjPtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QD3DObject, theEntryID);
	if (myPointer != NULL) {
	
		myAttributeSet = VR3DObjects_GetModelAttributeSet(myPointer);
		if (myAttributeSet != NULL) {
			TQ3ColorRGB				myColor;
			
			// remove the existing diffuse color attribute
			Q3AttributeSet_Clear(myAttributeSet, kQ3AttributeTypeDiffuseColor);
			Q3ColorRGB_Set(&myColor, theRed, theGreen, theBlue);
			Q3AttributeSet_Add(myAttributeSet, kQ3AttributeTypeDiffuseColor, &myColor);
		}
		
		Q3Object_Dispose(myAttributeSet);
	}
}


//////////
//
// VR3DObjects_SetTransparency
// Set the transparency level of a 3D object.
//
//////////

void VR3DObjects_SetTransparency (WindowObject theWindowObject, UInt32 theEntryID, float theRed, float theGreen, float theBlue, UInt32 theOptions)
{
#pragma unused(theOptions)
	VRScript3DObjPtr	myPointer;
	TQ3AttributeSet		myAttributeSet;
	
	myPointer = (VRScript3DObjPtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QD3DObject, theEntryID);
	if (myPointer != NULL) {
	
		myAttributeSet = VR3DObjects_GetModelAttributeSet(myPointer);
		if (myAttributeSet != NULL) {
			TQ3ColorRGB				myColor;
			
			// remove the existing transparency color attribute
			Q3AttributeSet_Clear(myAttributeSet, kQ3AttributeTypeTransparencyColor);
			Q3ColorRGB_Set(&myColor, theRed, theGreen, theBlue);
			Q3AttributeSet_Add(myAttributeSet, kQ3AttributeTypeTransparencyColor, &myColor);
		}
		
		Q3Object_Dispose(myAttributeSet);
	}
}


//////////
//
// VR3DObjects_SetInterpolation
// Set the interpolation style of a 3D object.
//
//////////

void VR3DObjects_SetInterpolation (WindowObject theWindowObject, UInt32 theEntryID, UInt32 theStyle, UInt32 theOptions)
{
#pragma unused(theOptions)
	VRScript3DObjPtr	myPointer;

	myPointer = (VRScript3DObjPtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QD3DObject, theEntryID);
	if (myPointer != NULL) 
		Q3InterpolationStyle_Set(myPointer->fInterpolation, (TQ3InterpolationStyle)theStyle);
}


//////////
//
// VR3DObjects_SetBackfacing
// Set the backfacing style of a 3D object.
//
//////////

void VR3DObjects_SetBackfacing (WindowObject theWindowObject, UInt32 theEntryID, UInt32 theStyle, UInt32 theOptions)
{
#pragma unused(theOptions)
	VRScript3DObjPtr	myPointer;

	myPointer = (VRScript3DObjPtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QD3DObject, theEntryID);
	if (myPointer != NULL) 
		Q3BackfacingStyle_Set(myPointer->fBackFacing, (TQ3BackfacingStyle)theStyle);
}


//////////
//
// VR3DObjects_SetFill
// Set the fill style of a 3D object.
//
//////////

void VR3DObjects_SetFill (WindowObject theWindowObject, UInt32 theEntryID, UInt32 theStyle, UInt32 theOptions)
{
#pragma unused(theOptions)
	VRScript3DObjPtr	myPointer;

	myPointer = (VRScript3DObjPtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QD3DObject, theEntryID);
	if (myPointer != NULL) 
		Q3FillStyle_Set(myPointer->fFillStyle, (TQ3FillStyle)theStyle);
}


//////////
//
// VR3DObjects_SetLocation
// Set the location of a 3D object.
//
//////////

void VR3DObjects_SetLocation (WindowObject theWindowObject, UInt32 theEntryID, float theX, float theY, float theZ, UInt32 theOptions)
{
	VRScript3DObjPtr	myPointer;

	myPointer = (VRScript3DObjPtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QD3DObject, theEntryID);
	if (myPointer != NULL) {
		if (theOptions == kVRValue_Relative) {
			myPointer->fGroupCenter.x -= theX;
			myPointer->fGroupCenter.y += theY;
			myPointer->fGroupCenter.z -= theZ;
		} else {
			myPointer->fGroupCenter.x = -1 * theX;
			myPointer->fGroupCenter.y = theY;
			myPointer->fGroupCenter.z = -1 * theZ;
		}
	}
}


//////////
//
// VR3DObjects_SetRotation
// Set the rotation factors of a 3D object.
//
//////////

void VR3DObjects_SetRotation (WindowObject theWindowObject, UInt32 theEntryID, float theX, float theY, float theZ, UInt32 theOptions)
{
	VRScript3DObjPtr	myPointer;

	myPointer = (VRScript3DObjPtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QD3DObject, theEntryID);
	if (myPointer != NULL) {
		if (theOptions == kVRValue_Relative) {
			myPointer->fRotateFactors.x += theX;
			myPointer->fRotateFactors.y += theY;
			myPointer->fRotateFactors.z += theZ;
		} else {
			myPointer->fRotateFactors.x = theX;
			myPointer->fRotateFactors.y = theY;
			myPointer->fRotateFactors.z = theZ;
		}
	}
}


//////////
//
// VR3DObjects_SetRotationState
// Set the rotation state (on or off) of a 3D object.
//
//////////

void VR3DObjects_SetRotationState (WindowObject theWindowObject, UInt32 theEntryID, Boolean theState, UInt32 theOptions)
{
#pragma unused(theOptions)
	VRScript3DObjPtr	myPointer;

	myPointer = (VRScript3DObjPtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QD3DObject, theEntryID);
	if (myPointer != NULL) {
		if (theState == kVRState_Toggle)
			theState = !(myPointer->fModelIsAnimated);
		myPointer->fModelIsAnimated = theState;
	}
}


//////////
//
// VR3DObjects_SetVisibleState
// Set the visibility state (on or off) of a 3D object.
//
//////////

void VR3DObjects_SetVisibleState (WindowObject theWindowObject, UInt32 theEntryID, Boolean theState, UInt32 theOptions)
{
#pragma unused(theOptions)
	VRScript3DObjPtr	myPointer;

	myPointer = (VRScript3DObjPtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QD3DObject, theEntryID);
	if (myPointer != NULL) {
		if (theState == kVRState_Toggle)
			theState = !(myPointer->fModelIsVisible);
		myPointer->fModelIsVisible = theState;
	}
}


//////////
//
// VR3DObjects_SetTexture
// Set the texture of a 3D object.
//
//////////

void VR3DObjects_SetTexture (WindowObject theWindowObject, UInt32 theEntryID, Boolean isMovie, UInt32 theOptions, char *thePathName)
{
#pragma unused(theOptions)

	VRScript3DObjPtr	myPointer;

	myPointer = (VRScript3DObjPtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QD3DObject, theEntryID);
	if (myPointer != NULL) {
	
		// delete any existing texture structure, since we'll soon create another one	
		if (myPointer->fTexture != NULL) {
			VR3DTexture_Delete(myPointer->fTexture);
			myPointer->fTexture = NULL;
		}
			
		myPointer->fTextureIsMovie = isMovie;
		myPointer->fTexture = VR3DTexture_New(thePathName, isMovie);
		
		if (myPointer->fTexture != NULL)
			VR3DTexture_AddToGroup(myPointer->fTexture, myPointer->fModel);
	}
}


//////////
//
// VR3DObjects_AnimateModel
// Animate the QuickDraw 3D model.
//
//////////

void VR3DObjects_AnimateModel (VRScript3DObjPtr thePointer)
{
	TQ3Matrix4x4		myMatrix;
	TQ3Vector3D			myVector;
	
	if (thePointer != NULL) {		
		// rotate the object around the local x axis
		Q3Vector3D_Set(&myVector, 1.0, 0.0, 0.0);
		Q3Matrix4x4_SetRotateAboutAxis(&myMatrix, &(thePointer->fGroupCenter), &myVector, thePointer->fRotateFactors.x);
		Q3Matrix4x4_Multiply(&(thePointer->fRotation), &myMatrix, &(thePointer->fRotation));
		
		// rotate the object around the local y axis
		Q3Vector3D_Set(&myVector, 0.0, 1.0, 0.0);
		Q3Matrix4x4_SetRotateAboutAxis(&myMatrix, &(thePointer->fGroupCenter), &myVector, thePointer->fRotateFactors.y);
		Q3Matrix4x4_Multiply(&(thePointer->fRotation), &myMatrix, &(thePointer->fRotation));
		
		// rotate the object around the local z axis
		Q3Vector3D_Set(&myVector, 0.0, 0.0, 1.0);
		Q3Matrix4x4_SetRotateAboutAxis(&myMatrix, &(thePointer->fGroupCenter), &myVector, thePointer->fRotateFactors.z);
		Q3Matrix4x4_Multiply(&(thePointer->fRotation), &myMatrix, &(thePointer->fRotation));
	}
}


//////////
//
// VR3DObjects_DrawModel
// Draw any QuickDraw 3D models into the (offscreen GWorld) pixmap draw context.
// This routine is called only in the prescreen buffer imaging complete procedure.
//
//////////

TQ3Status VR3DObjects_DrawModel (WindowObject theWindowObject)
{
	ApplicationDataHdl	myAppData;
	TQ3Status			myStatus = kQ3Failure;
	TQ3ViewObject		myView;
	VRScript3DObjPtr	myPointer;
	
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);	
	if (myAppData == NULL)
		return(myStatus);
	
	myView = (**myAppData).fView;
	if (myView == NULL)
		return(myStatus);
	
	// if the camera needs to be updated, do so now
	if ((**myAppData).fViewHasChanged)
		VR3DObjects_SetCamera(theWindowObject);

	// our rendering loop
	myStatus = Q3View_StartRendering(myView);
	if (myStatus != kQ3Failure)
		do {
			// walk our linked list of 3D objects and render any visible objects,
			// after updating any object-specific properties 
			myPointer = (VRScript3DObjPtr)(**myAppData).fListArray[kVREntry_QD3DObject];
			while (myPointer != NULL) {
				if (myPointer->fModelIsVisible) {
					// if there is an active movie texture, advance the movie to the next frame
					if (myPointer->fTexture)
						if (myPointer->fTextureIsMovie)
							VR3DTexture_NextFrame(myPointer->fTexture);
					
					// if animation is active, animate the model
					if (myPointer->fModelIsAnimated)
						VR3DObjects_AnimateModel(myPointer);
						
					// submit the model
					Q3Push_Submit(myView);
					VR3DObjects_SubmitModel(myPointer, myView);
					Q3Pop_Submit(myView);
				}
				
				myPointer = myPointer->fNextEntry;
			}
		
		} while (Q3View_EndRendering(myView) == kQ3ViewStatusRetraverse);
		
	// wait until the rendering is completed...
	Q3View_Sync(myView);
	
	return(myStatus);
}


//////////
//
// VR3DObjects_SubmitModel
// Submit a QuickDraw 3D model for rendering, picking, bounding, or writing.
//
//////////

TQ3Status VR3DObjects_SubmitModel (VRScript3DObjPtr thePointer, TQ3ViewObject theView)
{
	TQ3Vector3D		myTranslate;
	TQ3Vector3D		myScale;

	if (thePointer != NULL) {
		myTranslate = *(TQ3Vector3D *) &(thePointer->fGroupCenter);
		myScale.x = myScale.y = myScale.z = thePointer->fGroupScale;

		Q3Style_Submit(thePointer->fInterpolation, theView);
		Q3Style_Submit(thePointer->fBackFacing, theView);
		Q3Style_Submit(thePointer->fFillStyle, theView);
		Q3MatrixTransform_Submit(&(thePointer->fRotation), theView);
		Q3TranslateTransform_Submit(&myTranslate, theView);
		Q3ScaleTransform_Submit(&myScale, theView);
		Q3DisplayGroup_Submit(thePointer->fModel, theView);
		return(kQ3Success);
	}
	
	return(kQ3Failure);
}


//////////
//
// VR3DObjects_GetModelFromFile
// Open a model contained in a 3DMF file.
//
//////////

TQ3GroupObject VR3DObjects_GetModelFromFile (char *thePathName)
{
	TQ3StorageObject	myStorage;
	TQ3GroupObject 		myModel;
	OSErr				myErr = noErr;
	
#if TARGET_OS_MAC
	FSSpec				myFSSpec;
	short				myRefNum;

	// for Macintosh, open the file and create a Mac storage object
	FSMakeFSSpec(0, 0L, c2pstr(thePathName), &myFSSpec);
	myErr = FSpOpenDF(&myFSSpec, fsRdPerm, &myRefNum);
	if (myErr == noErr)
		myStorage = Q3MacintoshStorage_New(myRefNum);
#endif

#if TARGET_OS_WIN32
	// for Windows, create a UNIX pathname storage object (Q3UnixPathStorage_New calls fopen internally)
	myStorage = Q3UnixPathStorage_New(thePathName);
#endif	
	
	if (myStorage != NULL) {
		TQ3FileObject		myFile;
		TQ3Status			myStatus;
		TQ3Object			myObject;
		TQ3Boolean			isEOF;
		
		// create a file object
		myFile = Q3File_New();
		if (myFile == NULL)
			return(NULL);
		
		// associate the storage with the file
		Q3File_SetStorage(myFile, myStorage);
		Q3Object_Dispose(myStorage);
	
		// read the drawable objects from the file object into a new group
		myModel = Q3DisplayGroup_New();
		if (myModel != NULL) {
			myStatus = Q3File_OpenRead(myFile, NULL);
			if (myStatus == kQ3Success) {
			
				do {
					myObject = Q3File_ReadObject(myFile);
					
					// if object read is not NULL, then process object
					if (myObject != NULL) {
						if (Q3Object_IsDrawable(myObject))
							Q3Group_AddObject(myModel, myObject);
						
						Q3Object_Dispose(myObject);
					}
					
					// check to see whether we've reached the end of file yet
					isEOF = Q3File_IsEndOfFile(myFile);
				} while (isEOF == kQ3False);
				
			}
		}
		
		// close the file object
		Q3File_Close(myFile);
	}
	
	// apply illumination shader to model
	myModel = VR3DObjects_CreateDefaultGroup(myModel);

#if TARGET_OS_MAC
	// close the file, since we've got the data we need
	FSClose(myRefNum);
#endif

#if TARGET_OS_WIN32
	// no action required for UNIX pathname storage
#endif

	return(myModel);
}


///////////
//
// VR3DObjects_PrescreenRoutine
// Draw the 3D objects into the current graphics world.
//
//////////

void VR3DObjects_PrescreenRoutine (QTVRInstance theInstance, WindowObject theWindowObject)
{
#pragma unused(theInstance)

	ApplicationDataHdl	myAppData;
	CGrafPtr			myGWorld;
	GDHandle			myGDevice;
	PixMapHandle		mySrcPixMap;
	PixMapHandle		myDstPixMap;
				
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;

	// render any objects into the pixmap draw context
	VR3DObjects_DrawModel(theWindowObject);
			
	// get the current graphics world
	// (on entry, the current graphics world is set to the prescreen buffer)
	GetGWorld(&myGWorld, &myGDevice);
	
	RGBBackColor(&(**myAppData).fQD3DKeyColor);
	OpColor(&(**myAppData).fQD3DKeyColor);
	
	mySrcPixMap = GetGWorldPixMap((**myAppData).fQD3DDCGWorld);
	myDstPixMap = GetGWorldPixMap(myGWorld);
	// we don't need to lock the source pixel map, since it was locked when the draw context was created
	LockPixels(myDstPixMap);
	
	// copy the rendered image to the current graphics world
	CopyBits((BitMapPtr)(*mySrcPixMap),
			 (BitMapPtr)(*myDstPixMap),
			 &(**myAppData).fQD3DDCGWorld->portRect, 
			 &myGWorld->portRect,
			 srcCopy | transparent, 
			 NULL);
			 
	UnlockPixels(myDstPixMap);
}


//////////
//
// VR3DObjects_SetCamera
// Set the FOV of the 3D camera associated with the specified window object;
// then set the point-of-interest and the location of the 3D camera.
//
//////////

void VR3DObjects_SetCamera (WindowObject theWindowObject)
{
	ApplicationDataHdl		myAppData;
	TQ3ViewObject			myView;
	TQ3CameraObject			myCamera;
	TQ3CameraPlacement		myCameraPos;
	QTVRInstance			myInstance;
	
	if (theWindowObject == NULL)
		return;
	
	// get the QTVR instance associated with the specified window
	myInstance = (**theWindowObject).fInstance;
	if (myInstance == NULL)
		return;
		
	// get the view object associated with the specified window
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);	
	if (myAppData == NULL)
		return;
	myView = (**myAppData).fView;

	// get the camera associated with the view object
	Q3View_GetCamera(myView, &myCamera);
	
	if (myCamera != NULL) {
		float				myFOV, myPan, myTilt;
		TQ3Point3D			myPoint;
		TQ3Vector3D			myUpVector;
		
		// set the camera's field of view
		myFOV = QTVRGetFieldOfView(myInstance);
		
		if ((**myAppData).fQD3DFOVIsVert) {
			Q3ViewAngleAspectCamera_SetFOV(myCamera, myFOV);
		} else {
			float			myRatio;
			
			Q3ViewAngleAspectCamera_GetAspectRatio(myCamera, &myRatio);
			Q3ViewAngleAspectCamera_SetFOV(myCamera, myFOV * myRatio);
		}

		// get the camera's current pan and tilt angles
		myPan = QTVRGetPanAngle(myInstance);
		myTilt = QTVRGetTiltAngle(myInstance);

		// calculate the new point-of-interest
		myPoint.x = sin(myPan) * cos(myTilt) * k3DObjectDistance;
		myPoint.y = sin(myTilt) * k3DObjectDistance;
		myPoint.z = cos(myPan) * cos(myTilt) * k3DObjectDistance;
		
		// calculate the new up vector of the camera
		myUpVector.x = -sin(myTilt) * sin(myPan);
		myUpVector.y = +cos(myTilt);
		myUpVector.z = -sin(myTilt) * cos(myPan);
		Q3Vector3D_Normalize(&myUpVector, &myUpVector);
		
		Q3Camera_GetPlacement(myCamera, &myCameraPos);
		myCameraPos.upVector = myUpVector;
		
		myCameraPos.pointOfInterest = myPoint;
		myCameraPos.cameraLocation = kCameraOrigin;
		Q3Camera_SetPlacement(myCamera, &myCameraPos);
		
		// update the QD3D camera
		Q3View_SetCamera(myView, myCamera);
		Q3Object_Dispose(myCamera);
	}
}


//////////
//
// VR3DObjects_SetCameraAspectRatio
// Adjust the aspect ratio of the QuickDraw 3D camera based on current window shape.
//
//////////

void VR3DObjects_SetCameraAspectRatio (WindowObject theWindowObject)
{
	ApplicationDataHdl				myAppData;
	TQ3ViewObject					myView;
	TQ3CameraObject					myCamera;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);	
	if (myAppData == NULL)
		return;
		
	// get the window's view and camera objects
	myView = (**myAppData).fView;
	Q3View_GetCamera(myView, &myCamera);

	// adjust the aspect ratio of the camera
	if (myCamera != NULL) {
		Rect						myRect;
		float						myRatio;
	
		// get the size of the specified window
		if ((**theWindowObject).fMovie != NULL)
			GetMovieBox((**theWindowObject).fMovie, &myRect);
	
		// calculate the aspect ratio of the movie rectangle
		myRatio = (float)(myRect.right - myRect.left) / (float)(myRect.bottom - myRect.top);

		// determine whether QD3D FOV is horizontal or vertical
		(**myAppData).fQD3DFOVIsVert = (myRatio >= 1.0);
				
		// adjust camera's aspect ratio
		Q3ViewAngleAspectCamera_SetAspectRatio(myCamera, myRatio);
		Q3Object_Dispose(myCamera);
	}
}


//////////
//
// VR3DObjects_UpdateDrawContext
// Delete current QD3D draw context and create a new one based on current window size.
//
//////////

void VR3DObjects_UpdateDrawContext (WindowObject theWindowObject)
{
	ApplicationDataHdl				myAppData;
	TQ3ViewObject					myView;
	TQ3DrawContextObject			myDrawContext;
	CGrafPtr						mySavedPort;
	GDHandle						mySavedDevice;
	Rect							myRect;
	OSErr							myErr = noErr;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);	
	if (myAppData == NULL)
		return;
		
	// lock the application data handle
	HLock((Handle)myAppData);
	
	// get the size of the movie in the specified window
	if ((**theWindowObject).fMovie != NULL)
		GetMovieBox((**theWindowObject).fMovie, &myRect);
	
	// get the current drawing environment
	GetGWorld(&mySavedPort, &mySavedDevice);

	// update the pixmap draw context GWorld: dispose of the existing one and then allocate a new one
	DisposeGWorld((**myAppData).fQD3DDCGWorld);
	(**myAppData).fQD3DDCGWorld = NULL;
	
	myErr = QTNewGWorld(&(**myAppData).fQD3DDCGWorld, kOffscreenPixelType, &myRect, NULL, NULL, kICMTempThenAppMemory);
	if (myErr != noErr)
		goto bail;

	// get the window's view and draw context objects
	myView = (**myAppData).fView;
	Q3View_GetDrawContext(myView, &myDrawContext);

	// associate the new GWorld with the QD3D draw context
	if (myDrawContext != NULL) {
		// get rid of existing draw context	
		Q3Object_Dispose(myDrawContext);		
	
		// create and set new draw context
		myDrawContext = VR3DObjects_CreateDrawContext((**myAppData).fQD3DDCGWorld);
		Q3View_SetDrawContext(myView, myDrawContext);
		Q3Object_Dispose(myDrawContext);
	}
	
bail:
	// restore the original drawing environment
	SetGWorld(mySavedPort, mySavedDevice);
	HUnlock((Handle)myAppData);
}


//////////
//
// VR3DObjects_DumpEntryMem
// Release any memory associated with the specified list entry.
//
//////////

void VR3DObjects_DumpEntryMem (VRScript3DObjPtr theEntry)
{	
	if (theEntry != NULL) {
		if (theEntry->fModel)
			Q3Object_Dispose(theEntry->fModel);
		if (theEntry->fInterpolation)
			Q3Object_Dispose(theEntry->fInterpolation);
		if (theEntry->fBackFacing)
			Q3Object_Dispose(theEntry->fBackFacing);
		if (theEntry->fFillStyle)
			Q3Object_Dispose(theEntry->fFillStyle);
		VR3DTexture_Delete(theEntry->fTexture);
	}
}

#endif	// QD3D_AVAIL
