sources/PickOne_Support.c

/*  PickOne_Support.c
                                                                            
    Quickdraw 3D sample code
    
                                                                            
    Nick Thompson
    Scott Kuechle - March 1997
    (c)1994-97 Apple computer Inc., All Rights Reserved                             
 
*/
 
/* --------------------------------------------------------------------
** Includes
*/
 
#include    "PickOne_Support.h"
#include    "PickOne_documentStructure.h"
 
#include    "QD3DDrawContext.h"
#include    "QD3DRenderer.h"
#include    "QD3DShader.h"
#include    "QD3DCamera.h"
#include    "QD3DLight.h"
#include    "QD3DGeometry.h"
#include    "QD3DGroup.h"
#include    "QD3DMath.h"
#include    "QD3DTransform.h"
 
 
 
/* --------------------------------------------------------------------
** Global Variables
*/
static  TQ3Point3D  documentGroupCenter;
static  float       documentGroupScale;
 
/* --------------------------------------------------------------------
** Local Functions
*/
TQ3DrawContextObject    MyNewDrawContext(WindowPtr theWindow) ;
TQ3CameraObject         MyNewCamera(WindowPtr theWindow) ;
TQ3GroupObject          MyNewLights(void) ;
 
 
TQ3ViewObject MyNewView(WindowPtr theWindow)
{
    TQ3Status               myStatus;
    TQ3ViewObject           myView;
    TQ3DrawContextObject    myDrawContext;
    TQ3RendererObject       myRenderer;
    TQ3CameraObject         myCamera;
    TQ3GroupObject          myLights;
    
    myView = Q3View_New();
    
    /*  Create and set draw context. */
    if ((myDrawContext = MyNewDrawContext(theWindow)) == nil )
        goto bail;
        
    if ((myStatus = Q3View_SetDrawContext(myView, myDrawContext)) == kQ3Failure )
        goto bail;
 
    Q3Object_Dispose( myDrawContext ) ;
    
    /*  Create and set renderer. */
    
    
    
    /*  this would use the wireframe renderer */
    /*  Not very useful for this app    */
#if 0
    myRenderer = Q3Renderer_NewFromType(kQ3RendererTypeWireFrame);
    if ((myStatus = Q3View_SetRenderer(myView, myRenderer)) == kQ3Failure ) {
        goto bail;
    }
#else
    /*  this would use the interactive plug-in renderer */
 
    if ((myRenderer = Q3Renderer_NewFromType(kQ3RendererTypeInteractive)) != nil ) {
        if ((myStatus = Q3View_SetRenderer(myView, myRenderer)) == kQ3Failure ) {
            goto bail;
        }
    }
    else {
        goto bail;
    }
#endif
 
    Q3Object_Dispose( myRenderer ) ;
    
    /*  Create and set camera. */
    if ( (myCamera = MyNewCamera(theWindow)) == nil )
        goto bail;
        
    if ((myStatus = Q3View_SetCamera(myView, myCamera)) == kQ3Failure )
        goto bail;
 
    Q3Object_Dispose( myCamera ) ;
    
    /*  Create and set lights. */
    if ((myLights = MyNewLights()) == nil )
        goto bail;
        
    if ((myStatus = Q3View_SetLightGroup(myView, myLights)) == kQ3Failure )
        goto bail;
        
    Q3Object_Dispose(myLights);
 
    /*  Done!!! */
    return ( myView );
    
bail:
    /*  If any of the above failed, then don't return a view. */
    return ( nil );
}
 
/* ---------------------------------------------------------------------------------- */
 
