Open Inventor Release 2024.2.0
 
Loading...
Searching...
No Matches
Picking

SoRayPickAction finds objects along a ray from the camera through a point on the near plane of the view volume. This ray is typically specified by giving the coordinates of a window-space pixel through which it passes. SoRayPickAction traverses the scene graph you apply the action to and then returns the paths to all shapes along the picking ray, sorted from nearest to farthest. The picking action is primarily interested in geometry, transformation, and shape nodes.

The SoSelection node picks objects automatically. You don't need to explicitly use the pick action to select objects. The SoHandleEvent action also performs picking automatically. In addition, the SoEventCallback node allows you to register a callback function that is invoked whenever a certain event (such as a mouse press) occurs over a specified object. See Handling Events and Selection for more information on SoSelection , SoHandleEvent, and SoEventCallback .

Picking Style

By default, all objects in the scene graph are pickable (even invisible and transparent objects). To make an object or group of objects invisible to the pick action, insert an SoPickStyle node in the scene graph and set its style field to UNPICKABLE. Anything that follows in the scene graph cannot be picked until the SoPickStyle node is reset to SHAPE (to pick points on the shape objects in the scene) or BOUNDING_BOX (to pick points on the bounding boxes for the objects in the scene). BOUNDING_BOX pick style is most often used for SoText3 nodes. The pick style, like all other properties, is saved and restored by SoSeparator groups.

Create an Instance of the Action

The constructor for SoRayPickAction has one parameter, the viewport region (a required parameter).

An example of creating an instance of SoRayPickAction is

C++ :

SbViewportRegion myViewport;
SoRayPickAction myPickAction( myViewport );

C# :

SbViewportRegion myViewport = new SbViewportRegion();
SoRayPickAction myPickAction = new SoRayPickAction( myViewport );

Java :

SbViewportRegion myViewport = new SbViewportRegion();
SoRayPickAction myPickAction = new SoRayPickAction( myViewport );

The viewport region is used to compute the bounding boxes for screen-aligned objects such as SoText2.

Set Parameters

Before you apply the picking action, you can set the following parameters:

  • Ray to pick along
  • Whether to return all objects along the ray, or only the closest one The picking ray can be specified in one of two ways: either specify a window point and a radius, or specify a point and a direction in world space. The first method is the more typical for interactive programs, since you are generally most interested in the area underneath the cursor.

Specifying the Picking Ray with a Window Point

Before you apply the picking action, use the setPoint() and setRadius() methods to set the ray to be used for picking.

The ray to pick along is typically specified in viewport coordinates, where (0, 0) is the lower left corner of the viewport and (vpWidth-1, vpHeight-1) is the upper right corner (see Cone Representing the Picking Ray for a Perspective Camera ). In the figure, the viewport is 1000 by 1000. The near plane of the camera maps to the picking viewport.

To make it easier to pick lines and points, the ray can be augmented to be a cone (for a perspective camera; see Cone Representing the Picking Ray for a Perspective Camera ) or a cylinder (for an orthographic camera). Use the setRadius() method to control the size of this cone or cylinder where it intersects the near plane of the camera. (The default radius is 5 pixels.) Things that are picked must fall within this cone (or cylinder), as follows:

  • For points and lines, if any part of the shape falls within this cone, it is picked. (A sphere drawn with LINES draw-style is still picked as a solid sphere.)
  • For all other shapes, the ray itself must intersect the shape for it to be picked.

Cone Representing the Picking Ray for a Perspective Camera

Specifying the Picking Ray with a World-Space Ray

You can also specify the picking ray by specifying a world-space ray along which to pick. The ray is defined as a starting point, a direction vector, and a near distance and far distance for the picked objects. No radius is used. For example:

C++ :

SbViewportRegion viewport( 400, 300 );
SbVec2s cursorPosition( 250, 125 );
SoRayPickAction myPickAction( viewport );
myPickAction.setRay( SbVec3f( 0.0, 0.0, 0.0 ), // starting point
SbVec3f( 0.0, 0.0, -1.0 ) ); // direction vector

C# :

SbViewportRegion viewport = new SbViewportRegion( 400, 300 );
SbVec2s cursorPosition = new SbVec2s( 250, 125 );
SoRayPickAction myPickAction = new SoRayPickAction( viewport );
myPickAction.SetRay( new SbVec3f( 0.0f, 0.0f, 0.0f ), // starting point
new SbVec3f( 0.0f, 0.0f, -1.0f ) ); // direction vector

Java :

