3D Graphics Programming with QuickDraw 3D

15 Using Pick Objects

A pick object contains all the information necessary to calculate geometric intersections between the pick geometry and the objects in a model. To create a pick object, you need to fill out data structures with the appropriate information, including

The following sections illustrate how to perform these tasks.

15 Handling Object Picking

Listing 15-1 illustrates how to create, use, and dispose of pick objects. It defines a function, MyHandleClickInWindow, that takes a window pointer and an event record and handles mouse clicks in that window.

Listing 15-1 Picking objects

TQ3Status MyHandleClickInWindow (CGrafPtr myWindow, EventRec myEvent)
{
 TQ3WindowPointPickData        myWPPickData;
 TQ3PickObject        myPickObject;
 TQ3HitData        myHitData;
 unsigned long        myNumHits;
 unsigned long        myIndex;
 Point        myPoint;
 TQ3Point2D        my2DPoint;
 TQ3ViewObject        myView;

 /*Get the window coordinates of a mouse click.*/
 SetPort(myWindow);
 myPoint = myEvent.where;          /*get location of mouse click*/
 GlobalToLocal(&myPoint);          /*convert to window coordinates*/
 my2DPoint.x = myPoint.h;          /*configure a 2D point*/
 my2DPoint.y = myPoint.v;

 /*Set up picking data structures.*/
 /*Set sorting type: objects nearer to pick origin are returned first.*/
 myWPPickData.data.sort = kQ3PickSortNearToFar;
 myWPPickData.data.mask = kQ3PickDetailMaskPickID | kQ3PickDetailMaskXYZ | 
             kQ3PickDetailMaskObject;
 myWPPickData.data.numHitsToReturn = kQ3ReturnAllHits;
 myWPPickData.point = my2DPoint;
 myWPPickData.vertexTolerance = 2.0;
 myWPPickData.edgeTolerance = 2.0;

 /*Create a new window-point pick object.*/
 myPickObject = Q3WindowPointPick_New(&myWPPickData);

 myView = MyGetViewFromWindow(myWindow);            /*increments reference count*/

 /*Pick a group object.*/
 Q3View_StartPicking(myView, myPickObject);
 do {
  Q3DisplayGroup_Submit(gGroup, myPickObject, myView);
 } while (Q3View_EndPicking(myView) == kQ3ViewStatusRetraverse);

 /*See whether any hits occurred.*/
 if (!Q3Pick_GetNumHits(myPickObject, &myNumHits) || !(myNumHits == 0)) {
  Q3Object_Dispose(myPickObject);
  return;
 }

 /*Process each hit.*/
 for (myIndex = 0; myIndex < myNumHits; myIndex++) {
  Q3Pick_GetHitData(myPickObject, myIndex, &myHitData);
  /*operate on myHitData, then...*/
   ...
  Q3Hit_EmptyData(&myHitData);           /*dispose of hit data*/
 }

 /*Dispose of all hits in the hit list.*/
 Q3Pick_EmptyHitList(myPickObject);

 /*Dispose of the pick object.*/
 Q3Object_Dispose(myPickObject);

 /*Dispose of the view object.*/
 Q3Object_Dispose(myView);
}
Note that the call to Q3Pick_EmptyHitList is redundant, because disposing
of a pick object (by calling Q3Object_Dispose) also disposes of its associated hit list. The call is included in Listing 15-1 simply to illustrate how to
call Q3Pick_EmptyHitList. You would, however, need to call to Q3Pick_EmptyHitList if you wanted to reuse the associated pick object in another pick operation.

15 Handling Mesh Part Picking

When a model includes a mesh, you can decide whether the entire mesh only or parts of the mesh also are eligible for picking. You do this by specifying an appropriate hit information mask. For example, to allow mesh parts to be selected, you can set up the hit information mask like this:

myPickData.mask = kQ3PickDetailMaskShapePart | 
      kQ3PickDetailMaskObject | 
      kQ3PickDetailMaskDistance;
This line of code indicates that you want QuickDraw3D to return information about objects and any distinguishable parts of objects, as well as the distances from the objects to the pick origin. (To prevent mesh parts from being selected, you simply omit adding in the kQ3PickDetailMaskShapePart mask.)

You can determine whether a hit data structure returned by Q3Pick_GetHitData applies to a shape part by inspecting the shapePart field of that structure. If the value in the field is non-NULL, the structure contains information about a shape part. Currently the only available shape parts are mesh parts. Listing 15-2 illustrates how to use the shapePart field to determine the type of mesh part selected and to perform some operation on the selected mesh part.