TQ3DrawContextObject MyNewDrawContext(WindowPtr theWindow)
{
    TQ3DrawContextData      myDrawContextData;
    TQ3MacDrawContextData   myMacDrawContextData;
    TQ3ColorARGB            ClearColor;
    TQ3DrawContextObject    myDrawContext ;
    
    /*  Set the background color. */
    ClearColor.a = 1.0;
    ClearColor.r = 0.0;
    ClearColor.g = 0.2;
    ClearColor.b = 1.0;
    
    /*  Fill in draw context data. */
    myDrawContextData.clearImageMethod = kQ3ClearMethodWithColor;
    myDrawContextData.clearImageColor = ClearColor;
    myDrawContextData.paneState = kQ3False;
    myDrawContextData.maskState = kQ3False;
    myDrawContextData.doubleBufferState = kQ3True;
 
    myMacDrawContextData.drawContextData = myDrawContextData;
    
    myMacDrawContextData.window = (CGrafPtr) theWindow;     /*  this is the window associated with the view */
    myMacDrawContextData.library = kQ3Mac2DLibraryNone;
    myMacDrawContextData.viewPort = nil;
    myMacDrawContextData.grafPort = nil;
    
    /*  Create draw context and return it, if itÕs nil the caller must handle */
    myDrawContext = Q3MacDrawContext_New(&myMacDrawContextData) ;
 
    return myDrawContext ;
}
 
/* ---------------------------------------------------------------------------------- */
 
TQ3CameraObject MyNewCamera(WindowPtr theWindow)
{
    TQ3ViewAngleAspectCameraData    perspectiveData;
    TQ3CameraData                   someCameraData;
    
    TQ3CameraObject             camera;
    
    TQ3Point3D                  from    = { 0.0, 0.0, 3.0 };
    TQ3Point3D                  to      = { 0.0, 0.0, 0.0 };
    TQ3Vector3D                 up      = { 0.0, 1.0, 0.0 };
 
    float                       fieldOfView = 1.0;
    float                       hither      =  0.00001;
    float                       yon         =  10;
    
    TQ3Status                   returnVal = kQ3Failure ;
 
 
    someCameraData.placement.cameraLocation     = from;
    someCameraData.placement.pointOfInterest    = to;
    someCameraData.placement.upVector           = up;
 
    someCameraData.range.hither = hither;
    someCameraData.range.yon    = yon;
 
    someCameraData.viewPort.origin.x =  -1.0;
    someCameraData.viewPort.origin.y = 1.0;
    someCameraData.viewPort.width = 2.0;
    someCameraData.viewPort.height = 2.0;
    
    perspectiveData.cameraData      = someCameraData;
    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 ;
}
 
 
/* ---------------------------------------------------------------------------------- */
 
TQ3GroupObject MyNewLights(void)
{
    TQ3GroupPosition        myGroupPosition;
    TQ3GroupObject          myLightList;
    TQ3LightData            myLightData;
    TQ3PointLightData       myPointLightData;
    TQ3DirectionalLightData myDirectionalLightData;
    TQ3LightObject          myAmbientLight, myPointLight, myFillLight;
    TQ3Point3D              pointLocation = { -10.0, 0.0, 10.0 };
    TQ3Vector3D             fillDirection = { 10.0, 0.0, 10.0 };
    TQ3ColorRGB             WhiteLight = { 1.0, 1.0, 1.0 };
    
    /*  Set up light data for ambient light.  This light data will be used for point and fill */
    /*  light also. */
 
    myLightData.isOn = kQ3True;
    myLightData.color = WhiteLight;
    
    /*  Create ambient light. */
    myLightData.brightness = .3;
    myAmbientLight = Q3AmbientLight_New(&myLightData);
    if ( myAmbientLight == nil )
        goto bail;
    
    /*  Create point light. */
    myLightData.brightness = 0.60;
    myPointLightData.lightData = myLightData;
    myPointLightData.castsShadows = kQ3False;
    myPointLightData.attenuation = kQ3AttenuationTypeNone;
    myPointLightData.location = pointLocation;
    myPointLight = Q3PointLight_New(&myPointLightData);
    if ( myPointLight == nil )
        goto bail;
 
    /*  Create fill light. */
    myLightData.brightness = .2;
    myDirectionalLightData.lightData = myLightData;
    myDirectionalLightData.castsShadows = kQ3False;
    myDirectionalLightData.direction = fillDirection;
    myFillLight = Q3DirectionalLight_New(&myDirectionalLightData);
    if ( myFillLight == nil )
        goto bail;
 
    /*  Create light group and add each of the lights into the group. */
    myLightList = Q3LightGroup_New();
    if ( myLightList == nil )
        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 ) ;
 
    /*  Done! */
    return ( myLightList );
    
