Sources/PickMeshShapePart.c

// PickMeshShapePart.c
//
// This demonstrates how to use picks  to test which parts of a mesh geometry have
// been hit.  When the mesh is picked the fPickParts value set from the ShapeParts
// menu controls the parts tested: object, face, edge, or vertex.  As the returned
// hits  in the pick are processed the corresponding part of the mesh is draw in a
// different color  and the name  of that part is displayed in the window's  upper
// left corner.
//
// The vertex and edge tolerance levels  set in CreatePick() control how close the
// window point pick must be to the given part.
//
// Robert Dierkes - November 11, 1995
// 
// ©1994-95 Apple computer Inc., All Rights Reserved
//
 
// system headers
#include <Windows.h>
 
// for QuickDraw 3D
#include "QD3D.h"
#include "QD3DMath.h"
#include "QD3DDrawContext.h"
#include "QD3DShader.h"
#include "QD3DGroup.h"
#include "QD3DPick.h"
 
 
#include "PickMeshShapePart.h"
 
 
//-------------------------------------------------------------------------------------------
// function prototypes
 
void TickDelay(
    long    time);
 
TQ3Status DoGroupPicking(
    TQ3ViewObject       view,
    TQ3GroupObject      group,
    TQ3PickObject       pick);
 
void GetRandomRGBColor(
    TQ3ColorRGB     *pColorRGB);
 
TQ3Status ChangeMeshFaceColor(
    TQ3GeometryObject   meshGeo,
    TQ3MeshFace         meshFace,
    DocumentPtr         theDocument);
 
TQ3Status ChangeMeshEdgeColor(
    TQ3GeometryObject   meshGeo,
    TQ3MeshEdge         meshEdge,
    DocumentPtr         theDocument);
 
TQ3Status ChangeMeshVertexColor(
    TQ3GeometryObject   meshGeo,
    TQ3MeshVertex       meshVertex,
    DocumentPtr         theDocument);
 
TQ3Status HandleMeshShapePart(
    TQ3GeometryObject   geometry,
    TQ3ShapePartObject  shapePart,
    DocumentPtr         theDocument);
 
TQ3Status HandleMeshPickHits(
    TQ3PickObject       pick,
    DocumentPtr         theDocument);
 
TQ3Status CreatePick(
    TQ3PickObject       *pPick);
 
 
//-------------------------------------------------------------------------------------------
//
 
#define kDelay          30
#define kTextLocationX  30
#define kTextLocationY  30
 
static TQ3PickObject    gPick = NULL;
 
 
 
//-----------------------------------------------------------------------------
// 
//  InitPicking
 
 
TQ3Status InitPicking(
    void)
{
    return CreatePick(&gPick);
}
 
 
//-----------------------------------------------------------------------------
// 
//  ExitPicking
 
TQ3Status ExitPicking(
    void)
{
    if (gPick != NULL) {
        Q3Object_Dispose(gPick);
        gPick = NULL;
    }
 
    return kQ3Success;
}
 
 
//-----------------------------------------------------------------------------
// 
//  TickDelay
 
static
void TickDelay(
    long    time)
{
    long    ticks,
            startTicks;
 
    startTicks = ticks = TickCount();
 
    while (ticks - startTicks != time ) {
        ticks = TickCount();
    }
}
 
 
//-----------------------------------------------------------------------------
// 
//  DoGroupPicking
 
static
TQ3Status DoGroupPicking(
    TQ3ViewObject       view,
    TQ3GroupObject      group,
    TQ3PickObject       pick)
{
    TQ3Status           status;
    TQ3ViewStatus       viewStatus;
 
    if ((status = Q3View_StartPicking(view, pick)) == kQ3Failure)
        return status;
 
    do {
        status = Q3DisplayGroup_Submit(group, view);
 
        viewStatus = Q3View_EndPicking(view);
    } while (viewStatus == kQ3ViewStatusRetraverse);
 
    if (viewStatus != kQ3ViewStatusDone) {
        status = kQ3Failure;
    }
 
    return status;
}
 
 
//-----------------------------------------------------------------------------
// 
//  GetRandomRGBColor
 
static
void GetRandomRGBColor(
    TQ3ColorRGB     *pColorRGB)
{
    unsigned long   ticks;
 
    ticks = TickCount();
    Q3ColorRGB_Set(pColorRGB,
        (ticks % 10)   / 10.0,
        (ticks % 100)  / 100.0,
        (ticks % 1000) / 1000.0);
}
 
 
//-----------------------------------------------------------------------------
// 
//  ChangeMeshFaceColor
 