Listing 15-2 Picking mesh parts

Q3Pick_GetHitData(myPickObject, myIndex, &myHitData);

if (myHitData.shapePart != NULL) {
 switch(Q3Object_GetLeafType(myHitData.shapePart)) {
  case kQ3MeshPartTypeMeshFacePart:
   Q3MeshFacePart_GetFace(myHitData.shapePart, &myFace);
   MyDoPickFace(myHitData.object, myFace);
   break;
  case kQ3MeshPartTypeMeshEdgePart:
   Q3MeshEdgePart_GetEdge(myHitData.shapePart, &myEdge);
   MyDoPickEdge(myHitData.object, myEdge);
   break;
  case kQ3MeshPartTypeMeshVertexPart:
   Q3MeshVertexPart_GetVertex(myHitData.shapePart, &myVertex);
   MyDoPickVertex(myHitData.object, myVertex);
   break;
 }
}
This code branches on the type of the mesh part indicated by the shapePart field. For each defined type of mesh part, the code calls a QuickDraw3D routine to retrieve the corresponding mesh face, edge, or vertex. Then it calls an application-defined routine (for example, MyDoPickFace) to handle the mesh part selection.

15 Picking in Immediate Mode

Picking IDs are particularly useful when picking in immediate mode. Listing 15-3 shows how to create a triangle, attach a picking ID to it, and
then process hits.

Listing 15-3 Picking in immediate mode

void MyImmediateModePickID (TQ3ViewObject view, WindowPtr window)
{
 TQ3WindowRectPickData         myPickData;
 TQ3TriangleData         myTriangleData;
 TQ3PickObject         myPick;
 TQ3ViewStatus         myViewStatus;
 TQ3HitData         myHitData;
 Rect         myPortRect;
 Point         myCenter;
 unsigned long         myNumHits;

 /*Set up a triangle.*/
 Q3Point3D_Set(&myTriangleData.vertices[0].point, -1.0, -0.5, 0.0);
 Q3Point3D_Set(&myTriangleData.vertices[1].point,  1.0,  0.0, 0.0);
 Q3Point3D_Set(&myTriangleData.vertices[2].point, -0.5,  1.5, 0.0);
 myTriangleData.vertices[0].attributeSet = NULL;
 myTriangleData.vertices[1].attributeSet = NULL;
 myTriangleData.vertices[2].attributeSet = NULL;
 myTriangleData.triangleAttributeSet = NULL;

 /*Set up TQ3WindowPointPickData structure.*/
 myPickData.data.sort = kQ3PickSortNone;
 myPickData.data.mask = kQ3PickDetailMaskPickID | kQ3PickDetailMaskObject;
 myPickData.data.numHitsToReturn = kQ3ReturnAllHits;

 myPortRect = ((GrafPtr) window)->myPortRect;
 myCenter.h = (myPortRect.right  - myPortRect.left)/2.0;
 myCenter.v = (myPortRect.bottom - myPortRect.top) /2.0;

 Q3Point2D_Set(&myPickData.rect.min, myCenter.h - 5, myCenter.v - 5);
 Q3Point2D_Set(&myPickData.rect.max, myCenter.h + 5, myCenter.v + 5);


 /*Create the window rectangle window pick.*/
 myPick = Q3WindowRectPick_New(&myPickData);

 /*Submit the pick ID and triangle in immediate mode.*/
 Q3View_StartPicking(view, myPick);
 do
 {
  Q3PickIDStyle_Submit(kPickID, view);
  Q3Triangle_Submit(&myTriangleData, view);
  myViewStatus = Q3View_EndPicking(view);
 } while (myViewStatus == kQ3ViewStatusRetraverse);

 Q3Pick_GetNumHits(myPick, &myNumHits);
 if (numHits == 1)
 {
  /*Get the hit data and check its pick ID.*/
  Q3Pick_GetHitData(myPick, 0, &myHitData);
  if (myHitData.pickID == kPickID)
  {
   /*picked on triangle with pick ID*/
  }
 }

 Q3Object_Dispose(myPick);
}
15 - Handling Object Picking
15 - Handling Mesh Part Picking
15 - Picking in Immediate Mode

3D Graphics Programming with QuickDraw 3D - 17 OCT 1995

© Apple Computer, Inc.

Let us know what you think of these prototype pages.

Generated with Harlequin WebMaker