DirectCameraControl.py 29 KB

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