static
TQ3Status ChangeMeshFaceColor(
    TQ3GeometryObject   meshGeo,
    TQ3MeshFace         meshFace,
    DocumentPtr         theDocument)
{
    TQ3Status       status;
    TQ3AttributeSet attributeSet;
    TQ3ColorRGB     colorRGB;
 
    status = kQ3Failure;
 
    if (meshGeo  == NULL  ||
        meshFace == NULL) {
        return status;
    }
 
    /* Get the face attributes */
    if ((status = Q3Mesh_GetFaceAttributeSet(meshGeo, meshFace, &attributeSet)) == kQ3Failure) {
        return status;
    }
 
    /* Create an attributes if none */
    if (attributeSet == NULL) {
        if ((attributeSet = Q3AttributeSet_New()) == NULL) {
            return kQ3Failure;
        }
    }
 
    GetRandomRGBColor(&colorRGB);
    if ((status = Q3AttributeSet_Add(attributeSet, kQ3AttributeTypeDiffuseColor, &colorRGB)) == kQ3Success) {
 
        char    partTypeString[] = {"\pFace"};
 
        /* Set new face color */
        status = Q3Mesh_SetFaceAttributeSet(meshGeo, meshFace, attributeSet);
 
        /* Redraw the mesh */
        DrawDocumentData(theDocument);
        MoveTo(kTextLocationX, kTextLocationY);
        DrawText((void *) partTypeString, 1, (short) partTypeString[0]);
        TickDelay(kDelay);
 
        /* Remove new face color */
        status = Q3Mesh_SetFaceAttributeSet(meshGeo, meshFace, NULL);
        DrawDocumentData(theDocument);
    }
 
    Q3Object_Dispose(attributeSet);
 
    return status;
}
 
 
//-----------------------------------------------------------------------------
// 
//  ChangeMeshEdgeColor
 
static
TQ3Status ChangeMeshEdgeColor(
    TQ3GeometryObject   meshGeo,
    TQ3MeshEdge         meshEdge,
    DocumentPtr         theDocument)
{
    TQ3Status       status;
    TQ3AttributeSet attributeSet1,
                    attributeSet2;
    TQ3MeshVertex   meshVertex1,
                    meshVertex2;
    TQ3ColorRGB     colorRGB;
 
    status = kQ3Failure;
 
    if ((meshGeo  == NULL)  ||
        (meshEdge == NULL)  ||
        (theDocument == NULL)) {
        debugstr("ChangeMeshEdgeColor: NULL parameter passed");
        return status;
    }
 
    attributeSet1 = NULL;
    attributeSet2 = NULL;
 
    /*
     * Set color on both vertices of this edge
     *
     * We change the vertex attributes instead of the edge attributes because we can only
     * see the effects of an edge attribute when the fill style is kQ3FillStyleEdges.  We
     * could use kQ3FillStyleEdges rather than the current fill of kQ3FillStyleFilled but
     * then we couldn't see the attributes on the vertex and face. 
     */
    if ((status = Q3Mesh_GetEdgeVertices(meshGeo, meshEdge, &meshVertex1, &meshVertex2)) == kQ3Failure) {
        return status;
    }
 
    /* Get the vertex attributes */
    if (((status = Q3Mesh_GetVertexAttributeSet(meshGeo, meshVertex1, &attributeSet1)) == kQ3Failure)  ||
        ((status = Q3Mesh_GetVertexAttributeSet(meshGeo, meshVertex2, &attributeSet2)) == kQ3Failure)) {
        goto ChangeMeshEdgeColor_Exit;
    }
 
    /* Create an attributes if none */
    if (attributeSet1 == NULL) {
        if ((attributeSet1 = Q3AttributeSet_New()) == NULL) {
            goto ChangeMeshEdgeColor_Exit;
        }
    }
 
    if (attributeSet2 == NULL) {
        if ((attributeSet2 = Q3AttributeSet_New()) == NULL) {
            goto ChangeMeshEdgeColor_Exit;
        }
    }
 
    GetRandomRGBColor(&colorRGB);
    if (((status = Q3AttributeSet_Add(attributeSet1, kQ3AttributeTypeDiffuseColor, &colorRGB)) == kQ3Success)  &&
        ((status = Q3AttributeSet_Add(attributeSet2, kQ3AttributeTypeDiffuseColor, &colorRGB)) == kQ3Success)) {
 
        char    partTypeString[] = {"\pEdge"};
 
        /* Set new vertex color */
        if (((status = Q3Mesh_SetVertexAttributeSet(meshGeo, meshVertex1, attributeSet1)) == kQ3Success)  &&
            ((status = Q3Mesh_SetVertexAttributeSet(meshGeo, meshVertex2, attributeSet2)) == kQ3Success)) {
 
            /* Redraw the mesh */
            DrawDocumentData(theDocument);
            MoveTo(kTextLocationX, kTextLocationY);
            DrawText((void *) partTypeString, 1, (short) partTypeString[0]);
            TickDelay(kDelay);
        }
 
        /* Remove new vertex color */
        status = Q3Mesh_SetVertexAttributeSet(meshGeo, meshVertex1, NULL);
        status = Q3Mesh_SetVertexAttributeSet(meshGeo, meshVertex2, NULL);
        DrawDocumentData(theDocument);
    }
 
ChangeMeshEdgeColor_Exit:
 
    if (attributeSet1 != NULL)
        Q3Object_Dispose(attributeSet1);
 
    if (attributeSet2 != NULL)
        Q3Object_Dispose(attributeSet2);
 
    return status;
}
 
 
//-----------------------------------------------------------------------------
// 
//  ChangeMeshVertexColor
 
