Sources/TriGridShell.c

//
// This is box, the QuickDraw 3D starter program.  Written for the
// Getting started with QuickDraw 3D Develop article.  This app does not have 
// graceful error handling - it's purpose is to illustrate a very basic QuickDraw 
// 3D program.
//
// Nick Thompson - January 6th 1994
//
// Modification History:
//
//  1/6/95      nick    initial version based on cube
//  12/31/94    nick    cube modifications for QuickDraw 3d sample code
//  03/22/95    rdd     adapted from BoxShell.c: added simple picking
//                      and trigrid selection via number keys
//  04/12/95    rdd     added SetCameraLocation.
//  04/14/95    rdd     added menu support.
//  
 
 
// system headers
#include <Dialogs.h>
#include <Devices.h>
#include <DiskInit.h>
#include <Fonts.h>
#include <Menus.h>
#include <QDOffScreen.h>
#include <QuickDraw.h>
#include <Resources.h>
#include <SegLoad.h>
#include <StandardFile.h>
#include <TextEdit.h>
#include <ToolUtils.h>
 
// for QuickDraw 3D
#include "QD3D.h"
#include "QD3DMath.h"
#include "QD3DCamera.h"
#include "QD3DDrawContext.h"
#include "QD3DShader.h"
#include "QD3DTransform.h"
#include "QD3DGroup.h"
#include "QD3DPick.h"
 
 
#include "TriGridShell.h"
#include "TriGrid3DSupport.h"
#include "GeometrySample.h"
#include "Textures2.h"
 
//-------------------------------------------------------------------------------------------
 
struct _documentRecord {
    TQ3ViewObject   fView ;                 // the view for the scene
    TQ3GroupObject  fModel ;                // object in the scene being modelled
    TQ3StyleObject  fInterpolation ;        // interpolation style used when rendering
    TQ3StyleObject  fBackFacing ;           // whether to draw shapes that face away from the camera
    TQ3StyleObject  fFillStyle ;            // whether drawn as solid filled object or decomposed to components
    TQ3Matrix4x4    fRotation ;             // the transform for the model
    unsigned short  fGeometryNum ;          // triGrid geometry 1-9 (menu item number)
    unsigned short  fTextureType ;          // method of uv parameterization for texture (menu item number)
    unsigned short  fPictureNum ;           // PICT (menu item number)
};
 
typedef struct _documentRecord DocumentRec, *DocumentPtr, **DocumentHdl ;
 
 
//-------------------------------------------------------------------------------------------
// function prototypes
 
static void         InitToolbox( void ) ;
static Boolean      SetUpMenus (void) ;
static void         IntializeMenuItems (DocumentPtr theDocument) ;
static void         MainEventLoop( void ) ;
static void         HandleKeyPress(EventRecord *pEvent) ;
static void         DoAboutBox (void);
static void         DoMenuCommand (long menuResult) ;
static void         HandleOSEvent(EventRecord *event) ;
static void         InitDocumentData( DocumentPtr theDocument ) ;
static TQ3Status    DocumentDraw3DData( DocumentPtr theDocument ) ;
static void         DisposeDocumentData( DocumentPtr theDocument) ;
static void         SetCameraLocation (TQ3ViewObject view, float x, float y, float z);
static void         ChangeGeometry (DocumentPtr theDocument) ;
static void         DoPicking(EventRecord *event) ;
 
 
//-------------------------------------------------------------------------------------------
//
 
Boolean         gQuitFlag       = false ;
WindowPtr       gMainWindow     = nil ;
DocumentRec     gDocument ;
Handle          ghMenuBar ;
short           gNumPictures ;
 
 
//-------------------------------------------------------------------------------------------
// main()
// entry point for the application, initialize the toolbox, initialize QuickDraw 3D
// and enter the main event loop.  On exit from the main event loop, we want to call
// the QuickDraw 3D exit function to clean up QuickDraw 3d.
 
void main(void)
{
    TQ3Status   myStatus;
 
    InitToolbox() ;
    SetUpMenus () ;
 
    //  Initialize QuickDraw 3D, open a connection to the QuickDraw 3D library
    myStatus = Q3Initialize();
    if ( myStatus == kQ3Failure )
        DebugStr("\pErInitialize returned failure.");           
 
    // set up our globals
    gQuitFlag = false ;
    gMainWindow = GetNewCWindow(kWindowRsrcID, nil, (WindowPtr)-1);
 
    InitDocumentData( &gDocument ) ;
    IntializeMenuItems ( &gDocument ) ;
 
    MainEventLoop();
    
    DisposeDocumentData( &gDocument ) ;
    
    //  Close our connection to the QuickDraw 3D library
    myStatus = Q3Exit();
    if ( myStatus == kQ3Failure )
        DebugStr("\pErExit returned failure.");
    
}
 
