DirectCameraControl.py 15 KB

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