DirectJoybox.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. """ Class used to create and control joybox device """
  2. from PandaObject import *
  3. from DirectDeviceManager import *
  4. import OnscreenText
  5. """
  6. TODO:
  7. Handle interaction between widget, followSelectedTask and updateTask
  8. """
  9. # BUTTONS
  10. L_STICK = 0
  11. L_UPPER = 1
  12. L_LOWER = 2
  13. R_STICK = 3
  14. R_UPPER = 4
  15. R_LOWER = 5
  16. # ANALOGS
  17. NULL_AXIS = -1
  18. L_LEFT_RIGHT = 0
  19. L_FWD_BACK = 1
  20. L_TWIST = 2
  21. L_SLIDE = 3
  22. R_LEFT_RIGHT = 4
  23. R_FWD_BACK = 5
  24. R_TWIST = 6
  25. R_SLIDE = 7
  26. class DirectJoybox(PandaObject):
  27. joyboxCount = 0
  28. xyzMultiplier = 1.0
  29. hprMultiplier = 1.0
  30. def __init__(self, device = 'CerealBox', nodePath = direct.camera):
  31. # See if device manager has been initialized
  32. if direct.deviceManager == None:
  33. direct.deviceManager = DirectDeviceManager()
  34. # Set name
  35. self.name = 'Joybox-' + `DirectJoybox.joyboxCount`
  36. # Get buttons and analogs
  37. self.device = device
  38. self.analogs = direct.deviceManager.createAnalogs(self.device)
  39. self.buttons = direct.deviceManager.createButtons(self.device)
  40. self.aList = [0,0,0,0,0,0,0,0]
  41. self.bList = [0,0,0,0,0,0,0,0]
  42. # For joybox fly mode
  43. # Default is joe mode
  44. self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, L_FWD_BACK,
  45. R_TWIST, L_TWIST, NULL_AXIS]
  46. self.modifier = [1,1,1,-1,-1,0]
  47. # Initialize time
  48. self.lastTime = globalClock.getTime()
  49. # Record node path
  50. self.nodePath = nodePath
  51. # Ref CS for orbit mode
  52. self.refCS = direct.cameraControl.coaMarker
  53. self.tempCS = direct.group.attachNewNode('JoyboxTempCS')
  54. # Text object to display current mode
  55. self.readout = OnscreenText.OnscreenText( '', -0.9, -0.95 )
  56. # List of functions to cycle through
  57. self.modeList = [self.joeMode, self.driveMode, self.orbitMode]
  58. # Pick initial mode
  59. self.updateFunc = self.joyboxFly
  60. self.modeName = 'Joe Mode'
  61. # Button registry
  62. self.addButtonEvents()
  63. # Spawn update task
  64. self.enable()
  65. def enable(self):
  66. # Kill existing task
  67. self.disable()
  68. # Accept button events
  69. self.acceptSwitchModeEvent()
  70. self.acceptUprightCameraEvent()
  71. # Update task
  72. taskMgr.spawnMethodNamed(self.updateTask, self.name + '-updateTask')
  73. def disable(self):
  74. taskMgr.removeTasksNamed(self.name + '-updateTask')
  75. # Ignore button events
  76. self.ignoreSwitchModeEvent()
  77. self.ignoreUprightCameraEvent()
  78. def destroy(self):
  79. self.disable()
  80. self.tempCS.removeNode()
  81. def addButtonEvents(self):
  82. self.breg = ButtonRegistry.ptr()
  83. # MRM: Hard coded!
  84. for i in range(8):
  85. self.buttons.setButtonMap(
  86. i, self.breg.getButton(self.getEventName(i)))
  87. self.eventThrower = self.buttons.getNodePath().attachNewNode(
  88. ButtonThrower())
  89. def setNodePath(self, nodePath):
  90. self.nodePath = nodePath
  91. def getNodePath(self):
  92. return self.nodePath
  93. def setRefCS(self, refCS):
  94. self.refCS = refCS
  95. def getRefCS(self):
  96. return self.refCS
  97. def getEventName(self, index):
  98. return self.name + '-button-' + `index`
  99. def setXyzMultiplier(self, multiplier):
  100. DirectJoybox.xyzMultiplier = multiplier
  101. def getXyzMultiplier(self):
  102. return DirectJoybox.xyzMultiplier
  103. def setHprMultiplier(self, multiplier):
  104. DirectJoybox.hprMultiplier = multiplier
  105. def getHprMultiplier(self):
  106. return DirectJoybox.hprMultiplier
  107. def updateTask(self, state):
  108. self.updateVals()
  109. self.updateFunc()
  110. return Task.cont
  111. def updateVals(self):
  112. # Update delta time
  113. cTime = globalClock.getTime()
  114. self.deltaTime = cTime - self.lastTime
  115. self.lastTime = cTime
  116. # Update analogs
  117. for i in range(len(self.analogs)):
  118. self.aList[i] = self.analogs.normalizeChannel(i)
  119. # Update buttons
  120. for i in range(len(self.buttons)):
  121. try:
  122. self.bList[i] = self.buttons[i]
  123. except IndexError:
  124. # That channel may not have been updated yet
  125. self.bList[i] = 0
  126. def acceptSwitchModeEvent(self, button = R_UPPER):
  127. self.accept(self.getEventName(button), self.switchMode)
  128. def ignoreSwitchModeEvent(self, button = R_UPPER):
  129. self.ignore(self.getEventName(button))
  130. def switchMode(self):
  131. try:
  132. # Get current mode
  133. self.modeFunc = self.modeList[0]
  134. # Rotate mode list
  135. self.modeList = self.modeList[1:] + self.modeList[:1]
  136. # Call new mode
  137. self.modeFunc()
  138. except IndexError:
  139. pass
  140. def showMode(self, modeText):
  141. def hideText(state, s = self):
  142. s.readout.setText('')
  143. return Task.done
  144. taskMgr.removeTasksNamed(self.name + '-showMode')
  145. # Update display
  146. self.readout.setText(modeText)
  147. t = taskMgr.doMethodLater(3.0, hideText, self.name + '-showMode')
  148. t.uponDeath = hideText
  149. def acceptUprightCameraEvent(self, button = L_UPPER):
  150. self.accept(self.getEventName(button),
  151. direct.cameraControl.orbitUprightCam)
  152. def ignoreUprightCameraEvent(self, button = L_UPPER):
  153. self.ignore(self.getEventName(button))
  154. def setMode(self, func, name):
  155. self.disable()
  156. self.updateFunc = func
  157. self.modeName = name
  158. self.showMode(self.modeName)
  159. self.enable()
  160. def joyboxFly(self):
  161. # Do nothing if no nodePath selected
  162. if self.nodePath == None:
  163. return
  164. hprScale = (self.analogs.normalizeChannel(L_SLIDE, 0.1, 100) *
  165. DirectJoybox.hprMultiplier)
  166. posScale = (self.analogs.normalizeChannel(R_SLIDE, 0.1, 100) *
  167. DirectJoybox.xyzMultiplier)
  168. def getAxisVal(index, s = self):
  169. try:
  170. return s.aList[s.mapping[index]]
  171. except IndexError:
  172. # If it is a null axis return 0
  173. return 0.0
  174. x = getAxisVal(0) * self.modifier[0]
  175. y = getAxisVal(1) * self.modifier[1]
  176. z = getAxisVal(2) * self.modifier[2]
  177. pos = Vec3(x,y,z) * (posScale * self.deltaTime)
  178. h = getAxisVal(3) * self.modifier[3]
  179. p = getAxisVal(4) * self.modifier[4]
  180. r = getAxisVal(5) * self.modifier[5]
  181. hpr = Vec3(h,p,r) * (hprScale * self.deltaTime)
  182. # Move node path
  183. self.nodePath.setPosHpr(self.nodePath, pos, hpr)
  184. def joeMode(self):
  185. self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, L_FWD_BACK,
  186. R_TWIST, L_TWIST, NULL_AXIS]
  187. self.modifier = [1,1,1,-1,-1,0]
  188. self.setMode(self.joyboxFly, 'Joe Mode')
  189. def driveMode(self):
  190. self.mapping = [L_LEFT_RIGHT, R_FWD_BACK, R_TWIST,
  191. R_LEFT_RIGHT, L_FWD_BACK, NULL_AXIS]
  192. self.modifier = [1,1,-1,-1,-1,0]
  193. self.setMode(self.joyboxFly, 'Drive Mode')
  194. def lookAtMode(self):
  195. self.mapping = [R_LEFT_RIGHT, R_TWIST, R_FWD_BACK,
  196. L_LEFT_RIGHT, L_FWD_BACK, NULL_AXIS]
  197. self.modifier = [1,1,1,-1,1,0]
  198. self.setMode(self.joyboxFly, 'Look At Mode')
  199. def lookAroundMode(self):
  200. self.mapping = [NULL_AXIS, NULL_AXIS, NULL_AXIS,
  201. R_LEFT_RIGHT, R_FWD_BACK, NULL_AXIS]
  202. self.modifier = [0,0,0,-1,-1,0]
  203. self.setMode(self.joyboxFly, 'Lookaround Mode')
  204. def demoMode(self):
  205. self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, L_FWD_BACK,
  206. R_TWIST, NULL_AXIS, NULL_AXIS]
  207. self.modifier = [1,1,1,-1,0,0]
  208. self.setMode(self.joyboxFly, 'Demo Mode')
  209. def hprXyzMode(self):
  210. self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, R_TWIST,
  211. L_TWIST, L_FWD_BACK, L_LEFT_RIGHT]
  212. self.modifier = [1,1,-1,-1,-1,1]
  213. self.setMode(self.joyboxFly, 'HprXyz Mode')
  214. def walkthruMode(self):
  215. self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, L_TWIST,
  216. R_TWIST, L_FWD_BACK, L_LEFT_RIGHT]
  217. self.modifier = [1,1,-1,-1,-1, 1]
  218. self.setMode(self.joyboxFly, 'Walkthru Mode')
  219. def orbitMode(self):
  220. self.setMode(self.orbitFly, 'Orbit Mode')
  221. def orbitFly(self):
  222. # Do nothing if no nodePath selected
  223. if self.nodePath == None:
  224. return
  225. hprScale = (self.analogs.normalizeChannel(L_SLIDE, 0.1, 100) *
  226. DirectJoybox.hprMultiplier)
  227. posScale = (self.analogs.normalizeChannel(R_SLIDE, 0.1, 100) *
  228. DirectJoybox.xyzMultiplier)
  229. r = -0.01 * posScale * self.aList[R_FWD_BACK] * self.deltaTime
  230. rx = hprScale * self.aList[R_LEFT_RIGHT] * self.deltaTime
  231. ry = hprScale * self.aList[R_TWIST] * self.deltaTime
  232. x = posScale * self.aList[L_LEFT_RIGHT] * self.deltaTime
  233. z = posScale * self.aList[L_FWD_BACK] * self.deltaTime
  234. h = -1 * hprScale * self.aList[L_TWIST] * self.deltaTime
  235. # Move dcs
  236. self.nodePath.setX(self.nodePath, x)
  237. self.nodePath.setZ(self.nodePath, z)
  238. self.nodePath.setH(self.nodePath, h)
  239. self.orbitNode(rx, ry, 0)
  240. pos = self.nodePath.getPos(self.refCS)
  241. if Vec3(pos).length() < 0.005:
  242. pos.set(0,-0.01, 0)
  243. # Now move on out
  244. pos.assign(pos * (1 + r))
  245. self.nodePath.setPos(self.refCS, pos)
  246. def orbitNode(self, h, p, r):
  247. # Position the temp node path at the ref CS
  248. self.tempCS.setPos(self.refCS, 0, 0, 0)
  249. # Orient the temp node path to align with the orbiting node path
  250. self.tempCS.setHpr(self.nodePath, 0, 0, 0)
  251. # Record the position of the orbiter wrt the helper
  252. pos = self.nodePath.getPos(self.tempCS)
  253. # Turn the temp node path
  254. self.tempCS.setHpr(self.tempCS, h, p, r)
  255. # Position the orbiter "back" to its position wrt the helper
  256. self.nodePath.setPos(self.tempCS, pos)
  257. # Restore the original hpr of the orbiter
  258. self.nodePath.setHpr(self.tempCS, 0, 0, 0)