//-------------------------------------------------------------------------------------------
//
 
void InitDocumentData( DocumentPtr theDocument ) 
{
    // sets up the 3d data for the scene
    // Create view for QuickDraw 3D.
    theDocument->fView = MyNewView( (WindowPtr)gMainWindow ) ;
 
    // the drawing styles:
    theDocument->fInterpolation = Q3InterpolationStyle_New(kQ3InterpolationStyleNone) ;
    theDocument->fBackFacing = Q3BackfacingStyle_New(kQ3BackfacingStyleBoth ) ;
    theDocument->fFillStyle = Q3FillStyle_New(kQ3FillStyleFilled ) ;
 
    // set the rotation matrix the identity matrix
    Q3Matrix4x4_SetIdentity(&theDocument->fRotation);       
 
    theDocument->fGeometryNum = iFlat ;
    theDocument->fTextureType = iNoTexture ;
    theDocument->fPictureNum  = iPictureFirst ;
 
    // the main display group:
    theDocument->fModel = MyNewModel() ;
    ChangeGeometry (theDocument);
}
 
void DisposeDocumentData( DocumentPtr theDocument)
{
 
    if(theDocument->fView)
        Q3Object_Dispose(theDocument->fView) ;              // the view for the scene
 
    if(theDocument->fModel)
        Q3Object_Dispose(theDocument->fModel) ;             // object in the scene being modelled
 
    if(theDocument->fInterpolation)
        Q3Object_Dispose(theDocument->fInterpolation) ;     // interpolation style used when rendering
 
    if(theDocument->fBackFacing)
        Q3Object_Dispose(theDocument->fBackFacing) ;        // whether to draw shapes that face away from the camera
 
    if(theDocument->fFillStyle)
        Q3Object_Dispose(theDocument->fFillStyle) ;         // whether drawn as solid filled object or decomposed to components
 
}
//-----------------------------------------------------------------------------
// 
 
