DirectCameraControl.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. from PandaObject import *
  2. CAM_MOVE_DURATION = 1.0
  3. class DirectCameraControl(PandaObject):
  4. def __init__(self, direct):
  5. # Create the grid
  6. self.direct = direct
  7. self.defChan = direct.chanCenter
  8. self.camera = self.defChan.camera
  9. self.orthoViewRoll = 0.0
  10. self.lastView = 0
  11. self.coa = Point3(0)
  12. self.coaMarker = loader.loadModel('misc/sphere')
  13. self.coaMarker.setColor(1,0,0)
  14. self.coaMarkerPos = Point3(0)
  15. self.relNodePath = render.attachNewNode(NamedNode('targetNode'))
  16. self.zeroBaseVec = VBase3(0)
  17. self.zeroVector = Vec3(0)
  18. self.centerVec = Vec3(0, 1, 0)
  19. self.zeroPoint = Point3(0)
  20. def mouseFlyStart(self, chan):
  21. # Record starting mouse positions
  22. self.initMouseX = chan.mouseX
  23. self.initMouseY = chan.mouseY
  24. # Where are we in the channel?
  25. if ((abs(self.initMouseX) < 0.9) & (abs(self.initMouseY) < 0.9)):
  26. # MOUSE IS IN CENTRAL REGION
  27. if (self.direct.fShift):
  28. # If shift key is pressed, just perform horiz and vert pan:
  29. self.spawnHPPan()
  30. else:
  31. # Otherwise, check for a hit point based on current mouse position
  32. # And then spawn task to determine mouse mode
  33. numEntries = self.direct.iRay.pick(render,chan.mouseX,chan.mouseY)
  34. if(numEntries):
  35. # Start off with first point
  36. minPt = 0
  37. # Find hit point in camera's space
  38. self.coa = self.direct.iRay.camToHitPt(minPt)
  39. self.coaDist = Vec3(self.coa - self.zeroPoint).length()
  40. # Check other intersection points, sorting them
  41. # TBD: Use TBS C++ function to do this
  42. if numEntries > 1:
  43. for i in range(1,numEntries):
  44. hitPt = self.direct.iRay.camToHitPt(i)
  45. dist = Vec3(hitPt - self.zeroPoint).length()
  46. if (dist < self.coaDist):
  47. self.coaDist = dist
  48. self.coa = hitPt
  49. minPt = i
  50. # Handle case of bad coa point (too close or too far)
  51. if ((self.coaDist < (1.1 * self.defChan.near)) |
  52. (self.coaDist > self.defChan.far)):
  53. # Put it out in front of the camera
  54. self.coa.set(0,10,0)
  55. self.coaDist = 10
  56. else:
  57. # If no intersection point:
  58. # Put coa out in front of the camera
  59. self.coa.set(0,10,0)
  60. self.coaDist = 10
  61. # Place the marker in render space
  62. self.coaMarker.setPos(self.camera,self.coa)
  63. # Record this point for later use
  64. self.coaMarkerPos = self.coaMarker.getPos()
  65. # Now spawn task to determine mouse fly mode
  66. self.determineMouseFlyMode()
  67. # END MOUSE IN CENTRAL REGION
  68. else:
  69. # Mouse is in outer frame, spawn mouseRotateTask
  70. self.spawnMouseRotateTask()
  71. def mouseFlyStop(self):
  72. taskMgr.removeTasksNamed('determineMouseFlyMode')
  73. taskMgr.removeTasksNamed('manipulateCamera')
  74. def determineMouseFlyMode(self):
  75. if (self.direct.fShift):
  76. # If shift key is pressed:
  77. self.spawnHPPan()
  78. else:
  79. # Otherwise, determine mouse fly mode
  80. t = Task.Task(self.determineMouseFlyModeTask)
  81. taskMgr.spawnTaskNamed(t, 'determineMouseFlyMode')
  82. def determineMouseFlyModeTask(self, state):
  83. deltaX = self.defChan.mouseX - self.initMouseX
  84. deltaY = self.defChan.mouseY - self.initMouseY
  85. if ((abs(deltaX) < 0.1) & (abs(deltaY) < 0.1)):
  86. return Task.cont
  87. else:
  88. if (abs(deltaY) > 0.1):
  89. self.spawnHPanYZoom()
  90. else:
  91. self.spawnXZTranslate()
  92. return Task.done
  93. def homeCam(self, chan):
  94. chan.camera.setMat(Mat4.identMat())
  95. def uprightCam(self, chan):
  96. taskMgr.removeTasksNamed('manipulateCamera')
  97. currH = chan.camera.getH()
  98. chan.camera.lerpHpr(currH, 0, 0,
  99. CAM_MOVE_DURATION,
  100. other = render,
  101. blendType = 'easeInOut',
  102. task = 'manipulateCamera')
  103. def centerCam(self, chan):
  104. # Chan is a display region context
  105. self.centerCamIn(chan, 1.0)
  106. def centerCamNow(self, chan):
  107. self.centerCamIn(chan, 0.)
  108. def centerCamIn(self, chan,t):
  109. # Chan is a display region context
  110. taskMgr.removeTasksNamed('manipulateCamera')
  111. markerToCam = self.coaMarker.getPos( chan.camera )
  112. dist = Vec3(markerToCam - self.zeroPoint).length()
  113. scaledCenterVec = self.centerVec * dist
  114. delta = markerToCam - scaledCenterVec
  115. self.relNodePath.setPosHpr(chan.camera, Point3(0), Point3(0))
  116. chan.camera.lerpPos(Point3(delta),
  117. CAM_MOVE_DURATION,
  118. other = self.relNodePath,
  119. blendType = 'easeInOut',
  120. task = 'manipulateCamera')
  121. def zoomCam(self, chan, zoomFactor, t):
  122. taskMgr.removeTasksNamed('manipulateCamera')
  123. # Find a point zoom factor times the current separation
  124. # of the widget and cam
  125. zoomPtToCam = self.coaMarker.getPos(chan.camera) * zoomFactor
  126. # Put a target nodePath there
  127. self.relNodePath.setPos(chan.camera, zoomPtToCam)
  128. # Move to that point
  129. chan.camera.lerpPos(self.zeroPoint,
  130. CAM_MOVE_DURATION,
  131. other = self.relNodePath,
  132. blendType = 'easeInOut',
  133. task = 'manipulateCamera')
  134. def SpawnMoveToView(self, chan, view):
  135. # Kill any existing tasks
  136. taskMgr.removeTasksNamed('manipulateCamera')
  137. # Calc hprOffset
  138. hprOffset = VBase3()
  139. if view == 8:
  140. # Try the next roll angle
  141. self.orthoViewRoll = (self.orthoViewRoll + 90.0) % 360.0
  142. # but use the last view
  143. view = self.lastView
  144. if view == 1:
  145. hprOffset.set(180., 0., 0.)
  146. elif view == 2:
  147. hprOffset.set(0., 0., 0.)
  148. elif view == 3:
  149. hprOffset.set(90., 0., 0.)
  150. elif view == 4:
  151. hprOffset.set(-90., 0., 0.)
  152. elif view == 5:
  153. hprOffset.set(0., -90., 0.)
  154. elif view == 6:
  155. hprOffset.set(0., 90., 0.)
  156. elif view == 7:
  157. hprOffset.set(135., -35.264, 0.)
  158. # Position target
  159. self.relNodePath.setPosHpr(self.coaMarker, self.zeroBaseVec,
  160. hprOffset)
  161. # Scale center vec by current distance to target
  162. offsetDistance = Vec3(chan.camera.getPos(self.relNodePath) -
  163. self.zeroPoint).length()
  164. scaledCenterVec = self.centerVec * (-1.0 * offsetDistance)
  165. # Now put the relNodePath at that point
  166. self.relNodePath.setPosHpr(self.relNodePath,
  167. scaledCenterVec,
  168. self.zeroBaseVec)
  169. # Store this view for next time
  170. # Reset orthoViewRoll if you change views
  171. if view != self.lastView:
  172. self.orthoViewRoll = 0.0
  173. self.lastView = view
  174. chan.camera.lerpPosHpr(self.zeroPoint,
  175. VBase3(0,0,self.orthoViewRoll),
  176. CAM_MOVE_DURATION,
  177. other = self.relNodePath,
  178. blendType = 'easeInOut',
  179. task = 'manipulateCamera')
  180. def swingCamAboutWidget(self, chan, degrees, t):
  181. # Remove existing camera manipulation task
  182. taskMgr.removeTasksNamed('manipulateCamera')
  183. # Coincident with widget
  184. self.relNodePath.setPos(self.coaMarker, self.zeroPoint)
  185. # But aligned with render space
  186. self.relNodePath.setHpr(self.zeroPoint)
  187. parent = self.defChan.camera.getParent()
  188. self.defChan.camera.wrtReparentTo(self.relNodePath)
  189. manipTask = self.relNodePath.lerpHpr(VBase3(degrees,0,0),
  190. CAM_MOVE_DURATION,
  191. blendType = 'easeInOut',
  192. task = 'manipulateCamera')
  193. # Upon death, reparent Cam to parent
  194. manipTask.parent = parent
  195. manipTask.uponDeath = self.reparentCam
  196. def reparentCam(self, state):
  197. self.defChan.camera.wrtReparentTo(state.parent)
  198. def spawnHPanYZoom(self):
  199. # Negate vec to give it the correct sense for mouse motion below
  200. # targetVector = self.coa * -1
  201. targetVector = self.coa * -1
  202. t = Task.Task(self.HPanYZoomTask)
  203. t.targetVector = targetVector
  204. taskMgr.spawnTaskNamed(t, 'manipulateCamera')
  205. def HPanYZoomTask(self,state):
  206. targetVector = state.targetVector
  207. distToMove = targetVector * self.defChan.mouseDeltaY
  208. self.defChan.camera.setPosHpr(self.defChan.camera,
  209. distToMove[0],
  210. distToMove[1],
  211. distToMove[2],
  212. (0.5 * self.defChan.mouseDeltaX *
  213. self.defChan.fovH),
  214. 0.0, 0.0)
  215. return Task.cont
  216. def spawnXZTranslateOrHPPan(self):
  217. t = Task.Task(self.XZTranslateOrHPPanTask)
  218. t.scaleFactor = (self.coaDist / self.defChan.near)
  219. taskMgr.spawnTaskNamed(t, 'manipulateCamera')
  220. def XZTranslateOrHPPanTask(self, state):
  221. if self.direct.fShift:
  222. self.defChan.camera.setHpr(self.defChan.camera,
  223. (0.5 * self.defChan.mouseDeltaX *
  224. self.defChan.fovH),
  225. (-0.5 * self.defChan.mouseDeltaY *
  226. self.defChan.fovV),
  227. 0.0)
  228. else:
  229. self.defChan.camera.setPos(self.defChan.camera,
  230. (-0.5 * self.defChan.mouseDeltaX *
  231. self.defChan.nearWidth *
  232. state.scaleFactor),
  233. 0.0,
  234. (-0.5 * self.defChan.mouseDeltaY *
  235. self.defChan.nearHeight *
  236. state.scaleFactor))
  237. return Task.cont
  238. def spawnXZTranslate(self):
  239. t = Task.Task(self.XZTranslateTask)
  240. t.scaleFactor = (self.coaDist / self.defChan.near)
  241. taskMgr.spawnTaskNamed(t, 'manipulateCamera')
  242. def XZTranslateTask(self,state):
  243. self.defChan.camera.setPos(self.defChan.camera,
  244. (-0.5 * self.defChan.mouseDeltaX *
  245. self.defChan.nearWidth *
  246. state.scaleFactor),
  247. 0.0,
  248. (-0.5 * self.defChan.mouseDeltaY *
  249. self.defChan.nearHeight *
  250. state.scaleFactor))
  251. return Task.cont
  252. def spawnMouseRotateTask(self):
  253. self.relNodePath.setPos(render, self.coaMarkerPos)
  254. self.relNodePath.setHpr(self.defChan.camera, self.zeroPoint)
  255. t = Task.Task(self.mouseRotateTask)
  256. t.wrtMat = self.defChan.camera.getMat( self.relNodePath )
  257. taskMgr.spawnTaskNamed(t, 'manipulateCamera')
  258. def mouseRotateTask(self, state):
  259. wrtMat = state.wrtMat
  260. self.relNodePath.setHpr(self.relNodePath,
  261. (-0.5 * self.defChan.mouseDeltaX * 180.0),
  262. (0.5 * self.defChan.mouseDeltaY * 180.0),
  263. 0.0)
  264. self.defChan.camera.setMat(self.relNodePath, wrtMat)
  265. return Task.cont
  266. def spawnHPPan(self):
  267. t = Task.Task(self.HPPanTask)
  268. taskMgr.spawnTaskNamed(t, 'manipulateCamera')
  269. def HPPanTask(self, state):
  270. self.defChan.camera.setHpr(self.defChan.camera,
  271. (0.5 * self.defChan.mouseDeltaX *
  272. self.defChan.fovH),
  273. (-0.5 * self.defChan.mouseDeltaY *
  274. self.defChan.fovV),
  275. 0.0)
  276. return Task.cont
  277. def enableMouseFly(self):
  278. self.enableMouseInteraction()
  279. self.enableHotKeys()
  280. self.coaMarker.reparentTo(render)
  281. def enableMouseInteraction(self):
  282. # disable C++ fly interface
  283. base.disableMouse()
  284. # Accept middle mouse events
  285. self.accept('mouse2', self.mouseFlyStart, [self.defChan])
  286. self.accept('mouse2-up', self.mouseFlyStop)
  287. def enableHotKeys(self):
  288. t = CAM_MOVE_DURATION
  289. self.accept('u', self.uprightCam, [self.defChan])
  290. self.accept('c', self.centerCamIn, [self.defChan, 0.5])
  291. self.accept('h', self.homeCam, [self.defChan])
  292. for i in range(1,9):
  293. self.accept(`i`, self.SpawnMoveToView, [self.defChan, i])
  294. self.accept('9', self.swingCamAboutWidget, [self.defChan, -90.0, t])
  295. self.accept('0', self.swingCamAboutWidget, [self.defChan, 90.0, t])
  296. self.accept('`', self.removeManipulateCameraTask)
  297. self.accept('=', self.zoomCam, [self.defChan, 0.5, t])
  298. self.accept('+', self.zoomCam, [self.defChan, 0.5, t])
  299. self.accept('-', self.zoomCam, [self.defChan, -2.0, t])
  300. self.accept('_', self.zoomCam, [self.defChan, -2.0, t])
  301. def disableMouseFly(self):
  302. # Hide the marker
  303. self.coaMarker.reparentTo(hidden)
  304. # Accept middle mouse events
  305. self.ignore('mouse2')
  306. self.ignore('mouse2-up')
  307. self.ignore('u')
  308. self.ignore('c')
  309. self.ignore('h')
  310. for i in range(0,10):
  311. self.ignore(`i`)
  312. self.ignore('=')
  313. self.ignore('+')
  314. self.ignore('-')
  315. self.ignore('_')
  316. self.ignore('`')
  317. def removeManipulateCameraTask(self):
  318. taskMgr.removeTasksNamed('manipulateCamera')