TumblerSource/Tumbler_document.c

//  Tumbler_document.c
//
//  Document handling routines.
//      
//  Author:     Nick Thompson & Pablo Fernicola, with thanks to the QuickDraw 3D team
//
//  Copyright © 1992-95 Apple Computer, Inc., All Rights Reserved
//
//  Modification History:
//
//  11/26/94    nick    clean up, remove old TE references from dragtext, remove
//                      all other dead code.
//                      Modify CloseDocument & CloseAllDocuments to return a Boolean.
//  01/26/95    nick    parameterized NewDocument to take a (possibly null) group
//                      and viewhints.  Factored this routine as it was way too long
//
// to do: store the document reference as a handle in the window's refcon
//
 
#include <Drag.h>
#include <QuickDraw.h>
 
 
#include "Tumbler_globals.h"
#include "Tumbler_prototypes.h"
#include "Tumbler_resources.h"
 
#include "QD3DDrawContext.h"
#include "QD3DCamera.h"
#include "QD3DView.h"
#include "QD3DLight.h"
#include "QD3DGeometry.h"
#include "QD3DSet.h"
#include "QD3DTransform.h"
#include "QD3DShader.h"
#include "QD3DShader.h"
#include "QD3DMath.h"
#include "QD3DIO.h"
 
#include "Tumbler_camera.h"
#include "Tumbler_document.h"
#include "Tumbler_file.h"
 
#include "Tumbler_utility.h"
 
extern pascal OSErr MyTrackingHandler(short message, WindowPtr theWindow,
                                      void *handlerRefCon, DragReference theDrag);
 
extern pascal OSErr MyReceiveDropHandler(WindowPtr theWindow, void *handlerRefCon,
                                         DragReference theDrag);
 