bail:
    /*  If any of the above failed, then return nothing! */
    return ( nil );
}
 
 
static TQ3GroupPosition MyAddTransformedObjectToGroup( TQ3GroupObject theGroup, TQ3Object theObject, TQ3Vector3D *translation )
{
    TQ3TransformObject  transform;
 
    transform = Q3TranslateTransform_New(translation);
    Q3Group_AddObject(theGroup, transform); 
    Q3Object_Dispose(transform);
    return Q3Group_AddObject(theGroup, theObject);  
}
 
 
TQ3GroupObject MyNewModel()
{
    TQ3GroupObject          myGroup = NULL;
    TQ3GroupObject          mySubGroupCone = NULL,
                            mySubGroupEllipsoid = NULL,
                            mySubGroupBox  = NULL;
    TQ3GeometryObject       myEllipsoid, cone, myBox;
    TQ3ShaderObject         myIlluminationShader ;
    TQ3DisplayGroupState    theState;
    TQ3StyleObject          theStyleObject;
    TQ3SubdivisionStyleData theSubdivisionStyleData;
 
    
    if ((myGroup = Q3DisplayGroup_New()) != NULL ) {
 
        
        /*  Define a shading type for the group */
        /*  and add the shader to the group */
    
        myIlluminationShader = Q3PhongIllumination_New();
/*      myIlluminationShader = Q3LambertIllumination_New();
*/      Q3Group_AddObject(myGroup, myIlluminationShader);
        /*  dispose of the objects we created here */
        if( myIlluminationShader ) 
            Q3Object_Dispose(myIlluminationShader); 
        
        theStyleObject = Q3InterpolationStyle_New(kQ3InterpolationStyleVertex);
        Q3Group_AddObject(myGroup, theStyleObject) ;
        Q3Object_Dispose(theStyleObject);
        
        theSubdivisionStyleData.method = kQ3SubdivisionMethodConstant;
        theSubdivisionStyleData.c1 = 16;
        theSubdivisionStyleData.c2 = 16;
        
        theStyleObject = Q3SubdivisionStyle_New(&theSubdivisionStyleData);
        Q3Group_AddObject(myGroup, theStyleObject) ;
        Q3Object_Dispose(theStyleObject);
 
 
        if ((mySubGroupBox = Q3OrderedDisplayGroup_New()) != NULL ) {
 
            // Define a shading type for the group
            // and add the shader to the group
            myIlluminationShader = Q3PhongIllumination_New();
            Q3Group_AddObject(mySubGroupBox, myIlluminationShader);
            if( myIlluminationShader ) 
                Q3Object_Dispose(myIlluminationShader); 
 
            myBox = MyNewBox();
            if( myBox ) {
                Q3Group_AddObject(mySubGroupBox, myBox) ;
                Q3Object_Dispose( myBox );
            }
 
            myAddMatrixTransformToGroup( mySubGroupBox );
            mySetGroupState(mySubGroupBox);
            Q3Group_AddObject(myGroup, mySubGroupBox) ;
            Q3Object_Dispose(mySubGroupBox);
        }
 
        if ((mySubGroupEllipsoid = Q3OrderedDisplayGroup_New()) != NULL ) {
        
            myEllipsoid = MyNewEllipsoid();
            if( myEllipsoid ) {
                Q3Group_AddObject(mySubGroupEllipsoid, myEllipsoid);
                Q3Object_Dispose( myEllipsoid );
            }
 
            myAddMatrixTransformToGroup( mySubGroupEllipsoid );
            mySetGroupState(mySubGroupEllipsoid);
            Q3Group_AddObject(myGroup, mySubGroupEllipsoid) ;
            Q3Object_Dispose(mySubGroupEllipsoid);
        }
 
 
        if ((mySubGroupCone = Q3OrderedDisplayGroup_New()) != NULL ) {
 
            cone = MyNewCone();
            if( cone ) {
                Q3Group_AddObject(mySubGroupCone, cone);
                Q3Object_Dispose( cone );
            }
            
            myAddMatrixTransformToGroup( mySubGroupCone );
            mySetGroupState(mySubGroupCone);
            Q3Group_AddObject(myGroup, mySubGroupCone) ;
            Q3Object_Dispose(mySubGroupCone);
            
        }
 
    }   
 
        /*  Now, get the state of the current model */
        Q3DisplayGroup_GetState(myGroup, &theState);
        
        /*  we don't want to push and pop for PERFORMANCE!! */
        theState = theState | (kQ3DisplayGroupStateMaskIsInline);
        Q3DisplayGroup_SetState(myGroup,  theState);
        theState = theState | (kQ3DisplayGroupStateMaskIsPicked);
        Q3DisplayGroup_SetState(myGroup,  theState);
 
/*  Done! */
    return ( myGroup );
}
 
 
 