SbViewportRegion viewport = new SbViewportRegion( ( short )400, ( short )300 );
SbVec2s cursorPosition = new SbVec2s( ( short )250, ( short )125 );
SoRayPickAction myPickAction = new SoRayPickAction( viewport );
myPickAction.setRay( new SbVec3f( 0.0f, 0.0f, 0.0f ), // starting point
new SbVec3f( 0.0f, 0.0f, -1.0f ) ); // direction vector

This example uses the default near and far distances, which disables clipping to the near and far planes.

Picking the Closest Object

Use the setPickAll() method to specify whether you want information returned for all objects picked (sorted from closest to farthest), or just the closest one. Specify TRUE for all objects, or FALSE (the default) for only the closest one.

Apply the Action

The picking action can be applied to either a node, a path, or a path list. To apply the picking action to the root node of a scene graph:

C++ :

pickAction->apply( rootNode );

C# :

pickAction.Apply( rootnode );

Java :

pickAction.apply( rootnode );

Obtain Results

The results of the pick are stored in an SoPickedPoint (for the first hit) or an SoPickedPointList (for information on all hit objects). Use the methods on SoPickedPoint to obtain this information.

SoPickedPoint

An SoPickedPoint represents a point on the surface of an object that was picked. The picked point contains the point of intersection, the surface normal and texture coordinates at that point, the index into the current set of materials, and the path to the object that was intersected. Use the following methods on SoPickedPoint to obtain this information:

getPoint() returns the intersection point, in world space.
getNormal() returns the surface normal at the intersected point, in world space.
getTextureCoords() returns the texture coordinates at the intersection point, in image space.
getMaterialIndex() returns the index into the current set of materials that is used at the intersection point. If the materials are interpolated between vertices, the index corresponds to the material at the closest vertex.
getPath() returns the path to the object that was intersected.

For example:

C++ :

SoPath* pathToPickedObject;
const SoPickedPoint* myPickedPoint = myPickAction.getPickedPoint();
if ( myPickedPoint != NULL )
pathToPickedObject = myPickedPoint->getPath();

C# :

SoPath pathToPickedObject;
SoPickedPoint myPickedPoint = myPickAction.GetPickedPoint();
if ( myPickedPoint != null )
pathToPickedObject = myPickedPoint.GetPath();

Java :

SoPath pathToPickedObject;
SoPickedPoint myPickedPoint = myPickAction.getPickedPoint();
if ( myPickedPoint != null )
pathToPickedObject = myPickedPoint.getPath();

Detail Classes shows the path returned by an SoRayPickAction (which can be obtained with the getPath() method on SoPickedPoint). This path contains a pointer to each node in the path to the picked object. Use the following methods on SoPickedPoint to obtain information about the pick in the object space of a particular node in the path chain. You pass in a pointer to the node you are interested in, or use the default (NULL) to obtain information about the tail of the path:

getObjectPoint() returns the intersection point, in object space
getObjectNormal() returns the surface normal for the picked point
getObjectTextureCoords() returns the texture coordinates for the picked point
Detail Classes

Table 8.2. Classes That Store an SoDetail

Class Name Type of Detail Added Information Provided
SoCone SoConeDetail Contains information about which part of the cone was hit
SoCube SoCubeDetail Contains information about which face (part) of the cube was hit
SoCylinder SoCylinderDetail Contains information about which part of the cylinder was hit
SoText2, SoText3 SoTextDetail Specifies the index of the string that was hit; the index of the character within the string that was hit; which part of the text was hit; the object-space bounding box of the character that was intersected
SoFaceSet; all vertex-based shapes except lines, points, and NURBS SoFaceDetail Specifies which face in the shape was hit
SoLineSet, SoIndexedLineSet SoLineDetail Specifies which line in the line set was hit
SoPointSet SoPointDetail Specifies which point in the point set was hit

Use the getDetail() method on SoPickedPoint to return the detail for a given node in the picked path. This method takes a pointer to a node in the picked path. It returns information for the tail of the path if NULL or no node is specified. For example, to determine whether a cylinder was hit and, if so, whether it was the top part of the cylinder, the code would be as follows:

C++ :

const SoDetail* pickDetail = myPickedPoint->getDetail();
if ( pickDetail != NULL && pickDetail->getTypeId() == SoCylinderDetail::getClassTypeId() )
{
// Picked object is a cylinder
SoCylinderDetail* cylDetail = ( SoCylinderDetail* )pickDetail;
// See if top of the cylinder was hit
if ( cylDetail->getPart() == SoCylinder::TOP )
{
printf( "Top of cylinder was hit\n" );
}
}

