PhysicsWalker.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1. """
  2. PhysicsWalker.py is for avatars.
  3. A walker control such as this one provides:
  4. - creation of the collision nodes
  5. - handling the keyboard and mouse input for avatar movement
  6. - moving the avatar
  7. it does not:
  8. - play sounds
  9. - play animations
  10. although it does send messeges that allow a listener to play sounds or
  11. animations based on walker events.
  12. """
  13. from direct.showbase.ShowBaseGlobal import *
  14. from direct.directnotify import DirectNotifyGlobal
  15. from direct.showbase import DirectObject
  16. from pandac.PandaModules import PhysicsManager
  17. import math
  18. #import LineStream
  19. class PhysicsWalker(DirectObject.DirectObject):
  20. notify = DirectNotifyGlobal.directNotify.newCategory("PhysicsWalker")
  21. wantDebugIndicator = base.config.GetBool('want-avatar-physics-indicator', 0)
  22. wantAvatarPhysicsIndicator = base.config.GetBool('want-avatar-physics-indicator', 0)
  23. useLifter = 0
  24. useHeightRay = 0
  25. # special methods
  26. def __init__(self, gravity = -32.1740, standableGround=0.707,
  27. hardLandingForce=16.0):
  28. assert(self.debugPrint("PhysicsWalker(gravity=%s, standableGround=%s)"%(
  29. gravity, standableGround)))
  30. DirectObject.DirectObject.__init__(self)
  31. self.__gravity=gravity
  32. self.__standableGround=standableGround
  33. self.__hardLandingForce=hardLandingForce
  34. self.needToDeltaPos = 0
  35. self.physVelocityIndicator=None
  36. self.avatarControlForwardSpeed=0
  37. self.avatarControlJumpForce=0
  38. self.avatarControlReverseSpeed=0
  39. self.avatarControlRotateSpeed=0
  40. self.__oldAirborneHeight=None
  41. self.getAirborneHeight=None
  42. self.__oldContact=None
  43. self.__oldPosDelta=Vec3(0)
  44. self.__oldDt=0
  45. self.__speed=0.0
  46. self.__rotationSpeed=0.0
  47. self.__slideSpeed=0.0
  48. self.__vel=Vec3(0.0)
  49. self.collisionsActive = 0
  50. self.isAirborne = 0
  51. self.highMark = 0
  52. """
  53. def spawnTest(self):
  54. assert(self.debugPrint("\n\nspawnTest()\n"))
  55. if not self.wantDebugIndicator:
  56. return
  57. from pandac.PandaModules import *
  58. from direct.interval.IntervalGlobal import *
  59. from toontown.coghq import MovingPlatform
  60. if hasattr(self, "platform"):
  61. # Remove the prior instantiation:
  62. self.moveIval.pause()
  63. del self.moveIval
  64. self.platform.destroy()
  65. del self.platform
  66. model = loader.loadModelCopy('phase_9/models/cogHQ/platform1')
  67. fakeId = id(self)
  68. self.platform = MovingPlatform.MovingPlatform()
  69. self.platform.setupCopyModel(fakeId, model, 'platformcollision')
  70. self.platformRoot = render.attachNewNode("physicsWalker-spawnTest-%s"%fakeId)
  71. self.platformRoot.setPos(base.localAvatar, Vec3(0.0, 3.0, 1.0))
  72. self.platformRoot.setHpr(base.localAvatar, Vec3.zero())
  73. self.platform.reparentTo(self.platformRoot)
  74. startPos = Vec3(0.0, -15.0, 0.0)
  75. endPos = Vec3(0.0, 15.0, 0.0)
  76. distance = Vec3(startPos-endPos).length()
  77. duration = distance/4
  78. self.moveIval = Sequence(
  79. WaitInterval(0.3),
  80. LerpPosInterval(self.platform, duration,
  81. endPos, startPos=startPos,
  82. name='platformOut%s' % fakeId,
  83. fluid = 1),
  84. WaitInterval(0.3),
  85. LerpPosInterval(self.platform, duration,
  86. startPos, startPos=endPos,
  87. name='platformBack%s' % fakeId,
  88. fluid = 1),
  89. name='platformIval%s' % fakeId,
  90. )
  91. self.moveIval.loop()
  92. """
  93. def setWalkSpeed(self, forward, jump, reverse, rotate):
  94. assert(self.debugPrint("setWalkSpeed()"))
  95. self.avatarControlForwardSpeed=forward
  96. self.avatarControlJumpForce=jump
  97. self.avatarControlReverseSpeed=reverse
  98. self.avatarControlRotateSpeed=rotate
  99. def getSpeeds(self):
  100. #assert(self.debugPrint("getSpeeds()"))
  101. return (self.__speed, self.__rotationSpeed)
  102. def setAvatar(self, avatar):
  103. self.avatar = avatar
  104. if avatar is not None:
  105. self.setupPhysics(avatar)
  106. def setupRay(self, floorBitmask, floorOffset):
  107. # This is a ray cast from your head down to detect floor polygons
  108. # A toon is about 4.0 feet high, so start it there
  109. self.cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)
  110. cRayNode = CollisionNode('PW.cRayNode')
  111. cRayNode.addSolid(self.cRay)
  112. self.cRayNodePath = self.avatarNodePath.attachNewNode(cRayNode)
  113. self.cRayBitMask = floorBitmask
  114. cRayNode.setFromCollideMask(self.cRayBitMask)
  115. cRayNode.setIntoCollideMask(BitMask32.allOff())
  116. if 0 or self.useLifter:
  117. # set up floor collision mechanism
  118. self.lifter = CollisionHandlerFloor()
  119. self.lifter.setInPattern("enter%in")
  120. self.lifter.setOutPattern("exit%in")
  121. self.lifter.setOffset(floorOffset)
  122. # Limit our rate-of-fall with the lifter.
  123. # If this is too low, we actually "fall" off steep stairs
  124. # and float above them as we go down. I increased this
  125. # from 8.0 to 16.0 to prevent this
  126. #self.lifter.setMaxVelocity(16.0)
  127. #self.bobNodePath = self.avatarNodePath.attachNewNode("bob")
  128. #self.lifter.addCollider(self.cRayNodePath, self.cRayNodePath)
  129. self.lifter.addCollider(self.cRayNodePath, self.avatarNodePath)
  130. else: # useCollisionHandlerQueue
  131. self.cRayQueue = CollisionHandlerQueue()
  132. self.cTrav.addCollider(self.cRayNodePath, self.cRayQueue)
  133. def determineHeight(self):
  134. """
  135. returns the height of the avatar above the ground.
  136. If there is no floor below the avatar, 0.0 is returned.
  137. aka get airborne height.
  138. """
  139. if self.useLifter:
  140. height = self.avatarNodePath.getPos(self.cRayNodePath)
  141. # If the shadow where not pointed strait down, we would need to
  142. # get magnitude of the vector. Since it is strait down, we'll
  143. # just get the z:
  144. #spammy --> assert self.debugPrint("getAirborneHeight() returning %s"%(height.getZ(),))
  145. assert onScreenDebug.add("height", height.getZ())
  146. return height.getZ() - self.floorOffset
  147. else: # useCollisionHandlerQueue
  148. """
  149. returns the height of the avatar above the ground.
  150. If there is no floor below the avatar, 0.0 is returned.
  151. aka get airborne height.
  152. """
  153. height = 0.0
  154. #*#self.cRayTrav.traverse(render)
  155. if self.cRayQueue.getNumEntries() != 0:
  156. # We have a floor.
  157. # Choose the highest of the possibly several floors we're over:
  158. self.cRayQueue.sortEntries()
  159. floorPoint = self.cRayQueue.getEntry(0).getFromIntersectionPoint()
  160. height = -floorPoint.getZ()
  161. self.cRayQueue.clearEntries()
  162. if __debug__:
  163. onScreenDebug.add("height", height)
  164. return height
  165. def setupSphere(self, bitmask, avatarRadius):
  166. """
  167. Set up the collision sphere
  168. """
  169. # This is a sphere on the ground to detect barrier collisions
  170. self.avatarRadius = avatarRadius
  171. centerHeight = avatarRadius
  172. if self.useHeightRay:
  173. centerHeight *= 2.0
  174. self.cSphere = CollisionSphere(0.0, 0.0, centerHeight, avatarRadius)
  175. cSphereNode = CollisionNode('PW.cSphereNode')
  176. cSphereNode.addSolid(self.cSphere)
  177. self.cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode)
  178. self.cSphereBitMask = bitmask
  179. cSphereNode.setFromCollideMask(self.cSphereBitMask)
  180. cSphereNode.setIntoCollideMask(BitMask32.allOff())
  181. # set up collision mechanism
  182. self.pusher = PhysicsCollisionHandler()
  183. self.pusher.setInPattern("enter%in")
  184. self.pusher.setOutPattern("exit%in")
  185. self.pusher.addCollider(self.cSphereNodePath, self.avatarNodePath)
  186. def setupPhysics(self, avatarNodePath):
  187. assert(self.debugPrint("setupPhysics()"))
  188. # Connect to Physics Manager:
  189. self.actorNode=ActorNode("PW physicsActor")
  190. self.actorNode.getPhysicsObject().setOriented(1)
  191. self.actorNode.getPhysical(0).setViscosity(0.1)
  192. physicsActor=NodePath(self.actorNode)
  193. avatarNodePath.reparentTo(physicsActor)
  194. avatarNodePath.assign(physicsActor)
  195. self.phys=PhysicsManager()
  196. fn=ForceNode("gravity")
  197. fnp=NodePath(fn)
  198. #fnp.reparentTo(physicsActor)
  199. fnp.reparentTo(render)
  200. gravity=LinearVectorForce(0.0, 0.0, self.__gravity)
  201. fn.addForce(gravity)
  202. self.phys.addLinearForce(gravity)
  203. self.gravity = gravity
  204. fn=ForceNode("priorParent")
  205. fnp=NodePath(fn)
  206. fnp.reparentTo(render)
  207. priorParent=LinearVectorForce(0.0, 0.0, 0.0)
  208. fn.addForce(priorParent)
  209. self.phys.addLinearForce(priorParent)
  210. self.priorParentNp = fnp
  211. self.priorParent = priorParent
  212. fn=ForceNode("viscosity")
  213. fnp=NodePath(fn)
  214. #fnp.reparentTo(physicsActor)
  215. fnp.reparentTo(render)
  216. self.avatarViscosity=LinearFrictionForce(0.0, 1.0, 0)
  217. #self.avatarViscosity.setCoef(0.9)
  218. fn.addForce(self.avatarViscosity)
  219. self.phys.addLinearForce(self.avatarViscosity)
  220. self.phys.attachLinearIntegrator(LinearEulerIntegrator())
  221. self.phys.attachPhysicalnode(physicsActor.node())
  222. self.acForce=LinearVectorForce(0.0, 0.0, 0.0)
  223. fn=ForceNode("avatarControls")
  224. fnp=NodePath(fn)
  225. fnp.reparentTo(render)
  226. fn.addForce(self.acForce)
  227. self.phys.addLinearForce(self.acForce)
  228. #self.phys.removeLinearForce(self.acForce)
  229. #fnp.remove()
  230. return avatarNodePath
  231. def initializeCollisions(self, collisionTraverser, avatarNodePath,
  232. wallBitmask, floorBitmask,
  233. avatarRadius = 1.4, floorOffset = 1.0, reach = 1.0):
  234. """
  235. Set up the avatar collisions
  236. """
  237. assert(self.debugPrint("initializeCollisions()"))
  238. assert not avatarNodePath.isEmpty()
  239. self.cTrav = collisionTraverser
  240. self.floorOffset = floorOffset = 7.0
  241. self.avatarNodePath = self.setupPhysics(avatarNodePath)
  242. if 0 or self.useHeightRay:
  243. #self.setupRay(floorBitmask, avatarRadius)
  244. self.setupRay(floorBitmask, 0.0)
  245. self.setupSphere(wallBitmask|floorBitmask, avatarRadius)
  246. self.setCollisionsActive(1)
  247. def setAirborneHeightFunc(self, getAirborneHeight):
  248. self.getAirborneHeight = getAirborneHeight
  249. def setAvatarPhysicsIndicator(self, indicator):
  250. """
  251. indicator is a NodePath
  252. """
  253. assert(self.debugPrint("setAvatarPhysicsIndicator()"))
  254. self.cSphereNodePath.show()
  255. if indicator:
  256. # Indicator Node:
  257. change=render.attachNewNode("change")
  258. #change.setPos(Vec3(1.0, 1.0, 1.0))
  259. #change.setHpr(0.0, 0.0, 0.0)
  260. change.setScale(0.1)
  261. #change.setColor(Vec4(1.0, 1.0, 1.0, 1.0))
  262. indicator.reparentTo(change)
  263. indicatorNode=render.attachNewNode("physVelocityIndicator")
  264. #indicatorNode.setScale(0.1)
  265. #indicatorNode.setP(90.0)
  266. indicatorNode.setPos(self.avatarNodePath, 0.0, 0.0, 6.0)
  267. indicatorNode.setColor(0.0, 0.0, 1.0, 1.0)
  268. change.reparentTo(indicatorNode)
  269. self.physVelocityIndicator=indicatorNode
  270. # Contact Node:
  271. contactIndicatorNode=render.attachNewNode("physContactIndicator")
  272. contactIndicatorNode.setScale(0.25)
  273. contactIndicatorNode.setP(90.0)
  274. contactIndicatorNode.setPos(self.avatarNodePath, 0.0, 0.0, 5.0)
  275. contactIndicatorNode.setColor(1.0, 0.0, 0.0, 1.0)
  276. indicator.instanceTo(contactIndicatorNode)
  277. self.physContactIndicator=contactIndicatorNode
  278. else:
  279. print "failed load of physics indicator"
  280. def avatarPhysicsIndicator(self, task):
  281. #assert(self.debugPrint("avatarPhysicsIndicator()"))
  282. # Velocity:
  283. self.physVelocityIndicator.setPos(self.avatarNodePath, 0.0, 0.0, 6.0)
  284. physObject=self.actorNode.getPhysicsObject()
  285. a=physObject.getVelocity()
  286. self.physVelocityIndicator.setScale(math.sqrt(a.length()))
  287. a+=self.physVelocityIndicator.getPos()
  288. self.physVelocityIndicator.lookAt(Point3(a))
  289. # Contact:
  290. contact=self.actorNode.getContactVector()
  291. if contact==Vec3.zero():
  292. self.physContactIndicator.hide()
  293. else:
  294. self.physContactIndicator.show()
  295. self.physContactIndicator.setPos(self.avatarNodePath, 0.0, 0.0, 5.0)
  296. #contact=self.actorNode.getContactVector()
  297. point=Point3(contact+self.physContactIndicator.getPos())
  298. self.physContactIndicator.lookAt(point)
  299. return Task.cont
  300. def deleteCollisions(self):
  301. assert(self.debugPrint("deleteCollisions()"))
  302. del self.cTrav
  303. if self.useHeightRay:
  304. del self.cRayQueue
  305. self.cRayNodePath.removeNode()
  306. del self.cRayNodePath
  307. del self.cSphere
  308. self.cSphereNodePath.removeNode()
  309. del self.cSphereNodePath
  310. del self.pusher
  311. del self.getAirborneHeight
  312. def setCollisionsActive(self, active = 1):
  313. assert(self.debugPrint("collisionsActive(active=%s)"%(active,)))
  314. if self.collisionsActive != active:
  315. self.collisionsActive = active
  316. if active:
  317. self.cTrav.addCollider(self.cSphereNodePath, self.pusher)
  318. if self.useHeightRay:
  319. if self.useLifter:
  320. self.cTrav.addCollider(self.cRayNodePath, self.lifter)
  321. else:
  322. self.cTrav.addCollider(self.cRayNodePath, self.cRayQueue)
  323. else:
  324. self.cTrav.removeCollider(self.cSphereNodePath)
  325. if self.useHeightRay:
  326. self.cTrav.removeCollider(self.cRayNodePath)
  327. # Now that we have disabled collisions, make one more pass
  328. # right now to ensure we aren't standing in a wall.
  329. self.oneTimeCollide()
  330. def getCollisionsActive(self):
  331. assert(self.debugPrint("getCollisionsActive() returning=%s"%(
  332. self.collisionsActive,)))
  333. return self.collisionsActive
  334. def placeOnFloor(self):
  335. """
  336. Make a reasonable effort to place the avatar on the ground.
  337. For example, this is useful when switching away from the
  338. current walker.
  339. """
  340. self.oneTimeCollide()
  341. self.avatarNodePath.setZ(self.avatarNodePath.getZ()-self.getAirborneHeight())
  342. def oneTimeCollide(self):
  343. """
  344. Makes one quick collision pass for the avatar, for instance as
  345. a one-time straighten-things-up operation after collisions
  346. have been disabled.
  347. """
  348. assert(self.debugPrint("oneTimeCollide()"))
  349. tempCTrav = CollisionTraverser("oneTimeCollide")
  350. if self.useHeightRay:
  351. if self.useLifter:
  352. tempCTrav.addCollider(self.cRayNodePath, self.lifter)
  353. else:
  354. tempCTrav.addCollider(self.cRayNodePath, self.cRayQueue)
  355. tempCTrav.traverse(render)
  356. def addBlastForce(self, vector):
  357. pass
  358. def displayDebugInfo(self):
  359. """
  360. For debug use.
  361. """
  362. onScreenDebug.add("w controls", "PhysicsWalker")
  363. if self.useLifter:
  364. onScreenDebug.add("w airborneHeight", self.lifter.getAirborneHeight())
  365. onScreenDebug.add("w isOnGround", self.lifter.isOnGround())
  366. #onScreenDebug.add("w gravity", self.lifter.getGravity())
  367. onScreenDebug.add("w contact normal", self.lifter.getContactNormal().pPrintValues())
  368. onScreenDebug.add("w impact", self.lifter.getImpactVelocity())
  369. onScreenDebug.add("w velocity", self.lifter.getVelocity())
  370. onScreenDebug.add("w hasContact", self.lifter.hasContact())
  371. #onScreenDebug.add("w falling", self.falling)
  372. #onScreenDebug.add("w jumpForce", self.avatarControlJumpForce)
  373. #onScreenDebug.add("w mayJump", self.mayJump)
  374. onScreenDebug.add("w isAirborne", self.isAirborne)
  375. def handleAvatarControls(self, task):
  376. """
  377. Check on the arrow keys and update the avatar.
  378. """
  379. if __debug__:
  380. if self.wantDebugIndicator:
  381. onScreenDebug.append("localAvatar pos = %s\n"%(base.localAvatar.getPos().pPrintValues(),))
  382. onScreenDebug.append("localAvatar h = % 10.4f\n"%(base.localAvatar.getH(),))
  383. onScreenDebug.append("localAvatar anim = %s\n"%(base.localAvatar.animFSM.getCurrentState().getName(),))
  384. #assert(self.debugPrint("handleAvatarControls(task=%s)"%(task,)))
  385. physObject=self.actorNode.getPhysicsObject()
  386. #rotAvatarToPhys=Mat3.rotateMatNormaxis(-self.avatarNodePath.getH(), Vec3.up())
  387. #rotPhysToAvatar=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
  388. contact=self.actorNode.getContactVector()
  389. # hack fix for falling through the floor:
  390. if contact==Vec3.zero() and self.avatarNodePath.getZ()<-50.0:
  391. # DCR: don't reset X and Y; allow player to move
  392. self.reset()
  393. self.avatarNodePath.setZ(50.0)
  394. messenger.send("walkerIsOutOfWorld", [self.avatarNodePath])
  395. if self.wantDebugIndicator:
  396. self.displayDebugInfo()
  397. # get the button states:
  398. forward = inputState.isSet("forward")
  399. reverse = inputState.isSet("reverse")
  400. turnLeft = inputState.isSet("turnLeft")
  401. turnRight = inputState.isSet("turnRight")
  402. slide = 0#inputState.isSet("slide")
  403. slideLeft = 0#inputState.isSet("slideLeft")
  404. slideRight = 0#inputState.isSet("slideRight")
  405. jump = inputState.isSet("jump")
  406. # Determine what the speeds are based on the buttons:
  407. self.__speed=(forward and self.avatarControlForwardSpeed or
  408. reverse and -self.avatarControlReverseSpeed)
  409. avatarSlideSpeed=self.avatarControlForwardSpeed*0.5
  410. #self.__slideSpeed=slide and (
  411. # (turnLeft and -avatarSlideSpeed) or
  412. # (turnRight and avatarSlideSpeed))
  413. self.__slideSpeed=(
  414. (slideLeft and -avatarSlideSpeed) or
  415. (slideRight and avatarSlideSpeed))
  416. self.__rotationSpeed=not slide and (
  417. (turnLeft and self.avatarControlRotateSpeed) or
  418. (turnRight and -self.avatarControlRotateSpeed))
  419. # How far did we move based on the amount of time elapsed?
  420. dt=ClockObject.getGlobalClock().getDt()
  421. if self.needToDeltaPos:
  422. self.setPriorParentVector()
  423. self.needToDeltaPos = 0
  424. #self.__oldPosDelta = render.getRelativeVector(
  425. # self.avatarNodePath,
  426. # self.avatarNodePath.getPosDelta(render))
  427. #self.__oldPosDelta = self.avatarNodePath.getRelativeVector(
  428. # render,
  429. # self.avatarNodePath.getPosDelta(render))
  430. self.__oldPosDelta = self.avatarNodePath.getPosDelta(render)
  431. self.__oldDt = dt
  432. #posDelta = self.avatarNodePath.getPosDelta(render)
  433. #if posDelta==Vec3.zero():
  434. # self.priorParent.setVector(self.__oldPosDelta)
  435. #else:
  436. # self.priorParent.setVector(Vec3.zero())
  437. # # We must copy the vector to preserve it:
  438. # self.__oldPosDelta=Vec3(posDelta)
  439. if __debug__:
  440. if self.wantDebugIndicator:
  441. onScreenDebug.add("posDelta1",
  442. self.avatarNodePath.getPosDelta(render).pPrintValues())
  443. if 0:
  444. onScreenDebug.add("posDelta3",
  445. render.getRelativeVector(
  446. self.avatarNodePath,
  447. self.avatarNodePath.getPosDelta(render)).pPrintValues())
  448. if 0:
  449. onScreenDebug.add("gravity",
  450. self.gravity.getLocalVector().pPrintValues())
  451. onScreenDebug.add("priorParent",
  452. self.priorParent.getLocalVector().pPrintValues())
  453. onScreenDebug.add("avatarViscosity",
  454. "% 10.4f"%(self.avatarViscosity.getCoef(),))
  455. onScreenDebug.add("physObject pos",
  456. physObject.getPosition().pPrintValues())
  457. onScreenDebug.add("physObject hpr",
  458. physObject.getOrientation().getHpr().pPrintValues())
  459. onScreenDebug.add("physObject orien",
  460. physObject.getOrientation().pPrintValues())
  461. if 1:
  462. onScreenDebug.add("physObject vel",
  463. physObject.getVelocity().pPrintValues())
  464. onScreenDebug.add("physObject len",
  465. "% 10.4f"%physObject.getVelocity().length())
  466. if 0:
  467. onScreenDebug.add("posDelta4",
  468. self.priorParentNp.getRelativeVector(
  469. render,
  470. self.avatarNodePath.getPosDelta(render)).pPrintValues())
  471. if 1:
  472. onScreenDebug.add("priorParent",
  473. self.priorParent.getLocalVector().pPrintValues())
  474. if 0:
  475. onScreenDebug.add("priorParent po",
  476. self.priorParent.getVector(physObject).pPrintValues())
  477. if 0:
  478. onScreenDebug.add("__posDelta",
  479. self.__oldPosDelta.pPrintValues())
  480. if 1:
  481. onScreenDebug.add("contact",
  482. contact.pPrintValues())
  483. #onScreenDebug.add("airborneHeight", "% 10.4f"%(
  484. # self.getAirborneHeight(),))
  485. if 0:
  486. onScreenDebug.add("__oldContact",
  487. contact.pPrintValues())
  488. onScreenDebug.add("__oldAirborneHeight", "% 10.4f"%(
  489. self.getAirborneHeight(),))
  490. airborneHeight=self.getAirborneHeight()
  491. if airborneHeight > self.highMark:
  492. self.highMark = airborneHeight
  493. if __debug__:
  494. onScreenDebug.add("highMark", "% 10.4f"%(self.highMark,))
  495. #if airborneHeight < 0.1: #contact!=Vec3.zero():
  496. if 1:
  497. if (airborneHeight > self.avatarRadius*0.5
  498. or physObject.getVelocity().getZ() > 0.0
  499. ): # Check stair angles before changing this.
  500. # ...the avatar is airborne (maybe a lot or a tiny amount).
  501. self.isAirborne = 1
  502. else:
  503. # ...the avatar is very close to the ground (close enough to be
  504. # considered on the ground).
  505. if self.isAirborne and physObject.getVelocity().getZ() <= 0.0:
  506. # ...the avatar has landed.
  507. contactLength = contact.length()
  508. if contactLength>self.__hardLandingForce:
  509. #print "jumpHardLand"
  510. messenger.send("jumpHardLand")
  511. else:
  512. #print "jumpLand"
  513. messenger.send("jumpLand")
  514. self.priorParent.setVector(Vec3.zero())
  515. self.isAirborne = 0
  516. elif jump:
  517. #print "jump"
  518. #self.__jumpButton=0
  519. messenger.send("jumpStart")
  520. if 0:
  521. # ...jump away from walls and with with the slope normal.
  522. jumpVec=Vec3(contact+Vec3.up())
  523. #jumpVec=Vec3(rotAvatarToPhys.xform(jumpVec))
  524. jumpVec.normalize()
  525. else:
  526. # ...jump straight up, even if next to a wall.
  527. jumpVec=Vec3.up()
  528. jumpVec*=self.avatarControlJumpForce
  529. physObject.addImpulse(Vec3(jumpVec))
  530. self.isAirborne = 1 # Avoid double impulse before fully airborne.
  531. else:
  532. self.isAirborne = 0
  533. if __debug__:
  534. onScreenDebug.add("isAirborne", "%d"%(self.isAirborne,))
  535. else:
  536. if contact!=Vec3.zero():
  537. # ...the avatar has touched something (but might not be on the ground).
  538. contactLength = contact.length()
  539. contact.normalize()
  540. angle=contact.dot(Vec3.up())
  541. if angle>self.__standableGround:
  542. # ...avatar is on standable ground.
  543. if self.__oldContact==Vec3.zero():
  544. #if self.__oldAirborneHeight > 0.1: #self.__oldContact==Vec3.zero():
  545. # ...avatar was airborne.
  546. self.jumpCount-=1
  547. if contactLength>self.__hardLandingForce:
  548. messenger.send("jumpHardLand")
  549. else:
  550. messenger.send("jumpLand")
  551. elif jump:
  552. self.jumpCount+=1
  553. #self.__jumpButton=0
  554. messenger.send("jumpStart")
  555. jump=Vec3(contact+Vec3.up())
  556. #jump=Vec3(rotAvatarToPhys.xform(jump))
  557. jump.normalize()
  558. jump*=self.avatarControlJumpForce
  559. physObject.addImpulse(Vec3(jump))
  560. if contact!=self.__oldContact:
  561. # We must copy the vector to preserve it:
  562. self.__oldContact=Vec3(contact)
  563. self.__oldAirborneHeight=airborneHeight
  564. moveToGround = Vec3.zero()
  565. if not self.useHeightRay or self.isAirborne:
  566. # ...the airborne check is a hack to stop sliding.
  567. self.phys.doPhysics(dt)
  568. if __debug__:
  569. onScreenDebug.add("phys", "on")
  570. else:
  571. physObject.setVelocity(Vec3.zero())
  572. #if airborneHeight>0.001 and contact==Vec3.zero():
  573. # moveToGround = Vec3(0.0, 0.0, -airborneHeight)
  574. #moveToGround = Vec3(0.0, 0.0, -airborneHeight)
  575. moveToGround = Vec3(0.0, 0.0, -self.determineHeight())
  576. if __debug__:
  577. onScreenDebug.add("phys", "off")
  578. # Check to see if we're moving at all:
  579. if self.__speed or self.__slideSpeed or self.__rotationSpeed or moveToGround!=Vec3.zero():
  580. distance = dt * self.__speed
  581. slideDistance = dt * self.__slideSpeed
  582. rotation = dt * self.__rotationSpeed
  583. #debugTempH=self.avatarNodePath.getH()
  584. assert self.avatarNodePath.getQuat().isSameDirection(physObject.getOrientation())
  585. assert self.avatarNodePath.getPos().almostEqual(physObject.getPosition(), 0.0001)
  586. # update pos:
  587. # Take a step in the direction of our previous heading.
  588. self.__vel=Vec3(
  589. Vec3.forward() * distance +
  590. Vec3.right() * slideDistance)
  591. # rotMat is the rotation matrix corresponding to
  592. # our previous heading.
  593. rotMat=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
  594. step=rotMat.xform(self.__vel)
  595. physObject.setPosition(Point3(
  596. physObject.getPosition()+step+moveToGround))
  597. # update hpr:
  598. o=physObject.getOrientation()
  599. r=LRotationf()
  600. r.setHpr(Vec3(rotation, 0.0, 0.0))
  601. physObject.setOrientation(o*r)
  602. # sync the change:
  603. self.actorNode.updateTransform()
  604. assert self.avatarNodePath.getQuat().isSameDirection(physObject.getOrientation())
  605. assert self.avatarNodePath.getPos().almostEqual(physObject.getPosition(), 0.0001)
  606. #assert self.avatarNodePath.getH()==debugTempH-rotation
  607. messenger.send("avatarMoving")
  608. else:
  609. self.__vel.set(0.0, 0.0, 0.0)
  610. # Clear the contact vector so we can tell if we contact something next frame:
  611. self.actorNode.setContactVector(Vec3.zero())
  612. return Task.cont
  613. def doDeltaPos(self):
  614. assert(self.debugPrint("doDeltaPos()"))
  615. self.needToDeltaPos = 1
  616. def setPriorParentVector(self):
  617. assert(self.debugPrint("doDeltaPos()"))
  618. print "self.__oldDt", self.__oldDt, "self.__oldPosDelta", self.__oldPosDelta
  619. if __debug__:
  620. onScreenDebug.add("__oldDt", "% 10.4f"%self.__oldDt)
  621. onScreenDebug.add("self.__oldPosDelta",
  622. self.__oldPosDelta.pPrintValues())
  623. velocity = self.__oldPosDelta*(1/self.__oldDt)*4.0 # *4.0 is a hack
  624. assert(self.debugPrint(" __oldPosDelta=%s"%(self.__oldPosDelta,)))
  625. assert(self.debugPrint(" velocity=%s"%(velocity,)))
  626. self.priorParent.setVector(Vec3(velocity))
  627. if __debug__:
  628. if self.wantDebugIndicator:
  629. onScreenDebug.add("velocity", velocity.pPrintValues())
  630. def reset(self):
  631. assert(self.debugPrint("reset()"))
  632. self.actorNode.getPhysicsObject().resetPosition(self.avatarNodePath.getPos())
  633. self.priorParent.setVector(Vec3.zero())
  634. self.highMark = 0
  635. self.actorNode.setContactVector(Vec3.zero())
  636. if __debug__:
  637. contact=self.actorNode.getContactVector()
  638. onScreenDebug.add("priorParent po",
  639. self.priorParent.getVector(self.actorNode.getPhysicsObject()).pPrintValues())
  640. onScreenDebug.add("highMark", "% 10.4f"%(self.highMark,))
  641. onScreenDebug.add("contact", contact.pPrintValues())
  642. def getVelocity(self):
  643. physObject=self.actorNode.getPhysicsObject()
  644. return physObject.getVelocity()
  645. def enableAvatarControls(self):
  646. """
  647. Activate the arrow keys, etc.
  648. """
  649. assert(self.debugPrint("enableAvatarControls()"))
  650. assert self.collisionsActive
  651. if __debug__:
  652. #self.accept("control-f3", self.spawnTest) #*#
  653. self.accept("f3", self.reset) # for debugging only.
  654. taskName = "AvatarControls-%s"%(id(self),)
  655. # remove any old
  656. taskMgr.remove(taskName)
  657. # spawn the new task
  658. taskMgr.add(self.handleAvatarControls, taskName, 25)
  659. if self.physVelocityIndicator:
  660. taskMgr.add(self.avatarPhysicsIndicator, "AvatarControlsIndicator%s"%(id(self),), 35)
  661. def disableAvatarControls(self):
  662. """
  663. Ignore the arrow keys, etc.
  664. """
  665. assert(self.debugPrint("disableAvatarControls()"))
  666. taskName = "AvatarControls-%s"%(id(self),)
  667. taskMgr.remove(taskName)
  668. taskName = "AvatarControlsIndicator%s"%(id(self),)
  669. taskMgr.remove(taskName)
  670. if __debug__:
  671. self.ignore("control-f3") #*#
  672. self.ignore("f3")
  673. if __debug__:
  674. def setupAvatarPhysicsIndicator(self):
  675. if self.wantDebugIndicator:
  676. indicator=loader.loadModelCopy('phase_5/models/props/dagger')
  677. #self.walkControls.setAvatarPhysicsIndicator(indicator)
  678. def debugPrint(self, message):
  679. """for debugging"""
  680. return self.notify.debug(
  681. str(id(self))+' '+message)