/* ----------------------------------------------------------------------------------------------- */
 
TQ3Status GetDocumentGroupBoundingBox( 
    TQ3ViewObject view,
    TQ3GroupObject  group, 
    TQ3BoundingBox *viewBBox)
{
    TQ3Status       status;
    TQ3ViewStatus   viewStatus ;
    
    status = Q3View_StartBoundingBox( view, kQ3ComputeBoundsExact );
    do {
        status = Q3DisplayGroup_Submit(group, view);
    } while((viewStatus = Q3View_EndBoundingBox( view, viewBBox )) == kQ3ViewStatusRetraverse );
    return status ;
}
 
TQ3Status ComputeCenterOfBoundingBox(TQ3BoundingBox *viewBBox,
                                    TQ3Point3D  *center)
{
 
    if (viewBBox->isEmpty == kQ3True) {
        return kQ3Failure;
    }
    else {
        center->x = (viewBBox->min.x + viewBBox->max.x)/2;
        center->y = (viewBBox->min.y + viewBBox->max.y)/2;
        center->z = (viewBBox->min.z + viewBBox->max.z)/2;
 
        return kQ3Success;
    }
}
 
void MyColorBoxFaces( TQ3BoxData *myBoxData )
{
    TQ3ColorRGB             faceColor ;
    short                   face ;
    
    // sanity check - you need to have set up 
    // the face attribute set for the box data 
    // before calling this.
    
    if( myBoxData->faceAttributeSet == NULL )
        return ;
        
    // make each face of a box a different color
    
    for( face = 0; face < 6; face++) {
        
        myBoxData->faceAttributeSet[face] = Q3AttributeSet_New();
        switch( face ) {
            case 0:
                faceColor.r = 0.0;
                faceColor.g = 1.0;
                faceColor.b = 0.0;
                break;
            
            case 1:
                faceColor.r = 0.0;
                faceColor.g = 1.0;
                faceColor.b = 0.0;
                break;
            
            case 2:
                faceColor.r = 0.0;
                faceColor.g = 1.0;
                faceColor.b = 0.0;
                break;
            
            case 3:
                faceColor.r = 0.0;
                faceColor.g = 1.0;
                faceColor.b = 0.0;
                break;
            
            case 4:
                faceColor.r = 0.0;
                faceColor.g = 1.0;
                faceColor.b = 0.0;
                break;
            
            case 5:
                faceColor.r = 0.0;
                faceColor.g = 1.0;
                faceColor.b = 0.0;
                break;
        }
        Q3AttributeSet_Add(myBoxData->faceAttributeSet[face], kQ3AttributeTypeDiffuseColor, &faceColor);
    }
}
 
 
TQ3GroupObject MyNewBox()
{
    TQ3BoxData              myBoxData;
    TQ3GeometryObject       myBox = NULL;
    TQ3SetObject            faces[6] ;
    short                   face ;
    
        myBoxData.boxAttributeSet = nil;
        myBoxData.faceAttributeSet = faces;
 
        MyColorBoxFaces( &myBoxData ) ;
        
        /*  create the box itself */
 
        Q3Point3D_Set(&myBoxData.origin, 0.5, 0, 0);
        Q3Vector3D_Set(&myBoxData.orientation, 0, 0.5, 0);
        Q3Vector3D_Set(&myBoxData.majorAxis, 0, 0, 0.5);    
        Q3Vector3D_Set(&myBoxData.minorAxis, 0.5, 0, 0);    
        
        myBox = Q3Box_New(&myBoxData);
 
        for( face = 0; face < 6; face++) {
            if( myBoxData.faceAttributeSet[face] != NULL )
                Q3Object_Dispose(myBoxData.faceAttributeSet[face]);
        }
        
    return myBox;
}
 
 
TQ3GroupObject MyNewCone()
{
    TQ3ConeData             theConeData;
    TQ3GeometryObject       cone = NULL;
    TQ3ColorRGB             theColor;
    TQ3Status               status;
 
        Q3Point3D_Set(&theConeData.origin, 0, 0, 0);
        Q3Vector3D_Set(&theConeData.orientation, 0, 1.0, 0);
        Q3Vector3D_Set(&theConeData.majorRadius, 0.0, 0.0, 0.26);
        Q3Vector3D_Set(&theConeData.minorRadius, 0.26, 0.0, 0.0);
        theConeData.uMin = 0.0; theConeData.uMax = 1.0;
        theConeData.vMin = 0.0; theConeData.vMax = 1.0;
        theConeData.caps = kQ3EndCapMaskBottom;
        theConeData.interiorAttributeSet = NULL;
        theConeData.bottomAttributeSet = NULL;
        theConeData.faceAttributeSet = NULL;
        theConeData.coneAttributeSet = Q3AttributeSet_New() ;
 
        theColor.r = 1.0; theColor.g = 1.0; theColor.b = 0.0;
 
        status = Q3AttributeSet_Add (theConeData.coneAttributeSet,
                                    kQ3AttributeTypeDiffuseColor,
                                    &theColor);
        
        cone = Q3Cone_New(&theConeData);
 
        Q3Object_Dispose(theConeData.coneAttributeSet);
        
    return cone;
}
 
 
TQ3GroupObject MyNewEllipsoid()
{
    TQ3GeometryObject       myEllipsoid = NULL;
    TQ3EllipsoidData        myEllipsoidData;
    TQ3ColorRGB             theColor;
    TQ3Status               status;
 
        myEllipsoidData.interiorAttributeSet = NULL;
        myEllipsoidData.ellipsoidAttributeSet = Q3AttributeSet_New();
        myEllipsoidData.caps = kQ3EndCapNone;
        
        Q3Point3D_Set(&myEllipsoidData.origin, -1.0, 0.5, 0);
        Q3Vector3D_Set(&myEllipsoidData.orientation, 0, 0.5, 0);
        Q3Vector3D_Set(&myEllipsoidData.minorRadius, 0, 0, -0.4);   
        Q3Vector3D_Set(&myEllipsoidData.majorRadius, 0.5, 0, 0);
        
        myEllipsoidData.uMin = 0.0; myEllipsoidData.uMax = 1.0;
        myEllipsoidData.vMin = 0.0; myEllipsoidData.vMax = 1.0;
 
        theColor.r = 1.0; theColor.g = 0.0; theColor.b = 0.0;
        status = Q3AttributeSet_Add (myEllipsoidData.ellipsoidAttributeSet,
                                    kQ3AttributeTypeDiffuseColor,
                                    &theColor);
 
        myEllipsoid = Q3Ellipsoid_New(&myEllipsoidData);
 
        Q3Object_Dispose(myEllipsoidData.ellipsoidAttributeSet);
 
    return myEllipsoid;
}
 
 
TQ3Status myAddMatrixTransformToGroup( TQ3GroupObject theGroup)
{
    TQ3TransformObject      transform;
    TQ3Matrix4x4            matrix;
    TQ3Status               status;
    TQ3GroupPosition        groupPos;
 
 
        status = kQ3Failure;
        Q3Matrix4x4_SetRotate_XYZ(&matrix, 0.0, 0.0, 0.0);
        transform = Q3MatrixTransform_New(&matrix);
        if (transform != NULL) {
            groupPos = Q3Group_AddObject(theGroup, transform) ;
            if (groupPos != NULL) {
                status = kQ3Success;
            }
            Q3Object_Dispose(transform);
        }
    
    return status;
}
 
TQ3Status mySetGroupState(TQ3GroupObject theGroup)
{
    TQ3DisplayGroupState    theState;
    TQ3Status               status;
 
        status = Q3DisplayGroup_GetState(theGroup, &theState);
        if (status == kQ3Success) {
            theState = theState | (kQ3DisplayGroupStateMaskIsPicked);
            status = Q3DisplayGroup_SetState(theGroup,  theState);
        }
 
    return status;
}