//----------------------------------------------------------------------
TQ3Status TumblerDocument_NewLights( DocumentPtr theDocument ) 
{
    TQ3GroupObject          lightGroup;
    TQ3LightObject          light;
    TQ3PointLightData       pointData;
    TQ3LightData                ambientData;
    
    lightGroup = Q3LightGroup_New();
 
    pointData.lightData.isOn        = kQ3True;
    pointData.lightData.brightness  = 0.950;
    pointData.lightData.color.r     = 1.0;
    pointData.lightData.color.g     = 1.0;
    pointData.lightData.color.b     = 1.0;
    pointData.location.x            = -10.0;
    pointData.location.y            = 10.0;
    pointData.location.z            = 30.0;
    pointData.castsShadows          = kQ3False;
    pointData.attenuation           = kQ3AttenuationTypeNone;
    
    light = Q3PointLight_New(&pointData);
    Q3Group_AddObject(lightGroup, light);
    Q3Object_Dispose(light);
 
    ambientData.isOn        = kQ3True;
    ambientData.brightness  = 0.70;
    ambientData.color.r     = 0.4;
    ambientData.color.g     = 0.4;
    ambientData.color.b     = 0.4;
    
    light = Q3AmbientLight_New(&ambientData);
    Q3Group_AddObject(lightGroup, light);
    Q3Object_Dispose(light);
 
    pointData.lightData.isOn        = kQ3False;
    pointData.lightData.brightness  = 0.80;
    pointData.lightData.color.r     = 1.0;
    pointData.lightData.color.g     = 0.0;
    pointData.lightData.color.b     = 0.0;
    pointData.location.x            = -10.0;
    pointData.location.y            = 0.0;
    pointData.location.z            = 5.0;
    pointData.castsShadows          = kQ3False;
    pointData.attenuation           = kQ3AttenuationTypeInverseDistance;
    
    light = Q3PointLight_New(&pointData);
    Q3Group_AddObject(lightGroup, light);
    Q3Object_Dispose(light);
 
    pointData.lightData.isOn        = kQ3False;
    pointData.lightData.brightness  = 0.80;
    pointData.lightData.color.r     = 0.0;
    pointData.lightData.color.g     = 1.0;
    pointData.lightData.color.b     = 0.0;
    pointData.location.x            = 10.0;
    pointData.location.y            = 0.0;
    pointData.location.z            = 5.0;
    pointData.castsShadows          = kQ3False;
    pointData.attenuation           = kQ3AttenuationTypeInverseDistance;
    
    light = Q3PointLight_New(&pointData);
    Q3Group_AddObject(lightGroup, light);
    Q3Object_Dispose(light);
 
 
    pointData.lightData.isOn        = kQ3False;
    pointData.lightData.brightness  = 0.80;
    pointData.lightData.color.r     = 0.0;
    pointData.lightData.color.g     = 0.0;
    pointData.lightData.color.b     = 1.0;
    pointData.location.x            = 10.0;
    pointData.location.y            = 0.0;
    pointData.location.z            = 5.0;
    pointData.castsShadows          = kQ3False;
    pointData.attenuation           = kQ3AttenuationTypeInverseDistance;
    
    light = Q3PointLight_New(&pointData);
    Q3Group_AddObject(lightGroup, light);
    Q3Object_Dispose(light);
 
    Q3View_SetLightGroup(theDocument->theView, lightGroup);
    Q3Object_Dispose(lightGroup);
//      
    {
#if defined(ESCHER_VER_15) && ESCHER_VER_15
 
        TQ3ColorRGB                     ellipsoidColor;
        TQ3EllipsoidData                    ellipsoidData;
        TQ3GeometryObject               ellipsoid;
 
 
        TQ3RotateTransformData          rotateData;
        
        theDocument->dynamicLights = Q3DisplayGroup_New();
        
        ellipsoidData.origin.x = -10.0;
        ellipsoidData.origin.y = 0.0;
        ellipsoidData.origin.z = 5.0;
        
        ellipsoidData.orientation.x = 0.0;
        ellipsoidData.orientation.y = 1.0;
        ellipsoidData.orientation.z = 0.0;
        
        ellipsoidData.majorRadius.x = 0.0;
        ellipsoidData.majorRadius.y = 0.0;
        ellipsoidData.majorRadius.z = 1.0;
        
        ellipsoidData.minorRadius.x = 1.0;
        ellipsoidData.minorRadius.y = 0.0;
        ellipsoidData.minorRadius.z = 0.0;
 
        ellipsoidData.ellipsoidAttributeSet = Q3AttributeSet_New();
        
        /* Red light ball */
        ellipsoidColor.r = 1.0;
        ellipsoidColor.g = 0.0;
        ellipsoidColor.b = 0.0;
        
        Q3AttributeSet_Add( ellipsoidData.ellipsoidAttributeSet, 
        kQ3AttributeTypeDiffuseColor, &ellipsoidColor);
 
        Q3AttributeSet_Add(ellipsoidData.ellipsoidAttributeSet, 
        kQ3AttributeTypeSpecularColor, &ellipsoidColor);
    
        ellipsoid = Q3Ellipsoid_New(&ellipsoidData);
        Q3Object_Dispose(ellipsoidData.ellipsoidAttributeSet);
        theDocument->light1 = Q3OrderedDisplayGroup_New();
        Q3Group_AddObject(theDocument->light1, ellipsoid);
        Q3Object_Dispose(ellipsoid);
 
        rotateData.axis = kQ3AxisY;
        rotateData.radians = 0.0;
        
        theDocument->lightXform1 = Q3RotateTransform_New(&rotateData);
        Q3Group_AddObject(theDocument->light1, theDocument->lightXform1);
        
        Q3Group_AddObject(theDocument->dynamicLights, theDocument->light1);
        
        /* Green light ball */
        ellipsoidData.origin.x = 10.0;
        ellipsoidData.origin.y = 0.0;
        ellipsoidData.origin.z = 5.0;
 
        ellipsoidData.ellipsoidAttributeSet = Q3AttributeSet_New();
        
        ellipsoidColor.r = 0.0;
        ellipsoidColor.g = 1.0;
        ellipsoidColor.b = 0.0;
        Q3AttributeSet_Add( ellipsoidData.ellipsoidAttributeSet, 
        kQ3AttributeTypeDiffuseColor, (const void *) &ellipsoidColor);
 
        Q3AttributeSet_Add(ellipsoidData.ellipsoidAttributeSet, 
        kQ3AttributeTypeSpecularColor, &ellipsoidColor);
    
        ellipsoid = Q3Ellipsoid_New(&ellipsoidData);
        Q3Object_Dispose(ellipsoidData.ellipsoidAttributeSet);
        theDocument->light2 = Q3OrderedDisplayGroup_New();
        Q3Group_AddObject(theDocument->light2, ellipsoid);
        Q3Object_Dispose(ellipsoid);                            
 
        theDocument->lightXform2 = Q3RotateTransform_New(&rotateData);
        Q3Group_AddObject(theDocument->light2, theDocument->lightXform2);
 
        Q3Group_AddObject(theDocument->dynamicLights, theDocument->light2);
        
        /* Blue light ball */
        ellipsoidData.origin.x = 5.0;
        ellipsoidData.origin.y = 0.0;
        ellipsoidData.origin.z = 10.0;
 
        ellipsoidData.ellipsoidAttributeSet = Q3AttributeSet_New();
        
        ellipsoidColor.r = 0.0;
        ellipsoidColor.g = 0.0;
        ellipsoidColor.b = 1.0;
        Q3AttributeSet_Add( ellipsoidData.ellipsoidAttributeSet, 
        kQ3AttributeTypeDiffuseColor, (const void *) &ellipsoidColor);
 
        Q3AttributeSet_Add(ellipsoidData.ellipsoidAttributeSet, 
        kQ3AttributeTypeSpecularColor, &ellipsoidColor);
    
        ellipsoid = Q3Ellipsoid_New(&ellipsoidData);
        Q3Object_Dispose(ellipsoidData.ellipsoidAttributeSet);
        theDocument->light3 = Q3OrderedDisplayGroup_New();
        Q3Group_AddObject(theDocument->light3, ellipsoid);
        Q3Object_Dispose(ellipsoid);                            
 
        rotateData.axis = kQ3AxisX;
        theDocument->lightXform3 = Q3RotateTransform_New(&rotateData);
        Q3Group_AddObject(theDocument->light3, theDocument->lightXform3);
 
        Q3Group_AddObject(theDocument->dynamicLights, theDocument->light3);
#endif      
    }
    
    theDocument->animateLights = kQ3False;
 
    return(kQ3Success);
}
 
