Clicking on 3D Objects
The simplest way to click on 3D objects in Panda3D is to use very simplistic
collision detection coupled with event processing. First, after a
CollisonTraverser
and a CollisionHandler
have been set up, attach a
CollisionRay
node to the camera. This node will have its “from” collision
mask set to GeomNode.getDefaultCollideMask()
in order to be as general as
possible.
PT(MouseWatcher) mouseWatcher;
PT(CollisionRay) pickerRay;
CollisionTraverser myTraverser = CollisionTraverser("ctraverser");
PT(CollisionHandlerQueue) myHandler;
PT(CollisionNode) pickerNode;
NodePath pickerNP;
pickerNode = new CollisionNode("mouseRay");
pickerNP = camera.attach_new_node (pickerNode);
pickerNode->set_from_collide_mask(GeomNode::get_default_collide_mask());
pickerRay = new CollisionRay();
pickerNode->add_solid(pickerRay);
myHandler = new CollisionHandlerQueue();
myTraverser.add_collider(pickerNP, myHandler);
For any object that you want to be pickable you should add a flag to it. The
easiest way is to use the setTag()
function:
object1.set_tag("myObjectTag", "1");
object2.set_tag("myObjectTag", "2");
The above example sets the tag 'myObjectTag'
on two objects in your graph
that you want to designate as pickable. We will check for the presence of this
tag after we get the response back from the collision system.
Now assume that the function myFunction()
is set up to be called for the
'mouse1'
event. In myFunction()
is where you call
pickerRay.setFromLens(origin, destX, destY)
. This makes the ray’s origin
origin
and the ray’s vector the direction from origin
to the point
(destX
, destY
).
void myFunction() {
if (!mouseWatcher->has_mouse()) {
// The mouse is probably outside the screen.
return;
}
// This gives up the screen coordinates of the mouse.
LPoint2 mpos = mouseWatcher->get_mouse();
// This makes the ray's origin the camera and makes the ray point
// to the screen coordinates of the mouse.
pickerRay->set_from_lens(window->get_camera(0), mpos.get_x(), mpos.get_y());
}
After this, you now call the traverser like any other collision, get the closest object and “pick” it.
The node returned by the collision system may not be the object itself, but
might be just a part of the object. In particular, it will be one of the
GeomNodes
that make up the object. (The GeomNode
class contains the
visible geometry primitives that are used to define renderable objects in
Panda3D.) Since your object might consist of more than one GeomNode
,
what you probably would prefer to get is the NodePath
that represents the
parent of all of these GeomNodes
that is, the NodePath
that you set
the 'myObjectTag'
tag on above. You can use nodePath.findNetTag()
to
return the parent NodePath
that contains a specified tag. (There are also
other, similar methods on NodePath
that can be used to query the tag
specified on a parent node, such as getNetTag()
and hasNetTag()
. For
simplicity, we shall restrict this example to findNetTag()
.)