static
TQ3Status ChangeMeshVertexColor(
    TQ3GeometryObject   meshGeo,
    TQ3MeshVertex       meshVertex,
    DocumentPtr         theDocument)
{
    TQ3Status       status;
    TQ3AttributeSet attributeSet;
    TQ3ColorRGB     colorRGB;
 
    status = kQ3Failure;
 
    if ((meshGeo  == NULL)  ||
        (meshVertex == NULL)  ||
        (theDocument == NULL)) {
        debugstr("ChangeMeshVertexColor: NULL parameter passed");
        return status;
    }
 
    /* Get the vertex attributes */
    if ((status = Q3Mesh_GetVertexAttributeSet(meshGeo, meshVertex, &attributeSet)) == kQ3Failure) {
        return status;
    }
 
    /* Create an attributes if none */
    if (attributeSet == NULL) {
        if ((attributeSet = Q3AttributeSet_New()) == NULL) {
            return kQ3Failure;
        }
    }
 
    GetRandomRGBColor(&colorRGB);
    if ((status = Q3AttributeSet_Add(attributeSet, kQ3AttributeTypeDiffuseColor, &colorRGB)) == kQ3Success) {
 
        char    partTypeString[] = {"\pVertex"};
 
        /* Set new vertex color */
        status = Q3Mesh_SetVertexAttributeSet(meshGeo, meshVertex, attributeSet);
 
        /* Redraw the mesh */
        DrawDocumentData(theDocument);
        MoveTo(kTextLocationX, kTextLocationY);
        DrawText((void *) partTypeString, 1, (short) partTypeString[0]);
        TickDelay(kDelay);
 
        /* Remove new vertex color */
        status = Q3Mesh_SetVertexAttributeSet(meshGeo, meshVertex, NULL);
        DrawDocumentData(theDocument);
    }
 
    Q3Object_Dispose(attributeSet);
 
    return status;
}
 
 
//-----------------------------------------------------------------------------
// 
//  HandleMeshShapePart
 
