Picking with a Mouse ClickPicking projects a pick shape (usually a line or ray) into the scene from the user's viewpoint through the mouse pointer position on screen until it intersects with a shape in the scene (see Figure 23-3). Java 3D's PickCanvas class is used to turn a mouse click into a ray (a PickShape object). Java 3D finds the pickable shapes that intersect with the PickShape object, returning them as a list of PickResult objects. Returning the PickResult object closest to the viewer is possible. A single PickResult may contain many PickIntersection objects, which hold the data for each intersection of the shape (e.g., the ray may go through the front and back face of a shape, leading to two intersection points). The complexity of the picking coding is somewhat alleviated by using Java 3D's PickMouseBehavior utility class, a subclass of Behaviour, which hides much of the picking mechanism. The general format for a subclass of PickMouseBehavior is given in Example 23-1. Example 23-1. A typical PickMouseBehavior subclassimport javax.media.j3d.*; import com.sun.j3d.utils.picking.PickTool; import com.sun.j3d.utils.picking.PickResult; import com.sun.j3d.utils.picking.behaviors.PickMouseBehavior; // other imports as necessary... public class ExamplePickBehavior extends PickMouseBehavior { public PickHighlightBehavior(Canvas3D canvas, BranchGroup bg, Bounds bounds) { super(canvas, bg, bounds); setSchedulingBounds(bounds); pickCanvas.setMode(PickTool.GEOMETRY_INTERSECT_INFO); // allows PickIntersection objects to be returned } public void updateScene(int xpos, int ypos) { pickCanvas.setShapeLocation(xpos, ypos); // register mouse pointer location on the screen (canvas) Point3d eyePos = pickCanvas.getStartPosition( ); // get the viewer's eye location PickResult pickResult = null; pickResult = pickCanvas.pickClosest( ); // get the intersected shape closest to the viewer if (pickResult != null) { PickIntersection pi = pickResult.getClosestIntersection(eyePos); // get the closest intersect to the eyePos point Point3d intercept = pi.getPointCoordinatesVW( ); // extract the intersection pt in scene coords space // use the intersection pt in some way... } } // end of updateScene( ) } // end of ExamplePickBehavior class The constructor must pass the Canvas3D object, a BranchGroup, and bounds information to the superclass for the superclass to create a PickCanvas object and a PickShape. The PickCanvas object, pickCanvas, is available and can be used to configure the PickShape, such as changing the pick shape from a ray to a cone or adjusting the tolerance for how close a shape needs to be to the PickShape to be picked. Many subclasses of PickShape (e.g., PickRay, PickCone, PickCylinder) specify different kinds of ray geometries; the default one employed by PickMouseBehavior is a line (PickRay). PickCanvas is intended to make picking based on mouse events easier. It's a subclass of PickTool, which has several additional methods for changing the pick shape and specifying what is returned by the picking operation. The call to pickCanvas.setMode( ) in ExamplePickBehavior's constructor sets the level of detail for the returned pick and intersection data. The various modes are:
From Mouse Click to Picked ObjectThe PickMouseBehavior class contains fully implemented initialize( ) and processStimulus( ) methods, which should not be changed when PickMouseBehavior is subclassed. Instead, the programmer should implement the updateScene( ) method, which is called whenever the user clicks the mouse buttonthis method is passed the (x, y) coordinate of the mouse click on the screen (the Canvas3D). The first step in updateScene( ) is to call setShapeLocation( ) to inform pickCanvas of the mouse position so the PickShape (the ray) can be cast into the scene. The intersecting shapes can be obtained in various ways: pickClosest( ) gets the PickResult object closest to the viewer. There are other methods: PickResult[] pickAll( ); PickResult[] pickAllSorted( ); PickResult pickAny( ); The first two return all the intersecting shapes, with the second method sorting them into increasing distance from the viewer. pickAny( ) returns any shape from the ones found, which should be quicker than finding the closest. Finding IntersectionsA PickResult will usually refer to a Shape3D containing a GeometryArray subclass made up of many surfaces. All the intersections between the shape and the ray can be obtained in the following way: PickIntersection pi; for (int i = 0; i < pickResult.numIntersections( ); i++) { pi = pickResult.getIntersection(i); // use pi in some way... } More commonly, the intersection closest to some point in the scene is obtained: PickIntersection pi = pickResult.getClosestIntersection(pt); In ExamplePickBehavior, the point is the viewer's position, which is extracted from pickCanvas with getStartPosition( ). A PickIntersection object can hold much information about the GeometryArray, such as the point, line, triangle, or quad that was intersected. The intersection point can be retrieved in terms of the picked shape's local coordinate system or in terms of the scene's coordinate system. If the picking level for the shape is INTERSECT_FULL, then there will be details about the closest vertex to the intersection point, and the color, normal and texture coordinates at the intersection point. The call to getPointCoordinatesVW( ) obtains the intercept point in the scene's coordinate space: Point3d intercept = pi.getPointCoordinatesVW( ); |