sources/PickOne_document.c

/*  PickOne_document.c                                                                          
 
    This contains all the document-specific code.
                                                                                    
    Michael Bishop - August 21 1996                                                 
    Nick Thompson
    Robert Dierkes
    Scott Kuechle - March 1997                                                                              
    (c)1994-97 Apple computer Inc., All Rights Reserved                             
 
*/
 
 
/* --------------------------------------------------------------------
** Includes
*/
#include    <Quickdraw.h>   /*  For Show/HideCursor routines    */
#include    <assert.h>
 
#include    "PickOne_document.h"
#include    "PickOne_documentStructure.h"
 
#include    "PickOne_Support.h"
#include    "PickOne_window.h"
#include    "PickOne_utility.h"
 
#include    "PictRead.h"
 
 
/* QuickDraw 3D stuff */
#include    "QD3DMath.h"
#include    "QD3DCamera.h"
#include    "QD3DTransform.h"
#include    "QD3DGroup.h"
#include    "QD3DShader.h"
#include    "QD3DStorage.h"
#include    "QD3DIO.h"
#include    "QD3DPick.h"
#include    "QD3DRenderer.h"
#include    "QD3DAcceleration.h"
 
 
/* --------------------------------------------------------------------
** Local Functions
*/
static  void            Document_Init( DocumentHdl theDocument) ;
static  unsigned long   Document_DoPick( DocumentPtr theDocument, TQ3PickObject pickObject);
static  TQ3PickObject   Document_NewPick(TQ3WindowPointPickData *pWPPData);
 
/* -------------------------------------------------------------------------------------------
**  Document_New
**  Loads a new Document with a blank (white) canvas for a texture
*/
 
DocumentHdl Document_New(void)
{
    DocumentHdl theDocument = (DocumentHdl)NewHandle( sizeof(DocumentRec)) ;
    
    if( theDocument != NULL )
    {
        Document_Init(theDocument);
        
        /*  Get a window for it */
        (**theDocument).fWindow = Window_New();
        if ((**theDocument).fWindow == NULL) goto bail;
        
        /* store a reference to the document record in the window's refcon */
        Window_SetDocument((**theDocument).fWindow, theDocument ) ;
        
        /*  sets up the 3d data for the scene */
        /*  Create view for QuickDraw 3D. */
        (**theDocument).fView = MyNewView( (**theDocument).fWindow ) ;
        if ((**theDocument).fView == NULL) {    BP_DebugString("View could not be created!");
                                                goto bail;  }
        
        /*  the main display group: */
        (**theDocument).fModel = MyNewModel(theDocument) ;
        if ((**theDocument).fModel == NULL) goto bail;
    
        /*  the drawing styles: */
        (**theDocument).fInterpolation =
                Q3InterpolationStyle_New(kQ3InterpolationStyleNone) ;
        if ((**theDocument).fInterpolation == NULL) goto bail;
        
        (**theDocument).fBackFacing =
                Q3BackfacingStyle_New(kQ3BackfacingStyleRemove ) ;
        if ((**theDocument).fBackFacing == NULL) goto bail;
        
        (**theDocument).fFillStyle =
                Q3FillStyle_New(kQ3FillStyleFilled ) ;
        if ((**theDocument).fFillStyle == NULL) goto bail;
 
    }
    return theDocument ;
bail:
    Document_Dispose(theDocument);
    theDocument = NULL;
    return theDocument;
}
 
/* -------------------------------------------------------------------------------------------
** Document_Dispose
** Destroys and deletes all the data in a document
*/
 
void Document_Dispose( DocumentHdl theDocument)
{
    assert(theDocument != NULL);
    assert(*theDocument != NULL);
    
    if ((**theDocument).fView != NULL)
        Q3Object_Dispose((**theDocument).fView) ;
        
    if ((**theDocument).fModel != NULL)
        Q3Object_Dispose((**theDocument).fModel) ;
        
    if ((**theDocument).fInterpolation != NULL)
        Q3Object_Dispose((**theDocument).fInterpolation) ;
        
    if ((**theDocument).fBackFacing != NULL)
        Q3Object_Dispose((**theDocument).fBackFacing) ;
        
    if ((**theDocument).fFillStyle != NULL)
        Q3Object_Dispose((**theDocument).fFillStyle) ;
    
    if ((**theDocument).fWindow != NULL)
        Window_Dispose ( (**theDocument).fWindow ) ;
 
    /*  Do this last    */
    DisposeHandle( (Handle)theDocument ) ;
}
 
 
 
