Picking is the process of determining which object in the world is selected through user interaction, usually a mouse click. It can be used for game actions such as aiming at targets (for example shooting) and triggering (for example flicking switches or pressing door handles).
Typically, picking involves the selection of a 3D object from its 2D projection on the screen by pointing and clicking the mouse. The process involves casting a ray into the world from a selected point on the projection plane. Any objects the ray intersects after passing the projection plane are potential objects for selection. Picking is a specific case of Collision Detection. A ray is a line with a start point and a direction.
Support for picking in a hierarchical scene graph amounts to examining the graph recursively until each leaf node is either reached or thrown out. To exploit the scene graph, if a Node is determined to not to be selected all children of this Node not not selected for further tests.
Picking makes extensive use of BoundingVolume's intersection method (see Utility Methods in the BoundingVolume entry.) However, there is a complete Picking System built untop of this functionality to make Picking easier.
At the core of the picking system is Spatial's findPick method. A call to this method will cause one of two things to happen: If the Spatial is a Node, the BoundingVolume that contains this node is checked against a Ray, if that Ray collides with the volume, then each of the Node's children are processed (their respective findPick methods are called). If the Spatial is a Geometry then the Geometry's bounding is checked, and if that Ray collides, then the Geometry object is added to the PickResults.
findPick takes two parameters: The Ray to check against, and the PickResults to store them. The Ray is defined by the user as the line for picking. For example, the user might select the origin as the location of the Camera and the direction as the Camera's direction.
PickResults determines the accuracy of the Picking. It's subclasses define how the leaf node will be processed. BoundingPickResults defines accuracy to the leaf node's bounding volume. This is (in many cases) plenty of accuracy. If the bounding is a “good fit” for the geometry it contains, then the picking will also be fairly accurate. However, in some cases it may be necessary to obtain accuracy to the triangle's hit by the Ray. To do this, make use of the TrianglePickResults subclass.
Which ever accuracy value is used, when a leaf node is determined to have been selected, the PickResults addPick method is called. The Geometry that was selected is then added to a list (and in the case of TrianglePickResults further processed to determine which triangle was selected).
The final results that are contained can then be processed by the user as desired. As stated above, if TrianglePickResults is used, further processing will occur in the addPick method. If the Geometry passed to addPick is actually a TriMesh, then TriMesh's findTrianglePick is called. This runs through the OBBTree containing the mesh's triangles and determines which are touched by the Ray.
Once all elements are added to the PickResults they can be obtained from the list. These elements are called PickData and contain: The ray involved, the Geometry hit, and if TrianglePickResults is used the list of indices for the triangles hit.
Optionally, the distances for each Geometry can be calculated. This is the distance from the origin of the Ray to the nearest point on the BoundingVolume of the Geometry. This distance is stored in PickData and the PickData is ordered in PickResults from near to far. That is, the 0th element in the list will be the closest result.
Ray ray = new Ray(camera.getLocation(), camera.getDirection()); PickResults results = new BoundingPickResults(); scene.findPick(ray,results);
Sphere s = new Sphere("Sphere", 10, 10, 5); s.updateCollisionTree(); //required to allow OBBTree interaction //... Ray ray = new Ray(camera.getLocation(), camera.getDirection()); PickResults results = new TrianglePickResults(); scene.findPick(ray,results);
Ray ray = new Ray(camera.getLocation(), camera.getDirection()); PickResults results = new TrianglePickResults(); results.setCheckDistance(true); scene.findPick(ray,results); //check if we hit something if(results.getNumber() > 0) { PickData closest = results.getPickData(0); System.out.println("The closest hit: " + closest.getTargetMesh().getName()); }
private static void mousePick() { //Get the mouse position Vector2f mousePosition = new Vector2f(MouseInput.get().getXAbsolute(), MouseInput.get().getYAbsolute()); //Create a pick ray from the display Ray ray = DisplaySystem.getDisplaySystem().getPickRay(mousePosition, false, new Ray()); //Find the results (Use which ever method suits your needs PickResults results = new TrianglePickResults(); //PickResults results = new BoundingPickResults(); //We normally want the distance to see which object is closest results.setCheckDistance(true); //Get the results from a node rootNode.findPick(ray, results); //Loop the results for (int i = 0; i < results.getNumber(); i++) { PickData hit = results.getPickData(i); System.out.println("Hit: " + hit.getTargetMesh().getName() + "(" + hit.getDistance() + ")"); } }