TQ3Status DocumentDraw3DData( DocumentPtr theDocument )
{   
    Q3View_StartRendering(theDocument->fView );
    do {
        Q3Style_Submit( theDocument->fInterpolation, theDocument->fView );
        Q3Style_Submit( theDocument->fBackFacing, theDocument->fView );
        Q3Style_Submit( theDocument->fFillStyle, theDocument->fView );
        Q3MatrixTransform_Submit( &theDocument->fRotation, theDocument->fView );
        Q3DisplayGroup_Submit( theDocument->fModel, theDocument->fView );
    } while (Q3View_EndRendering(theDocument->fView) == kQ3ViewStatusRetraverse );
    return kQ3Success ;
}
 
 
//----------------------------------------------------------------------------------
//
void InitToolbox()
{
    Handle      menuBar = nil;
 
    MaxApplZone() ;
    MoreMasters() ; MoreMasters() ; MoreMasters() ; 
    
    InitGraf( &qd.thePort );
    InitFonts();
    InitWindows();
    InitCursor();
 
    FlushEvents( everyEvent, 0 ) ;
    // initialize application globals
    
    gQuitFlag = false;
    
}
 
 
//-------------------------------------------------------------------------------------------
//
Boolean SetUpMenus (void)
{
    Boolean     goodMenus;
 
    goodMenus = true;
 
    ghMenuBar = GetNewMBar (kMenuBarRsrc);
    if (ghMenuBar != nil)
    {
        SetMenuBar (ghMenuBar);
        AppendResMenu (GetMenuHandle (mApple), (ResType) 'DRVR');
        DrawMenuBar ();
    }
    else
    {
        DebugStr ("\pSetUpMenus: Couldn't find menu bar.");
        ghMenuBar = nil;
        goodMenus = false;
    }
 
     return (goodMenus);
}
 
 
//-------------------------------------------------------------------------------------------
//
void IntializeMenuItems (DocumentPtr theDocument)
{
    MenuHandle  hMenu;
    Str255      menuPrefix = {"\pPicture "},
                itemText,
                numString;
    long        item;
    short       rsrcID;
    ResType     rsrcType;
    Handle      hPict;
 
    hMenu = GetMenuHandle(mTexture);
 
    gNumPictures = Count1Resources ('PICT');
 
    /* PICT resources to be used as textures must be numbered starting with kFirstPICTRsrcID */
    SetResLoad (false);
    for (item = 0; item < gNumPictures; item++)
    {
        itemText[0] = 0;
 
        hPict = GetResource ('PICT', kFirstPICTRsrcID + item);
        if (hPict != NULL)
            GetResInfo (hPict, &rsrcID, &rsrcType, itemText);
        else
            break;
 
        if (itemText[0] == 0)
        {
            BlockMove ((Ptr) menuPrefix, (Ptr) itemText, (Size) menuPrefix[0]+1);
            NumToString (item+1, numString);
            itemText[0] += numString[0];
            BlockMove ((Ptr) &numString[1], (Ptr) itemText[itemText[0]+1], (Size) numString[0]);
        }
 
        AppendMenu (hMenu, itemText);
    }
    SetResLoad (true);
 
    gNumPictures = item;
 
    if (theDocument->fTextureType == iNoTexture)
    {
        for (item = iPictureFirst; item < iPictureFirst + gNumPictures; item++)
            DisableItem(hMenu, item);
    }
 
 
    CheckItem (GetMenuHandle (mGeometry), theDocument->fGeometryNum, true);
    CheckItem (hMenu, theDocument->fTextureType, true);
    CheckItem (hMenu, theDocument->fPictureNum,  true);
}
 
 
//-------------------------------------------------------------------------------------------
//
void MainEventLoop()
{
    EventRecord     event;
    WindowPtr       window;
    short           thePart;
    Rect            screenRect, updateRect;
    Point           aPoint = {100, 100};
    
 
    while( !gQuitFlag )
    {
        if (WaitNextEvent( everyEvent, &event, 0, nil ))
        {
 
            switch (event.what) {
                case mouseDown:
                
                    thePart = FindWindow( event.where, &window );
                    
                    switch( thePart ) {
                        case inMenuBar: 
                            DoMenuCommand (MenuSelect (event.where));
                            break;
 
                        case inSysWindow:
                            SystemClick (&event, window);
                            break;
 
                        case inDrag:
                            screenRect = (**GetGrayRgn()).rgnBBox;
                            DragWindow( window, event.where, &screenRect );
                            break ;
                    
                        case inContent:
                            if (window != FrontWindow())
                                SelectWindow( window );
                                DoPicking(&event);
                            break ;
                    
                        case inGoAway:
                            if (TrackGoAway( window, event.where )) {
                                DisposeWindow ( window );
                                gQuitFlag = true;
                            }
                            break ;
                            
                        default:
                        case inGrow:
                        case inZoomIn:
                        case inZoomOut:
                            break ;
                    }
                    break ;
                            
                        
                case updateEvt:
                    window = (WindowPtr)event.message;
                    updateRect = (**(window->visRgn)).rgnBBox;
                    SetPort( window ) ;
                    BeginUpdate( window );
                    DocumentDraw3DData( &gDocument ) ;
                    EndUpdate( window );
                    break ;
                    
                case keyDown:
                case autoKey:
                    HandleKeyPress(&event);
                    break;
                    
                case diskEvt:
                    if ( HiWord(event.message) != noErr ) 
                        (void) DIBadMount(aPoint, event.message);
                    break;
                    
                case osEvt:
                case activateEvt:
                    break;
 
 
            }
        }
        else {
            // we received a null event, rotate the cube
            TQ3Matrix4x4    tmp;
            Rect            theRect = ((GrafPtr)gMainWindow)->portRect ;
            
            SetPort((GrafPtr)gMainWindow) ;
            Q3Matrix4x4_SetRotate_XYZ(&tmp, 0.1, 0.12, 0.08);
            Q3Matrix4x4_Multiply(&gDocument.fRotation, &tmp, &gDocument.fRotation);
 
            InvalRect( &theRect ) ;
        }
    }
}
 
 
//-------------------------------------------------------------------------------------------
//
void HandleKeyPress(EventRecord *pEvent)
{
    char            charCode;
 
    charCode = pEvent->message & charCodeMask;
 
    if (pEvent->modifiers & btnState)
    {
        /* Button is UP with a key */
        if (pEvent->modifiers & cmdKey)
        {
            DoMenuCommand (MenuKey (charCode));
        }
    }
    else
    {
        /* Button is DOWN with a key */
    }
}
 
 
//-------------------------------------------------------------------------------------------
//
void DoAboutBox (void)
{
    DialogPtr   theDialog;
    short       itemHit;
 
    theDialog = GetNewDialog(kDialogRsrcID, nil, (WindowPtr)-1);
    if (theDialog == nil)
        return;
        
    do
    {
        ModalDialog (nil, &itemHit);
    }
    while (itemHit != ok);
 
    DisposeDialog (theDialog);
}
 
 
//-------------------------------------------------------------------------------------------
//
void DoMenuCommand (long menuResult)
{
    short       menuID,
                itemNumber;
 
    if (! menuResult)
        return;
 
    menuID     = HiWord (menuResult);
    itemNumber = LoWord (menuResult);
 
    switch (menuID)
    {
        case mApple:
            switch (itemNumber)
            {
            case iAbout:
                DoAboutBox();
            break;
 
            default:
                {
                    MenuHandle  hMenu;
                    Str255      deskAccName;
                    GrafPtr     oldPort;
 
                    hMenu = GetMenuHandle (menuID);
                    if (hMenu != nil)
                    {
                        GetPort (&oldPort);
 
                        GetMenuItemText (hMenu, itemNumber, deskAccName);
                        (void) OpenDeskAcc (deskAccName);
                        SetPort (oldPort);
                    }
                }
                break;
            }
            break;
 
        case mFile:
            switch (itemNumber)
            {
            case iNew:          break;
            case iOpen:         break;
            case iClose:        break;
 
            case iQuit:
                gQuitFlag = true;break;
 
            default: SysBeep(1);break;
            }
            break;
 
        case mEdit:
            if (! SystemEdit (itemNumber - 1))
                switch (itemNumber)
                {
                case iUndo:     break;
                case iCut:      break;
                case iCopy:     break;
                case iPaste:    break;
                case iClear:    break;
                }
            break;
 
        case mGeometry:
            if (itemNumber != gDocument.fGeometryNum)
            {
                CheckItem (GetMenuHandle (mGeometry), gDocument.fGeometryNum, false);
                gDocument.fGeometryNum = itemNumber;
                CheckItem (GetMenuHandle (mGeometry), gDocument.fGeometryNum, true);
                ChangeGeometry (&gDocument);
            }
            break;
 
        case mTexture:
            if (itemNumber >= iNoTexture  &&  itemNumber <= iFaceTexture)
            {
                if (itemNumber != gDocument.fTextureType)
                {
                    long    item;
 
                    if (gDocument.fTextureType == iNoTexture)
                    {
                        for (item = iPictureFirst; item < iPictureFirst + gNumPictures; item++)
                            EnableItem(GetMenuHandle (menuID), item);
                    }
                    else
                    if (itemNumber == iNoTexture)
                    {
                        for (item = iPictureFirst; item < iPictureFirst + gNumPictures; item++)
                            DisableItem(GetMenuHandle (menuID), item);
                    }
 
                    CheckItem (GetMenuHandle (mTexture), gDocument.fTextureType, false);
                    gDocument.fTextureType = itemNumber;
                    CheckItem (GetMenuHandle (mTexture), gDocument.fTextureType, true);
                    ChangeGeometry (&gDocument);
                }
            }
            else
            if (itemNumber >= iPictureFirst  && itemNumber <= iPictureFirst + gNumPictures - 1)
            {
                if (itemNumber != gDocument.fPictureNum)
                {
                    CheckItem (GetMenuHandle (mTexture), gDocument.fPictureNum, false);
                    gDocument.fPictureNum = itemNumber;
                    CheckItem (GetMenuHandle (mTexture), gDocument.fPictureNum, true);
                    ChangeGeometry (&gDocument);
                }
            }
            break;
 
        default:
            break;
 
    }  /*  switch (menuID)  */
 
    HiliteMenu (0);
}
 
 
//-------------------------------------------------------------------------------------------
//
void SetCameraLocation (TQ3ViewObject view, float x, float y, float z)
{
    TQ3CameraObject camera;
    TQ3CameraData   cameraData;
 
    Q3View_GetCamera(view, &camera);
    if (camera != nil)
    {
        Q3Camera_GetData(camera, &cameraData);
        Q3Point3D_Set(&cameraData.placement.cameraLocation, x, y, z);
        Q3Camera_SetData(camera, &cameraData);
        Q3Object_Dispose(camera);
    }
}
 
 
//-------------------------------------------------------------------------------------------
//
void ChangeGeometry (DocumentPtr theDocument)
{
    TQ3Status           status;
    TQ3Object           object;
    TQ3GroupPosition    position;
    unsigned long       triGridLibNum;
 
    if (theDocument->fModel == nil)
        return;
 
    status = Q3Group_GetFirstPositionOfType(theDocument->fModel, kQ3ShapeTypeGeometry, &position);
    if (status == kQ3Success  &&  position != nil)
    {
        object = Q3Group_RemovePosition(theDocument->fModel, position);
        Q3Object_Dispose(object);
        object = NULL;
    }
 
    switch (theDocument->fTextureType)
    {
        case iNoTexture:        triGridLibNum = (theDocument->fGeometryNum-1) + kGeometryLibraryRange_Simple;           break;
        case iGeometryTexture:  triGridLibNum = (theDocument->fGeometryNum-1) + kGeometryLibraryRange_UVGeoAttributes;  break;
        case iFaceTexture:      triGridLibNum = (theDocument->fGeometryNum-1) + kGeometryLibraryRange_UVFaceAttributes;break;
    }
    object = NewLibraryTriGrid(triGridLibNum);
    if (object == NULL)
        return;
    Q3Group_AddObject (theDocument->fModel, object);
 
    /* Add diffuse color if no texture */
    if (theDocument->fTextureType == iNoTexture)
    {
        TQ3AttributeSet     attrSet;
        TQ3ColorRGB         rgbColor;
 
        attrSet = Q3AttributeSet_New();
        if (attrSet != NULL)
        {
            Q3ColorRGB_Set(&rgbColor, 0.0, 0.75, 0.75);
            status = Q3AttributeSet_Add(attrSet, kQ3AttributeTypeDiffuseColor, &rgbColor);
            if (status == kQ3Success)
            {
                Q3Geometry_SetAttributeSet(object, attrSet);
                Q3Object_Dispose(attrSet);
            }
        }
    }
    else
        /* Add texture shader */
        AddResourceTextureToGroup(theDocument->fPictureNum - iPictureFirst + kFirstPICTRsrcID, theDocument->fModel);
 
    Q3Object_Dispose(object);
 
    /* (These cameral locations could be calculated) */
    switch (theDocument->fGeometryNum)
    {
    case iFlat:         SetCameraLocation (theDocument->fView, 0.0, 0.0,  4.0); break;
    case iTorus:        SetCameraLocation (theDocument->fView, 0.0, 0.0, 10.0); break;
    case iWaveyTorus:   SetCameraLocation (theDocument->fView, 0.0, 0.0, 10.0); break;
    case iSplash:       SetCameraLocation (theDocument->fView, 0.0, 0.0, 25.0); break;
    case iSphere:       SetCameraLocation (theDocument->fView, 0.0, 0.0,  7.0); break;
    case iCone:         SetCameraLocation (theDocument->fView, 0.0, 0.0,  9.0); break;
    case iPipe:         SetCameraLocation (theDocument->fView, 0.0, 0.0, 10.0); break;
    case iSteps:        SetCameraLocation (theDocument->fView, 0.0, 0.0, 30.0); break;
    case iSpring:       SetCameraLocation (theDocument->fView, 0.0, 0.0, 11.0); break;
    }
}
 
 
//-------------------------------------------------------------------------------------------
//
void DoPicking(EventRecord *event)
{
    Point                   screenPoint;
    TQ3WindowPointPickData  withData;
    unsigned long           numPicked;
    TQ3PickObject           pickObject;
    TQ3Status               status;
    TQ3ViewStatus           viewStatus;
    
    withData.data.sort          =   kQ3PickSortNone;
    withData.data.numHitsToReturn=  kQ3ReturnAllHits;
    withData.data.mask          =   kQ3PickDetailMaskObject;
                                
    screenPoint = event->where;
    GlobalToLocal (&screenPoint);
    withData.point.x = screenPoint.h;
    withData.point.y = screenPoint.v;
 
    withData.vertexTolerance = withData.edgeTolerance = 2.0;
 
    pickObject = Q3WindowPointPick_New(&withData);
 
    status = Q3View_StartPicking(gDocument.fView, pickObject);
    if (status == kQ3Failure)
        debugstr ("DoPicking: Q3View_StartPicking failed.");
    do {
        status = Q3DisplayGroup_Submit(gDocument.fModel, gDocument.fView);
        if (status == kQ3Failure)
            debugstr ("DoPicking: Q3View_StartPicking failed.");
 
        viewStatus = Q3View_EndPicking(gDocument.fView);
    } while (viewStatus == kQ3ViewStatusRetraverse);
 
    if (viewStatus != kQ3ViewStatusDone)
        debugstr ("DoPicking: Q3View_EndPicking failed.");
 
 
    if (Q3Pick_GetNumHits(pickObject, &numPicked) && (numPicked != 0)) {
        SetCursor(*GetCursor(plusCursor));
        SysBeep(1);
    } else {
        SetCursor(&qd.arrow);
    }
 
    Q3Object_Dispose(pickObject);
}