Panda PhysX

We do.

Anyone know why:

from panda3d.physx import PhysxControllerReport

doesn’t work? It won’t let me import this class. I need it to be able to handle when the controller bumps into stuff.

For reference I’m trying to figure out how I can setup a callback for when the controller runs into stuff.

You can not create instances of this class because the constructor of this class is not published to Python. The reason for this is because you currently can’t do anything with this class. It is an internal component of the Panda3D PhysX module.

Within the PhysxControllerReport we provide a “default” implementation for the callbacks which is admittedly a bit, um, simple. Being able to handle the contacts yourself is a reasonable request. However, I’m a bit reluctant to add features to this old (PhysX 2.8.4) code, because my current development environmant is set up for PhysX 3.2. Anyway, I will give it a try and perhaps I can come up with this new feature. But it will take a few days (at least).

I have a question!

I want to reparent a physx shape to a nodepath (actor joint), the shape doesn’t need a body, it’s just for contact tests) but I can’t find a way to do that…

Thank you in advance!

The PhysX 2.8.4 module does not have objects derived from PandaNode, so you can not reparent any PhysX object to scene graph nodes.

A shape (PhysxShape) can not be created standalone. It is always part of an actor (PhysxActor).

You probably want to create a kinematic actor. This is done by setting the BF_kinematic flag on the actor. You have to update the actor’s position yourself every frame (read it from the exposed joint and set it on the actor). Pay attention to use the move_global_pos/hpr/mat methods to move kinematic objects around.

Again a question for the physics master…

I am currently making a ragdoll descriptor and it work pretty well.
My problem is for spherical joints configuration, all the possible settings(twist, spring, swing, global_axis, twist_axis…) are going to blow up my head.

I take the example of the soulders:
To understand the settings I have use this website:
source: http://www.vtmod.com/public/vtPhysX/pjball.html

So i try to reproduce what I seen on the picture above.

But when I apply physics, the arm escapes from the swing limit cone. Also, I don’t understand the twist limit.

My description for the soulder joint is:
(The twist settings are completely random because I don’t understand how to use it but I think I really need it)

    joint_desc = PhysxSphericalJointDesc()
    joint_desc.setGlobalAxis(Vec3( 1, 0, 0))
    joint_desc.setFlag(PhysxEnums.SJFTwistLimitEnabled, True)
    joint_desc.setFlag(PhysxEnums.SJFSwingLimitEnabled, True)
    joint_desc.setSwingLimit(PhysxJointLimitDesc(pi/1.5, 0, 0))
    joint_desc.setSwingAxis(Vec3(0, 0, 1)) 
    joint_desc.setTwistLimitLow(PhysxJointLimitDesc(0, 0, 0))
    joint_desc.setTwistLimitHigh(PhysxJointLimitDesc(pi/2, 0, 0))

I spend the last 4 hours trying differents settings and no one produces a possible “human effect”…
I hope you can explain me how to set the spherical joints. Thanks!

Here is a demo where you can shoot some ragdoll with the space key.
http://www.mediafire.com/?t0l84f8cf466yw0

The bad point is that there is a lot of flickering and maybe some bugs…

Joints can be a bit tricky, using every physics engine. I suggest starting with simple setup until you have understood how the PhysX spherical joint works: one or two bodies, nothing else. A few tips:

  • Turn of those nasty filters, since they affect the debug renderer too. You can enable them later again, at production time. You NEED to know the colours of different arrows!
  • Only use the swing limit at first. When you are comfortable add the twist limit.
  • For ragdolls it might be better to setup joints using the local achor/axis and not the global anchor/axis.
  • For the debug renderer show the LOCAL axes of a joint (not the global). The blue arrow has to be inside the cone limit. When this limit is violated the cone will be renderer yellow.
  • Swing limt angle is in radians. I think you know, since you multiply with pi. Now, a cone opening of pi/1.5 is 120 degree, which is more than 90 degree. Might be a bit too much. Look at the cone in your screenshot. At first I though you limit the upper arms to the inner part of the cone, but this would be an opening of 60 degree. Since you allow an opening of 120 degree I guess your axis simply points in the wrong direction.

