Dave Schuyler 22 лет назад
Родитель
Сommit
6aefeadd6c

+ 3 - 4
direct/src/showbase/Messenger.py

@@ -8,7 +8,7 @@ class Messenger:
     notify = None    
 
     def __init__(self):
-        """ __init__(self)
+        """
         One dictionary does it all. It has the following structure:
             {event1 : {object1: [method, extraArgs, persistent],
                        object2: [method, extraArgs, persistent]},
@@ -36,7 +36,6 @@ class Messenger:
         If the persistent flag is set, it will continue to respond
         to this event, otherwise it will respond only once.
         """
-
         if Messenger.notify.getDebug():
             Messenger.notify.debug('object: ' + `object`
                                    + '\n now accepting: ' + `event`
@@ -96,7 +95,7 @@ class Messenger:
         return 0
 
     def whoAccepts(self, event):
-        """ whoAccepts(self, event):
+        """
         Return objects accepting the given event
         """
         return self.dict.get(event, None)
@@ -147,7 +146,7 @@ class Messenger:
                 apply(method, (extraArgs + sentArgs))
 
     def clear(self):
-        """clear(self)
+        """
         Start fresh with a clear dict
         """
         self.dict.clear()

+ 292 - 0
direct/src/showbase/NonPhysicsWalker.py

@@ -0,0 +1,292 @@
+"""NonPhysicsWalker.py is for avatars."""
+
+from ShowBaseGlobal import *
+
+import DirectNotifyGlobal
+import DirectObject
+
+class NonPhysicsWalker(DirectObject.DirectObject):
+
+    notify = DirectNotifyGlobal.directNotify.newCategory("NonPhysicsWalker")
+
+    # special methods
+    def __init__(self):
+        DirectObject.DirectObject.__init__(self)
+        self.forwardButton=0
+        self.reverseButton=0
+        self.jumpButton=0
+        self.leftButton=0
+        self.rightButton=0
+        self.speed=0.0
+        self.rotationSpeed=0.0
+        self.vel=Vec3(0.0, 0.0, 0.0)
+        self.stopThisFrame = 0
+        self.fSlide = 0
+
+    def setWalkSpeed(self, forward, jump, reverse, rotate):
+        assert(self.debugPrint("setWalkSpeed()"))
+        self.avatarControlForwardSpeed=forward
+        #self.avatarControlJumpForce=jump
+        self.avatarControlReverseSpeed=reverse
+        self.avatarControlRotateSpeed=rotate
+
+    def getSpeeds(self):
+        #assert(self.debugPrint("getSpeeds()"))
+        return (self.speed, self.rotationSpeed)
+
+    def initializeCollisions(self, collisionTraverser, avatarNodePath, 
+            wallCollideMask, floorCollideMask,
+            avatarRadius = 1.4, floorOffset = 1.0):
+        """
+        Set up the avatar for collisions
+        """
+        self.cTrav = collisionTraverser
+        self.avatarNodePath = avatarNodePath
+
+        # Set up the collision sphere
+        # This is a sphere on the ground to detect barrier collisions
+        self.cSphere = CollisionSphere(0.0, 0.0, 0.0, avatarRadius)
+        self.cSphereNode = CollisionNode('cSphereNode')
+        self.cSphereNode.addSolid(self.cSphere)
+        self.cSphereNodePath = avatarNodePath.attachNewNode(self.cSphereNode)
+        self.cSphereBitMask = wallCollideMask
+
+        self.cSphereNode.setFromCollideMask(self.cSphereBitMask)
+        self.cSphereNode.setIntoCollideMask(BitMask32.allOff())
+
+        # Set up the collison ray
+        # 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, 4.0, 0.0, 0.0, -1.0)
+        self.cRayNode = CollisionNode('cRayNode')
+        self.cRayNode.addSolid(self.cRay)
+        self.cRayNodePath = avatarNodePath.attachNewNode(self.cRayNode)
+        self.cRayBitMask = floorCollideMask
+        self.cRayNode.setFromCollideMask(self.cRayBitMask)
+        self.cRayNode.setIntoCollideMask(BitMask32.allOff())
+
+        # set up wall collision mechanism
+        self.pusher = CollisionHandlerPusher()
+        self.pusher.setInPattern("enter%in")
+        self.pusher.setOutPattern("exit%in")
+
+        # set up floor collision mechanism
+        self.lifter = CollisionHandlerFloor()
+        self.lifter.setInPattern("on-floor")
+        self.lifter.setOutPattern("off-floor")
+        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)
+
+        # activate the collider with the traverser and pusher
+        self.collisionsOn()
+        
+        self.pusher.addColliderNode(self.cSphereNode, avatarNodePath.node())
+        self.lifter.addColliderNode(self.cRayNode, avatarNodePath.node())
+
+    def deleteCollisions(self):
+        del self.cTrav
+
+        del self.cSphere
+        del self.cSphereNode
+        self.cSphereNodePath.removeNode()
+        del self.cSphereNodePath
+
+        del self.cRay
+        del self.cRayNode
+        self.cRayNodePath.removeNode()
+        del self.cRayNodePath
+
+        del self.pusher
+        del self.lifter
+
+    def collisionsOff(self):
+        self.cTrav.removeCollider(self.cSphereNode)
+        self.cTrav.removeCollider(self.cRayNode)
+
+        # Now that we have disabled collisions, make one more pass
+        # right now to ensure we aren't standing in a wall.
+        self.oneTimeCollide()
+
+    def collisionsOn(self):
+        self.cTrav.addCollider(self.cSphereNode, self.pusher)
+        self.cTrav.addCollider(self.cRayNode, self.lifter)
+
+    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.
+        """
+        tempCTrav = CollisionTraverser()
+        tempCTrav.addCollider(self.cSphereNode, self.pusher)
+        tempCTrav.addCollider(self.cRayNode, self.lifter)
+        tempCTrav.traverse(render)
+
+    def handleAvatarControls(self, task):
+        """
+        Check on the arrow keys and update the avatar.
+        """
+        # Determine what the speeds are based on the buttons:
+        self.speed=(self.forwardButton and self.avatarControlForwardSpeed or 
+                    self.reverseButton and -self.avatarControlReverseSpeed)
+        # Should fSlide be renamed slideButton?
+        self.slideSpeed=self.fSlide and (
+                (self.leftButton and -self.avatarControlForwardSpeed) or 
+                (self.rightButton and self.avatarControlForwardSpeed))
+        self.rotationSpeed=not self.fSlide and (
+                (self.leftButton and self.avatarControlRotateSpeed) or
+                (self.rightButton and -self.avatarControlRotateSpeed))
+        # How far did we move based on the amount of time elapsed?
+        dt=min(ClockObject.getGlobalClock().getDt(), 0.1)
+        # Check to see if we're moving at all:
+        if self.speed or self.slideSpeed or self.rotationSpeed:
+            if self.stopThisFrame:
+                distance = 0.0
+                slideDistance = 0.0
+                rotation = 0.0
+                self.stopThisFrame = 0
+            else:
+                distance = dt * self.speed
+                slideDistance = dt * self.slideSpeed
+                rotation = dt * self.rotationSpeed
+
+            # Take a step in the direction of our previous heading.
+            self.vel=Vec3(Vec3.forward() * distance + 
+                          Vec3.right() * slideDistance)
+            if self.vel != Vec3.zero():
+                # rotMat is the rotation matrix corresponding to
+                # our previous heading.
+                rotMat=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
+                step=rotMat.xform(self.vel)
+                self.avatarNodePath.setPos(Point3(self.avatarNodePath.getPos()+step))
+            self.avatarNodePath.setH(self.avatarNodePath.getH()+rotation)
+            messenger.send("avatarMoving")
+        else:
+            self.vel.set(0.0, 0.0, 0.0)
+        # Set collision sphere node:
+        self.cSphereNode.setVelocity(self.vel)
+        return Task.cont
+
+    def enableAvatarControls(self):
+        """
+        Activate the arrow keys, etc.
+        """
+        self.accept("control-arrow_left", self.moveTurnLeft, [1])
+        self.accept("control-arrow_left-up", self.moveTurnLeft, [0])
+        self.accept("control-arrow_right", self.moveTurnRight, [1])
+        self.accept("control-arrow_right-up", self.moveTurnRight, [0])
+        self.accept("control-arrow_up", self.moveForward, [1])
+        self.accept("control-arrow_up-up", self.moveForward, [0])
+        self.accept("control-arrow_down", self.moveInReverse, [1])
+        self.accept("control-arrow_down-up", self.moveInReverse, [0])
+        
+        self.accept("arrow_left", self.moveTurnLeft, [1])
+        self.accept("arrow_left-up", self.moveTurnLeft, [0])
+        self.accept("arrow_right", self.moveTurnRight, [1])
+        self.accept("arrow_right-up", self.moveTurnRight, [0])
+        self.accept("arrow_up", self.moveForward, [1])
+        self.accept("arrow_up-up", self.moveForward, [0])
+        self.accept("arrow_down", self.moveInReverse, [1])
+        self.accept("arrow_down-up", self.moveInReverse, [0])
+
+        taskName = "AvatarControls%s"%(id(self),)
+        # remove any old
+        taskMgr.remove(taskName)
+        # spawn the new task
+        taskMgr.add(self.handleAvatarControls, taskName)
+
+    def disableAvatarControls(self):
+        """
+        Ignore the arrow keys, etc.
+        """
+        taskName = "AvatarControls%s"%(id(self),)
+        taskMgr.remove(taskName)
+
+        self.ignore("control")
+        self.ignore("control-up")
+        self.ignore("control-arrow_left")
+        self.ignore("control-arrow_left-up")
+        self.ignore("control-arrow_right")
+        self.ignore("control-arrow_right-up")
+        self.ignore("control-arrow_up")
+        self.ignore("control-arrow_up-up")
+        self.ignore("control-arrow_down")
+        self.ignore("control-arrow_down-up")
+
+        self.ignore("arrow_left")
+        self.ignore("arrow_left-up")
+        self.ignore("arrow_right")
+        self.ignore("arrow_right-up")
+        self.ignore("arrow_up")
+        self.ignore("arrow_up-up")
+        self.ignore("arrow_down")
+        self.ignore("arrow_down-up")
+
+        # reset state
+        self.moveTurnLeft(0)
+        self.moveTurnRight(0)
+        self.moveForward(0)
+        self.moveInReverse(0)
+        self.moveJumpLeft(0)
+        self.moveJumpRight(0)
+        self.moveJumpForward(0)
+        self.moveJumpInReverse(0)
+        self.moveJump(0)
+
+    def moveTurnLeft(self, isButtonDown):
+        self.leftButton=isButtonDown
+
+    def moveTurnRight(self, isButtonDown):
+        self.rightButton=isButtonDown
+
+    def moveForward(self, isButtonDown):
+        self.forwardButton=isButtonDown
+
+    def moveInReverse(self, isButtonDown):
+        self.reverseButton=isButtonDown
+
+    def moveJumpLeft(self, isButtonDown):
+        self.jumpButton=isButtonDown
+        self.leftButton=isButtonDown
+
+    def moveJumpRight(self, isButtonDown):
+        self.jumpButton=isButtonDown
+        self.rightButton=isButtonDown
+
+    def moveJumpForward(self, isButtonDown):
+        self.jumpButton=isButtonDown
+        self.forwardButton=isButtonDown
+
+    def moveJumpInReverse(self, isButtonDown):
+        self.jumpButton=isButtonDown
+        self.reverseButton=isButtonDown
+
+    def moveJump(self, isButtonDown):
+        self.jumpButton=isButtonDown
+
+    def toggleSlide(self):
+        self.fSlide = not self.fSlide
+
+    def enableSlideMode(self):
+        self.accept("control-up", self.toggleSlide)
+
+    def disableSlideMode(self):
+        self.fSlide = 0
+        self.ignore("control-up")
+
+    def slideLeft(self, isButtonDown):
+        self.slideLeftButton=isButtonDown
+
+    def slideRight(self, isButtonDown):
+        self.slideRightButton=isButtonDown
+    
+    if __debug__:
+        def debugPrint(self, message):
+            """for debugging"""
+            return self.notify.debug(
+                    str(id(self))+' '+message)

+ 2 - 0
direct/src/showbase/PhysicsRoller.py

@@ -0,0 +1,2 @@
+"""PhysicsRoller is for wheels, soccer balls, billiard balls, and other things that roll."""
+

+ 434 - 0
direct/src/showbase/PhysicsWalker.py

@@ -0,0 +1,434 @@
+"""PhysicsWalker.py is for avatars."""
+
+from ShowBaseGlobal import *
+
+import DirectNotifyGlobal
+import DirectObject
+import PhysicsManager
+import math
+
+class PhysicsWalker(DirectObject.DirectObject):
+
+    notify = DirectNotifyGlobal.directNotify.newCategory("PhysicsWalker")
+    wantAvatarPhysicsIndicator = base.config.GetBool('want-avatar-physics-indicator', 1)
+
+    # special methods
+    def __init__(self, gravity = -32.1740, standableGround=0.707):
+        assert(self.debugPrint("PhysicsWalker(gravity=%s, standableGround=%s)"%(
+                gravity, standableGround)))
+        DirectObject.DirectObject.__init__(self)
+        self.__gravity=gravity
+        self.__standableGround=standableGround
+
+        self.physVelocityIndicator=None
+        self.__old_contact=None
+        self.__forwardButton=0
+        self.__reverseButton=0
+        self.__jumpButton=0
+        self.__leftButton=0
+        self.__rightButton=0
+        self.__speed=0.0
+        self.__rotationSpeed=0.0
+        self.__slideSpeed=0.0
+        self.__vel=Vec3(0.0)
+        self.__slideButton = 0
+
+    def setWalkSpeed(self, forward, jump, reverse, rotate):
+        assert(self.debugPrint("setWalkSpeed()"))
+        self.avatarControlForwardSpeed=forward
+        self.avatarControlJumpForce=jump
+        self.avatarControlReverseSpeed=reverse
+        self.avatarControlRotateSpeed=rotate
+
+    def getSpeeds(self):
+        #assert(self.debugPrint("getSpeeds()"))
+        return (self.__speed, self.__rotationSpeed)
+
+    def initializeCollisions(self, collisionTraverser, avatarNodePath, 
+            wallBitmask, floorBitmask, 
+            avatarRadius = 1.4, floorOffset = 1.0):
+        """
+        Set up the avatar collisions
+        """
+        assert(self.debugPrint("initializeCollisions()"))
+        self.cTrav = collisionTraverser
+
+        # Set up the collision sphere
+        # This is a sphere on the ground to detect barrier collisions
+        self.cSphere = CollisionSphere(0.0, 0.0, avatarRadius, avatarRadius)
+        self.cSphereNode = CollisionNode('cSphereNode')
+        self.cSphereNode.addSolid(self.cSphere)
+        self.cSphereNodePath = avatarNodePath.attachNewNode(self.cSphereNode)
+        self.cSphereBitMask = wallBitmask|floorBitmask
+
+        self.cSphereNode.setFromCollideMask(self.cSphereBitMask)
+        self.cSphereNode.setIntoCollideMask(BitMask32.allOff())
+
+        # set up collision mechanism
+        self.pusher = PhysicsCollisionHandler()
+        self.pusher.setInPattern("enter%in")
+        self.pusher.setOutPattern("exit%in")
+
+        # 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)
+
+        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()
+
+        # activate the collider with the traverser and pusher
+        self.collisionsOn()
+        self.pusher.addColliderNode(self.cSphereNode, avatarNodePath.node())
+        
+        self.avatarNodePath = avatarNodePath
+
+    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()"))
+        # Velosity:
+        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
+
+        del self.cSphere
+        del self.cSphereNode
+        self.cSphereNodePath.removeNode()
+        del self.cSphereNodePath
+
+        del self.pusher
+
+    def collisionsOff(self):
+        assert(self.debugPrint("collisionsOff()"))
+        self.cTrav.removeCollider(self.cSphereNode)
+        # Now that we have disabled collisions, make one more pass
+        # right now to ensure we aren't standing in a wall.
+        self.oneTimeCollide()
+
+    def collisionsOn(self):
+        assert(self.debugPrint("collisionsOn()"))
+        self.cTrav.addCollider(self.cSphereNode, self.pusher)
+
+    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()
+        tempCTrav.addCollider(self.cSphereNode, self.pusher)
+        tempCTrav.traverse(render)
+
+    def handleAvatarControls(self, task):
+        """
+        Check on the arrow keys and update the avatar.
+        """
+        #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:
+            # reset:
+            self.setPos(Vec3(0.0, 0.0, 20.0))
+
+        # Determine what the speeds are based on the buttons:
+        self.__speed=(self.__forwardButton and self.avatarControlForwardSpeed or 
+                    self.__reverseButton and -self.avatarControlReverseSpeed)
+        self.__slideSpeed=self.__slideButton and (
+                (self.__leftButton and -self.avatarControlForwardSpeed) or 
+                (self.__rightButton and self.avatarControlForwardSpeed))
+        self.__rotationSpeed=not self.__slideButton and (
+                (self.__leftButton and self.avatarControlRotateSpeed) or
+                (self.__rightButton and -self.avatarControlRotateSpeed))
+        # How far did we move based on the amount of time elapsed?
+        dt=min(ClockObject.getGlobalClock().getDt(), 0.1)
+
+        if contact!=Vec3.zero():
+            contactLength = contact.length()
+            contact.normalize()
+            angle=contact.dot(Vec3.up())
+            if angle>self.__standableGround:
+                # ...avatar is on standable ground.
+                if self.__old_contact==Vec3.zero():
+                    jumpTime = 0.0
+                    print "contactLength", contactLength
+                    if contactLength>14.0:
+                        # ...avatar was airborne.
+                        messenger.send("jumpHardLand")
+                    else:
+                        messenger.send("jumpLand")
+                if self.__jumpButton:
+                    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.__old_contact:
+            # We must copy the vector to preserve it:
+            self.__old_contact=Vec3(contact)
+        self.phys.doPhysics(dt)
+        # Check to see if we're moving at all:
+        if self.__speed or self.__slideSpeed or self.__rotationSpeed:
+            distance = dt * self.__speed
+            slideDistance = dt * self.__slideSpeed
+            rotation = dt * self.__rotationSpeed
+
+            #debugTempH=self.avatarNodePath.getH()
+            assert self.avatarNodePath.getPos()==physObject.getPosition()
+            assert self.avatarNodePath.getHpr()==physObject.getOrientation().getHpr()
+
+            # 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)
+            physObject.setPosition(Point3(
+                physObject.getPosition()+step))
+            # update hpr:
+            o=physObject.getOrientation()
+            r=LOrientationf()
+            r.setHpr(Vec3(rotation, 0.0, 0.0))
+            physObject.setOrientation(o*r)
+            # sync the change:
+            self.actorNode.updateTransform()
+
+            assert self.avatarNodePath.getHpr()==physObject.getOrientation().getHpr()
+            assert self.avatarNodePath.getPos()==physObject.getPosition()
+            #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())
+        # Set collision sphere node:
+        v=physObject.getImplicitVelocity()
+        v=rotAvatarToPhys.xform(v)
+        self.cSphereNode.setVelocity(Vec3(v))
+        return Task.cont
+    
+    def resetPhys(self):
+        assert(self.debugPrint("resetPhys()"))
+        self.actorNode.getPhysicsObject().resetPosition(self.avatarNodePath.getPos())
+        self.actorNode.setContactVector(Vec3.zero())
+        self.cSphereNode.setVelocity(Vec3(0.0))
+
+    def enableAvatarControls(self):
+        """
+        Activate the arrow keys, etc.
+        """
+        assert(self.debugPrint("enableAvatarControls()"))
+        self.accept("control", self.moveJump, [1])
+        self.accept("control-up", self.moveJump, [0])
+        self.accept("control-arrow_left", self.moveJumpLeft, [1])
+        self.accept("control-arrow_left-up", self.moveJumpLeft, [0])
+        self.accept("control-arrow_right", self.moveJumpRight, [1])
+        self.accept("control-arrow_right-up", self.moveJumpRight, [0])
+        self.accept("control-arrow_up", self.moveJumpForward, [1])
+        self.accept("control-arrow_up-up", self.moveJumpForward, [0])
+        self.accept("control-arrow_down", self.moveJumpInReverse, [1])
+        self.accept("control-arrow_down-up", self.moveJumpInReverse, [0])
+        
+        self.accept("arrow_left", self.moveTurnLeft, [1])
+        self.accept("arrow_left-up", self.moveTurnLeft, [0])
+        self.accept("arrow_right", self.moveTurnRight, [1])
+        self.accept("arrow_right-up", self.moveTurnRight, [0])
+        self.accept("arrow_up", self.moveForward, [1])
+        self.accept("arrow_up-up", self.moveForward, [0])
+        self.accept("arrow_down", self.moveInReverse, [1])
+        self.accept("arrow_down-up", self.moveInReverse, [0])
+
+        if 1:
+            self.accept("f3", self.resetPhys) # for debugging only.
+
+        taskName = "AvatarControls%s"%(id(self),)
+        # remove any old
+        taskMgr.remove(taskName)
+        # spawn the new task
+        taskMgr.add(self.handleAvatarControls, taskName)
+        if self.physVelocityIndicator:
+            taskMgr.add(self.avatarPhysicsIndicator, "AvatarControlsIndicator%s"%(id(self),), 47)
+
+    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)
+
+        self.ignore("control")
+        self.ignore("control-up")
+        self.ignore("control-arrow_left")
+        self.ignore("control-arrow_left-up")
+        self.ignore("control-arrow_right")
+        self.ignore("control-arrow_right-up")
+        self.ignore("control-arrow_up")
+        self.ignore("control-arrow_up-up")
+        self.ignore("control-arrow_down")
+        self.ignore("control-arrow_down-up")
+
+        self.ignore("arrow_left")
+        self.ignore("arrow_left-up")
+        self.ignore("arrow_right")
+        self.ignore("arrow_right-up")
+        self.ignore("arrow_up")
+        self.ignore("arrow_up-up")
+        self.ignore("arrow_down")
+        self.ignore("arrow_down-up")
+
+        self.ignore("f3")
+
+        # reset state
+        self.moveTurnLeft(0)
+        self.moveTurnRight(0)
+        self.moveForward(0)
+        self.moveInReverse(0)
+        self.moveJumpLeft(0)
+        self.moveJumpRight(0)
+        self.moveJumpForward(0)
+        self.moveJumpInReverse(0)
+        self.moveJump(0)
+        self.moveSlide(0)
+
+    def moveTurnLeft(self, isButtonDown):
+        assert(self.debugPrint("moveTurnLeft(isButtonDown=%s)"%(isButtonDown,)))
+        self.__leftButton=isButtonDown
+
+    def moveTurnRight(self, isButtonDown):
+        assert(self.debugPrint("moveTurnRight(isButtonDown=%s)"%(isButtonDown,)))
+        self.__rightButton=isButtonDown
+
+    def moveForward(self, isButtonDown):
+        assert(self.debugPrint("moveForward(isButtonDown=%s)"%(isButtonDown,)))
+        self.__forwardButton=isButtonDown
+
+    def moveInReverse(self, isButtonDown):
+        assert(self.debugPrint("moveInReverse(isButtonDown=%s)"%(isButtonDown,)))
+        self.__reverseButton=isButtonDown
+
+    def moveJumpLeft(self, isButtonDown):
+        assert(self.debugPrint("moveJumpLeft(isButtonDown=%s)"%(isButtonDown,)))
+        self.__jumpButton=isButtonDown
+        self.__leftButton=isButtonDown
+
+    def moveJumpRight(self, isButtonDown):
+        assert(self.debugPrint("moveJumpRight(isButtonDown=%s)"%(isButtonDown,)))
+        self.__jumpButton=isButtonDown
+        self.__rightButton=isButtonDown
+
+    def moveJumpForward(self, isButtonDown):
+        assert(self.debugPrint("moveJumpForward(isButtonDown=%s)"%(isButtonDown,)))
+        self.__jumpButton=isButtonDown
+        self.__forwardButton=isButtonDown
+
+    def moveJumpInReverse(self, isButtonDown):
+        assert(self.debugPrint("moveJumpInReverse(isButtonDown=%s)"%(isButtonDown,)))
+        self.__jumpButton=isButtonDown
+        self.__reverseButton=isButtonDown
+
+    def moveJump(self, isButtonDown):
+        assert(self.debugPrint("moveJump()"))
+        self.__jumpButton=isButtonDown
+
+    def moveSlide(self, isButtonDown):
+        assert(self.debugPrint("moveSlide(isButtonDown=%s)"%(isButtonDown,)))
+        self.__slideButton=isButtonDown
+    
+    if __debug__:
+        def debugPrint(self, message):
+            """for debugging"""
+            return self.notify.debug(
+                    str(id(self))+' '+message)