Here’s the test case for the PhysxRay issue I’m experiencing. Basically, if there is a capsule collider, it always collides with the PhysxRay, even if its group is not in the ray test’s allowed groups mask.
#!/usr/bin/env python
"""Demonstrate issues with PhysxRay hit reporting.
When using a capsule collider, the PhysxRay always collides with it, even if
the allowed groups bitmask doesn't contain the group of the capsule collider.
There are 3 actor groups used in this app:
1 - Generic box actors
2 - The player-controlled actor
3 - The ground plane
You can put the player-controlled box back in group 1 by commenting out the
line "actorA.setGroup(2)" below; the problem still happens with that change.
"""
# Use a capsule for the player. (False causes it to use a box instead)
capsulePlayer = True
# Use Dvorak keyboard bindings (., o, e, u) instead of QWERTY. (w, a, s, d)
dvorak = True
import direct.directbase.DirectStart
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
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 PhysxBoxShapeDesc
from panda3d.physx import PhysxCapsuleShapeDesc
from panda3d.physx import PhysxPlaneShapeDesc
from panda3d.physx import PhysxEnums
from panda3d.physx import PhysxRay
from panda3d.physx import PhysxGroupsMask
from panda3d.physx import PhysxMask
import sys
# Base
base.setBackgroundColor(0, 0, 0.6, 1)
base.setFrameRateMeter(True)
base.cam.setPos(0, -20, 4)
base.cam.lookAt(0, 0, 0)
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)
if dvorak:
inputState.watchWithModifiers('forward', '.')
inputState.watchWithModifiers('left', 'o')
inputState.watchWithModifiers('reverse', 'e')
inputState.watchWithModifiers('right', 'u')
else:
inputState.watchWithModifiers('forward', 'w')
inputState.watchWithModifiers('left', 'a')
inputState.watchWithModifiers('reverse', 's')
inputState.watchWithModifiers('right', 'd')
# Scene
sceneDesc = PhysxSceneDesc()
sceneDesc.setGravity(Vec3(0, 0, -9.81))
scene = PhysxManager.getGlobalPtr().createScene(sceneDesc)
# Plane
shapeDesc = PhysxPlaneShapeDesc()
shapeDesc.setPlane(Vec3(0, 0, 1), -2)
actorDesc = PhysxActorDesc()
actorDesc.setName('Plane')
actorDesc.addShape(shapeDesc)
actorP = scene.createActor(actorDesc)
actorP.setGroup(3)
# Actors
def createBox(name, *pos):
shapeDesc = PhysxBoxShapeDesc()
shapeDesc.setDimensions(Vec3(0.5, 0.5, 0.5))
bodyDesc = PhysxBodyDesc()
bodyDesc.setMass(10.0)
actorDesc = PhysxActorDesc()
actorDesc.setBody(bodyDesc)
actorDesc.setName(name)
actorDesc.addShape(shapeDesc)
actorDesc.setGlobalPos(Point3(*pos))
actor = scene.createActor(actorDesc)
actor.setGroup(1)
print "Created actor '%s'." % (actor.getName(), )
return actor
def createCapsule(name, *pos):
shapeDesc = PhysxCapsuleShapeDesc()
shapeDesc.setHeight(1)
shapeDesc.setRadius(0.5)
bodyDesc = PhysxBodyDesc()
bodyDesc.setMass(10.0)
actorDesc = PhysxActorDesc()
actorDesc.setBody(bodyDesc)
actorDesc.setName(name)
actorDesc.addShape(shapeDesc)
actorDesc.setGlobalPos(Point3(*pos))
actor = scene.createActor(actorDesc)
actor.setGroup(1)
print "Created actor '%s'." % (actor.getName(), )
return actor
# Create box actors
for x in range(-8, 10, 2):
for y in range(-8, 10, 2):
if x == 0 and y == 0:
if capsulePlayer:
actorA = createCapsule('Capsule-A', x, y, 2)
else:
actorA = createBox('Box-A', x, y, 2)
# Set the player-controlled actor to have a different group.
actorA.setGroup(2)
else:
createBox('Box%+d%+d' % (x, y), x, y, 2)
# Ray
rayLength = 2.0
ray = PhysxRay()
ray.setLength(rayLength)
# The ray's group mask
jumpRayMask = PhysxGroupsMask.allOn()
jumpRayMask.clearBit(2)
# Debug
debugNP = render.attachNewNode(scene.getDebugGeomNode())
debugNP.node().on()
debugNP.node().visualizeWorldAxes(True)
def updateWorld(task):
dt = globalClock.getDt()
force = Vec3(0, 0, 0)
if inputState.isSet('forward'):
force.setY(90)
if inputState.isSet('reverse'):
force.setY(-90)
if inputState.isSet('left'):
force.setX(-90)
if inputState.isSet('right'):
force.setX(90)
actorA.addForce(force)
scene.simulate(dt)
scene.fetchResults()
p = actorA.getGlobalPos()
d = actorA.getGlobalQuat().getForward()
ray.setOrigin(p)
ray.setDirection(d)
hit = scene.raycastClosestShape(ray, PhysxEnums.STAll, PhysxMask.allOn(), jumpRayMask)
if not hit.isEmpty():
dist = hit.getDistance()
name = hit.getShape().getActor().getName()
print 'Hit: %f "%s"' % (dist, name)
return task.cont
taskMgr.add(updateWorld, 'updateWorld')
def doExit():
sys.exit(1)
def doScreenshot():
base.screenshot('test')
def toggleWireframe():
base.toggleWireframe()
def toggleDebug():
debugNP.node().toggle()
base.accept('escape', doExit)
base.accept('f1', toggleWireframe)
base.accept('f3', toggleDebug)
base.accept('f5', doScreenshot)
# Contact reporting
scene.enableContactReporting(True)
scene.setActorGroupPairFlag(1, 1, PhysxEnums.CPFNotifyOnStartTouch, 1)
scene.setActorGroupPairFlag(1, 1, PhysxEnums.CPFNotifyOnEndTouch, 1)
scene.setActorGroupPairFlag(1, 1, PhysxEnums.CPFNotifyOnTouch, 1)
scene.setActorGroupPairFlag(2, 1, PhysxEnums.CPFNotifyOnStartTouch, 1)
scene.setActorGroupPairFlag(2, 1, PhysxEnums.CPFNotifyOnEndTouch, 1)
scene.setActorGroupPairFlag(2, 1, PhysxEnums.CPFNotifyOnTouch, 1)
def onContactStart(pair):
print 'START', pair.getActorA().getName(), pair.getActorB().getName(), pair.getActorA().getGroup(), pair.getActorB().getGroup()
for contact in pair.getContactPoints():
print ' ', contact.getPoint()
base.accept('physx-contact-start', onContactStart)
# Run
run()