Here is an example to play around a bit:

import direct.directbase.DirectStart

from direct.showbase.InputStateGlobal import inputState

from panda3d.core import Vec3
from panda3d.core import Vec4
from panda3d.core import Mat4
from panda3d.core import Point3

from panda3d.physx import PhysxManager
from panda3d.physx import PhysxSceneDesc
from panda3d.physx import PhysxBodyDesc
from panda3d.physx import PhysxActorDesc
from panda3d.physx import PhysxCapsuleShapeDesc
from panda3d.physx import PhysxSphericalJointDesc
from panda3d.physx import PhysxJointLimitDesc
from panda3d.physx import PhysxSpringDesc
from panda3d.physx import PhysxEnums

from base import BaseWorld

class World(BaseWorld):

  def __init__(self):
    BaseWorld.__init__(self)

  def setup(self):

    # Scene
    sceneDesc = PhysxSceneDesc()
    sceneDesc.setGravity(Vec3(0, 0, -9.81))
    sceneDesc.setFlag(PhysxEnums.SFForceConeFriction, True)

    self.scene = PhysxManager.getGlobalPtr().createScene(sceneDesc)

    # Material
    m0 = self.scene.getMaterial(0)
    m0.setRestitution(0.2)
    m0.setStaticFriction(0.3)
    m0.setDynamicFriction(0.3)

    # Actor 0 (kinematic)
    self.np0 = loader.loadModel('models/box.egg')
    self.np0.reparentTo(render)
    self.np0.setPos(Point3(2, 0, 4))
    self.np0.setP(90)

    shapeDesc = PhysxCapsuleShapeDesc()
    shapeDesc.setRadius(0.5)
    shapeDesc.setHeight(2.0)

    bodyDesc = PhysxBodyDesc()
    bodyDesc.setMass(10.0)

    actorDesc = PhysxActorDesc()
    actorDesc.setBody(bodyDesc)
    actorDesc.setName('0')
    actorDesc.addShape(shapeDesc)
    actorDesc.setGlobalMat(self.np0.getMat())

    self.actor0 = self.scene.createActor(actorDesc)
    self.actor0.attachNodePath(self.np0)
    self.actor0.setBodyFlag(PhysxEnums.BFKinematic, 1)

    # Actor 1 (dynamic)
    self.np1 = loader.loadModel('models/box.egg')
    self.np1.reparentTo(render)
    self.np1.setPos(Point3(2, 0, 0))
    self.np1.setP(90)

    shapeDesc = PhysxCapsuleShapeDesc()
    shapeDesc.setRadius(0.5)
    shapeDesc.setHeight(2.0)

    bodyDesc = PhysxBodyDesc()
    bodyDesc.setMass(10.0)

    actorDesc = PhysxActorDesc()
    actorDesc.setBody(bodyDesc)
    actorDesc.setName('1')
    actorDesc.addShape(shapeDesc)
    actorDesc.setGlobalMat(self.np1.getMat())

    self.actor1 = self.scene.createActor(actorDesc)
    self.actor1.attachNodePath(self.np1)

    # Joint
    jointDesc = PhysxSphericalJointDesc()
    jointDesc.setName('Spherical Joint')
    jointDesc.setActor(0, self.actor0)
    jointDesc.setActor(1, self.actor1)
    jointDesc.setGlobalAnchor(Point3(2, 0, 2))
    jointDesc.setGlobalAxis(Vec3(0, 0, 1))
    #jointDesc.setLocalAnchor(0, Point3(0, -2, 0))
    #jointDesc.setLocalAnchor(1, Point3(0, 2, 0))
    #jointDesc.setLocalAxis(0, Vec3(0, 1, 0))
    #jointDesc.setLocalAxis(1, Vec3(0, 1, 0))

    #print jointDesc.isValid()

    pi = 3.14

    # Swing Limit
    jointDesc.setFlag(PhysxEnums.SJFSwingLimitEnabled, True)
    jointDesc.setSwingAxis(Vec3(0, 0, 1))
    jointDesc.setSwingLimit(PhysxJointLimitDesc(0.25 * pi, 0, 1))

    # Swing Spring
    #jointDesc.setFlag(PhysxEnums.SJFSwingSpringEnabled, True)
    #jointDesc.setSwingSpring(PhysxSpringDesc(6000, 0, 0));

    # Twist Limit
    #jointDesc.setFlag(PhysxEnums.SJFTwistLimitEnabled, True)
    #jointDesc.setTwistLimitLow(PhysxJointLimitDesc(0, 0, 1))
    #jointDesc.setTwistLimitHigh(PhysxJointLimitDesc(1.0 * pi, 0, 1)) 

    joint = self.scene.createJoint(jointDesc)

    # Debug renderer
    self.debugNP = render.attachNewNode(self.scene.getDebugGeomNode())
    self.debugNP.node().on()
    self.debugNP.node().visualizeWorldAxes(True)
    self.debugNP.node().visualizeActorAxes(True)
    self.debugNP.node().visualizeJointLocalAxes(True)
    self.debugNP.node().visualizeJointWorldAxes(False)
    self.debugNP.node().visualizeJointLimits(True)

  def cleanup(self):
    self.np0.removeNode()
    self.np1.removeNode()
    self.debugNP.removeNode()
    self.scene.release()
    render.ls()

  def update(self, task):
    dt = globalClock.getDt()
    self.processInput()
    self.scene.simulate(dt)
    self.scene.fetchResults()
    return task.cont

  def processInput(self):
    force = Vec3(0, 0, 0)
    torque = Vec3(0, 0, 0)

    if inputState.isSet('forward'): force.setY( 1.0)
    if inputState.isSet('reverse'): force.setY(-1.0)
    if inputState.isSet('left'):    force.setX(-1.0)
    if inputState.isSet('right'):   force.setX( 1.0)

    if inputState.isSet('turnLeft'):  torque.setZ( 1.0)
    if inputState.isSet('turnRight'): torque.setZ(-1.0)

    force *= 90.0
    torque *= 90.0

    self.actor1.addForce(force)
    self.actor1.addTorque(torque)

