DirectCameraControl.py 26 KB

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