Unclear behaviour when moving nodes with collision shape

Hi all,

I am trying to figure out how I can move collisions shape and corresponding nodes around, but I am having some issues. Here is an example. I am setting up a simple collision node (teapotNode) and its model (teapotModel), the two are connected to the same nodepath (teapotPointer).

Now I am attaching this last to another nodepath (nodePointer) and I move it around before updating the physics. What occurs is a bit disconceting. The teapot and the collision shape move upward up to some point, then the teapot stops and the collision shape continues to move upward.

If the code is modified by setting the mass of the teapot to zero, then the teapot does not move at all.

Could you please explain me why I am having this issue? What exactly is happening and what am I doing wrong? Thanks in advance

Odexi
from direct.showbase.ShowBase import ShowBase
from panda3d.core import *
from panda3d.bullet import *

 
class  Sfc(ShowBase):

	def __init__(self):
		ShowBase.__init__(self)
		base.disableMouse()

		self.world = BulletWorld()
		debugNode = BulletDebugNode('Debug')
		debugNode.showWireframe(True)
		debugNode.showConstraints(True)
		debugNode.showBoundingBoxes(False)
		debugNode.showNormals(False)
		debugNP = render.attachNewNode(debugNode)
		debugNP.show()
		self.world.setDebugNode(debugNP.node())

		self.nodePointer = render.attachNewNode( 'mainPointer' )
		teapotCollisionShape = BulletCapsuleShape(1,1, ZUp)
		teapotNode = BulletRigidBodyNode('teapot')
		teapotNode.setMass( 1 )
		teapotNode.addShape( teapotCollisionShape )
		teapotPointer = render.attachNewNode( teapotNode )

		self.world.attachRigidBody( teapotNode )
		teapotModel = loader.loadModel( "../teapot.egg" )
		teapotModel.setScale( 0.5 )
		teapotModel.reparentTo( teapotPointer )
		teapotPointer.reparentTo( self.nodePointer )

		camera.reparentTo( self.nodePointer )
		camera.setPos( 0.0, -40.0, 0.0 )
		camera.lookAt( self.nodePointer )


		taskMgr.add( self.updatePhys, 'updateTeapot' )


	def updatePhys(self, task):
		dt = globalClock.getDt()
		self.nodePointer.setPos( self.nodePointer.getPos() + Vec3( 0, 0, -0.01 ) )
		self.world.doPhysics(dt)
		return task.cont

sfc = Sfc()
sfc.run()

Here are three screenshots in sequence. As you can see at the beginning (moving from 1 to 2) the collision shape and the model are moving together, then the model stops and the collision shape continues to move upward. I am failing to understand what is happening.




I could be wrong, but I seem to remember that moving bullet nodes with setPos() was generally a bad idea, and that forces should be used instead. (Eg. Using setLinearMovement() & setAngularMovement())

If you need to use setPos(), I seem to recall having to deactivate the bulletnode or setting it to kinematic, move it, and then reactivate it to stop weird things registering with bulletworld.

Thanks for the great hint! Deactivating and activating the collision node before and after the movement seems to work.

I am doing this because I am trying to have a set of objects in interaction between themselves (they exert attractive and repulsive forces between themselves depending on their distance, like little molecules), but they should be all movable at the user will, using keyboard or mouse input. Moving them does not break the physics (in real terms) as the forces are internal, so their dynamics with respect to the main node holding them all is not affected by the move.

Thanks again

Ok, after much struggle I concluded that it is impossible to maintain a consistent camera control. For instance in the images above the camera should move with the teapot, but they don’t. For much complex systems and camera controls it becomes almost impossible to move the camera as needed. Apparently relations between objects get somehow altered when bullet is invoked, it’s not very clear to what extent, but it seems so.

Does anybody knows of an alternative to implement this simple behaviour? I suspect that all physics engine will show the same problems, while what I am doing in shorts is just moving the reference system of the physics world around.

Ode

I think a few explanations are yet missing.