if __name__ == '__main__':
  world = World()
  run()

base.py:

from direct.showbase.DirectObject import DirectObject
from direct.showbase.InputStateGlobal import inputState

from panda3d.core import AmbientLight
from panda3d.core import DirectionalLight
from panda3d.core import Vec3
from panda3d.core import Vec4

import sys

class BaseWorld(DirectObject):

  def __init__(self):
    base.setBackgroundColor(0, 0, 0.3, 1)
    base.setFrameRateMeter(True)

    base.cam.setPos(0, -20, 4)
    base.cam.lookAt(0, 0, 1)

    # Light
    alight = AmbientLight('ambientLight')
    alight.setColor(Vec4(0.5, 0.5, 0.5, 1))
    alightNP = render.attachNewNode(alight)

    dlight = DirectionalLight('directionalLight')
    dlight.setDirection(Vec3(1, 1, -1))
    dlight.setColor(Vec4(0.7, 0.7, 0.7, 1))
    dlightNP = render.attachNewNode(dlight)

    render.clearLight()
    render.setLight(alightNP)
    render.setLight(dlightNP)

    # Input
    self.accept('escape', self.doExit)
    self.accept('r', self.doReset)
    self.accept('f1', self.toggleWireframe)
    self.accept('f2', self.toggleTexture)
    self.accept('f3', self.toggleDebug)
    self.accept('f5', self.doScreenshot)

    inputState.watchWithModifiers( 'forward', 'w' )
    inputState.watchWithModifiers( 'left', 'a' )
    inputState.watchWithModifiers( 'reverse', 's' )
    inputState.watchWithModifiers( 'right', 'd' )
    inputState.watchWithModifiers( 'turnLeft', 'q' )
    inputState.watchWithModifiers( 'turnRight', 'e' )

    self.debugNP = None

    # Task
    taskMgr.add(self.update, 'updateWorld')

    # Physics
    self.setup()

  def doExit(self):
    self.cleanup()
    sys.exit(1)

  def doReset(self):
    self.cleanup()
    self.setup()

  def toggleWireframe(self):
    base.toggleWireframe()

  def toggleTexture(self):
    base.toggleTexture()

  def toggleDebug(self):
    if self.debugNP:
      self.debugNP.node().toggle()

  def doScreenshot(self):
    base.screenshot('PhysX')

  def setup(self):
    pass

  def cleanup(self):
    pass

  def update(self, task):
    return task.cont