/* -------------------------------------------------------------------------------------------
**  Document_GetCamera
**  Returns the Camera associated with the document
*/
TQ3CameraObject     Document_GetCamera(DocumentHdl  theDocument)
{
    TQ3CameraObject theCamera;
    
    assert(theDocument != NULL);
    assert(*theDocument != NULL);
    
    Q3View_GetCamera((**theDocument).fView, &theCamera);
    
    return theCamera;
}
 
 
/* -------------------------------------------------------------------------------------------
**  Document_GetRendererPreferences
**  Returns the Renderer Preferences associated with the document
*/
TQ3Status   Document_GetRendererPreferences(DocumentHdl theDocument,
                                            long        *theVendorID,
                                            long        *theEngineID)
{
    TQ3RendererObject   theRenderer;
    TQ3Status           theStatus = kQ3Success;
    
    assert(theDocument != NULL);
    assert(*theDocument != NULL);
    
    if (Q3View_GetRenderer((**theDocument).fView, &theRenderer) != kQ3Failure)
    {
        if (Q3Renderer_GetType(theRenderer) == kQ3RendererTypeInteractive)
            Q3InteractiveRenderer_GetPreferences(theRenderer, theVendorID, theEngineID);
        else theStatus = kQ3Failure;
        
        Q3Object_Dispose(theRenderer);
        
    } else theStatus = kQ3Failure;
    
    return theStatus;
}
 
/* -------------------------------------------------------------------------------------------
**  Document_SetRendererPreferences
**  Sets the Renderer Preferences
*/
TQ3Status   Document_SetRendererPreferences(DocumentHdl theDocument,
                                            long        theVendorID,
                                            long        theEngineID)
{
    TQ3RendererObject   theRenderer;
    TQ3Status           theStatus = kQ3Success;
    
    assert(theDocument != NULL);
    assert(*theDocument != NULL);
    
    if (Q3View_GetRenderer((**theDocument).fView, &theRenderer) != kQ3Failure)
    {
        if (Q3Renderer_GetType(theRenderer) == kQ3RendererTypeInteractive)
        {
            Q3InteractiveRenderer_SetPreferences(theRenderer, theVendorID, theEngineID);
            Q3View_SetRenderer((**theDocument).fView, theRenderer);
        }
        else theStatus = kQ3Failure;
        
        Q3Object_Dispose(theRenderer);
        
    } else theStatus = kQ3Failure;
    
    
    return theStatus;
}
 
/* -------------------------------------------------------------------------------------------
**  Document_Open
**  Loads a new Document with a Pict for a texture
*/
 
TQ3Status Document_Open(DocumentHdl theDocument)
{   
    assert(theDocument != NULL);
    assert(*theDocument != NULL);
    
    return kQ3Failure;  
    
}
 
 
/* -------------------------------------------------------------------------------------------
**  Document_Save
**  Saves the *Model*
*/
 
TQ3Status Document_Save(DocumentHdl theDocument)
{
    assert(theDocument != NULL);
    assert(*theDocument != NULL);
    
    /*  If you can figure out how to save the model,
        This is a good place to put in that code    */
    theDocument;    /*  in the meantime, no op  */
    return kQ3Failure;
}
    
/* -------------------------------------------------------------------------------------------
**  Document_Draw
**  Draws the document in the current DrawContext
*/
 
TQ3Status Document_Draw( DocumentPtr theDocument )
{   
    TQ3Status       status = kQ3Failure;
    
    assert(theDocument != NULL);
    
    if ((status = Q3View_StartRendering(theDocument->fView)) != kQ3Failure)
    do {
        Document_SubmitScene( theDocument ) ;
    } while (Q3View_EndRendering(theDocument->fView) == kQ3ViewStatusRetraverse );
    
bail:
    
    return status ;
 
}
 
 
 
/* -------------------------------------------------------------------------------------------
**  Document_SubmitScene
**  if you make a function like this, you can easily use it inside a
**  Rendering or Picking or BoundingSphere/Box loop
**  (See Document_Draw)
*/
 