//-----------------------------------------------------------------------------------
TQ3CameraObject TumblerDocument_NewCamera(WindowPtr theWindow)
{
    TQ3ViewAngleAspectCameraData    perspectiveData;
    TQ3CameraObject             camera;
    
    TQ3Point3D                  from    = { 0.0, 0.0, 30.0 };
    TQ3Point3D                  to      = { 0.0, 0.0, 0.0 };
    TQ3Vector3D                     up      = { 0.0, 1.0, 0.0 };
 
    float                       fieldOfView = .52359333333;
    float                       hither      =  0.001;
    float                       yon         =  1000;
    
    TQ3Status                   returnVal = kQ3Failure ;
 
 
    perspectiveData.cameraData.placement.cameraLocation     = from;
    perspectiveData.cameraData.placement.pointOfInterest    = to;
    perspectiveData.cameraData.placement.upVector           = up;
 
    perspectiveData.cameraData.range.hither = hither;
    perspectiveData.cameraData.range.yon    = yon;
 
    perspectiveData.cameraData.viewPort.origin.x = -1.0;
    perspectiveData.cameraData.viewPort.origin.y = 1.0;
    perspectiveData.cameraData.viewPort.width = 2.0;
    perspectiveData.cameraData.viewPort.height = 2.0;
    
    perspectiveData.fov             = fieldOfView;
    perspectiveData.aspectRatioXToY =
        (float) (theWindow->portRect.right - theWindow->portRect.left) / 
        (float) (theWindow->portRect.bottom - theWindow->portRect.top);
        
    camera = Q3ViewAngleAspectCamera_New(&perspectiveData);
 
    return camera ;
}
 
//--------------------------------------------------------------------------------
 
#ifdef PODIUM_APP
 
