DirectCameraControl.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. from PandaObject import *
  2. from DirectUtil import *
  3. from DirectGeometry import *
  4. import Task
  5. CAM_MOVE_DURATION = 1.2
  6. COA_MARKER_SF = 0.0075
  7. Y_AXIS = Vec3(0,1,0)
  8. class DirectCameraControl(PandaObject):
  9. def __init__(self):
  10. # Create the grid
  11. self.startT = 0.0
  12. self.startF = 0
  13. self.orthoViewRoll = 0.0
  14. self.lastView = 0
  15. self.coa = Point3(0,100,0)
  16. self.coaMarker = loader.loadModel('models/misc/sphere')
  17. self.coaMarker.setName('DirectCameraCOAMarker')
  18. self.coaMarker.setTransparency(1)
  19. self.coaMarker.setColor(1,0,0,0)
  20. self.coaMarker.setPos(0,100,0)
  21. useDirectRenderStyle(self.coaMarker)
  22. self.coaMarkerPos = Point3(0)
  23. self.fLockCOA = 0
  24. self.nullHitPointCount = 0
  25. self.cqEntries = []
  26. self.coaMarkerRef = direct.group.attachNewNode('coaMarkerRef')
  27. self.camManipRef = direct.group.attachNewNode('camManipRef')
  28. t = CAM_MOVE_DURATION
  29. self.actionEvents = [
  30. ['DIRECT-mouse2', self.mouseFlyStart],
  31. ['DIRECT-mouse2Up', self.mouseFlyStop],
  32. ['c', self.centerCamIn, 0.5],
  33. ['f', self.fitOnWidget],
  34. ['h', self.homeCam],
  35. ['shift-v', self.toggleMarkerVis],
  36. ['m', self.moveToFit],
  37. ['n', self.pickNextCOA],
  38. ['u', self.orbitUprightCam],
  39. ['shift-u', self.uprightCam],
  40. [`1`, self.spawnMoveToView, 1],
  41. [`2`, self.spawnMoveToView, 2],
  42. [`3`, self.spawnMoveToView, 3],
  43. [`4`, self.spawnMoveToView, 4],
  44. [`5`, self.spawnMoveToView, 5],
  45. [`6`, self.spawnMoveToView, 6],
  46. [`7`, self.spawnMoveToView, 7],
  47. [`8`, self.spawnMoveToView, 8],
  48. ['9', self.swingCamAboutWidget, -90.0, t],
  49. ['0', self.swingCamAboutWidget, 90.0, t],
  50. ['`', self.removeManipulateCameraTask],
  51. ['=', self.zoomCam, 0.5, t],
  52. ['+', self.zoomCam, 0.5, t],
  53. ['-', self.zoomCam, -2.0, t],
  54. ['_', self.zoomCam, -2.0, t],
  55. ]
  56. def toggleMarkerVis(self):
  57. if direct.cameraControl.coaMarker.isHidden():
  58. direct.cameraControl.coaMarker.show()
  59. else:
  60. direct.cameraControl.coaMarker.hide()
  61. def mouseFlyStart(self, modifiers):
  62. # Record undo point
  63. direct.pushUndo([direct.camera])
  64. # Where are we in the display region?
  65. if ((abs(direct.dr.mouseX) < 0.9) and (abs(direct.dr.mouseY) < 0.9)):
  66. # MOUSE IS IN CENTRAL REGION
  67. # Hide the marker for this kind of motion
  68. self.coaMarker.hide()
  69. # Record time of start of mouse interaction
  70. self.startT= globalClock.getFrameTime()
  71. self.startF = globalClock.getFrameCount()
  72. # Start manipulation
  73. self.spawnXZTranslateOrHPanYZoom()
  74. # END MOUSE IN CENTRAL REGION
  75. else:
  76. if ((abs(direct.dr.mouseX) > 0.9) and
  77. (abs(direct.dr.mouseY) > 0.9)):
  78. # Mouse is in corners, spawn roll task
  79. self.spawnMouseRollTask()
  80. else:
  81. # Mouse is in outer frame, spawn mouseRotateTask
  82. self.spawnMouseRotateTask()
  83. def mouseFlyStop(self):
  84. taskMgr.remove('manipulateCamera')
  85. stopT = globalClock.getFrameTime()
  86. deltaT = stopT - self.startT
  87. stopF = globalClock.getFrameCount()
  88. deltaF = stopF - self.startF
  89. if (deltaT <= 0.25) or (deltaF <= 1):
  90. # Check for a hit point based on
  91. # current mouse position
  92. # Allow intersection with unpickable objects
  93. # And then spawn task to determine mouse mode
  94. node, hitPt, hitPtDist = direct.iRay.pickGeom(
  95. fIntersectUnpickable = 1,
  96. fIgnoreCamera = 1 - base.getControl())
  97. self.computeCOA(node, hitPt, hitPtDist)
  98. # Record reference point
  99. self.coaMarkerRef.iPosHprScale(direct.iRay.collisionRef)
  100. # Record entries
  101. self.cqEntries = []
  102. for i in range(direct.iRay.cq.getNumEntries()):
  103. self.cqEntries.append(direct.iRay.cq.getEntry(i))
  104. # Show the marker
  105. self.coaMarker.show()
  106. # Resize it
  107. self.updateCoaMarkerSize()
  108. def spawnXZTranslateOrHPanYZoom(self):
  109. # Kill any existing tasks
  110. taskMgr.remove('manipulateCamera')
  111. # Spawn the new task
  112. t = Task.Task(self.XZTranslateOrHPanYZoomTask)
  113. # For HPanYZoom
  114. t.zoomSF = Vec3(self.coaMarker.getPos(direct.camera)).length()
  115. taskMgr.add(t, 'manipulateCamera')
  116. def spawnXZTranslateOrHPPan(self):
  117. # Kill any existing tasks
  118. taskMgr.remove('manipulateCamera')
  119. # Spawn new task
  120. taskMgr.add(self.XZTranslateOrHPPanTask,
  121. 'manipulateCamera')
  122. def spawnXZTranslate(self):
  123. # Kill any existing tasks
  124. taskMgr.remove('manipulateCamera')
  125. # Spawn new task
  126. taskMgr.add(self.XZTranslateTask, 'manipulateCamera')
  127. def spawnHPanYZoom(self):
  128. # Kill any existing tasks
  129. taskMgr.remove('manipulateCamera')
  130. # Spawn new task
  131. t = Task.Task(self.HPanYZoomTask)
  132. t.zoomSF = Vec3(self.coaMarker.getPos(direct.camera)).length()
  133. taskMgr.add(t, 'manipulateCamera')
  134. def spawnHPPan(self):
  135. # Kill any existing tasks
  136. taskMgr.remove('manipulateCamera')
  137. # Spawn new task
  138. taskMgr.add(self.HPPanTask, 'manipulateCamera')
  139. def XZTranslateOrHPanYZoomTask(self, state):
  140. if direct.fShift:
  141. return self.XZTranslateTask(state)
  142. else:
  143. return self.HPanYZoomTask(state)
  144. def XZTranslateOrHPPanTask(self, state):
  145. if direct.fShift:
  146. # Panning action
  147. return self.HPPanTask(state)
  148. else:
  149. # Translation action
  150. return self.XZTranslateTask(state)
  151. def XZTranslateTask(self,state):
  152. coaDist = Vec3(self.coaMarker.getPos(direct.camera)).length()
  153. xlateSF = (coaDist / direct.dr.near)
  154. direct.camera.setPos(direct.camera,
  155. (-0.5 * direct.dr.mouseDeltaX *
  156. direct.dr.nearWidth *
  157. xlateSF),
  158. 0.0,
  159. (-0.5 * direct.dr.mouseDeltaY *
  160. direct.dr.nearHeight *
  161. xlateSF))
  162. return Task.cont
  163. def HPanYZoomTask(self,state):
  164. if direct.fControl:
  165. moveDir = Vec3(self.coaMarker.getPos(direct.camera))
  166. # If marker is behind camera invert vector
  167. if moveDir[1] < 0.0:
  168. moveDir.assign(moveDir * -1)
  169. moveDir.normalize()
  170. else:
  171. moveDir = Vec3(Y_AXIS)
  172. moveDir.assign(moveDir * (-1.0 * direct.dr.mouseDeltaY *
  173. state.zoomSF))
  174. if direct.dr.mouseDeltaY > 0.0:
  175. moveDir.setY(moveDir[1] * 1.0)
  176. direct.camera.setPosHpr(direct.camera,
  177. moveDir[0],
  178. moveDir[1],
  179. moveDir[2],
  180. (0.5 * direct.dr.mouseDeltaX *
  181. direct.dr.fovH),
  182. 0.0, 0.0)
  183. return Task.cont
  184. def HPPanTask(self, state):
  185. direct.camera.setHpr(direct.camera,
  186. (0.5 * direct.dr.mouseDeltaX *
  187. direct.dr.fovH),
  188. (-0.5 * direct.dr.mouseDeltaY *
  189. direct.dr.fovV),
  190. 0.0)
  191. return Task.cont
  192. def spawnMouseRotateTask(self):
  193. # Kill any existing tasks
  194. taskMgr.remove('manipulateCamera')
  195. # Set at markers position in render coordinates
  196. self.camManipRef.setPos(self.coaMarkerPos)
  197. self.camManipRef.setHpr(direct.camera, ZERO_POINT)
  198. t = Task.Task(self.mouseRotateTask)
  199. if abs(direct.dr.mouseX) > 0.9:
  200. t.constrainedDir = 'y'
  201. else:
  202. t.constrainedDir = 'x'
  203. taskMgr.add(t, 'manipulateCamera')
  204. def mouseRotateTask(self, state):
  205. # If moving outside of center, ignore motion perpendicular to edge
  206. if ((state.constrainedDir == 'y') and (abs(direct.dr.mouseX) > 0.9)):
  207. deltaX = 0
  208. deltaY = direct.dr.mouseDeltaY
  209. elif ((state.constrainedDir == 'x') and (abs(direct.dr.mouseY) > 0.9)):
  210. deltaX = direct.dr.mouseDeltaX
  211. deltaY = 0
  212. else:
  213. deltaX = direct.dr.mouseDeltaX
  214. deltaY = direct.dr.mouseDeltaY
  215. if direct.fShift:
  216. direct.camera.setHpr(direct.camera,
  217. (deltaX * direct.dr.fovH),
  218. (-deltaY * direct.dr.fovV),
  219. 0.0)
  220. self.camManipRef.setPos(self.coaMarkerPos)
  221. self.camManipRef.setHpr(direct.camera, ZERO_POINT)
  222. else:
  223. wrt = direct.camera.getTransform( self.camManipRef )
  224. self.camManipRef.setHpr(self.camManipRef,
  225. (-1 * deltaX * 180.0),
  226. (deltaY * 180.0),
  227. 0.0)
  228. direct.camera.setTransform(self.camManipRef, wrt)
  229. return Task.cont
  230. def spawnMouseRollTask(self):
  231. # Kill any existing tasks
  232. taskMgr.remove('manipulateCamera')
  233. # Set at markers position in render coordinates
  234. self.camManipRef.setPos(self.coaMarkerPos)
  235. self.camManipRef.setHpr(direct.camera, ZERO_POINT)
  236. t = Task.Task(self.mouseRollTask)
  237. t.coaCenter = getScreenXY(self.coaMarker)
  238. t.lastAngle = getCrankAngle(t.coaCenter)
  239. # Store the camera/manipRef offset transform
  240. t.wrt = direct.camera.getTransform( self.camManipRef )
  241. taskMgr.add(t, 'manipulateCamera')
  242. def mouseRollTask(self, state):
  243. wrt = state.wrt
  244. angle = getCrankAngle(state.coaCenter)
  245. deltaAngle = angle - state.lastAngle
  246. state.lastAngle = angle
  247. if base.config.GetBool('temp-hpr-fix',0):
  248. self.camManipRef.setHpr(self.camManipRef, 0, 0, deltaAngle)
  249. else:
  250. self.camManipRef.setHpr(self.camManipRef, 0, 0, -deltaAngle)
  251. direct.camera.setTransform(self.camManipRef, wrt)
  252. return Task.cont
  253. def lockCOA(self):
  254. self.fLockCOA = 1
  255. direct.message('COA Lock On')
  256. def unlockCOA(self):
  257. self.fLockCOA = 0
  258. direct.message('COA Lock Off')
  259. def toggleCOALock(self):
  260. self.fLockCOA = 1 - self.fLockCOA
  261. if self.fLockCOA:
  262. direct.message('COA Lock On')
  263. else:
  264. direct.message('COA Lock Off')
  265. def pickNextCOA(self):
  266. """ Cycle through collision handler entries """
  267. if self.cqEntries:
  268. # Get next entry and rotate entries
  269. entry = self.cqEntries[0]
  270. self.cqEntries = self.cqEntries[1:] + self.cqEntries[:1]
  271. # Filter out object's under camera
  272. node = entry.getIntoNode()
  273. nodePath = render.findPathTo(node)
  274. if direct.camera not in nodePath.getAncestry():
  275. # Compute hit point
  276. # KEH: use current display region ray
  277. # hitPt = direct.iRay.parentToHitPt(entry)
  278. hitPt = direct.drList.getCurrentDr().iRay.parentToHitPt(entry)
  279. # Move coa marker to new point
  280. self.updateCoa(hitPt, ref = self.coaMarkerRef)
  281. else:
  282. # Remove offending entry
  283. self.cqEntries = self.cqEntries[:-1]
  284. self.pickNextCOA()
  285. def computeCOA(self, node, hitPt, hitPtDist):
  286. coa = Point3(0)
  287. dr = direct.drList.getCurrentDr()
  288. if self.fLockCOA:
  289. # COA is locked, use existing point
  290. # Use existing point
  291. coa.assign(self.coaMarker.getPos(direct.camera))
  292. # Reset hit point count
  293. self.nullHitPointCount = 0
  294. elif node:
  295. # Got a hit point (hit point is in camera coordinates)
  296. # Set center of action
  297. coa.assign(hitPt)
  298. # Handle case of bad coa point (too close or too far)
  299. if ((hitPtDist < (1.1 * dr.near)) or
  300. (hitPtDist > dr.far)):
  301. # Just use existing point
  302. coa.assign(self.coaMarker.getPos(direct.camera))
  303. # Reset hit point count
  304. self.nullHitPointCount = 0
  305. else:
  306. # Increment null hit point count
  307. self.nullHitPointCount = (self.nullHitPointCount + 1) % 7
  308. # No COA lock and no intersection point
  309. # Use a point out in front of camera
  310. # Distance to point increases on multiple null hit points
  311. # MRM: Would be nice to be able to control this
  312. # At least display it
  313. dist = pow(10.0, self.nullHitPointCount)
  314. direct.message('COA Distance: ' + `dist`)
  315. coa.set(0,dist,0)
  316. # Compute COA Dist
  317. coaDist = Vec3(coa - ZERO_POINT).length()
  318. if coaDist < (1.1 * dr.near):
  319. coa.set(0,100,0)
  320. coaDist = 100
  321. # Update coa and marker
  322. self.updateCoa(coa, coaDist = coaDist)
  323. def updateCoa(self, ref2point, coaDist = None, ref = None):
  324. self.coa.set(ref2point[0], ref2point[1], ref2point[2])
  325. if not coaDist:
  326. coaDist = Vec3(self.coa - ZERO_POINT).length()
  327. # Place the marker in render space
  328. if ref == None:
  329. # KEH: use the current display region
  330. # ref = base.cam
  331. ref = direct.drList.getCurrentDr().cam
  332. self.coaMarker.setPos(ref, self.coa)
  333. pos = self.coaMarker.getPos()
  334. self.coaMarker.setPosHprScale(pos, Vec3(0), Vec3(1))
  335. # Resize it
  336. self.updateCoaMarkerSize(coaDist)
  337. # Record marker pos in render space
  338. self.coaMarkerPos.assign(self.coaMarker.getPos())
  339. def updateCoaMarkerSizeOnDeath(self, state):
  340. # Needed because tasks pass in state as first arg
  341. self.updateCoaMarkerSize()
  342. def updateCoaMarkerSize(self, coaDist = None):
  343. if not coaDist:
  344. coaDist = Vec3(self.coaMarker.getPos( direct.camera )).length()
  345. # KEH: use current display region for fov
  346. # sf = COA_MARKER_SF * coaDist * math.tan(deg2Rad(direct.dr.fovV))
  347. sf = COA_MARKER_SF * coaDist * math.tan(deg2Rad(direct.drList.getCurrentDr().fovV))
  348. if sf == 0.0:
  349. sf = 0.1
  350. self.coaMarker.setScale(sf)
  351. # Lerp color to fade out
  352. self.coaMarker.lerpColor(VBase4(1,0,0,1), VBase4(1,0,0,0), 3.0,
  353. task = 'fadeAway')
  354. def homeCam(self):
  355. # Record undo point
  356. direct.pushUndo([direct.camera])
  357. direct.camera.reparentTo(render)
  358. direct.camera.clearMat()
  359. # Resize coa marker
  360. self.updateCoaMarkerSize()
  361. def uprightCam(self):
  362. taskMgr.remove('manipulateCamera')
  363. # Record undo point
  364. direct.pushUndo([direct.camera])
  365. # Pitch camera till upright
  366. currH = direct.camera.getH()
  367. direct.camera.lerpHpr(currH, 0, 0,
  368. CAM_MOVE_DURATION,
  369. other = render,
  370. blendType = 'easeInOut',
  371. task = 'manipulateCamera')
  372. def orbitUprightCam(self):
  373. taskMgr.remove('manipulateCamera')
  374. # Record undo point
  375. direct.pushUndo([direct.camera])
  376. # Transform camera z axis to render space
  377. mCam2Render = Mat4()
  378. mCam2Render.assign(direct.camera.getMat(render))
  379. zAxis = Vec3(mCam2Render.xformVec(Z_AXIS))
  380. zAxis.normalize()
  381. # Compute rotation angle needed to upright cam
  382. orbitAngle = rad2Deg(math.acos(CLAMP(zAxis.dot(Z_AXIS),-1,1)))
  383. # Check angle
  384. if orbitAngle < 0.1:
  385. # Already upright
  386. return
  387. # Compute orthogonal axis of rotation
  388. rotAxis = Vec3(zAxis.cross(Z_AXIS))
  389. rotAxis.normalize()
  390. # Find angle between rot Axis and render X_AXIS
  391. rotAngle = rad2Deg(math.acos(CLAMP(rotAxis.dot(X_AXIS),-1,1)))
  392. # Determine sign or rotation angle
  393. if rotAxis[1] < 0:
  394. rotAngle *= -1
  395. # Position ref CS at coa marker with xaxis aligned with rot axis
  396. self.camManipRef.setPos(self.coaMarker, Vec3(0))
  397. self.camManipRef.setHpr(render, rotAngle, 0, 0)
  398. # Reparent Cam to ref Coordinate system
  399. parent = direct.camera.getParent()
  400. direct.camera.wrtReparentTo(self.camManipRef)
  401. # Rotate ref CS to final orientation
  402. t = self.camManipRef.lerpHpr(rotAngle, orbitAngle, 0,
  403. CAM_MOVE_DURATION,
  404. other = render,
  405. blendType = 'easeInOut',
  406. task = 'manipulateCamera')
  407. # Upon death, reparent Cam to parent
  408. t.parent = parent
  409. t.uponDeath = self.reparentCam
  410. def centerCam(self):
  411. self.centerCamIn(1.0)
  412. def centerCamNow(self):
  413. self.centerCamIn(0.)
  414. def centerCamIn(self, t):
  415. taskMgr.remove('manipulateCamera')
  416. # Record undo point
  417. direct.pushUndo([direct.camera])
  418. # Determine marker location
  419. markerToCam = self.coaMarker.getPos( direct.camera )
  420. dist = Vec3(markerToCam - ZERO_POINT).length()
  421. scaledCenterVec = Y_AXIS * dist
  422. delta = markerToCam - scaledCenterVec
  423. self.camManipRef.setPosHpr(direct.camera, Point3(0), Point3(0))
  424. t = direct.camera.lerpPos(Point3(delta),
  425. CAM_MOVE_DURATION,
  426. other = self.camManipRef,
  427. blendType = 'easeInOut',
  428. task = 'manipulateCamera')
  429. t.uponDeath = self.updateCoaMarkerSizeOnDeath
  430. def zoomCam(self, zoomFactor, t):
  431. taskMgr.remove('manipulateCamera')
  432. # Record undo point
  433. direct.pushUndo([direct.camera])
  434. # Find a point zoom factor times the current separation
  435. # of the widget and cam
  436. zoomPtToCam = self.coaMarker.getPos(direct.camera) * zoomFactor
  437. # Put a target nodePath there
  438. self.camManipRef.setPos(direct.camera, zoomPtToCam)
  439. # Move to that point
  440. t = direct.camera.lerpPos(ZERO_POINT,
  441. CAM_MOVE_DURATION,
  442. other = self.camManipRef,
  443. blendType = 'easeInOut',
  444. task = 'manipulateCamera')
  445. t.uponDeath = self.updateCoaMarkerSizeOnDeath
  446. def spawnMoveToView(self, view):
  447. # Kill any existing tasks
  448. taskMgr.remove('manipulateCamera')
  449. # Record undo point
  450. direct.pushUndo([direct.camera])
  451. # Calc hprOffset
  452. hprOffset = VBase3()
  453. if view == 8:
  454. # Try the next roll angle
  455. self.orthoViewRoll = (self.orthoViewRoll + 90.0) % 360.0
  456. # but use the last view
  457. view = self.lastView
  458. else:
  459. self.orthoViewRoll = 0.0
  460. # Adjust offset based on specified view
  461. if view == 1:
  462. hprOffset.set(180., 0., 0.)
  463. elif view == 2:
  464. hprOffset.set(0., 0., 0.)
  465. elif view == 3:
  466. hprOffset.set(90., 0., 0.)
  467. elif view == 4:
  468. hprOffset.set(-90., 0., 0.)
  469. elif view == 5:
  470. hprOffset.set(0., -90., 0.)
  471. elif view == 6:
  472. hprOffset.set(0., 90., 0.)
  473. elif view == 7:
  474. hprOffset.set(135., -35.264, 0.)
  475. # Position target
  476. self.camManipRef.setPosHpr(self.coaMarker, ZERO_VEC,
  477. hprOffset)
  478. # Scale center vec by current distance to target
  479. offsetDistance = Vec3(direct.camera.getPos(self.camManipRef) -
  480. ZERO_POINT).length()
  481. scaledCenterVec = Y_AXIS * (-1.0 * offsetDistance)
  482. # Now put the camManipRef at that point
  483. self.camManipRef.setPosHpr(self.camManipRef,
  484. scaledCenterVec,
  485. ZERO_VEC)
  486. # Record view for next time around
  487. self.lastView = view
  488. t = direct.camera.lerpPosHpr(ZERO_POINT,
  489. VBase3(0,0,self.orthoViewRoll),
  490. CAM_MOVE_DURATION,
  491. other = self.camManipRef,
  492. blendType = 'easeInOut',
  493. task = 'manipulateCamera')
  494. t.uponDeath = self.updateCoaMarkerSizeOnDeath
  495. def swingCamAboutWidget(self, degrees, t):
  496. # Remove existing camera manipulation task
  497. taskMgr.remove('manipulateCamera')
  498. # Record undo point
  499. direct.pushUndo([direct.camera])
  500. # Coincident with widget
  501. self.camManipRef.setPos(self.coaMarker, ZERO_POINT)
  502. # But aligned with render space
  503. self.camManipRef.setHpr(ZERO_POINT)
  504. parent = direct.camera.getParent()
  505. direct.camera.wrtReparentTo(self.camManipRef)
  506. manipTask = self.camManipRef.lerpHpr(VBase3(degrees,0,0),
  507. CAM_MOVE_DURATION,
  508. blendType = 'easeInOut',
  509. task = 'manipulateCamera')
  510. # Upon death, reparent Cam to parent
  511. manipTask.parent = parent
  512. manipTask.uponDeath = self.reparentCam
  513. def reparentCam(self, state):
  514. direct.camera.wrtReparentTo(state.parent)
  515. self.updateCoaMarkerSize()
  516. def fitOnWidget(self, nodePath = 'None Given'):
  517. # Fit the node on the screen
  518. # stop any ongoing tasks
  519. taskMgr.remove('manipulateCamera')
  520. # How big is the node?
  521. nodeScale = direct.widget.scalingNode.getScale(render)
  522. maxScale = max(nodeScale[0],nodeScale[1],nodeScale[2])
  523. maxDim = min(direct.dr.nearWidth, direct.dr.nearHeight)
  524. # At what distance does the object fill 30% of the screen?
  525. # Assuming radius of 1 on widget
  526. camY = direct.dr.near * (2.0 * maxScale)/(0.3 * maxDim)
  527. # What is the vector through the center of the screen?
  528. centerVec = Y_AXIS * camY
  529. # Where is the node relative to the viewpoint
  530. vWidget2Camera = direct.widget.getPos(direct.camera)
  531. # How far do you move the camera to be this distance from the node?
  532. deltaMove = vWidget2Camera - centerVec
  533. # Move a target there
  534. self.camManipRef.setPos(direct.camera, deltaMove)
  535. parent = direct.camera.getParent()
  536. direct.camera.wrtReparentTo(self.camManipRef)
  537. fitTask = direct.camera.lerpPos(Point3(0,0,0),
  538. CAM_MOVE_DURATION,
  539. blendType = 'easeInOut',
  540. task = 'manipulateCamera')
  541. # Upon death, reparent Cam to parent
  542. fitTask.parent = parent
  543. fitTask.uponDeath = self.reparentCam
  544. def moveToFit(self):
  545. # How bit is the active widget?
  546. widgetScale = direct.widget.scalingNode.getScale(render)
  547. maxScale = max(widgetScale[0], widgetScale[1], widgetScale[2])
  548. # At what distance does the widget fill 50% of the screen?
  549. camY = ((2 * direct.dr.near * (1.5 * maxScale)) /
  550. min(direct.dr.nearWidth, direct.dr.nearHeight))
  551. # Find a point this distance along the Y axis
  552. # MRM: This needs to be generalized to support non uniform frusta
  553. centerVec = Y_AXIS * camY
  554. # Before moving, record the relationship between the selected nodes
  555. # and the widget, so that this can be maintained
  556. direct.selected.getWrtAll()
  557. # Push state onto undo stack
  558. direct.pushUndo(direct.selected)
  559. # Remove the task to keep the widget attached to the object
  560. taskMgr.remove('followSelectedNodePath')
  561. # Spawn a task to keep the selected objects with the widget
  562. taskMgr.add(self.stickToWidgetTask, 'stickToWidget')
  563. # Spawn a task to move the widget
  564. t = direct.widget.lerpPos(Point3(centerVec),
  565. CAM_MOVE_DURATION,
  566. other = direct.camera,
  567. blendType = 'easeInOut',
  568. task = 'moveToFitTask')
  569. t.uponDeath = lambda state: taskMgr.remove('stickToWidget')
  570. def stickToWidgetTask(self, state):
  571. # Move the objects with the widget
  572. direct.selected.moveWrtWidgetAll()
  573. # Continue
  574. return Task.cont
  575. def enableMouseFly(self):
  576. # disable C++ fly interface
  577. base.disableMouse()
  578. # Enable events
  579. for event in self.actionEvents:
  580. self.accept(event[0], event[1], extraArgs = event[2:])
  581. # Show marker
  582. self.coaMarker.reparentTo(direct.group)
  583. def disableMouseFly(self):
  584. # Hide the marker
  585. self.coaMarker.reparentTo(hidden)
  586. # Ignore events
  587. for event in self.actionEvents:
  588. self.ignore(event[0])
  589. base.enableMouse()
  590. def removeManipulateCameraTask(self):
  591. taskMgr.remove('manipulateCamera')