Get a NodePath from a PandaNode subclass - typecheck problem

Actually I have two things here:

  1. I am using Bullet queries and the only result I have from the collisions is a node, not a NodePath. To be precise, it’s a BulletRigidBodyNode. But I need to get access to the NodePath and I don’t want to store it in the pythonTag. So here’s my first problem: I wanted to use NodePath(bulletNode) or NodePath.anyPath(bulletNode) to retrieve the np. However, both functions complain about the type, requiring PandaNode. But BulletRigidBodyNode is a subclass of PandaNode… If there is some type check under the hood maybe it should be changed to allow for subclasses of PandaNode?

  2. Ok, so I actually bypassed the above problem by using NodePath(bulletNode.makeCopy()) where makeCopy() returns a PandaNode. I was afraid that the copying would destroy the link to the NodePath but after testing, the position matches that of the original BulletRigidBodyNode. But now, a strange thing: while bulletNode.getBounds() return a nice (round :stuck_out_tongue: ) sphere, after I get the nodePath with the above method, both nodePath.getBounds() and nodePath.node().getBounds() return an empty bounding volume (it complains when I try to getRadius() of the bounding sphere). I am trying to understand - what is happening here? Is it because of the copying? Does the the retrieved nodePath point only to the copy and not to the original bulletNode?
    Or is there a better way of solving my goal from point 1)?

1 Like
  1. This is strange. A BulletRigidBodyNode is a type of PandaNode, and is therefore accepted by NodePath constructor and NodePath.anyPath (which do mostly the same thing). This may be a bug, can you show the lines of code necessary to reproduce this issue?

Using NodePath.anyPath (or the NodePath constructor) should be the correct solution here.

  1. If you make a copy of a node, and then wrap it in a NodePath, you essentially have created a new scene graph with just that new node in it. So, no, it does not point to the original node.

However, I don’t think this should change the behaviour of getBounds. Perhaps the copy constructor of BulletRigidBodyNode is bugged so that it doesn’t copy the information correctly. Hmm. Seeing some test code would help.

The below code is part of a larger project so there would be a bit to change, the code won’t work on it’s own… let me know if you need working code.

shape = BulletSphereShape(1.0)
position = self.nodePath.getPos(self.root)
fromTS = TransformState.makePos(position)
toTS = TransformState.makePos(self.root.getRelativePoint(self.nodePath, Point3(0, testLength, 0)))
sweepTestResult = self.bulletWorld.sweepTestClosest(shape, fromTS, toTS)
bulletNode = sweepTestResult.getNode()

and now, there are two options - NodePath() or NodePath.anyPath():

obstacle = NodePath.anyPath(bulletNode)

gives me

TypeError: NodePath.any_path() argument 0 must be PandaNode, not panda3d.bullet.BulletRigidBodyNode

while

obstacle = NodePath(bulletNode)

gives

TypeError: Arguments must match:
NodePath()
NodePath(PandaNode node)
NodePath(str top_node_name)
NodePath(const NodePath copy)
NodePath(const NodePath parent, PandaNode child_node)
NodePath(PandaNode node, Thread current_thread)
NodePath(str top_node_name, Thread current_thread)
NodePath(const NodePath parent, PandaNode child_node, Thread current_thread)
  1. I am not sure if what you’re saying about creating the new scene graph due to makeCopy() holds true…
    I was successful in doing this:
bulletNode = sweepTestResult.getNode()
obstacle = NodePath.anyPath(bulletNode.makeCopy()) #version with NodePath() works the same
obstaclePositionInRootFrame = obstacle.getPos()

the obstacle’s position in the render’s frame of reference matches the original position. A node doesn’t have a position, right? Only a nodepath does have a transform. So if as you say a new nodepath was being created, then the position would be (0, 0, 0) if I understand correctly. But it is non-zero as I expected. It seems that the makeCopy() either doesn’t make a proper copy or makes a shallow one (so that some data on which the anyPath() depends to match the node with the nodepath stays the same - but I dont know how it works under the hood). Anyway, the thing that does NOT match with the original is the bounding volume:

print bulletNode.getBounds()  # bsphere, c (81.4589 -56.5645 1.01), r 1.73205
obstacle = NodePath.anyPath(bulletNode.makeCopy())
print obstacle.getBounds()  # bsphere, empty
print obstacle.node().getBounds()  # bsphere, empty

Hmmm… the problem is that the Bullet APIs (e. g. BulletAllHitsRayResult::get_node()) return a const pointer, while the NodePath CTOR accept only non-const pointers of PandaNodes.

I think that returning a const pointer is not strictly necessary, but I have to check the code first.

A checked in a fix where nodes returned from queries a not const, and thus can be wrapped by a PandaNode or modified directly.

With a bit of basic testing I didn’t find any problems, but usually there is a reason why someone (Bullet developers) make a return values const. It might be that in some cases it is possible to modify the physics object in sucha way that an instable situation is created, and the physics simulation might even crash.

Well, let’s monitor this, and if there are any problems unto (back to const nodes).

Where did you check your change into, exactly? I don’t see it in the Git repository.

Forgot the push. Sorry, been late.