TQ3DrawContextObject TumblerDocument_NewPixmapDrawContext( DocumentPtr theDocument )
{
    TQ3PixmapDrawContextData    myDrawContextData;
    TQ3ColorARGB                clearColor = {1.0, 1.0, 1.0, 1.0} ;
    TQ3DrawContextObject        myDrawContext ;
    GWorldPtr                   theGWorld ;
    PixMapHandle                hPixMap ;
    Rect                        srcRect ;
    
    theGWorld = theDocument->geometriesOffscreen ;
    
    //  Fill in draw context data.
    myDrawContextData.drawContextData.clearImageMethod = kQ3ClearMethodWithColor;
    myDrawContextData.drawContextData.clearImageColor  = clearColor;
 
    myDrawContextData.drawContextData.paneState = kQ3False;
    myDrawContextData.drawContextData.maskState = kQ3False;
    
    myDrawContextData.drawContextData.doubleBufferState = kQ3False;
    
    hPixMap = GetGWorldPixMap(theGWorld);
    LockPixels(hPixMap);
 
    srcRect = theGWorld->portRect;
 
    myDrawContextData.pixmap.width = srcRect.right  - srcRect.left;
    myDrawContextData.pixmap.height= srcRect.bottom - srcRect.top;
    
    myDrawContextData.pixmap.rowBytes = (**hPixMap).rowBytes & 0x7FFF;
    myDrawContextData.pixmap.pixelType = kQ3PixelTypeRGB32;
    myDrawContextData.pixmap.pixelSize = 32;
 
    myDrawContextData.pixmap.bitOrder  = kQ3EndianBig;
    myDrawContextData.pixmap.byteOrder = kQ3EndianBig;
    
    myDrawContextData.pixmap.image      = (**hPixMap).baseAddr;
    
    return Q3PixmapDrawContext_New(&myDrawContextData);
}
 
#endif
 
//--------------------------------------------------------------------------------
 
