I am new to Panda3D, properly I learn Visual studio C++ programming language… and never learn python before. But for panda3D, mostly there are programming by python programming language. Then I am here to get some help from u all to help translate code example below into C++… then i can more know about collision detection from panda 3D…
These code is the tutorial from panda3d software… So I hopefully can giv me some example of coding about collision detection:
class World(DirectObject):
def __init__(self):
self.title = OnscreenText(text="Panda3D: Tutorial - Collision Detection",
style=1, fg=(1,1,1,1),
pos=(0.7,-0.95), scale = .07)
self.instructions = OnscreenText(text="Mouse pointer tilts the board",
pos = (-1.3, .95), fg=(1,1,1,1),
align = TextNode.ALeft, scale = .05)
self.accept("escape", sys.exit) # Escape quits
base.disableMouse() # Disable mouse-based camera control
camera.setPosHpr(0, 0, 25, 0, -90, 0) # Place the camera
# Load the maze and place it in the scene
self.maze = loader.loadModel("models/maze")
self.maze.reparentTo(render)
self.walls = self.maze.find("**/wall_collide")
self.walls.node().setIntoCollideMask(BitMask32.bit(0))
self.loseTriggers = []
for i in range(6):
trigger = self.maze.find("**/hole_collide" + str(i))
trigger.node().setIntoCollideMask(BitMask32.bit(0))
trigger.node().setName("loseTrigger")
self.loseTriggers.append(trigger)
self.mazeGround = self.maze.find("**/ground_collide")
self.mazeGround.node().setIntoCollideMask(BitMask32.bit(1))
self.ballRoot = render.attachNewNode("ballRoot")
self.ball = loader.loadModel("models/ball")
self.ball.reparentTo(self.ballRoot)
self.ballSphere = self.ball.find("**/ball")
self.ballSphere.node().setFromCollideMask(BitMask32.bit(0))
self.ballSphere.node().setIntoCollideMask(BitMask32.allOff())
self.ballGroundRay = CollisionRay() # Create the ray
self.ballGroundRay.setOrigin(0,0,10) # Set its origin
self.ballGroundRay.setDirection(0,0,-1) # And its direction
# Collision solids go in CollisionNode
self.ballGroundCol = CollisionNode('groundRay') # Create and name the node
self.ballGroundCol.addSolid(self.ballGroundRay) # Add the ray
self.ballGroundCol.setFromCollideMask(BitMask32.bit(1)) # Set its bitmasks
self.ballGroundCol.setIntoCollideMask(BitMask32.allOff())
self.ballGroundColNp = self.ballRoot.attachNewNode(self.ballGroundCol)
# Uncomment this line to see the ray
# self.ballGroundColNp.show()
# Finally, we create a CollisionTraverser. CollisionTraversers are what
# do the job of calculating collisions
self.cTrav = CollisionTraverser()
self.cHandler = CollisionHandlerQueue()
# Now we add the collision nodes that can create a collision to the
# traverser. The traverser will compare these to all others nodes in the
# scene. There is a limit of 32 CollisionNodes per traverser
# We add the collider, and the handler to use as a pair
self.cTrav.addCollider(self.ballSphere, self.cHandler)
self.cTrav.addCollider(self.ballGroundColNp, self.cHandler)
# Collision traversers have a built in tool to help visualize collisions.
# Uncomment the next line to see it.
# self.cTrav.showCollisions(render)
# This section deals with lighting for the ball. Only the ball was lit
# because the maze has static lighting pregenerated by the modeler
ambientLight = AmbientLight("ambientLight")
ambientLight.setColor(Vec4(.55, .55, .55, 1))
directionalLight = DirectionalLight("directionalLight")
directionalLight.setDirection(Vec3(0, 0, -1))
directionalLight.setColor(Vec4(0.375, 0.375, 0.375, 1))
directionalLight.setSpecularColor(Vec4(1, 1, 1, 1))
self.ballRoot.setLight(render.attachNewNode(ambientLight))
self.ballRoot.setLight(render.attachNewNode(directionalLight))
# This section deals with adding a specular highlight to the ball to make
# it look shiny
m = Material()
m.setSpecular(Vec4(1,1,1,1))
m.setShininess(96)
self.ball.setMaterial(m, 1)
self.start()
def start(self):
# The maze model also has a locator in it for where to start the ball
# To access it we use the find command
startPos = self.maze.find("**/start").getPos()
self.ballRoot.setPos(startPos) # Set the ball in the starting position
self.ballV = Vec3(0,0,0) # Initial velocity is 0
self.accelV = Vec3(0,0,0) # Initial acceleration is 0
# For a traverser to actually do collisions, you need to call
# traverser.traverse() on a part of the scene. Fortunatly, base has a
# task that does this for the entire scene once a frame. This sets up our
# traverser as the one to be called automatically
base.cTrav = self.cTrav
# Create the movement task, but first make sure it is not already running
taskMgr.remove("rollTask")
self.mainLoop = taskMgr.add(self.rollTask, "rollTask")
self.mainLoop.last = 0
# This function handles the collision between the ray and the ground
# Information about the interaction is passed in colEntry
def groundCollideHandler(self, colEntry):
# Set the ball to the appropriate Z value for it to be exactly on the ground
newZ = colEntry.getSurfacePoint(render).getZ()
self.ballRoot.setZ(newZ+.4)
norm = colEntry.getSurfaceNormal(render)
accelSide = norm.cross(UP)
# Then that vector is crossed with the surface normal to get a vector that
# points down the slope. By getting the acceleration in 3D like this rather
# than in 2D, we reduce the amount of error per-frame, reducing jitter
self.accelV = norm.cross(accelSide)
# This function handles the collision between the ball and a wall
def wallCollideHandler(self, colEntry):
# First we calculate some numbers we need to do a reflection
norm = colEntry.getSurfaceNormal(render) * -1 # The normal of the wall
curSpeed = self.ballV.length() # The current speed
inVec = self.ballV / curSpeed # The direction of travel
velAngle = norm.dot(inVec) # Angle of incidance
hitDir = colEntry.getSurfacePoint(render) - self.ballRoot.getPos()
hitDir.normalize()
hitAngle = norm.dot(hitDir) # The angle between the ball and the normal
# Ignore the collision if the ball is either moving away from the wall
# already (so that we don't accidentally send it back into the wall)
# and ignore it if the collision isn't dead-on (to avoid getting caught on
# corners)
if velAngle > 0 and hitAngle > .995:
# Standard reflection equation
reflectVec = (norm * norm.dot(inVec * -1) * 2) + inVec
# This makes the velocity half of what it was if the hit was dead-on
# and nearly exactly what it was if this is a glancing blow
self.ballV = reflectVec * (curSpeed * (((1-velAngle)*.5)+.5))
disp = (colEntry.getSurfacePoint(render) -
colEntry.getInteriorPoint(render))
newPos = self.ballRoot.getPos() + disp
self.ballRoot.setPos(newPos)
# This is the task that deals with making everything interactive
def rollTask(self, task):
# Standard technique for finding the amount of time since the last frame
dt = task.time - task.last
task.last = task.time
# If dt is large, then there has been a # hiccup that could cause the ball
# to leave the field if this functions runs, so ignore the frame
if dt > .2: return Task.cont
# The collision handler collects the collisions. We dispatch which function
# to handle the collision based on the name of what was collided into
for i in range(self.cHandler.getNumEntries()):
entry = self.cHandler.getEntry(i)
name = entry.getIntoNode().getName()
if name == "wall_collide": self.wallCollideHandler(entry)
elif name == "ground_collide": self.groundCollideHandler(entry)
elif name == "loseTrigger": self.loseGame(entry)
# Read the mouse position and tilt the maze accordingly
if base.mouseWatcherNode.hasMouse():
mpos = base.mouseWatcherNode.getMouse() # get the mouse position
self.maze.setP(mpos.getY() * -10)
self.maze.setR(mpos.getX() * 10)
# Finally, we move the ball
# Update the velocity based on acceleration
self.ballV += self.accelV * dt * ACCEL
# Clamp the velocity to the maximum speed
if self.ballV.lengthSquared() > MAX_SPEED_SQ:
self.ballV.normalize()
self.ballV *= MAX_SPEED
# Update the position based on the velocity
self.ballRoot.setPos(self.ballRoot.getPos() + (self.ballV * dt))
# This block of code rotates the ball. It uses something called a quaternion
# to rotate the ball around an arbitrary axis. That axis perpendicular to
# the balls rotation, and the amount has to do with the size of the ball
# This is multiplied on the previous rotation to incrimentally turn it.
prevRot = LRotationf(self.ball.getQuat())
axis = UP.cross(self.ballV)
newRot = LRotationf(axis, 45.5 * dt * self.ballV.length())
self.ball.setQuat(prevRot * newRot)
return Task.cont # Continue the task indefinitely
# If the ball hits a hole trigger, then it should fall in the hole.
# This is faked rather than dealing with the actual physics of it.
def loseGame(self, entry):
# The triggers are set up so that the center of the ball should move to the
# collision point to be in the hole
toPos = entry.getInteriorPoint(render)
taskMgr.remove('rollTask') # Stop the maze task
# Move the ball into the hole over a short sequence of time. Then wait a
# second and call start to reset the game
Sequence(
Parallel(
LerpFunc(self.ballRoot.setX, fromData = self.ballRoot.getX(),
toData = toPos.getX(), duration = .1),
LerpFunc(self.ballRoot.setY, fromData = self.ballRoot.getY(),
toData = toPos.getY(), duration = .1),
LerpFunc(self.ballRoot.setZ, fromData = self.ballRoot.getZ(),
toData = self.ballRoot.getZ() - .9, duration = .2)),
Wait(1),
Func(self.start)).start()
# Finally, create an instance of our class and start 3d rendering
w = World()
run()