|
|
@@ -0,0 +1,709 @@
|
|
|
+"""
|
|
|
+ShipPilot.py is for avatars pilotting ships (or more accurately, a ship as the avatar).
|
|
|
+
|
|
|
+A control such as this one provides:
|
|
|
+ - creation of the collision nodes
|
|
|
+ - handling the keyboard and mouse input for avatar movement
|
|
|
+ - moving the avatar
|
|
|
+
|
|
|
+it does not:
|
|
|
+ - play sounds
|
|
|
+ - play animations
|
|
|
+
|
|
|
+although it does send messeges that allow a listener to play sounds or
|
|
|
+animations based on control events.
|
|
|
+"""
|
|
|
+
|
|
|
+from direct.showbase.ShowBaseGlobal import *
|
|
|
+
|
|
|
+from direct.directnotify import DirectNotifyGlobal
|
|
|
+from pandac import PhysicsManager
|
|
|
+import math
|
|
|
+
|
|
|
+import PhysicsWalker
|
|
|
+
|
|
|
+#import LineStream
|
|
|
+
|
|
|
+class ShipPilot(PhysicsWalker.PhysicsWalker):
|
|
|
+
|
|
|
+ notify = DirectNotifyGlobal.directNotify.newCategory("PhysicsWalker")
|
|
|
+ wantAvatarPhysicsIndicator = base.config.GetBool('want-avatar-physics-indicator', 0)
|
|
|
+
|
|
|
+ useLifter = 0
|
|
|
+ useHeightRay = 0
|
|
|
+
|
|
|
+ # special methods
|
|
|
+ def __init__(self, gravity = -32.1740, standableGround=0.707,
|
|
|
+ hardLandingForce=16.0):
|
|
|
+ assert(self.debugPrint("PhysicsWalker(gravity=%s, standableGround=%s)"%(
|
|
|
+ gravity, standableGround)))
|
|
|
+ PhysicsWalker.PhysicsWalker.__init__(self, gravity = -32.1740, standableGround=0.707,
|
|
|
+ hardLandingForce=16.0)
|
|
|
+ self.__gravity=gravity
|
|
|
+ self.__standableGround=standableGround
|
|
|
+ self.__hardLandingForce=hardLandingForce
|
|
|
+
|
|
|
+ self.needToDeltaPos = 0
|
|
|
+ self.physVelocityIndicator=None
|
|
|
+ self.avatarControlForwardSpeed=0
|
|
|
+ self.avatarControlJumpForce=0
|
|
|
+ self.avatarControlReverseSpeed=0
|
|
|
+ self.avatarControlRotateSpeed=0
|
|
|
+ self.__oldAirborneHeight=None
|
|
|
+ self.getAirborneHeight=None
|
|
|
+ self.__oldContact=None
|
|
|
+ self.__oldPosDelta=Vec3(0)
|
|
|
+ self.__oldDt=0
|
|
|
+ self.__speed=0.0
|
|
|
+ self.__rotationSpeed=0.0
|
|
|
+ self.__slideSpeed=0.0
|
|
|
+ self.__vel=Vec3(0.0)
|
|
|
+ self.collisionsActive = 0
|
|
|
+
|
|
|
+ self.isAirborne = 0
|
|
|
+ self.highMark = 0
|
|
|
+
|
|
|
+ def setWalkSpeed(self, forward, jump, reverse, rotate):
|
|
|
+ assert(self.debugPrint("setWalkSpeed()"))
|
|
|
+ self.avatarControlForwardSpeed=forward
|
|
|
+ self.avatarControlJumpForce=0.0
|
|
|
+ self.avatarControlReverseSpeed=reverse
|
|
|
+ self.avatarControlRotateSpeed=rotate
|
|
|
+
|
|
|
+ def getSpeeds(self):
|
|
|
+ #assert(self.debugPrint("getSpeeds()"))
|
|
|
+ return (self.__speed, self.__rotationSpeed)
|
|
|
+
|
|
|
+ def setupRay(self, floorBitmask, floorOffset):
|
|
|
+ # This is a ray cast from your head down to detect floor polygons
|
|
|
+ # A toon is about 4.0 feet high, so start it there
|
|
|
+ self.cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)
|
|
|
+ cRayNode = CollisionNode('PW.cRayNode')
|
|
|
+ cRayNode.addSolid(self.cRay)
|
|
|
+ self.cRayNodePath = self.avatarNodePath.attachNewNode(cRayNode)
|
|
|
+ self.cRayBitMask = floorBitmask
|
|
|
+ cRayNode.setFromCollideMask(self.cRayBitMask)
|
|
|
+ cRayNode.setIntoCollideMask(BitMask32.allOff())
|
|
|
+
|
|
|
+ if 0 or self.useLifter:
|
|
|
+ # set up floor collision mechanism
|
|
|
+ self.lifter = CollisionHandlerFloor()
|
|
|
+ self.lifter.setInPattern("enter%in")
|
|
|
+ self.lifter.setOutPattern("exit%in")
|
|
|
+ self.lifter.setOffset(floorOffset)
|
|
|
+
|
|
|
+ # Limit our rate-of-fall with the lifter.
|
|
|
+ # If this is too low, we actually "fall" off steep stairs
|
|
|
+ # and float above them as we go down. I increased this
|
|
|
+ # from 8.0 to 16.0 to prevent this
|
|
|
+ #self.lifter.setMaxVelocity(16.0)
|
|
|
+
|
|
|
+ #self.bobNodePath = self.avatarNodePath.attachNewNode("bob")
|
|
|
+ #self.lifter.addCollider(self.cRayNodePath, self.cRayNodePath)
|
|
|
+ self.lifter.addCollider(self.cRayNodePath, self.avatarNodePath)
|
|
|
+ else: # useCollisionHandlerQueue
|
|
|
+ self.cRayQueue = CollisionHandlerQueue()
|
|
|
+ self.cTrav.addCollider(self.cRayNodePath, self.cRayQueue)
|
|
|
+
|
|
|
+ def determineHeight(self):
|
|
|
+ """
|
|
|
+ returns the height of the avatar above the ground.
|
|
|
+ If there is no floor below the avatar, 0.0 is returned.
|
|
|
+ aka get airborne height.
|
|
|
+ """
|
|
|
+ if self.useLifter:
|
|
|
+ height = self.avatarNodePath.getPos(self.cRayNodePath)
|
|
|
+ # If the shadow where not pointed strait down, we would need to
|
|
|
+ # get magnitude of the vector. Since it is strait down, we'll
|
|
|
+ # just get the z:
|
|
|
+ #spammy --> assert self.debugPrint("getAirborneHeight() returning %s"%(height.getZ(),))
|
|
|
+ assert onScreenDebug.add("height", height.getZ())
|
|
|
+ return height.getZ() - self.floorOffset
|
|
|
+ else: # useCollisionHandlerQueue
|
|
|
+ """
|
|
|
+ returns the height of the avatar above the ground.
|
|
|
+ If there is no floor below the avatar, 0.0 is returned.
|
|
|
+ aka get airborne height.
|
|
|
+ """
|
|
|
+ height = 0.0
|
|
|
+ #*#self.cRayTrav.traverse(render)
|
|
|
+ if self.cRayQueue.getNumEntries() != 0:
|
|
|
+ # ...we have a floor.
|
|
|
+ # Choose the highest of the possibly several floors we're over:
|
|
|
+ self.cRayQueue.sortEntries()
|
|
|
+ floorPoint = self.cRayQueue.getEntry(0).getFromIntersectionPoint()
|
|
|
+ height = -floorPoint.getZ()
|
|
|
+ self.cRayQueue.clearEntries()
|
|
|
+ if __debug__:
|
|
|
+ onScreenDebug.add("height", height)
|
|
|
+ return height
|
|
|
+
|
|
|
+ def setupSphere(self, bitmask, avatarRadius):
|
|
|
+ """
|
|
|
+ Set up the collision sphere
|
|
|
+ """
|
|
|
+ # This is a sphere on the ground to detect barrier collisions
|
|
|
+ self.avatarRadius = avatarRadius
|
|
|
+ centerHeight = avatarRadius
|
|
|
+ if self.useHeightRay:
|
|
|
+ centerHeight *= 2.0
|
|
|
+ self.cSphere = CollisionSphere(0.0, 0.0, centerHeight, avatarRadius)
|
|
|
+ cSphereNode = CollisionNode('PW.cSphereNode')
|
|
|
+ cSphereNode.addSolid(self.cSphere)
|
|
|
+ self.cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode)
|
|
|
+ self.cSphereBitMask = bitmask
|
|
|
+
|
|
|
+ cSphereNode.setFromCollideMask(self.cSphereBitMask)
|
|
|
+ cSphereNode.setIntoCollideMask(BitMask32.allOff())
|
|
|
+
|
|
|
+ # set up collision mechanism
|
|
|
+ self.pusher = PhysicsCollisionHandler()
|
|
|
+ self.pusher.setInPattern("enter%in")
|
|
|
+ self.pusher.setOutPattern("exit%in")
|
|
|
+
|
|
|
+ self.pusher.addCollider(self.cSphereNodePath, self.avatarNodePath)
|
|
|
+
|
|
|
+ def setupPhysics(self, avatarNodePath):
|
|
|
+ assert(self.debugPrint("setupPhysics()"))
|
|
|
+ # Connect to Physics Manager:
|
|
|
+ self.actorNode=ActorNode("physicsActor")
|
|
|
+ self.actorNode.getPhysicsObject().setOriented(1)
|
|
|
+ self.actorNode.getPhysical(0).setViscosity(0.1)
|
|
|
+ physicsActor=NodePath(self.actorNode)
|
|
|
+ avatarNodePath.reparentTo(physicsActor)
|
|
|
+ avatarNodePath.assign(physicsActor)
|
|
|
+ self.phys=PhysicsManager.PhysicsManager()
|
|
|
+
|
|
|
+ fn=ForceNode("gravity")
|
|
|
+ fnp=NodePath(fn)
|
|
|
+ #fnp.reparentTo(physicsActor)
|
|
|
+ fnp.reparentTo(render)
|
|
|
+ gravity=LinearVectorForce(0.0, 0.0, self.__gravity)
|
|
|
+ fn.addForce(gravity)
|
|
|
+ self.phys.addLinearForce(gravity)
|
|
|
+ self.gravity = gravity
|
|
|
+
|
|
|
+ fn=ForceNode("priorParent")
|
|
|
+ fnp=NodePath(fn)
|
|
|
+ fnp.reparentTo(render)
|
|
|
+ priorParent=LinearVectorForce(0.0, 0.0, 0.0)
|
|
|
+ fn.addForce(priorParent)
|
|
|
+ self.phys.addLinearForce(priorParent)
|
|
|
+ self.priorParentNp = fnp
|
|
|
+ self.priorParent = priorParent
|
|
|
+
|
|
|
+ fn=ForceNode("viscosity")
|
|
|
+ fnp=NodePath(fn)
|
|
|
+ #fnp.reparentTo(physicsActor)
|
|
|
+ fnp.reparentTo(render)
|
|
|
+ self.avatarViscosity=LinearFrictionForce(0.0, 1.0, 0)
|
|
|
+ #self.avatarViscosity.setCoef(0.9)
|
|
|
+ fn.addForce(self.avatarViscosity)
|
|
|
+ self.phys.addLinearForce(self.avatarViscosity)
|
|
|
+
|
|
|
+ self.phys.attachLinearIntegrator(LinearEulerIntegrator())
|
|
|
+ self.phys.attachPhysicalnode(physicsActor.node())
|
|
|
+
|
|
|
+ self.acForce=LinearVectorForce(0.0, 0.0, 0.0)
|
|
|
+ fn=ForceNode("avatarControls")
|
|
|
+ fnp=NodePath(fn)
|
|
|
+ fnp.reparentTo(render)
|
|
|
+ fn.addForce(self.acForce)
|
|
|
+ self.phys.addLinearForce(self.acForce)
|
|
|
+ #self.phys.removeLinearForce(self.acForce)
|
|
|
+ #fnp.remove()
|
|
|
+ return avatarNodePath
|
|
|
+
|
|
|
+ def initializeCollisions(self, collisionTraverser, avatarNodePath,
|
|
|
+ wallBitmask, floorBitmask,
|
|
|
+ avatarRadius = 1.4, floorOffset = 1.0, reach = 1.0):
|
|
|
+ """
|
|
|
+ Set up the avatar collisions
|
|
|
+ """
|
|
|
+ assert(self.debugPrint("initializeCollisions()"))
|
|
|
+
|
|
|
+ assert not avatarNodePath.isEmpty()
|
|
|
+
|
|
|
+ self.cTrav = collisionTraverser
|
|
|
+ self.floorOffset = floorOffset = 7.0
|
|
|
+
|
|
|
+ self.avatarNodePath = self.setupPhysics(avatarNodePath)
|
|
|
+ if 0 or self.useHeightRay:
|
|
|
+ #self.setupRay(floorBitmask, avatarRadius)
|
|
|
+ self.setupRay(floorBitmask, 0.0)
|
|
|
+ self.setupSphere(wallBitmask|floorBitmask, avatarRadius)
|
|
|
+
|
|
|
+ self.setCollisionsActive(1)
|
|
|
+
|
|
|
+ def setAirborneHeightFunc(self, getAirborneHeight):
|
|
|
+ self.getAirborneHeight = getAirborneHeight
|
|
|
+
|
|
|
+ def setAvatarPhysicsIndicator(self, indicator):
|
|
|
+ """
|
|
|
+ indicator is a NodePath
|
|
|
+ """
|
|
|
+ assert(self.debugPrint("setAvatarPhysicsIndicator()"))
|
|
|
+ self.cSphereNodePath.show()
|
|
|
+ if indicator:
|
|
|
+ # Indicator Node:
|
|
|
+ change=render.attachNewNode("change")
|
|
|
+ #change.setPos(Vec3(1.0, 1.0, 1.0))
|
|
|
+ #change.setHpr(0.0, 0.0, 0.0)
|
|
|
+ change.setScale(0.1)
|
|
|
+ #change.setColor(Vec4(1.0, 1.0, 1.0, 1.0))
|
|
|
+ indicator.reparentTo(change)
|
|
|
+
|
|
|
+ indicatorNode=render.attachNewNode("physVelocityIndicator")
|
|
|
+ #indicatorNode.setScale(0.1)
|
|
|
+ #indicatorNode.setP(90.0)
|
|
|
+ indicatorNode.setPos(self.avatarNodePath, 0.0, 0.0, 6.0)
|
|
|
+ indicatorNode.setColor(0.0, 0.0, 1.0, 1.0)
|
|
|
+ change.reparentTo(indicatorNode)
|
|
|
+
|
|
|
+ self.physVelocityIndicator=indicatorNode
|
|
|
+ # Contact Node:
|
|
|
+ contactIndicatorNode=render.attachNewNode("physContactIndicator")
|
|
|
+ contactIndicatorNode.setScale(0.25)
|
|
|
+ contactIndicatorNode.setP(90.0)
|
|
|
+ contactIndicatorNode.setPos(self.avatarNodePath, 0.0, 0.0, 5.0)
|
|
|
+ contactIndicatorNode.setColor(1.0, 0.0, 0.0, 1.0)
|
|
|
+ indicator.instanceTo(contactIndicatorNode)
|
|
|
+ self.physContactIndicator=contactIndicatorNode
|
|
|
+ else:
|
|
|
+ print "failed load of physics indicator"
|
|
|
+
|
|
|
+ def avatarPhysicsIndicator(self, task):
|
|
|
+ #assert(self.debugPrint("avatarPhysicsIndicator()"))
|
|
|
+ # Velocity:
|
|
|
+ self.physVelocityIndicator.setPos(self.avatarNodePath, 0.0, 0.0, 6.0)
|
|
|
+ physObject=self.actorNode.getPhysicsObject()
|
|
|
+ a=physObject.getVelocity()
|
|
|
+ self.physVelocityIndicator.setScale(math.sqrt(a.length()))
|
|
|
+ a+=self.physVelocityIndicator.getPos()
|
|
|
+ self.physVelocityIndicator.lookAt(Point3(a))
|
|
|
+ # Contact:
|
|
|
+ contact=self.actorNode.getContactVector()
|
|
|
+ if contact==Vec3.zero():
|
|
|
+ self.physContactIndicator.hide()
|
|
|
+ else:
|
|
|
+ self.physContactIndicator.show()
|
|
|
+ self.physContactIndicator.setPos(self.avatarNodePath, 0.0, 0.0, 5.0)
|
|
|
+ #contact=self.actorNode.getContactVector()
|
|
|
+ point=Point3(contact+self.physContactIndicator.getPos())
|
|
|
+ self.physContactIndicator.lookAt(point)
|
|
|
+ return Task.cont
|
|
|
+
|
|
|
+ def deleteCollisions(self):
|
|
|
+ assert(self.debugPrint("deleteCollisions()"))
|
|
|
+ del self.cTrav
|
|
|
+
|
|
|
+ if self.useHeightRay:
|
|
|
+ del self.cRayQueue
|
|
|
+ self.cRayNodePath.removeNode()
|
|
|
+ del self.cRayNodePath
|
|
|
+
|
|
|
+ del self.cSphere
|
|
|
+ self.cSphereNodePath.removeNode()
|
|
|
+ del self.cSphereNodePath
|
|
|
+
|
|
|
+ del self.pusher
|
|
|
+
|
|
|
+ del self.getAirborneHeight
|
|
|
+
|
|
|
+ def setCollisionsActive(self, active = 1):
|
|
|
+ assert(self.debugPrint("collisionsActive(active=%s)"%(active,)))
|
|
|
+ if self.collisionsActive != active:
|
|
|
+ self.collisionsActive = active
|
|
|
+ if active:
|
|
|
+ self.cTrav.addCollider(self.cSphereNodePath, self.pusher)
|
|
|
+ if self.useHeightRay:
|
|
|
+ if self.useLifter:
|
|
|
+ self.cTrav.addCollider(self.cRayNodePath, self.lifter)
|
|
|
+ else:
|
|
|
+ self.cTrav.addCollider(self.cRayNodePath, self.cRayQueue)
|
|
|
+ else:
|
|
|
+ self.cTrav.removeCollider(self.cSphereNodePath)
|
|
|
+ if self.useHeightRay:
|
|
|
+ self.cTrav.removeCollider(self.cRayNodePath)
|
|
|
+ # Now that we have disabled collisions, make one more pass
|
|
|
+ # right now to ensure we aren't standing in a wall.
|
|
|
+ self.oneTimeCollide()
|
|
|
+
|
|
|
+ def getCollisionsActive(self):
|
|
|
+ assert(self.debugPrint("getCollisionsActive() returning=%s"%(
|
|
|
+ self.collisionsActive,)))
|
|
|
+ return self.collisionsActive
|
|
|
+
|
|
|
+ def placeOnFloor(self):
|
|
|
+ """
|
|
|
+ Make a reasonable effort to place the avatar on the ground.
|
|
|
+ For example, this is useful when switching away from the
|
|
|
+ current walker.
|
|
|
+ """
|
|
|
+ self.oneTimeCollide()
|
|
|
+ self.avatarNodePath.setZ(self.avatarNodePath.getZ()-self.getAirborneHeight())
|
|
|
+
|
|
|
+ def oneTimeCollide(self):
|
|
|
+ """
|
|
|
+ Makes one quick collision pass for the avatar, for instance as
|
|
|
+ a one-time straighten-things-up operation after collisions
|
|
|
+ have been disabled.
|
|
|
+ """
|
|
|
+ assert(self.debugPrint("oneTimeCollide()"))
|
|
|
+ tempCTrav = CollisionTraverser("oneTimeCollide")
|
|
|
+ if self.useHeightRay:
|
|
|
+ if self.useLifter:
|
|
|
+ tempCTrav.addCollider(self.cRayNodePath, self.lifter)
|
|
|
+ else:
|
|
|
+ tempCTrav.addCollider(self.cRayNodePath, self.cRayQueue)
|
|
|
+ tempCTrav.traverse(render)
|
|
|
+
|
|
|
+ def handleAvatarControls(self, task):
|
|
|
+ """
|
|
|
+ Check on the arrow keys and update the avatar.
|
|
|
+ """
|
|
|
+ if __debug__:
|
|
|
+ if self.wantAvatarPhysicsIndicator:
|
|
|
+ onScreenDebug.append("localAvatar pos = %s\n"%(base.localAvatar.getPos().pPrintValues(),))
|
|
|
+ onScreenDebug.append("localAvatar h = % 10.4f\n"%(base.localAvatar.getH(),))
|
|
|
+ onScreenDebug.append("localAvatar anim = %s\n"%(base.localAvatar.animFSM.getCurrentState().getName(),))
|
|
|
+ #assert(self.debugPrint("handleAvatarControls(task=%s)"%(task,)))
|
|
|
+ physObject=self.actorNode.getPhysicsObject()
|
|
|
+ #rotAvatarToPhys=Mat3.rotateMatNormaxis(-self.avatarNodePath.getH(), Vec3.up())
|
|
|
+ #rotPhysToAvatar=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
|
|
|
+ contact=self.actorNode.getContactVector()
|
|
|
+
|
|
|
+ # hack fix for falling through the floor:
|
|
|
+ if contact==Vec3.zero() and self.avatarNodePath.getZ()<-50.0:
|
|
|
+ # DCR: don't reset X and Y; allow player to move
|
|
|
+ self.reset()
|
|
|
+ self.avatarNodePath.setZ(50.0)
|
|
|
+ messenger.send("walkerIsOutOfWorld", [self.avatarNodePath])
|
|
|
+
|
|
|
+ # get the button states:
|
|
|
+ forward = inputState.isSet("forward")
|
|
|
+ reverse = inputState.isSet("reverse")
|
|
|
+ turnLeft = inputState.isSet("turnLeft")
|
|
|
+ turnRight = inputState.isSet("turnRight")
|
|
|
+ slide = 0#inputState.isSet("slide")
|
|
|
+ slideLeft = 0#inputState.isSet("slideLeft")
|
|
|
+ slideRight = 0#inputState.isSet("slideRight")
|
|
|
+ jump = inputState.isSet("jump")
|
|
|
+ # Determine what the speeds are based on the buttons:
|
|
|
+ self.__speed=(forward and self.avatarControlForwardSpeed or
|
|
|
+ reverse and -self.avatarControlReverseSpeed)
|
|
|
+ avatarSlideSpeed=self.avatarControlForwardSpeed*0.5
|
|
|
+ #self.__slideSpeed=slide and (
|
|
|
+ # (turnLeft and -avatarSlideSpeed) or
|
|
|
+ # (turnRight and avatarSlideSpeed))
|
|
|
+ self.__slideSpeed=(
|
|
|
+ (slideLeft and -avatarSlideSpeed) or
|
|
|
+ (slideRight and avatarSlideSpeed))
|
|
|
+ self.__rotationSpeed=not slide and (
|
|
|
+ (turnLeft and self.avatarControlRotateSpeed) or
|
|
|
+ (turnRight and -self.avatarControlRotateSpeed))
|
|
|
+
|
|
|
+ # How far did we move based on the amount of time elapsed?
|
|
|
+ dt=ClockObject.getGlobalClock().getDt()
|
|
|
+
|
|
|
+ if self.needToDeltaPos:
|
|
|
+ self.setPriorParentVector()
|
|
|
+ self.needToDeltaPos = 0
|
|
|
+ #self.__oldPosDelta = render.getRelativeVector(
|
|
|
+ # self.avatarNodePath,
|
|
|
+ # self.avatarNodePath.getPosDelta(render))
|
|
|
+ #self.__oldPosDelta = self.avatarNodePath.getRelativeVector(
|
|
|
+ # render,
|
|
|
+ # self.avatarNodePath.getPosDelta(render))
|
|
|
+ self.__oldPosDelta = self.avatarNodePath.getPosDelta(render)
|
|
|
+ self.__oldDt = dt
|
|
|
+ #posDelta = self.avatarNodePath.getPosDelta(render)
|
|
|
+ #if posDelta==Vec3.zero():
|
|
|
+ # self.priorParent.setVector(self.__oldPosDelta)
|
|
|
+ #else:
|
|
|
+ # self.priorParent.setVector(Vec3.zero())
|
|
|
+ # # We must copy the vector to preserve it:
|
|
|
+ # self.__oldPosDelta=Vec3(posDelta)
|
|
|
+ if __debug__:
|
|
|
+ if self.wantAvatarPhysicsIndicator:
|
|
|
+ onScreenDebug.add("posDelta1",
|
|
|
+ self.avatarNodePath.getPosDelta(render).pPrintValues())
|
|
|
+
|
|
|
+ if 0:
|
|
|
+ onScreenDebug.add("posDelta3",
|
|
|
+ render.getRelativeVector(
|
|
|
+ self.avatarNodePath,
|
|
|
+ self.avatarNodePath.getPosDelta(render)).pPrintValues())
|
|
|
+
|
|
|
+ if 0:
|
|
|
+ onScreenDebug.add("gravity",
|
|
|
+ self.gravity.getLocalVector().pPrintValues())
|
|
|
+ onScreenDebug.add("priorParent",
|
|
|
+ self.priorParent.getLocalVector().pPrintValues())
|
|
|
+ onScreenDebug.add("avatarViscosity",
|
|
|
+ "% 10.4f"%(self.avatarViscosity.getCoef(),))
|
|
|
+
|
|
|
+ onScreenDebug.add("physObject pos",
|
|
|
+ physObject.getPosition().pPrintValues())
|
|
|
+ onScreenDebug.add("physObject hpr",
|
|
|
+ physObject.getOrientation().getHpr().pPrintValues())
|
|
|
+ onScreenDebug.add("physObject orien",
|
|
|
+ physObject.getOrientation().pPrintValues())
|
|
|
+
|
|
|
+ if 1:
|
|
|
+ onScreenDebug.add("physObject vel",
|
|
|
+ physObject.getVelocity().pPrintValues())
|
|
|
+ onScreenDebug.add("physObject len",
|
|
|
+ "% 10.4f"%physObject.getVelocity().length())
|
|
|
+
|
|
|
+ if 0:
|
|
|
+ onScreenDebug.add("posDelta4",
|
|
|
+ self.priorParentNp.getRelativeVector(
|
|
|
+ render,
|
|
|
+ self.avatarNodePath.getPosDelta(render)).pPrintValues())
|
|
|
+
|
|
|
+ if 1:
|
|
|
+ onScreenDebug.add("priorParent",
|
|
|
+ self.priorParent.getLocalVector().pPrintValues())
|
|
|
+
|
|
|
+ if 0:
|
|
|
+ onScreenDebug.add("priorParent po",
|
|
|
+ self.priorParent.getVector(physObject).pPrintValues())
|
|
|
+
|
|
|
+ if 0:
|
|
|
+ onScreenDebug.add("__posDelta",
|
|
|
+ self.__oldPosDelta.pPrintValues())
|
|
|
+
|
|
|
+ if 1:
|
|
|
+ onScreenDebug.add("contact",
|
|
|
+ contact.pPrintValues())
|
|
|
+ #onScreenDebug.add("airborneHeight", "% 10.4f"%(
|
|
|
+ # self.getAirborneHeight(),))
|
|
|
+
|
|
|
+ if 0:
|
|
|
+ onScreenDebug.add("__oldContact",
|
|
|
+ contact.pPrintValues())
|
|
|
+ onScreenDebug.add("__oldAirborneHeight", "% 10.4f"%(
|
|
|
+ self.getAirborneHeight(),))
|
|
|
+ airborneHeight=self.getAirborneHeight()
|
|
|
+ if airborneHeight > self.highMark:
|
|
|
+ self.highMark = airborneHeight
|
|
|
+ if __debug__:
|
|
|
+ onScreenDebug.add("highMark", "% 10.4f"%(self.highMark,))
|
|
|
+ #if airborneHeight < 0.1: #contact!=Vec3.zero():
|
|
|
+ if 1:
|
|
|
+ if (airborneHeight > self.avatarRadius*0.5
|
|
|
+ or physObject.getVelocity().getZ() > 0.0
|
|
|
+ ): # Check stair angles before changing this.
|
|
|
+ # ...the avatar is airborne (maybe a lot or a tiny amount).
|
|
|
+ self.isAirborne = 1
|
|
|
+ else:
|
|
|
+ # ...the avatar is very close to the ground (close enough to be
|
|
|
+ # considered on the ground).
|
|
|
+ if self.isAirborne and physObject.getVelocity().getZ() <= 0.0:
|
|
|
+ # ...the avatar has landed.
|
|
|
+ contactLength = contact.length()
|
|
|
+ if contactLength>self.__hardLandingForce:
|
|
|
+ #print "jumpHardLand"
|
|
|
+ messenger.send("jumpHardLand")
|
|
|
+ else:
|
|
|
+ #print "jumpLand"
|
|
|
+ messenger.send("jumpLand")
|
|
|
+ self.priorParent.setVector(Vec3.zero())
|
|
|
+ self.isAirborne = 0
|
|
|
+ elif jump:
|
|
|
+ #print "jump"
|
|
|
+ #self.__jumpButton=0
|
|
|
+ messenger.send("jumpStart")
|
|
|
+ if 0:
|
|
|
+ # ...jump away from walls and with with the slope normal.
|
|
|
+ jumpVec=Vec3(contact+Vec3.up())
|
|
|
+ #jumpVec=Vec3(rotAvatarToPhys.xform(jumpVec))
|
|
|
+ jumpVec.normalize()
|
|
|
+ else:
|
|
|
+ # ...jump straight up, even if next to a wall.
|
|
|
+ jumpVec=Vec3.up()
|
|
|
+ jumpVec*=self.avatarControlJumpForce
|
|
|
+ physObject.addImpulse(Vec3(jumpVec))
|
|
|
+ self.isAirborne = 1 # Avoid double impulse before fully airborne.
|
|
|
+ else:
|
|
|
+ self.isAirborne = 0
|
|
|
+ if __debug__:
|
|
|
+ onScreenDebug.add("isAirborne", "%d"%(self.isAirborne,))
|
|
|
+ else:
|
|
|
+ if contact!=Vec3.zero():
|
|
|
+ # ...the avatar has touched something (but might not be on the ground).
|
|
|
+ contactLength = contact.length()
|
|
|
+ contact.normalize()
|
|
|
+ angle=contact.dot(Vec3.up())
|
|
|
+ if angle>self.__standableGround:
|
|
|
+ # ...avatar is on standable ground.
|
|
|
+ if self.__oldContact==Vec3.zero():
|
|
|
+ #if self.__oldAirborneHeight > 0.1: #self.__oldContact==Vec3.zero():
|
|
|
+ # ...avatar was airborne.
|
|
|
+ self.jumpCount-=1
|
|
|
+ if contactLength>self.__hardLandingForce:
|
|
|
+ messenger.send("jumpHardLand")
|
|
|
+ else:
|
|
|
+ messenger.send("jumpLand")
|
|
|
+ elif jump:
|
|
|
+ self.jumpCount+=1
|
|
|
+ #self.__jumpButton=0
|
|
|
+ messenger.send("jumpStart")
|
|
|
+ jump=Vec3(contact+Vec3.up())
|
|
|
+ #jump=Vec3(rotAvatarToPhys.xform(jump))
|
|
|
+ jump.normalize()
|
|
|
+ jump*=self.avatarControlJumpForce
|
|
|
+ physObject.addImpulse(Vec3(jump))
|
|
|
+
|
|
|
+ if contact!=self.__oldContact:
|
|
|
+ # We must copy the vector to preserve it:
|
|
|
+ self.__oldContact=Vec3(contact)
|
|
|
+ self.__oldAirborneHeight=airborneHeight
|
|
|
+
|
|
|
+ moveToGround = Vec3.zero()
|
|
|
+ if not self.useHeightRay or self.isAirborne:
|
|
|
+ # ...the airborne check is a hack to stop sliding.
|
|
|
+ self.phys.doPhysics(dt)
|
|
|
+ if __debug__:
|
|
|
+ onScreenDebug.add("phys", "on")
|
|
|
+ else:
|
|
|
+ physObject.setVelocity(Vec3.zero())
|
|
|
+ #if airborneHeight>0.001 and contact==Vec3.zero():
|
|
|
+ # moveToGround = Vec3(0.0, 0.0, -airborneHeight)
|
|
|
+ #moveToGround = Vec3(0.0, 0.0, -airborneHeight)
|
|
|
+ moveToGround = Vec3(0.0, 0.0, -self.determineHeight())
|
|
|
+ if __debug__:
|
|
|
+ onScreenDebug.add("phys", "off")
|
|
|
+ # Check to see if we're moving at all:
|
|
|
+ if self.__speed or self.__slideSpeed or self.__rotationSpeed or moveToGround!=Vec3.zero():
|
|
|
+ distance = dt * self.__speed
|
|
|
+ slideDistance = dt * self.__slideSpeed
|
|
|
+ rotation = dt * self.__rotationSpeed
|
|
|
+
|
|
|
+ #debugTempH=self.avatarNodePath.getH()
|
|
|
+ assert self.avatarNodePath.getQuat().isSameDirection(physObject.getOrientation())
|
|
|
+ assert self.avatarNodePath.getPos().almostEqual(physObject.getPosition(), 0.0001)
|
|
|
+
|
|
|
+ # update pos:
|
|
|
+ # Take a step in the direction of our previous heading.
|
|
|
+ self.__vel=Vec3(
|
|
|
+ Vec3.forward() * distance +
|
|
|
+ Vec3.right() * slideDistance)
|
|
|
+
|
|
|
+ # rotMat is the rotation matrix corresponding to
|
|
|
+ # our previous heading.
|
|
|
+ rotMat=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
|
|
|
+ step=rotMat.xform(self.__vel)
|
|
|
+
|
|
|
+ newVector = self.acForce.getLocalVector()+Vec3(step*100.0)
|
|
|
+ maxLen = 500.0
|
|
|
+ if newVector.length() > maxLen:
|
|
|
+ newVector.normalize()
|
|
|
+ newVector *= maxLen
|
|
|
+ onScreenDebug.add("newVector",
|
|
|
+ newVector)
|
|
|
+ onScreenDebug.add("newVector length",
|
|
|
+ newVector.length())
|
|
|
+ self.acForce.setVector(Vec3(newVector))
|
|
|
+ #physObject.setPosition(Point3(
|
|
|
+ # physObject.getPosition()+step+moveToGround))
|
|
|
+
|
|
|
+ # update hpr:
|
|
|
+ o=physObject.getOrientation()
|
|
|
+ r=LRotationf()
|
|
|
+ r.setHpr(Vec3(rotation, 0.0, 0.0))
|
|
|
+ physObject.setOrientation(o*r)
|
|
|
+
|
|
|
+ # sync the change:
|
|
|
+ self.actorNode.updateTransform()
|
|
|
+
|
|
|
+ assert self.avatarNodePath.getQuat().isSameDirection(physObject.getOrientation())
|
|
|
+ assert self.avatarNodePath.getPos().almostEqual(physObject.getPosition(), 0.0001)
|
|
|
+ #assert self.avatarNodePath.getH()==debugTempH-rotation
|
|
|
+ messenger.send("avatarMoving")
|
|
|
+ else:
|
|
|
+ self.__vel.set(0.0, 0.0, 0.0)
|
|
|
+ # Clear the contact vector so we can tell if we contact something next frame:
|
|
|
+ self.actorNode.setContactVector(Vec3.zero())
|
|
|
+ return Task.cont
|
|
|
+
|
|
|
+ def doDeltaPos(self):
|
|
|
+ assert(self.debugPrint("doDeltaPos()"))
|
|
|
+ self.needToDeltaPos = 1
|
|
|
+
|
|
|
+ def setPriorParentVector(self):
|
|
|
+ assert(self.debugPrint("doDeltaPos()"))
|
|
|
+
|
|
|
+ print "self.__oldDt", self.__oldDt, "self.__oldPosDelta", self.__oldPosDelta
|
|
|
+ if __debug__:
|
|
|
+ onScreenDebug.add("__oldDt", "% 10.4f"%self.__oldDt)
|
|
|
+ onScreenDebug.add("self.__oldPosDelta",
|
|
|
+ self.__oldPosDelta.pPrintValues())
|
|
|
+
|
|
|
+ velocity = self.__oldPosDelta*(1/self.__oldDt)*4.0 # *4.0 is a hack
|
|
|
+ assert(self.debugPrint(" __oldPosDelta=%s"%(self.__oldPosDelta,)))
|
|
|
+ assert(self.debugPrint(" velocity=%s"%(velocity,)))
|
|
|
+ self.priorParent.setVector(Vec3(velocity))
|
|
|
+ if __debug__:
|
|
|
+ if self.wantAvatarPhysicsIndicator:
|
|
|
+ onScreenDebug.add("velocity", velocity.pPrintValues())
|
|
|
+
|
|
|
+ def reset(self):
|
|
|
+ assert(self.debugPrint("reset()"))
|
|
|
+ self.actorNode.getPhysicsObject().resetPosition(self.avatarNodePath.getPos())
|
|
|
+ self.priorParent.setVector(Vec3.zero())
|
|
|
+ self.highMark = 0
|
|
|
+ self.actorNode.setContactVector(Vec3.zero())
|
|
|
+ if __debug__:
|
|
|
+ contact=self.actorNode.getContactVector()
|
|
|
+ onScreenDebug.add("priorParent po",
|
|
|
+ self.priorParent.getVector(self.actorNode.getPhysicsObject()).pPrintValues())
|
|
|
+ onScreenDebug.add("highMark", "% 10.4f"%(self.highMark,))
|
|
|
+ onScreenDebug.add("contact", contact.pPrintValues())
|
|
|
+
|
|
|
+ def enableAvatarControls(self):
|
|
|
+ """
|
|
|
+ Activate the arrow keys, etc.
|
|
|
+ """
|
|
|
+ assert(self.debugPrint("enableAvatarControls()"))
|
|
|
+ assert self.collisionsActive
|
|
|
+
|
|
|
+ if __debug__:
|
|
|
+ #self.accept("control-f3", self.spawnTest) #*#
|
|
|
+ self.accept("f3", self.reset) # for debugging only.
|
|
|
+
|
|
|
+ taskName = "AvatarControls-%s"%(id(self),)
|
|
|
+ # remove any old
|
|
|
+ taskMgr.remove(taskName)
|
|
|
+ # spawn the new task
|
|
|
+ taskMgr.add(self.handleAvatarControls, taskName, 25)
|
|
|
+ if self.physVelocityIndicator:
|
|
|
+ taskMgr.add(self.avatarPhysicsIndicator, "AvatarControlsIndicator%s"%(id(self),), 35)
|
|
|
+
|
|
|
+ def disableAvatarControls(self):
|
|
|
+ """
|
|
|
+ Ignore the arrow keys, etc.
|
|
|
+ """
|
|
|
+ assert(self.debugPrint("disableAvatarControls()"))
|
|
|
+ taskName = "AvatarControls-%s"%(id(self),)
|
|
|
+ taskMgr.remove(taskName)
|
|
|
+
|
|
|
+ taskName = "AvatarControlsIndicator%s"%(id(self),)
|
|
|
+ taskMgr.remove(taskName)
|
|
|
+
|
|
|
+ if __debug__:
|
|
|
+ self.ignore("control-f3") #*#
|
|
|
+ self.ignore("f3")
|
|
|
+
|
|
|
+
|
|
|
+ if __debug__:
|
|
|
+ def setupAvatarPhysicsIndicator(self):
|
|
|
+ if self.wantAvatarPhysicsIndicator:
|
|
|
+ indicator=loader.loadModelCopy('phase_5/models/props/dagger')
|
|
|
+ #self.walkControls.setAvatarPhysicsIndicator(indicator)
|
|
|
+
|
|
|
+ def debugPrint(self, message):
|
|
|
+ """for debugging"""
|
|
|
+ return self.notify.debug(
|
|
|
+ str(id(self))+' '+message)
|