TQ3Status Document_SubmitScene( DocumentPtr theDocument ) 
{       
    TQ3Status   theStatus = kQ3Success;
 
    
    assert(theDocument != NULL);
    
    /*
     *  Interpolation
     */
    if (Q3Style_Submit( theDocument->fInterpolation,
                        theDocument->fView) == kQ3Failure)
        theStatus = kQ3Failure;
        
    /*
     *  BackFacing
     */
    if (Q3Style_Submit( theDocument->fBackFacing ,
                        theDocument->fView) == kQ3Failure)
        theStatus = kQ3Failure;
        
    /*
     *  FillStyle
     */
    if (Q3Style_Submit( theDocument->fFillStyle,
                        theDocument->fView) == kQ3Failure)
        theStatus = kQ3Failure;
        
    /*
     *  Model
     */
 
    if (Q3DisplayGroup_Submit(  theDocument->fModel,
                                theDocument->fView) == kQ3Failure)
        theStatus = kQ3Failure;
 
bail:
 
    return kQ3Success ;
}
 
 
 
 
/* -------------------------------------------------------------------------------------------
** Document_Idle
** Does the Idle Action
*/
void    Document_Idle(DocumentHdl theDocument)
{
    TQ3Matrix4x4        tmp;
    
    assert(theDocument != NULL);
    assert(*theDocument != NULL);
        
    /*  For now, this just rotates the model    */
 
    Q3Matrix4x4_SetRotate_XYZ(&tmp, 0.025, 0.03, 0.02);
    Q3Matrix4x4_Multiply(&(**theDocument).fRotation, &tmp, &(**theDocument).fRotation);
                            
    HLock( (Handle)theDocument  ) ;
    
    Document_Draw(*theDocument);
 
    HUnlock( (Handle)theDocument  ) ;
}
 
 
/* -------------------------------------------------------------------------------------------
** Document_Init
** Sets All Variables and objects common to all documents
*/
 
void Document_Init( DocumentHdl theDocument) 
{
    assert(theDocument != NULL);
    assert(*theDocument != NULL);
        
    /*  NULL out all of the parameters  */
    /*  Check out the header file for the parameter comments    */
    (**theDocument).fWindow = NULL ;
    
    (**theDocument).fView = NULL;
    (**theDocument).fModel = NULL ;
    (**theDocument).fInterpolation = NULL ;
    (**theDocument).fBackFacing = NULL ;
    (**theDocument).fFillStyle = NULL ;
    Q3Matrix4x4_SetIdentity(&(**theDocument).fRotation);
 
}
 
 
 
/*
 *  Document_DoPick
 *
 *  Submits objects for picking and returns the number of objects hit.
 *
 *  This should be identical to DrawDocumentData.
 */
static
unsigned long Document_DoPick(
    DocumentPtr     pDocument,
    TQ3PickObject   pickObject)
{
    TQ3ViewObject   aView;
    TQ3ViewStatus   viewStatus;
    unsigned long   numHits;
 
    assert(pDocument != NULL);
    
    aView   = pDocument->fView;
    numHits = 0;
 
    if (Q3View_StartPicking(aView, pickObject) == kQ3Success) {
        do {
            Document_SubmitScene( pDocument ) ;
 
            viewStatus = Q3View_EndPicking(aView);
 
        } while (viewStatus == kQ3ViewStatusRetraverse);
 
        if (viewStatus == kQ3ViewStatusDone) {
            Q3Pick_GetNumHits(pickObject, &numHits);
        }
        else {
            BP_DebugString("Document_DoPick: Q3View_EndPicking failed.");
        }
    }
    else {
        BP_DebugString("Document_DoPick: Q3View_StartPicking failed.");
    }
 
    return numHits;
}
 
/*  --------------------------------------------------------------------
**  Document_NewPick
**  Creates a new pick object. pWPPData->point is assumed to be initialized.
*/
static
TQ3PickObject Document_NewPick(
    TQ3WindowPointPickData  *pWPPData)
{
    if (pWPPData == NULL) {
        return NULL;
    }
 
    /* Create the window point pick */
    pWPPData->data.sort             = kQ3PickSortNearToFar;
    pWPPData->data.numHitsToReturn  = kQ3ReturnAllHits;
    /*  We want all this info   */
    pWPPData->data.mask             =   kQ3PickDetailMaskObject |
                                        kQ3PickDetailMaskPath;
    pWPPData->vertexTolerance =
    pWPPData->edgeTolerance   = 3.0;
 
    return (Q3WindowPointPick_New(pWPPData));
}
 
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
 
 
 