The reason why the collision shape stops moving after some time is a feature provided by all modern physics engines for games: objects with very small velocity get deactivated, until the collide with something else or an external force/torque is applied.

The debug renderer shows this very well: active objects are white, while deactivated objects are green.

In your case the collision shape has a velocity of zero, and thus gets deactivated. Keep in mind that teleporting an object around does not count as movement!

You can disable this feature on a per-object base by adding a single line of code:

teapotNode.setDeactivationEnabled(False)

Or, if you want to avoid the teleporting at all, you could make the object kinematic. But then other objects in the scene won’t be able to affect the kinematic object. So you should make it kinematic only while dragging it around.

Or you could do it the real physical way, and use forced to drag around objects.

What is going on becomes apparent when you print out the scene graph every frame, using “render.ls()” (I wonder why this simple technique is used so sparingly. In my eyes this is a wonderful way to find problems with your own code). Here is a sample from your script, after 82 frames:

PandaNode render S:(CullFaceAttrib RescaleNormalAttrib)
  BulletDebugNode Debug (2 geoms)
  PandaNode mainPointer T:(pos 0 0 -0.82)
    BulletRigidBodyNode teapot (1 shapes) active mass=1 T:(pos 0 0 0.82)
      ModelRoot teapot.egg T:(scale 0.5)
        PandaNode teapot
          GeomNode body (1 geoms: S:(ColorAttrib))
          GeomNode lid (1 geoms: S:(ColorAttrib))
          GeomNode handle (1 geoms: S:(ColorAttrib))
          GeomNode spout (1 geoms: S:(ColorAttrib))
    ModelNode camera T:(pos 0 -40 0)
      Camera cam ( PerspectiveLens )
        PerspectiveLens fov = 39.3201 30

The updatePhys fucntion moves the mainPointer (aka self.nodePointer) node down 1/100 unit each frame. Ok, we can see this is the debug output: “PandaNode mainPointer T:(pos 0 0 -0.82)”.

The camera node is below the mainPointer, which is as expected. So the camera moves along with the mainPointer.

But there is something weired. The “teapot” node has a z-value of +0.82 !!!
Why?
Well, the reason is that the teapot rigid body is still at the absolute position 0/0/0, where it has been created. In order to move a rigid body you have to set the position on the rigid body node itself, not a partent node.

I’m not sure what exactly you want to see with you sample, but this if you want to move around the teapot (geom + rigid body) and have the camera follow this movement this would be the right setup:

   def __init__(self):
      ShowBase.__init__(self)
      base.disableMouse()

      self.world = BulletWorld()
      debugNode = BulletDebugNode('Debug')
      debugNode.showWireframe(True)
      debugNode.showConstraints(True)
      debugNode.showBoundingBoxes(False)
      debugNode.showNormals(False)
      debugNP = render.attachNewNode(debugNode)
      debugNP.show()
      self.world.setDebugNode(debugNP.node())

      teapotCollisionShape = BulletCapsuleShape(1,1, ZUp)
      teapotNode = BulletRigidBodyNode('teapot')
      teapotNode.setMass( 1 )
      teapotNode.addShape( teapotCollisionShape )
      teapotNode.setDeactivationEnabled(False)
      self.teapotPointer = render.attachNewNode(teapotNode)
      self.world.attachRigidBody( teapotNode )

      camera.reparentTo( self.teapotPointer )
      camera.setPos( 0.0, -40.0, 0.0 )
      camera.lookAt( self.teapotPointer )

      teapotModel = loader.loadModel( "teapot.egg" )
      teapotModel.setScale( 0.5 )
      teapotModel.reparentTo( self.teapotPointer )

      taskMgr.add( self.updatePhys, 'updateTeapot' )

   def updatePhys(self, task):
      dt = globalClock.getDt()
      self.teapotPointer.setZ(self.teapotPointer, -0.01)
      self.world.doPhysics(dt)
      render.ls()
      return task.cont