static
TQ3Status HandleMeshShapePart(
    TQ3GeometryObject   geometry,
    TQ3ShapePartObject  shapePart,
    DocumentPtr         theDocument)
{
    TQ3Status           status;
    TQ3ObjectType       objectType;
 
    if ((geometry == NULL)  || (shapePart == NULL)  ||  (theDocument == NULL)) {
        debugstr("HandleMeshShapePart: NULL parameter passed");
        return kQ3Failure;
    }
 
    /* Is object a mesh geometry? */
    if (Q3Object_IsType (geometry, kQ3GeometryTypeMesh) == kQ3False) {
        debugstr("HandleMeshShapePart: geometry isn't a mesh");
        return kQ3Failure;
    }
 
    /* Is the shapePart a mesh part? */
    if (Q3ShapePart_GetType (shapePart) != kQ3ShapePartTypeMeshPart) {
        debugstr("HandleMeshShapePart: shape part isn't for a mesh");
        return kQ3Failure;
    }
 
    status = kQ3Failure;
 
    /* Or use this to get the shape part hit: objectType = Q3Object_GetLeafType(shapePart); */
    objectType = Q3MeshPart_GetType(shapePart);
 
    switch (objectType) {
        case kQ3MeshPartTypeMeshFacePart:
            {
                TQ3MeshFace     meshFace;
 
                if (Q3MeshFacePart_GetFace(shapePart, &meshFace) == kQ3Success) {
                    status = ChangeMeshFaceColor(geometry, meshFace, theDocument);
                }
            }
            break;
 
        case kQ3MeshPartTypeMeshEdgePart:
            {
                TQ3MeshEdge     meshEdge;
 
                if (Q3MeshEdgePart_GetEdge(shapePart, &meshEdge) == kQ3Success) {
                    status = ChangeMeshEdgeColor(geometry, meshEdge, theDocument);
                }
            }
            break;
 
        case kQ3MeshPartTypeMeshVertexPart:
            {
                TQ3MeshVertex   meshVertex;
 
                if (Q3MeshVertexPart_GetVertex(shapePart, &meshVertex) == kQ3Success) {
                    status = ChangeMeshVertexColor(geometry, meshVertex, theDocument);
                }
            }
            break;
 
        default:
            status = kQ3Failure;
            break;
    }
 
    return status;
}
 
 
//-----------------------------------------------------------------------------
// 
//  HandleMeshPickHits
 
static
TQ3Status HandleMeshPickHits(
    TQ3PickObject       pick,
    DocumentPtr         theDocument)
{
    TQ3Status           status;
    unsigned long       hitIndex,
                        numHits;
#if defined(QD3D_OBSOLETE) && QD3D_OBSOLETE
    TQ3HitData          hitData;
#else  /* ! QD3D_OBSOLETE */
    TQ3PickDetail       validMask;
    TQ3Object           object;
    TQ3ShapePartObject  shapePart;
 
    object    = NULL;
    shapePart = NULL;
#endif /* ! QD3D_OBSOLETE */
 
 
    if ((pick == NULL)  ||  (theDocument == NULL)) {
        debugstr("HandleMeshPickHits: NULL parameter passed");
        return kQ3Failure;
    }
 
    numHits = 0;
    if (Q3Pick_GetNumHits(pick, &numHits) == kQ3Failure) {
        debugstr("HandleMeshPickHits: NULL parameter passed");
        return kQ3Failure;
    }
 
    /* Process each hit in the pick */
    for (hitIndex = 0, status = kQ3Success;
        (hitIndex < numHits) && (status == kQ3Success);
         hitIndex++) {
 
#if defined(QD3D_OBSOLETE) && QD3D_OBSOLETE
 
        if (Q3Pick_GetHitData(pick, hitIndex, &hitData) == kQ3Failure) {
            debugstr("HandleMeshPickHits: Q3Pick_GetHitData failed");
            status = kQ3Failure;
            goto HandleMeshPickHits_Cleanup;
        }
 
        /* Test if hit data contains an object and shape part */
 
        /* Get reference to geometry object hit */
        if (! HitData_Has_Object(hitData)) {
            debugstr("HandleMeshPickHits: no object information returned in Q3Pick_GetHitData");
            status = kQ3Failure;
            goto HandleMeshPickHits_Cleanup;
        }
 
        /* Get reference to shapePart hit */
        if (! HitData_Has_ShapePart(hitData)) {
            debugstr("HandleMeshPickHits: no shape part information returned in Q3Pick_GetHitData");
            status = kQ3Failure;
            goto HandleMeshPickHits_Cleanup;
        }
 
        /* Color the shape part(s) hit */
        (void) HandleMeshShapePart(hitData.object, hitData.shapePart, theDocument);
 
        /* Dispose hitData.object and hitData.shapePart returned with Q3Pick_GetHitData */
HandleMeshPickHits_Cleanup:
        Q3Hit_EmptyData(&hitData);
 
#else   /* ! QD3D_OBSOLETE */
        
        /*
         * New Picking API for QuickDraw 3D Release 1.5
         */
 
        /* Get the TQ3PickDetail masks for this hit  (hitIndex) */
        if ((status = Q3Pick_GetPickDetailValidMask(pick, hitIndex, &validMask)) == kQ3Failure) {
            debugstr("HandleMeshPickHits: Q3Pick_GetPickDetailValidMask failed");
            goto HandleMeshPickHits_Cleanup;
        }
 
        /* Quick check to see if the hit contains an object and shape part */
        if (! (validMask & kQ3PickDetailMaskObject)  ||
            ! (validMask & kQ3PickDetailMaskShapePart) ) {
            debugstr("HandleMeshPickHits: expected object and shapePart TQ3PickDetailMasks masks to be set");
            status = kQ3Failure;
            goto HandleMeshPickHits_Cleanup;
        }
 
        /* Get reference to geometry object hit */
        if ((status = Q3Pick_GetPickDetailData(pick, hitIndex, kQ3PickDetailMaskObject, &object)) == kQ3Failure) {
            debugstr("HandleMeshPickHits: no object returned in Q3Pick_GetPickDetailData");
            goto HandleMeshPickHits_Cleanup;
        }
 
        /* Get reference to shapePart hit */
        if ((status = Q3Pick_GetPickDetailData(pick, hitIndex, kQ3PickDetailMaskShapePart, &shapePart)) == kQ3Failure) {
            debugstr("HandleMeshPickHits: no object returned in Q3Pick_GetPickDetailData");
            goto HandleMeshPickHits_Cleanup;
        }
 
        /* Color the shape part(s) hit */
        (void) HandleMeshShapePart(object, shapePart, theDocument);
 
        /* Dispose object and shapePart returned with Q3Pick_GetPickDetailData */
HandleMeshPickHits_Cleanup:
        if (object != NULL) {
            Q3Object_Dispose(object);
            object = NULL;
        }
        if (shapePart != NULL) {
            Q3Object_Dispose(shapePart);
            shapePart = NULL;
        }
 
#endif /* ! QD3D_OBSOLETE */
    }
 
 
    /*
     * If we reuse the pick object be sure to empty the hit list.
     * If the pick object is disposed immediately after picking
     * (not the case in this sample code) the hit list is automatically disposed.
     */
    if (Q3Pick_EmptyHitList(pick) == kQ3Failure) {
        debugstr("HandleMeshPickHits: Q3Pick_EmptyHitList failed");
    }
 
    return status;
}
 
 
//-----------------------------------------------------------------------------
// 
//  CreatePick
 
