click on 3d items while moving camera?

So, I’ve basically followed two of the tutorials which work in isolation, but not together.

How to move the camera: self.camrea.setHpr / … the_Camera

(My code which copies this: … y#L224-239)

How to detect clicks: … 3D_Objects

(My code which copies this: … y#L100-142 )

Everything works, except that the CollisionRay seems to be “left behind” by the camera moves. I’m hoping someone more experienced with the scene graph can point out what I need to do.

Do I need to add a node-path above the camera that both the CollisionRay and camera are reparented to so that they move in synch? Should I be moving the CollissionRay and camera in parallel?

The really weird thing is that the built-in camera controls work fine. (I tried looking at how the default camera controls are implemented, but that seems to be in C++, so copying that didn’t seem like an option.) If anybody knows how the default camera controls are implemented, that would be helpful since they seem to work.

Thanks in advance for your help!

You don’t have to create another node - you can parent the collision node to the camera itself instead. That way you can move the camera without having to always call

picker_node = CollisionNode(‘mouse_click_ray’)
self.picker_node_path =

So, I should be doing something else there?

Actually, in general I am confused – CollisionNode.addSolid() is used to create the collision geometry. But, all scene graph manipulation is done via NodePaths, not Nodes right?

So, whats going on here? Can the same CollisionNode be in several different places in the scene graph? Or is collision geometry an exception to the normal rules about Nodes and NodePaths?

(BTW: thank you very much for the reply, I suspect I am thinking about Panda’s API still in some fundamentally wrong ways, and once those are cleared up everything will go smoothly.)

Under the hood the scene graph consists out of PandaNodes. The NodePath class is a handle for the PandaNode, which contains many convenience functions.

What is a bit confusing in your code is the assignment of attachNewNode(), which returns the attached node. The same node should already be saved in picker_node. When reparenting nodes or attaching existing nodes you usually don’t carry about the return value.

Typical usage:
node1 = loader.loadModel(“whatever.egg”) # this returns a node path
node2 = NodePath(“new empty node”) # this does as well
node1.reparentTo(node2) # now node1 is a child of node2

Thanks very much for the explanation.

Hmm… CollisionNodes don’t seem to have a reparentTo?

from pandac.PandaModules import CollisionNode
c = CollisionNode(‘c’)
Traceback (most recent call last):
File “”, line 1, in
AttributeError: ‘libpanda.CollisionNode’ object has no attribute ‘reparentTo’

The tutorial also shows the use of attachNewNode(): … 3D_Objects

Maybe collision geometry works a bit differently?

I guess I’ll try reading some more code.

If I remember correctly, CollisionNode is still just derived from PandaNode - so it’s not a NodePath yet. So your code would in effect be doing this:



collSolid = CollisionSphere( 1 )
collNode = CollisionNode( 'collNode' )
collNode.addSolid( collSolid )
myNp.attachNewNode( collNode )

Is this:

node_path = parent_node_path.attachNewNode(my_node)

Equivalent to this:

node_path = NodePath(my_node)

I think this is the case:

(Pdb) self.picker_node_path

Hmm… is there a way to make collision solids visible for debug purposes? Or maybe I could attach a line segment as a sibling of the CollisionNode?

I tried something a little bit along those lines, but had some troubles:

#debug: make a visible line segment
from pandac.PandaModules import LineSegs, LVecBase4f, NodePath
seg_drawer = LineSegs()
seg_drawer.setColor(LVecBase4f(1, 0, 0, 1)) #red


iiiiiiinteresting (maybe)

If I reparentTo() the linesegs directly to, they show up; if they are reparentTo()'d the picker_node_path, they are invisible.

Okay, I think the problem is not in the collision geometry, but in the camera moving.

I added this little debug thingie so I could press ‘c’ to get the current camera Hpr:

self.accept('c', lambda: sys.stdout.write(repr("\n"))

I discovered, with the default camera controls, the Hpr would change as expected.

LVecBase3f(0, 0, 0)
LVecBase3f(67.589, 44.3592, -27.9738)
LVecBase3f(149.885, 20.7343, -74.9179)
LVecBase3f(176.79, -65.4756, -66.9206)

However, when calling setHpr or setQuat on the camera in code, getHpr() on the camera would always report 0,0,0

LVecBase3f(0, 0, 0)
LVecBase3f(0, 0, 0)
LVecBase3f(0, 0, 0)
def camera_task(self, task):
    q2 = Quat()
    q2.setHpr( (0, task.time, 0) )

So, the problem is apparently that even though the display rendered appears to be moving, as far as the scene graph is concerned, the camera is not.

The collision geometry is working correctly, it is just being left pointed in direction (0,0,0) since the camera NodePath is not updating.

Essentially, yes.

You should be able to show collision geometry by calling show() on the NodePath holding the CollisionNode. Parenting things to a hidden node will also cause them to hide, which may explain your linesegs issue.

With regards to your camera issue, are you transforming or is the actual camera, is its parent. Try outputting the xforms of both these nodes to see what’s going on.