TQ3DrawContextObject TumblerDocument_NewDrawContext( WindowPtr theWindow )
{
    TQ3DrawContextData      myDrawContextData;
    TQ3MacDrawContextData   myMacDrawContextData;
    TQ3DrawContextObject    myDrawContext ;
    TQ3ColorARGB            clearColor = {1.0, kTumblerDefaultClearColor, kTumblerDefaultClearColor, kTumblerDefaultClearColor};    // lt gray
 
    //  Fill in draw context data.
    myDrawContextData.clearImageMethod = kQ3ClearMethodWithColor;
    myDrawContextData.clearImageColor = clearColor;
    myDrawContextData.pane.min.x = (theWindow->portRect).left;
    myDrawContextData.pane.max.x = (theWindow->portRect).right; 
    myDrawContextData.pane.min.y = (theWindow->portRect).top;
    myDrawContextData.pane.max.y = (theWindow->portRect).bottom;
    myDrawContextData.paneState = kQ3False;
    myDrawContextData.maskState = kQ3False;
    myDrawContextData.doubleBufferState = kQ3True;
 
    myMacDrawContextData.drawContextData = myDrawContextData;
    
    myMacDrawContextData.library = kQ3Mac2DLibraryNone;
    myMacDrawContextData.window = (CWindowPtr) theWindow;
    myMacDrawContextData.grafPort = nil;
    myMacDrawContextData.viewPort = nil;
    
    //  Create draw context and return it, if itÕs nil the caller must handle
    myDrawContext = Q3MacDrawContext_New(&myMacDrawContextData) ;
 
    return myDrawContext ;
}
 
 
//--------------------------------------------------------------------------------
TQ3Status   TumblerDocument_UpdateView( DocumentPtr theDocument, TQ3SharedObject viewHints )
{
    TQ3Object               tempObject;
    TQ3CameraObject         camera ;
    
    if( theDocument->theView == NULL)
        return kQ3Failure ;
        
    TumblerDocument_NewLights( theDocument ) ;
 
    if( viewHints == NULL ) {
        
        // if we were called with null viewhints, then just set up
        // sensible defaults for the current document
 
        camera = TumblerDocument_NewCamera( theDocument->theWindow ) ;
        if( camera != NULL ) {
            Q3View_SetCamera(theDocument->theView, camera );
            Q3Object_Dispose(camera);
        }
        else 
            return kQ3Failure ;
        
        Q3View_SetRendererByType(theDocument->theView, kQ3RendererTypeInteractive);
    }
    else {
    
        // we think we have valid viewhints, so attempt to 
        // get the information we need from the viewhints 
        // passed in, where we dont have an item, then make
        // a sensible default.
        
        // renderer information
        Q3ViewHints_GetRenderer(viewHints, &tempObject);
#ifndef PODIUM_APP
        if (tempObject != NULL)
        {
            Q3View_SetRenderer(theDocument->theView, tempObject);
            Q3Object_Dispose(tempObject);
        }
        else
            Q3View_SetRendererByType(theDocument->theView, kQ3RendererTypeWireFrame);
#else
            Q3View_SetRendererByType(theDocument->theView, kQ3RendererTypeInteractive);
#endif
    
        // camera information
        tempObject = NULL ;
//      Q3ViewHints_GetCamera(viewHints, &tempObject);
        if (tempObject != NULL) {
            Q3View_SetCamera(theDocument->theView, tempObject);
            Q3Object_Dispose(tempObject);
        }   
        else {
            camera = TumblerDocument_NewCamera( theDocument->theWindow ) ;
            if( camera != NULL ) {
                Q3View_SetCamera(theDocument->theView, camera );
                Q3Object_Dispose(camera);
            }
            else 
                return kQ3Failure ;
#ifndef PODIUM_APP                  
            AdjustCamera(theDocument,
                                theDocument->theWindow->portRect.right - theDocument->theWindow->portRect.left,
                                theDocument->theWindow->portRect.bottom - theDocument->theWindow->portRect.top);    
 
#else
            AdjustCamera(theDocument,
                                theDocument->geometriesOffscreen->portRect.right - theDocument->geometriesOffscreen->portRect.left,
                                theDocument->geometriesOffscreen->portRect.bottom - theDocument->geometriesOffscreen->portRect.top);    
#endif
        }       
        
        // light information
        Q3ViewHints_GetLightGroup(viewHints, &tempObject);
        if (tempObject != NULL) {
            Q3View_SetLightGroup(theDocument->theView, tempObject);
            Q3Object_Dispose(tempObject);
        }           
        else {
            TumblerDocument_NewLights( theDocument ) ;
        }
    
    }
    
    // replace the existing view hints
    if( theDocument->viewHints ) {
        Q3Object_Dispose(theDocument->viewHints);
    }
    theDocument->viewHints = Q3ViewHints_New(theDocument->theView);
    
    SetPort( (GrafPtr)theDocument->theWindow) ;
    InvalRect( &theDocument->theWindow->portRect ) ;
    
    return kQ3Success ;
}
 
 
 
 
 
//--------------------------------------------------------------------------------
//  NewDocument is called when a new document window is needed. Creation of
//  document data structures is handled here.
//
 