C# :

SoDetail pickDetail = myPickedPoint.GetDetail();
if ( pickDetail != null && pickDetail is SoCylinderDetail )
{
// Picked object is a cylinder
SoCylinderDetail cylDetail = ( SoCylinderDetail )pickDetail;
// See if top of the cylinder was hit
if ( cylDetail.GetPart() == ( int )SoCylinder.PartType.TOP )
{
Console.WriteLine( "Top of cylinder was hit" );
}
}

Java :

SoDetail pickDetail = myPickedPoint.getDetail();
if ( pickDetail != null && pickDetail instanceof SoCylinderDetail )
{
// Picked object is a cylinder
SoCylinderDetail cylDetail = ( SoCylinderDetail )pickDetail;
// See if top of the cylinder was hit
if ( cylDetail.getPart() == SoCylinder.PartType.TOP.getValue() )
{
System.out.println( "Top of cylinder was hit" );
}
}

The following fragment shows how you could find the closest vertex to the hit point of a face-based shape using an SoFaceDetail. An SoFaceDetail contains an array of SoPointDetails. You can examine these details to find the coordinates of the point closest to the hit point by using the getCoordinateIndex() method on SoPointDetail. Finding the node that contains the coordinates is left to the application. (You can create a search action, apply it to the picked path, and ask for the last SoCoordinate3 node in the path. But you also need to know something about the structure of your graph—for example, whether it contains Override flags or Ignore flags that may affect the search.)

C++ :

// This function finds the closest vertex to an intersection
// point on a shape made of faces, passed in the
// "pickedPoint" argument. It returns the SoCoordinate3 node
// containing the vertex's coordinates in the "coordNode"
// argument and the index of the vertex in that node in the
// "closestIndex" argument. If the shape is not made of faces
// or there were any other problems, this returns FALSE.
static SbBool
findClosestVertex( const SoPickedPoint* pickedPoint, SoCoordinate3*& coordNode, int& closestIndex )
{
const SoDetail* pickDetail = pickedPoint->getDetail();
if ( pickDetail != NULL && pickDetail->getTypeId() == SoFaceDetail::getClassTypeId() )
{
// Picked object is made of faces
SoFaceDetail* faceDetail = ( SoFaceDetail* )pickDetail;
// Find the coordinate node that is used for the faces.
// Assume that it's the last SoCoordinate3 node traversed
// before the picked shape.
SoSearchAction mySearchAction;
mySearchAction.setType( SoCoordinate3::getClassTypeId() );
mySearchAction.setInterest( SoSearchAction::LAST );
mySearchAction.apply( pickedPoint->getPath() );
if ( mySearchAction.getPath() != NULL )
{ // We found one
coordNode = ( SoCoordinate3* )mySearchAction.getPath()->getTail();
// Get the intersection point in the object space
// of the picked shape
SbVec3f objIntersect = pickedPoint->getObjectPoint();
// See which of the points of the face is the closest
// to the intersection point
float minDistance = 1e12;
closestIndex = -1;
for ( int i = 0; i < faceDetail->getNumPoints(); i++ )
{
int pointIndex = faceDetail->getPoint( i )->getCoordinateIndex();
float curDistance = ( coordNode->point[pointIndex] - objIntersect ).length();
if ( curDistance < minDistance )
{
closestIndex = pointIndex;
minDistance = curDistance;
}
}
if ( closestIndex >= 0 )
return TRUE;
}
}
return FALSE;
}

C# :

static bool
findClosestVertex( SoPickedPoint pickedPoint, SoCoordinate3 coordNode, int closestIndex )
{
SoDetail pickDetail = pickedPoint.GetDetail();
if ( pickDetail != null && pickDetail.GetType() == typeof( SoFaceDetail ) )
{
// Picked object is made of faces
SoFaceDetail faceDetail = ( SoFaceDetail )pickDetail;
// Find the coordinate node that is used for the faces.
// Assume that it's the last SoCoordinate3 node traversed
// before the picked shape.
SoSearchAction mySearchAction = new SoSearchAction();
mySearchAction.SetType( typeof( SoCoordinate3 ) );
mySearchAction.SetInterest( SoSearchAction.Interests.LAST );
mySearchAction.Apply( pickedPoint.GetPath() );
if ( mySearchAction.GetPath() != null ) // We found one
{
coordNode = ( SoCoordinate3 )mySearchAction.GetPath().GetTail();
// Get the intersection point in the object space
// of the picked shape
SbVec3f objIntersect = pickedPoint.GetObjectPoint();
// See which of the points of the face is the closest
// to the intersection point
float minDistance = 1e12f;
closestIndex = -1;
for ( int i = 0; i < faceDetail.GetNumPoints(); i++ )
{
int pointIndex = faceDetail.GetPoint( i ).GetCoordinateIndex();
float curDistance = ( coordNode.point[pointIndex] - objIntersect ).Length();
if ( curDistance < minDistance )
{
closestIndex = pointIndex;
minDistance = curDistance;
}
}
if ( closestIndex >= 0 )
return true;
}
}
return false;
}