void Document_DoPickAndRotate( DocumentHdl pDocument)
{
    TQ3WindowPointPickData  wppData;
    TQ3PickObject           pickObject;
    TQ3Status               status = kQ3Failure;
    TQ3GroupObject          theHitGroupObject;      /*  What we hit: it's the group
                                                    holding the hit object  */
    TQ3HitPath              theHitPath;             /*  the group hierarchy */
    TQ3PickDetail           testDetail;             /*  Used to check if this mask is valid */
 
    
        assert(pDocument != NULL);
        
        HLock((Handle)pDocument);
 
        Utility_MyGetMouse(&wppData.point);
            /* Create a pick */
        if((pickObject = Document_NewPick(&wppData)) == NULL) {
            BP_DebugString("Document_DrawOnTexture: pickObject == NULL.");
            goto bail;
        }
 
        if (Q3WindowPointPick_SetPoint(pickObject, &wppData.point) == kQ3Failure) {
            goto bail;
        }
 
        if (Document_DoPick(*pDocument, pickObject) != 0) {
        
            /*  HERE'S WHERE ALL THE PICKING TAKES PLACE    */
            
            /*  Get the hit path    */
            if ( Q3Pick_GetPickDetailValidMask( pickObject, 0, &testDetail) == kQ3Success) {
 
                if ( testDetail & kQ3PickDetailMaskPath) {
 
                    if ( Q3Pick_GetPickDetailData(pickObject, 0, kQ3PickDetailMaskPath, &theHitPath) == kQ3Success) {
 
                        /*  Get the group that holds the hit object */
                        theHitGroupObject = Utility_GetEnclosingGroup((const TQ3HitPath *)&theHitPath);
 
                        Document_Rotate(pDocument, theHitGroupObject);
 
                        Q3HitPath_EmptyData(&theHitPath);   /*  Empty the hit path  */
 
                        Q3Object_Dispose(theHitGroupObject);
                    }
                }
            }
            Q3Pick_EmptyHitList(pickObject);    /*  Empty the list  */
        }
 
bail:
 
    if (pickObject != NULL) {
        Q3Object_Dispose(pickObject);
        pickObject = NULL;
    }
 
    HUnlock((Handle)pDocument);
    
}
 
 
/* -------------------------------------------------------------------------------------------
**  
**  
*/
 
void    Document_Rotate(DocumentHdl theDocumentHdl,
                        TQ3GroupObject  theGroup)
{
    TQ3Point2D  newMouse;
    TQ3Point2D  oldPoint;
    float       dx, dy;
    float       width, height;
    float       xRot, yRot;
    DocumentPtr theDocument;
    TQ3TransformObject transform;
    TQ3Matrix4x4 matrix, newMatrix;
    TQ3BoundingBox viewBBox;
    TQ3Point3D  center;
    TQ3Status status;
 
    
        assert(theDocumentHdl != NULL);
        
        HideCursor();
        
        HLock((Handle)theDocumentHdl);
 
        theDocument = *theDocumentHdl;
        
        Utility_MyGetMouse(&oldPoint);  /*  get the mouse in local co-ordinates,    */ 
                                        /*  local to the  current GrafPort  */
 
        width = theDocument->fWindow->portRect.left - theDocument->fWindow->portRect.right;
        height = theDocument->fWindow->portRect.bottom - theDocument->fWindow->portRect.top;
 
        while (Utility_MyStillDown()) {
        
            Utility_MyGetMouse(&newMouse);  /*  get the mouse in local co-ordinates,    */ 
                                            /*  local to the  current GrafPort  */
 
            dx = oldPoint.y - newMouse.y;
            dy = newMouse.x - oldPoint.x;
            
            if ((dx != 0) || (dy != 0)) {
                TQ3Matrix4x4        tempMatrix;
                
                xRot = ((float) dx * kQ3Pi) / width;
                yRot = ((float) dy * kQ3Pi) / height;
 
                if ((xRot != 0.0) || (yRot != 0.0)) {
 
                        /* Get the group's transform */
                    transform = Document_GetGroupTransform(theGroup);
                    if (transform != NULL) {
 
                        /* Change the transform's matrix with the translation */
                        Q3Transform_GetMatrix(transform, &matrix);
 
                        status = GetDocumentGroupBoundingBox( theDocument->fView,
                                                                theGroup, 
                                                                &viewBBox);
                        status = ComputeCenterOfBoundingBox(&viewBBox,
                                                            &center);
                        Q3Matrix4x4_SetRotateAboutPoint(&tempMatrix, &center, xRot, yRot, 0.0);
                        Q3Matrix4x4_Multiply(&matrix, &tempMatrix, &newMatrix);
 
                        Q3MatrixTransform_Set(transform, &newMatrix);
 
                        Document_Draw(theDocument);
                        Q3Object_Dispose(transform);
                    }
                }
            }
            oldPoint.x = newMouse.x; oldPoint.y = newMouse.y;
        }
 
        ShowCursor();
        
        HUnlock((Handle)theDocumentHdl);
    
}
 
TQ3TransformObject Document_GetGroupTransform(TQ3GroupObject    theGroup)
{
    TQ3TransformObject transform;
    TQ3GroupPosition transformPos;
    TQ3Status status;
 
        /* Get transform's position in this group */
        transformPos = NULL;
        status = Q3Group_GetFirstPositionOfType(theGroup,
                                                kQ3ShapeTypeTransform,
                                                &transformPos);
        if ((status == kQ3Success) && (transformPos != NULL)) {
 
            /* Get transform's object reference */
            transform = NULL;
            status = Q3Group_GetPositionObject(theGroup,
                                                transformPos,
                                                &transform);
            if ((status == kQ3Success) && (transform != NULL)) {
                return transform;
            }
        }
    
    return NULL;
}