DocumentPtr NewDocument( TQ3SharedObject viewHints, TQ3GroupObject group )
{
    DocumentPtr theDocument;
    WindowPtr       theWindow;
    Rect            theRect = { 0, 0, 16, 16 };
    Point           thePoint;
    TQ3Param2D      uvValues = {-1.0, -1.0};
    
    TQ3DrawContextObject        theDrawContext ;
    TQ3CameraObject         camera ;
    TQ3AttributeSet         viewSet;
    
    if (gDocumentCount == MaxDocumentCount)
        return( (DocumentPtr )nil );
        
        
    // make sure the pointer is initialised to nil, because we rely on
    // the fields being non nil in order to dispose of the correctly.
    theDocument = gDocumentList[gDocumentCount++] =
                            (DocumentPtr )NewPtrClear(sizeof(DocumentRecord));
 
#ifdef PODIUM_APP
    {
        // for podium set up two GWorlds, on for the bg picture, one for the 
        // pixmap draw context that we'll draw the geomentries into
        
        Rect        defaultGeometryRect = { 0, 0, 200, 200 } ;
        GWorldPtr   savedGWorld ;
        GDHandle    savedGDH ;
        
        GetGWorld( &savedGWorld, &savedGDH ) ;
        
        if( NewGWorld(&theDocument->bgOffscreen, 16, &defaultGeometryRect, nil, nil, 0L ) != noErr ) {
            CloseDocument( theDocument ) ;
            return NULL ;
        }
        SetGWorld( theDocument->bgOffscreen, nil ) ;
        EraseRect( &theDocument->bgOffscreen->portRect ) ;
        
        if( NewGWorld(&theDocument->screenBuffer, 16, &defaultGeometryRect, nil, nil, 0L ) != noErr ) {
            CloseDocument( theDocument ) ;
            return NULL ;
        }
        SetGWorld( theDocument->screenBuffer, nil ) ;
        EraseRect( &theDocument->screenBuffer->portRect ) ;
        
        if( NewGWorld(&theDocument->geometriesOffscreen, 32, &defaultGeometryRect, nil, nil, 0L ) != noErr ) {
            CloseDocument( theDocument ) ;
            return NULL ;
        }
        SetGWorld( theDocument->geometriesOffscreen, nil ) ;
        EraseRect( &theDocument->geometriesOffscreen->portRect ) ;
        theDocument->dropArea = defaultGeometryRect ;
        
        // create a 1-bit GW for the mask
//      if( NewGWorld( &theDocument->maskOffscreen, 1, &theDocument->geometriesOffscreen->portRect, nil, nil, 0L)  != noErr ) {
//          CloseDocument( theDocument ) ;
//          return NULL ;
//      }
//      SetGWorld( theDocument->maskOffscreen, nil ) ;
//      EraseRect( &theDocument->maskOffscreen->portRect ) ;
        
        SetGWorld( savedGWorld, savedGDH ) ;
        
        theDocument->currentInterpolation = kQ3InterpolationStyleVertex;
    }
#endif
 
 
    theWindow = GetNewCWindow(WindowTemplateID, 0L, (WindowPtr) -1L);
 
    if (theWindow == nil)
        return( (DocumentPtr )nil );
 
    
    theDocument->theWindow = theWindow ;
                    
    SetWRefCon(theWindow, (long)theDocument ) ;
 
    SetPort(theWindow);
 
#ifndef PODIUM_APP
    // only want to set the bg color up for Tumbler
    {
        RGBColor color = { 0xFFFF, 0xFFFF, 0xFFFF};
        theDocument->clearColor.r = kTumblerDefaultClearColor ;
        theDocument->clearColor.g = kTumblerDefaultClearColor ;
        theDocument->clearColor.b = kTumblerDefaultClearColor ;
        
        color.red *= theDocument->clearColor.r;
        color.green *= theDocument->clearColor.r;
        color.blue *= theDocument->clearColor.r;
        
        RGBBackColor(&color);
        EraseRect(&theWindow->portRect);
        
        color.blue = color.green = color.red  =  0xFFFF;
        RGBBackColor(&color);
    }
#endif
 
    thePoint = *((Point *) (&theWindow->portRect.top));
    LocalToGlobal(&thePoint);
    if (thePoint.h < 10) {
        MoveWindow(theWindow, InitialH, InitialV, false);
    }
 
    theDocument->fRefNum = 0;
 
    // zero out the file spec
    theDocument->theFileSpec.vRefNum = 0 ;
    theDocument->theFileSpec.parID = 0 ;
    theDocument->theFileSpec.name[0] = '\0' ;
    
    theDocument->dirty = false;
    
    // set up the draw context, camera and lights
#ifndef PODIUM_APP
    theDrawContext = TumblerDocument_NewDrawContext( theWindow ) ;
#else
    theDrawContext = TumblerDocument_NewPixmapDrawContext( theDocument ) ;
 
#endif
    if( theDrawContext != NULL ) {
        TQ3ColorRGB         clearColor = {1.0, 1.0, 1.0};
 
        theDocument->theView = Q3View_New();
    
        Q3View_GetDefaultAttributeSet(theDocument->theView, &viewSet);
        Q3AttributeSet_Add(viewSet, 
            kQ3AttributeTypeSpecularColor, &clearColor);
        Q3AttributeSet_Add(viewSet, 
            kQ3AttributeTypeSurfaceUV, &clearColor);
        Q3AttributeSet_Add(viewSet, 
            kQ3AttributeTypeShadingUV, &clearColor);
        Q3Object_Dispose(viewSet);
        
        Q3View_SetDrawContext(theDocument->theView, theDrawContext);
        Q3Object_Dispose(theDrawContext);
    }
    else
        return NULL ;
 
                            
    InstallTrackingHandler(NewDragTrackingHandlerProc(MyTrackingHandler), theWindow, (void *) theDocument);
    InstallReceiveHandler(NewDragReceiveHandlerProc(MyReceiveDropHandler), theWindow, (void *) theDocument);
 
    Q3Matrix4x4_SetIdentity(&theDocument->modelRotation);
    
    theDocument->animateModel = kQ3False;
    theDocument->light = kQ3False;
    theDocument->shaded = kQ3False;
    theDocument->documentGroup = nil;
    
#ifndef PODIUM_APP
    theDocument->currentInterpolation = kQ3InterpolationStyleNone;
#endif
 
    theDocument->backfacingStyle = Q3BackfacingStyle_New(kQ3BackfacingStyleRemove ) ;
    theDocument->viewHints = Q3ViewHints_New(theDocument->theView);\
    theDocument->illuminationShader = Q3PhongIllumination_New();
 
 
    return(theDocument);
}
 
 
//----------------------------------------------------------------------------------
//  CloseDocument is called when a document window is being closed. Storage
//  of the document file and disposal of document data structures is handled
//  here.
 