static
TQ3Status CreatePick(
    TQ3PickObject       *pPick)
{
    TQ3WindowPointPickData  data;
    TQ3Status               status;
 
    status = kQ3Failure;
 
    if (pPick  == NULL)
        return status;
 
    /* Create pick object */
    data.data.sort = kQ3PickSortNearToFar;
    data.data.mask = kQ3PickDetailMaskObject  | /* Request only object and shape parts */
                     kQ3PickDetailMaskShapePart;
    data.data.numHitsToReturn = kQ3ReturnAllHits;
    
    Q3Point2D_Set(&data.point, 0.0, 0.0);
    data.vertexTolerance = 5.0;                 /* Set vertex and edge tolerences */
    data.edgeTolerance   = 3.0;
 
    if ((*pPick = Q3WindowPointPick_New(&data)) != NULL)
        status = kQ3Success;
 
    return status;
}
 
 
//-----------------------------------------------------------------------------
// 
//  DoPicking
 
TQ3Status DoPicking(
    Point               *pWhere,    /* local window coordinates */
    DocumentPtr         theDocument)
{
    TQ3Status           status;
    TQ3Point2D          point;
 
    status = kQ3Failure;
 
    if (theDocument->fView  == NULL  ||
        theDocument->fModel == NULL  ||
        theDocument->fPickPartStyle == NULL  ||
        gPick == NULL) {
 
        return kQ3Failure;
    }
 
    /* Set pick part style with parts selected in the menu */
    if ((status = Q3PickPartsStyle_Set(theDocument->fPickPartStyle, theDocument->fPickParts)) == kQ3Failure)
        return kQ3Failure;
 
    /* Set the pick's point to mouse location */
    Q3Point2D_Set(&point, pWhere->h, pWhere->v);
    if ((status = Q3WindowPointPick_SetPoint(gPick, &point)) == kQ3Failure)
        return kQ3Failure;
 
    if ((status = DoGroupPicking(theDocument->fView, theDocument->fModel, gPick)) == kQ3Failure)
        return kQ3Failure;
 
    /* Handle hit(s) on mesh */
    return HandleMeshPickHits(gPick, theDocument);
}