DirectCameraControl.py 25 KB

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