Thanks you so much for all these good advices.
I have quickly update the code and the result is better with new joints settings:

http://www.mediafire.com/download.php?gezobj0a1bjbjkk

I have some questions:

  1. Sometimes when I shoot a ragdoll, the ball “wake up” the ragdoll physics but the ball just traverse the ragdoll and do not collide with the ragdoll bodies. This happens for example when I move back the camera and shoot a ragdoll from far away. (After some tests, I notice that this problem occurs at random times)

  2. Do you know if it’s possible to reduce all these flickering, it’s seems to occur because of joints limits.

  3. I have always some problems with joints axis. I think it’s because of the actor models joints hierarchy. For example, the wrist joint is easy to configure because it’s between 2 aligned limbs (hand and the down arm). I encountered problems with the shoulders, it’s a joint between the torso (Z align) and the arm (X align), so i tried to change the SwingAxis but I am not sure if it’s the good way:

    #shoulders
    joint_desc = PhysxSphericalJointDesc()
    joint_desc.setSwingAxis(Vec3(-1,0.2, -0.5))
    
    joint_desc.setFlag(PhysxEnums.SJFSwingLimitEnabled, True)
    joint_desc.setSwingLimit(PhysxJointLimitDesc(radians(20), 0, 1))
    
    joint_desc.setFlag(PhysxEnums.SJFSwingSpringEnabled, True)
    joint_desc.setSwingSpring(PhysxSpringDesc(200, 0, 0))
    
    joint_desc.setFlag(PhysxEnums.SJFTwistLimitEnabled, True)
    joint_desc.setTwistLimitLow(PhysxJointLimitDesc(-0.1, 0, 1))
    joint_desc.setTwistLimitHigh(PhysxJointLimitDesc(0.2, 0, 1)) 
    
    joint_desc.setLocalAxis(0, Vec3(0,1, 0))
    joint_desc.setLocalAxis(1, Vec3(0,1, 0))

Again thanks for your time, next week I will rewrite all the code, I am also in search of a way to abstract a bit all these joints settings.

I’m not able to download this. Can someone reupload?

I didn’t store a copy of the files Gatou provided. Sorry, but I guess the only one able to help you is Gatou himself.

Do you have current projects who use PhysX for physics? Would be nice to know, since I plan to replace the old PhysX 2.8.4 module with a new one using PhysX 3.3. It is a complete new API, and PhysX 3.x has support to some degree for ragdolls.

I noticed something weird today when I was working on a test, it seems the PhysxActor associated with my Controller is always rotated incorrectly in one axis. More specifically the Up Vector faces behind it while the Forward vector aims up.

Anyone know why this might be happening? I’ve tried some hacks to fix it but due to how the controller/actor are linked you can’t make any changes to the actor’s rotation independent of the controller.

The old (2.8) PhysX controller had it’s own conventions about what coordinate system to use. Y-up if I remember right.You can use Y-up for all your code. If you want to use Z-up this is fine too, since we handle the necessary transform under the hood.

Yeah Y-up is the issue I’m having. You say ‘old’ have you already updated p3d to use the newest version of physx?

Yes and No. Yes, because I have started working on PhysX 3.x over a year ago, and no because I have no commited a single line to the Panda3D repository so far.

PhysX 3.x is a complete rewrite of the PhysX library by NVIDIA, so don’t expect to have the same features or API as PhysX 2.x. One of the best things about PhysX 3.x is that all DLLs are redistributable (including the character controller).

So far I have rigid body physics working, also joints, vehicles (both wheeled and tank) and KCC. But I am having trouble with the ragdolls. And I have not yet started to work on particles and cloth. Before moving to the Panda3D repository I want to have a halfway complex RPG example, to be sure I didn’t miss a crucial feature.

Any updates on the new version of PhysX?

Not yet. with two small kids I am missing time for my old hobbies.