Java :

static boolean
findClosestVertex( SoPickedPoint pickedPoint, SoCoordinate3 coordNode, int closestIndex )
{
SoDetail pickDetail = pickedPoint.getDetail();
if ( pickDetail != null && pickDetail instanceof SoFaceDetail )
{
// Picked object is made of faces
SoFaceDetail faceDetail = ( SoFaceDetail )pickDetail;
// Find the coordinate node that is used for the faces.
// Assume that it's the last SoCoordinate3 node traversed
// before the picked shape.
SoSearchAction mySearchAction = new SoSearchAction();
mySearchAction.setNodeClass( SoCoordinate3.class );
mySearchAction.setInterest( SoSearchAction.Interests.LAST );
mySearchAction.apply( pickedPoint.getPath() );
if ( mySearchAction.getPath() != null ) // We found one
{
coordNode = ( SoCoordinate3 )mySearchAction.getPath().regular.getTail();
// Get the intersection point in the object space
// of the picked shape
SbVec3f objIntersect = pickedPoint.getObjectPoint();
// See which of the points of the face is the closest
// to the intersection point
float minDistance = 1e12f;
closestIndex = -1;
for ( int i = 0; i < faceDetail.getNumPoints(); i++ )
{
int pointIndex = faceDetail.getPoint( i ).getCoordinateIndex();
float curDistance = ( coordNode.point.getValueAt( pointIndex ).minus( objIntersect ) ).length();
if ( curDistance < minDistance )
{
closestIndex = pointIndex;
minDistance = curDistance;
}
}
if ( closestIndex >= 0 )
return true;
}
}
return false;
}

Using the Pick Action

Writing the Path to the Picked Object shows setting up the pick action and writing the path to the picked object to stdout.

Example : Writing the Path to the Picked Object

C++ :

SbBool
writePickedPath( SoNode* root, const SbViewportRegion& viewport, const SbVec2s& cursorPosition )
{
SoRayPickAction myPickAction( viewport );
// Set an 8-pixel wide region around the pixel
myPickAction.setPoint( cursorPosition );
myPickAction.setRadius( 8.0 );
// Start a pick traversal
myPickAction.apply( root );
const SoPickedPoint* myPickedPoint = myPickAction.getPickedPoint();
if ( myPickedPoint == NULL )
return FALSE; // no object was picked
// Write out the path to the picked object
SoWriteAction myWriteAction;
myWriteAction.apply( myPickedPoint->getPath() );
return TRUE;
}

C# :

bool
writePickedPath( SoNode root, SbViewportRegion viewport, SbVec2s cursorPosition )
{
SoRayPickAction myPickAction = new SoRayPickAction( viewport );
// Set an 8-pixel wide region around the pixel
myPickAction.SetPoint( cursorPosition );
myPickAction.SetRadius( 8.0f );
// Start a pick traversal
myPickAction.Apply( root );
SoPickedPoint myPickedPoint = myPickAction.GetPickedPoint();
if ( myPickedPoint == null )
return false;
// Write out the path to the picked object
SoWriteAction myWriteAction = new SoWriteAction();
myWriteAction.Apply( myPickedPoint.GetPath() );
return true;
}

Java :

boolean
writePickedPath( SoNode root, SbViewportRegion viewport, SbVec2s cursorPosition )
{
SoRayPickAction myPickAction = new SoRayPickAction( viewport );
// Set an 8-pixel wide region around the pixel
myPickAction.setPoint( cursorPosition );
myPickAction.setRadius( 8.0f );
// Start a pick traversal
myPickAction.apply( root );
SoPickedPoint myPickedPoint = myPickAction.getPickedPoint();
if ( myPickedPoint == null )
return false;
// Write out the path to the picked object
SoWriteAction myWriteAction = new SoWriteAction();
myWriteAction.apply( myPickedPoint.getPath() );
return true;
}