|
Basic Picking ExamplePicking is the process of seeing whether two objects intersect and reporting the results. Along with blitting triangles to screen, it is one of the most fundamental 3D graphics concepts. Picking is what allows you to select an object in the virtual world, perform terrain following, or collide with a tree. If you would like to see the complete version of this code, it can be found in the
examples/picking directory and is named Setting up the applicationIn order to illustrate picking, we need to show the effects of the picker both finding an object and not finding it. So for this example, we're going to start with familiar basic application framework and then add some animation so that you can see the result of picking. What we're going to do is have a piece of geometry that will be picked against that will move about, and the picking object remaining stationary. To indicate when a pick intersection has been found, we'll change the colour of the box. After setting up the basic scene graph structure as in previous examples, we need to create the pieces of geometry needed - a box with a transform over it (so we can animate it), and a piece of geometry that represents the location of the picker. By now, this should be fairly straight-forward, so here's the code: BoxGenerator generator = new BoxGenerator(0.15f, 0.15f, 0.15f); generator.generate(data); TriangleArray geom = new TriangleArray(); geom.setVertices(TriangleArray.COORDINATE_3, data.coordinates, data.vertexCount); geom.setNormals(data.normals); Material material = new Material(); material.setEmissiveColor(new float[] { 0, 0, 1 }); Appearance app = new Appearance(); app.setMaterial(material); Shape3D shape = new Shape3D(); shape.setGeometry(geom); shape.setAppearance(app); trans.set(0.5f, 0, 0); Matrix4f mat2 = new Matrix4f(); mat2.setIdentity(); mat2.setTranslation(trans); TransformGroup shape_transform = new TransformGroup(); shape_transform.addChild(shape); shape_transform.setTransform(mat2); scene_root.addChild(shape_transform); // Place a point object where the picker is float[] coords = new float[3]; PointArray point_geom = new PointArray(); point_geom.setVertices(PointArray.COORDINATE_3, coords, 1); material = new Material(); material.setEmissiveColor(new float[] { 1, 0, 0 }); PointAttributes p_attr = new PointAttributes(); p_attr.setPointSize(4); app = new Appearance(); app.setMaterial(material); app.setPointAttributes(p_attr); shape = new Shape3D(); shape.setGeometry(point_geom); shape.setAppearance(app); scene_root.addChild(shape); In addition, we're going to need a class that will handling the animation
and interaction with the picking code. That will be the job of the class named
public class BasicPickHandler implements ApplicationUpdateObserver, NodeUpdateListener { private Vector3f translation; private Matrix4f matrix; private TransformGroup transform; private float angle; private Group pickRoot; public BasicPickHandler(Group root, TransformGroup tx, Material mat) { pickRoot = root; material = mat; translation = new Vector3f(); matrix = new Matrix4f(); matrix.setIdentity(); transform = tx; } public void updateSceneGraph() { transform.boundsChanged(this); } public void updateNodeBoundsChanges(Object src) { angle += Math.PI / 100; float x = 0.4f * (float)Math.sin(angle); translation.x = x; matrix.setTranslation(translation); transform.setTransform(matrix); } public void updateNodeDataChanges(Object src) { } } The arguments to the constructor are the transform for our box, the
Picking and the Scene GraphPicking in Aviatrix3D is provided through a combination of a class and an
interface. The interface, named The other half of the pair is the class Picking within Aviatrix3D is very flexible and can be customised to your
application's needs. One particular feature that we include is the ability to
label pieces of your scene graph as containing a certain feature and then let
the picking subsystem intersect with only those features. This comes in the
form of a bitmask that can be set using the If you know you need to perform a number of different pick requests, there is the option of putting them all into a single set of requests to be evaluated as a single pass through the scene graph. For situations where you have a lot of different objects needing to pick (eg a set of lines or points for some form of modelling) this can be far more efficient than make multiple sets of single calls as the scene graph only needs to be traversed once, saving a lot of calculations like matrix transformations etc. Creating the Picking ObjectSetting up for picking requires the creation of an instance of
pointPick1 = new PickRequest(); pointPick1.pickGeometryType = PickRequest.PICK_POINT;
Next we need to tell the picking system about what sort of return results
are expected. For example, if you want to know if you've hit anything at all,
but don't care about what was actually hit, you can tell it to terminate
further searching after it has found the first intersection. On the other
end of the scale, you may want to have the system find every single
intersection and have them sort the results so that the closest intersection
is first and so on. The sorting is defined by the pointPick1.pickSortType = PickRequest.SORT_ALL; pointPick1.pickType = PickRequest.FIND_ALL; That's the minimum that needs to be set up for a pick. There is, however,
one more variable that can be set: One last thing to point out is the placement of this creation in the code.
Every frame we will be picking using exactly the same details, so there's no
need to create a new object every frame and populate the setup values. The
object can be reused every frame (even if you are changing the setup values
every time), so the above code snippets will be placed at the end of the
constructor of Testing for IntersectionsAfter creating the picking request, the next step is to make the request.
In the constructor, one of the arguments was the root object that we were
going to pick against. To make a pick, we're going to call the
Following the rest of the design of Aviatrix3D, there are only certain
points in time where you are allowed to make changes to the scene graph. There
is also a limited window in which you can make picks of the scene graph. Mostly
this is for your own sanity. It would be bad, for example, to make a pick
while the internals of the engine were in the middle of the state sorting
causing temporary internal variables to have strange values. Picking,
therefore, is restricted to being made only during the callback from the
Assuming that we've made public void updateSceneGraph() { ... transform.boundsChanged(this); pickRoot.pickSingle(pointPick1); } where the transform line is the one used to animate the box back and forth. Visualising the OutputFor most applications, the output of the picking won't lead to a visually
identifiable change on screen. It may be something subtle, such as not letting
the arms of two avatars pass through each other. For this example, we'll make
if quite obvious when a pick has returned a positive result. As that box is
moving back and forth past the picking point, it will change colour when
an intersection is detected ( To do this, we need to process the results of the pick request to see how many objects were found. If none were found, set the box to blue, otherwise, set it to green. A naive implementation would look something like this: public void updateSceneGraph() { transform.boundsChanged(this); pickRoot.pickSingle(pointPick1); material.dataChanged(this); } public void updateNodeDataChanges(Object src) { if(pointPick1.pickCount != 0) material.setEmissiveColor(GREEN); else material.setEmissiveColor(BLUE); } However, as you can see, this isn't exactly the most efficient code in the
world - every frame it is changing the object colour, even when it doesn't need
to. Optimising the above code requires us to look at the number of items picked
this frame, compare it to the number picked last frame and only change the
colour if they're different. That, is just a matter of being a little more
careful about our calls when doing the picking - just save the old count and
compare to the new one. No need to change the public void updateSceneGraph() { int old_count = pointPick1.pickCount; transform.boundsChanged(this); pickRoot.pickSingle(pointPick1); if(old_count != pointPick1.pickCount) material.dataChanged(this); } That is the complete code for this example. Basic picking is not all that difficult to implement. Careful selection of the code structures can result in a good high-performance application. To see more of what can be done with the AV3D picking APIs, see the next tutorial. |
[ Home ]
[ License ]
[ javadoc ]
[ Online Examples ]
[ Download ]
[ j3d.org ] [ Aviatrix3D ] [ Code Repository ] [ Java3D ] [ OpenGL ] [ Books ] [ Contact Us ] Last Updated: $Date: 2010-05-01 04:20:35 -0700 (Sat, 01 May 2010) $ |