Boolean CloseDocument( DocumentPtr theDocument )
 
{   short           index, response;
    Str255          theName, theVerb;
    Boolean         couldCloseIt = false ;
 
    index = 0;
    
    while ((gDocumentList[index] != theDocument) && (index < MaxDocumentCount))
        index++;
 
    if (gDocumentList[index] == theDocument) {
 
        if (theDocument->dirty) {
            GetWTitle(theDocument->theWindow, theName);
            GetIndString(theVerb, FileStringsID, (gQuitting) ? slQuittingIndex : slClosingIndex);
            ParamText( (ConstStr255Param)&theName, (ConstStr255Param) &theVerb, (ConstStr255Param) "", (ConstStr255Param) "");
            SetCursor(&qd.arrow);
            
            // check with the user - do they want to save this thing
            response = Alert(idSaveChangesALRT, 0L);
 
            if (response == 1) {            // Save
            
                // check to see that we were able to save the 
                // document as requested to by the user
                
                if (!DidSaveDocument(theDocument)) {
                
                    // signal that we are not quitting (if we were actually trying)
                    gQuitting = false;
                    
                    // signal that we couldn't save this document
                    couldCloseIt = false ;
                    
                    return couldCloseIt;
                }
                
            } else if (response == 3) {     /* Don't Save */
                ;
            } else {                        /* Cancel */
                gQuitting = false ;
                
                // signal that we didn't actually want to close this document
                couldCloseIt = false ;
                
                return couldCloseIt;
            }
        }
        
        // if we are here we can assume the document has been saved (if required) and 
        // that we can junk all of the data structures associated with the document
 
        // if it has a file associated, close it
        if (theDocument->fRefNum) {
            FSClose(theDocument->fRefNum);
        }
 
        // remove our drag manager handlers
        RemoveTrackingHandler(NewDragTrackingHandlerProc(MyTrackingHandler), theDocument->theWindow);
        RemoveReceiveHandler(NewDragReceiveHandlerProc(MyReceiveDropHandler), theDocument->theWindow);
 
        // and we can dispose of the window
        if(theDocument->theWindow)
            DisposeWindow(theDocument->theWindow);
 
        // close any holes left in the array we 
        // have for storing our document objects
        while (index < MaxDocumentCount) {
            gDocumentList[index] = gDocumentList[index + 1];
            index++;
        }
 
        // dispose of our QuickDraw 3d view object
        if(theDocument->theView)
            Q3Object_Dispose(theDocument->theView);
 
        if(theDocument->viewHints)
            Q3Object_Dispose(theDocument->viewHints);
 
        // if we have a group associated with the document, then delete that
        if(theDocument->documentGroup)
            Q3Object_Dispose(theDocument->documentGroup);
 
        // dispose of the lights we set up for this document
        if(theDocument->dynamicLights)
            Q3Object_Dispose(theDocument->dynamicLights);
 
        if(theDocument->light1) {
            Q3Object_Dispose(theDocument->light1);
            Q3Object_Dispose(theDocument->lightXform1);
        }
        
        if(theDocument->light2) {
            Q3Object_Dispose(theDocument->light2);
            Q3Object_Dispose(theDocument->lightXform2);
        }
        
        if(theDocument->light3) {
            Q3Object_Dispose(theDocument->light3);
            Q3Object_Dispose(theDocument->lightXform3);
        }
 
        if(theDocument->backfacingStyle) {
            Q3Object_Dispose(theDocument->backfacingStyle);
        }
 
        if(theDocument->illuminationShader) {
            Q3Object_Dispose(theDocument->illuminationShader);
        }
 
#ifdef PODIUM_APP
 
        if(theDocument->bgOffscreen != nil ) {
            DisposeGWorld(theDocument->bgOffscreen) ;
        }
        
        if(theDocument->screenBuffer != nil ) {
            DisposeGWorld(theDocument->screenBuffer) ;
        }
        
        if(theDocument->geometriesOffscreen != nil ) {
            DisposeGWorld(theDocument->geometriesOffscreen) ;
        }
        
//      if(theDocument->maskOffscreen != nil ) {
//          DisposeGWorld(theDocument->maskOffscreen) ;
//      }
        
#endif
 
        // finally dispose of the storage used for the document and 
        // decrement the document count
        if( theDocument != nil )
            DisposePtr((Ptr) theDocument);
        gDocumentCount--;
        
        // signal that we closed this document
        couldCloseIt = true ;
        return couldCloseIt;
        
    }
 
    // signal that we could't actually close this document
    couldCloseIt = false ;
    return couldCloseIt;
}
 
 
//----------------------------------------------------------------------------------
//  Closes all document windows.
//
 
