13.2.3 Picking model
As already mentioned, picking in Java 3D is a diffuse process. It is spread over a number
of cooperating classes and capabilities. This section will attempt to bring some
structure to the picking process by identifying and organizing key concepts and entities.
Such a model for picking can help to integrate and explain its details, and help
you to make design decisions.
By exposing such a diffuse process for such an important capability as picking,
Java 3D allows plenty of opportunity for fine-tuning— or does it? For the kinds of
techniques presented in this book, which stress high-quality intuitive user interfaces,
the flexibility offered by the API is of limited use because most of it concerns obtaining
faster but less precise results. Also, it seems that exposing so many of the intermediate
stages of the picking process in the API would interfere with the optimization of the
full, high-precision picking process, which should be a top priority in next-generation
3D applications.
In any case, picking is important, and understanding how the API supports it is
crucial if you will be doing any interactive UI code development. In the UI framework,
most of the picking process and several of its variations are gathered in the j3dui. control.
mappers.PickEngine class. The heart of the process starts with the method pickTargetFair,
and the full precision portion of it uses the pickTargetGeometry
method. The Sun utilities also support picking, but their emphasis is on discrete picking
under controlled circumstances. Also, the documentation is slim when it comes
to integrating the details, describing the model, and explaining the choices made.
Pick targets
Because of the complexity of the picking process, we need to refer to two different levels
of target entities that are involved. The low level target, or shape target, is the one
determined directly by the Java 3D picking apparatus. Bounds and geometry intersection
only work on leaf nodes, specifically ones with a geometric shape such as
Shape3D and Morph, hence the name "shape target." The high-level target, the object
target, is the one in which the application is really interested. It may be a shape leaf
node, but typically it is a group node high up in the scene graph that represents some
complex object, such as a desk, possibly made up of nested components, such as a desk
top, two sides, and a set of drawers. Ultimately, objects and their components are comprised
of individual leaf nodes defining actual geometric shapes— the shape targets.
Shape pickability
Shape leaf nodes have an attribute called pickability, which is not shared by group
nodes because they are not pickable, at least not directly. A leaf shape node can only
be picked in a bounds intersection if it is pickable. The API provides the Node. set-Pickable
method, which sets or resets the pickability of a leaf shape or, if used on a
group node, the pickability of all the group's descendant leaf shapes. Leaf shape nodes
are pickable by default. One odd feature of pickability is that if you directly set a leaf
shape's pickability to false, to make it pickable again you have to explicitly set it; you
can't use an ancestral group node to do it. Because the API seems focused on picking
shape targets, you may be wondering how to get some useful work out of it, such as
picking an object target.
Pick reporting
To perform object target picking you must use a capability called pick reporting,
which is a capability that is separate from the pickability of a shape target. The result
of a shape target bounds intersection is returned in a SceneGraphPath object. This
object includes the shape target and possibly the scene graph path leading to it. For a
group node above the shape target to appear in the SceneGraphPath result, the
group's Node. ENABLE_ PICK_ REPORTING capability must be set. By default, pick
reporting is disabled. It is important to note that pick reporting applies only to group
nodes; the leaf shape node will always be reported in the SceneGraphPath result.
The key concept here is that, although picking can occur only on leaf shape
nodes, any group nodes in the path leading to a shape target can also be reported in
the bounds hit result. One approach for translating the result's shape target hit into
an object target hit is efficient, but can be used only if certain conditions are met. If
the application has the good fortune to have only group node object targets, and if it
has the agility to enable pick reporting only on the object targets under the pick root
in which it is interested, and if the object targets are not themselves nested, then any
SceneGraphPath shape target that is hit will contain a single group node, which is the
object target containing (or sharing) the shape target.
If those conditions cannot be met or an application doesn't care to deal with setting
and resetting capability bits throughout the scene graph, there is an alternative
approach, which uses an explicit object target list. The reporting capability is set on
all of the nodes under the pick root, and, a list is kept that identifies the object targets
in which the application is currently interested in being able to pick. When a bounds
pick hit occurs, the SceneGraphPath nodes, including the shape target, are compared
against the object target list and, if a target is found, it becomes an object target hit.
Although a bit less efficient than relying solely on pick reporting to determine the
object target, the target list approach affords a large degree of flexibility and requires
a lot less finesse in setting up the scene graph. This is the approach used by the frame-work.
The target list is established using the j3dui. control. mappers. Pick-Engine.
setTargets method, and it can be seen in use in the pickTarget and
findHitTarget methods.
Pick ray
Rather than limiting picking to using only a ray, Java 3D allows the operation to use a
number of different picking shapes. The PickShape class serves as a base class for all
picking shapes, which include points, rays, line segments, and any shape based on a
Bounds object, which includes spheres, boxes, and polytopes. Future releases promise
to add cone and cylinder pick shapes. For UI work, ray-based picking is good for picking
polygonal shapes, but can prove difficult to use if attempting to pick individual
vertices or edges on an object. A better picking shape for these situations would be one
with a non-zero cross section, such as a long skinny box or one of the future shapes.
Regardless of the pick shape used, you still have to figure out how to position and
orient it in the virtual world. For mouse-based UI picking, the shape needs to be ray-like
with an origin position and a vector direction. The origin is set to the position of
the user's eyeball in the world space, which is typically the view position; and the direction
is set such that the ray projects through the position of the mouse cursor in the
view's display plane. This arrangement was shown in figure 13. 2.
In Java 3D, computing the ray geometry is easier said and illustrated than done.
Depending on how the view model is configured, the eye position may not be exactly
where the view is located. Also, the eye and mouse cursor positions are returned relative
to the "image plate" space, which corresponds to the 3D view space, not the
world space. To use these positions you have to first spatially transform them from
local to world space. This is shown in the code sample below, which assumes perspective
projection, and in a more generalized form in the framework utility method
j3dui.control.mappers.Mapper.buildPickRay.
Canvas3D canvas;
int mouseX, mouseY;
...
Point3d rayOrg = new Point3d();
Vector3d rayDir = new Vector3d ();
// get the eye position in view space
Point3d eyePos = new Point3d();
canvas.getCenterEyeInImagePlate(eyePos);
// translate the mouse canvas position to view space
Point3d mousePos = new Point3d();
canvas.getPixelLocationInImagePlate(
mouseX, mouseY, mousePos);
// get the view-to-world transform
Transform3D xform = new Transform3D();
canvas.getImagePlateToVworld(xform);
// transform eye and mouse from view to world space
xform.transform(eyePos);
xform.transform(mousePos);
// save the world pick ray origin
rayOrg.set(eyePos);
// build the world pick ray direction
rayDir.sub(mousePos, rayOrg);
rayDir.normalize();
To build a pick ray object, you use the computed ray origin and direction as parameters
in the PickRay constructor. If, instead, you want to use a bounds shape for the
picking ray, you have to first construct the bounds shape, then spatially transform it
to the correct world position and orientation, and then use it to construct a Pick-Bounds
object.
New on the Java Boutique:
New Review:
Time Management Made Easy with the Quartz Enterprise Job Scheduler
Why not just use the Java timer API? This open source scheduling
API boasts simplicity, ease-of-integration, a well-rounded feature
set, and it's free!
New Applet:
Reverse Complement
Reverse Complement is a simple applet that converts DNA or RNA
sequences into three useful formats.
Elsewhere on internet.com:
WebDeveloper Java
Lots of Java information on webdeveloper.com
WDVL Java
Thorough Java resource at the Web Developer's Virtual Library.
ScriptSearch Java
Hundreds of free Java code files to download.
jGuru: Your View of the Java Universe
Customizable portal with online training, FAQs, regular news updates, and tutorials.
|