Boolean CloseAllDocuments( void )
{
    short   index ;
    
    for( index = gDocumentCount - 1; index >= 0; index-- ) {
        if(CloseDocument(gDocumentList[ index ]) == false)
            return false;
    }
    return true ;
}
 
 
 
//----------------------------------------------------------------------------------
//  DoActivateDocument is called when an event is received that reports that
//  a document window is being either activated or deactivated.
 
void DoActivateDocument(DocumentPtr theDocument, short activate)
{
    if (theDocument) {
        if (activate) {
        
            // do whatever you'd like to do for a activate event
#ifdef PODIUM_APP           
            void FixGrayRgnAfterContextSwitch( void ) ;
            FixGrayRgnAfterContextSwitch();
#endif          
            
            LoadScrap() ;
 
        } else {
        
            // do whatever you'd like to do for a deactivate event
            UnloadScrap() ;
        }
    }
}
 
 
//----------------------------------------------------------------------------------
//  If the given WindowPtr is a pointer to a document window, this function
//  returns a pointer to a document data structure. If the window is not
//  a document window, the function returns nil.
 
DocumentPtr GetDocumentFromWindow(WindowPtr theWindow)
{
    short       index = 0;
    DocumentPtr theDocument;
 
    if( theWindow == nil )
        return((DocumentPtr ) 0L);
 
    theDocument = (DocumentPtr ) (((WindowPeek) theWindow)->refCon);
 
    while ((gDocumentList[index] != theDocument) && (index < gDocumentCount))
        index++;
 
    if (gDocumentList[index] == theDocument)
        return(theDocument);
    else
        return((DocumentPtr ) 0L);
}
 
 
 
TQ3Status InitializeGroup(TQ3GroupObject group)
{
    TQ3ShaderObject illuminationShader = Q3PhongIllumination_New();
 
    Q3Group_AddObject(group, illuminationShader);
    Q3Object_Dispose(illuminationShader);
    return(kQ3Success);
}