DirectCameraControl.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  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. base.direct.camera.setPosHpr(base.direct.camera,
  224. moveDir[0],
  225. moveDir[1],
  226. moveDir[2],
  227. (0.5 * base.direct.dr.mouseDeltaX *
  228. base.direct.dr.fovH),
  229. 0.0, 0.0)
  230. if (self.lockRoll == True):
  231. # flatten roll
  232. base.direct.camera.setR(0)
  233. return Task.cont
  234. def HPPanTask(self, state):
  235. base.direct.camera.setHpr(base.direct.camera,
  236. (0.5 * base.direct.dr.mouseDeltaX *
  237. base.direct.dr.fovH),
  238. (-0.5 * base.direct.dr.mouseDeltaY *
  239. base.direct.dr.fovV),
  240. 0.0)
  241. return Task.cont
  242. def spawnMouseRotateTask(self):
  243. # Kill any existing tasks
  244. taskMgr.remove('manipulateCamera')
  245. # Set at markers position in render coordinates
  246. self.camManipRef.setPos(self.coaMarkerPos)
  247. self.camManipRef.setHpr(base.direct.camera, ZERO_POINT)
  248. t = Task.Task(self.mouseRotateTask)
  249. if abs(base.direct.dr.mouseX) > 0.9:
  250. t.constrainedDir = 'y'
  251. else:
  252. t.constrainedDir = 'x'
  253. taskMgr.add(t, 'manipulateCamera')
  254. def mouseRotateTask(self, state):
  255. # If the cam is orthogonal, don't rotate.
  256. if (hasattr(base.direct.camera.node(), "getLens") and
  257. base.direct.camera.node().getLens().__class__.__name__ == "OrthographicLens"):
  258. return
  259. # If moving outside of center, ignore motion perpendicular to edge
  260. if ((state.constrainedDir == 'y') and (abs(base.direct.dr.mouseX) > 0.9)):
  261. deltaX = 0
  262. deltaY = base.direct.dr.mouseDeltaY
  263. elif ((state.constrainedDir == 'x') and (abs(base.direct.dr.mouseY) > 0.9)):
  264. deltaX = base.direct.dr.mouseDeltaX
  265. deltaY = 0
  266. else:
  267. deltaX = base.direct.dr.mouseDeltaX
  268. deltaY = base.direct.dr.mouseDeltaY
  269. if base.direct.fShift:
  270. base.direct.camera.setHpr(base.direct.camera,
  271. (deltaX * base.direct.dr.fovH),
  272. (-deltaY * base.direct.dr.fovV),
  273. 0.0)
  274. if (self.lockRoll == True):
  275. # flatten roll
  276. base.direct.camera.setR(0)
  277. self.camManipRef.setPos(self.coaMarkerPos)
  278. self.camManipRef.setHpr(base.direct.camera, ZERO_POINT)
  279. else:
  280. wrt = base.direct.camera.getTransform(self.camManipRef)
  281. self.camManipRef.setHpr(self.camManipRef,
  282. (-1 * deltaX * 180.0),
  283. (deltaY * 180.0),
  284. 0.0)
  285. if (self.lockRoll == True):
  286. # flatten roll
  287. self.camManipRef.setR(0)
  288. base.direct.camera.setTransform(self.camManipRef, wrt)
  289. return Task.cont
  290. def spawnMouseRollTask(self):
  291. # Kill any existing tasks
  292. taskMgr.remove('manipulateCamera')
  293. # Set at markers position in render coordinates
  294. self.camManipRef.setPos(self.coaMarkerPos)
  295. self.camManipRef.setHpr(base.direct.camera, ZERO_POINT)
  296. t = Task.Task(self.mouseRollTask)
  297. t.coaCenter = getScreenXY(self.coaMarker)
  298. t.lastAngle = getCrankAngle(t.coaCenter)
  299. # Store the camera/manipRef offset transform
  300. t.wrt = base.direct.camera.getTransform(self.camManipRef)
  301. taskMgr.add(t, 'manipulateCamera')
  302. def mouseRollTask(self, state):
  303. wrt = state.wrt
  304. angle = getCrankAngle(state.coaCenter)
  305. deltaAngle = angle - state.lastAngle
  306. state.lastAngle = angle
  307. self.camManipRef.setHpr(self.camManipRef, 0, 0, deltaAngle)
  308. if (self.lockRoll == True):
  309. # flatten roll
  310. self.camManipRef.setR(0)
  311. base.direct.camera.setTransform(self.camManipRef, wrt)
  312. return Task.cont
  313. def lockCOA(self):
  314. self.fLockCOA = 1
  315. base.direct.message('COA Lock On')
  316. def unlockCOA(self):
  317. self.fLockCOA = 0
  318. base.direct.message('COA Lock Off')
  319. def toggleCOALock(self):
  320. self.fLockCOA = 1 - self.fLockCOA
  321. if self.fLockCOA:
  322. base.direct.message('COA Lock On')
  323. else:
  324. base.direct.message('COA Lock Off')
  325. def pickNextCOA(self):
  326. """ Cycle through collision handler entries """
  327. if self.cqEntries:
  328. # Get next entry and rotate entries
  329. entry = self.cqEntries[0]
  330. self.cqEntries = self.cqEntries[1:] + self.cqEntries[:1]
  331. # Filter out object's under camera
  332. nodePath = entry.getIntoNodePath()
  333. if base.direct.camera not in nodePath.getAncestry():
  334. # Compute new hit point
  335. hitPt = entry.getSurfacePoint(entry.getFromNodePath())
  336. # Move coa marker to new point
  337. self.updateCoa(hitPt, ref = self.coaMarkerRef)
  338. else:
  339. # Remove offending entry
  340. self.cqEntries = self.cqEntries[:-1]
  341. self.pickNextCOA()
  342. def computeCOA(self, entry):
  343. coa = Point3(0)
  344. dr = base.direct.drList.getCurrentDr()
  345. if self.fLockCOA:
  346. # COA is locked, use existing point
  347. # Use existing point
  348. coa.assign(self.coaMarker.getPos(base.direct.camera))
  349. # Reset hit point count
  350. self.nullHitPointCount = 0
  351. elif entry:
  352. # Got a hit point (hit point is in camera coordinates)
  353. # Set center of action
  354. hitPt = entry.getSurfacePoint(entry.getFromNodePath())
  355. hitPtDist = Vec3(hitPt).length()
  356. coa.assign(hitPt)
  357. # Handle case of bad coa point (too close or too far)
  358. if ((hitPtDist < (1.1 * dr.near)) or
  359. (hitPtDist > dr.far)):
  360. # Just use existing point
  361. coa.assign(self.coaMarker.getPos(base.direct.camera))
  362. # Reset hit point count
  363. self.nullHitPointCount = 0
  364. else:
  365. # Increment null hit point count
  366. self.nullHitPointCount = (self.nullHitPointCount + 1) % 7
  367. # No COA lock and no intersection point
  368. # Use a point out in front of camera
  369. # Distance to point increases on multiple null hit points
  370. # MRM: Would be nice to be able to control this
  371. # At least display it
  372. dist = pow(10.0, self.nullHitPointCount)
  373. base.direct.message('COA Distance: ' + `dist`)
  374. coa.set(0, dist, 0)
  375. # Compute COA Dist
  376. coaDist = Vec3(coa - ZERO_POINT).length()
  377. if coaDist < (1.1 * dr.near):
  378. coa.set(0, 100, 0)
  379. coaDist = 100
  380. # Update coa and marker
  381. self.updateCoa(coa, coaDist = coaDist)
  382. def updateCoa(self, ref2point, coaDist = None, ref = None):
  383. self.coa.set(ref2point[0], ref2point[1], ref2point[2])
  384. if not coaDist:
  385. coaDist = Vec3(self.coa - ZERO_POINT).length()
  386. # Place the marker in render space
  387. if ref == None:
  388. # KEH: use the current display region
  389. # ref = base.cam
  390. ref = base.direct.drList.getCurrentDr().cam
  391. self.coaMarker.setPos(ref, self.coa)
  392. pos = self.coaMarker.getPos()
  393. self.coaMarker.setPosHprScale(pos, Vec3(0), Vec3(1))
  394. # Resize it
  395. self.updateCoaMarkerSize(coaDist)
  396. # Record marker pos in render space
  397. self.coaMarkerPos.assign(self.coaMarker.getPos())
  398. def updateCoaMarkerSizeOnDeath(self, state):
  399. # Needed because tasks pass in state as first arg
  400. self.updateCoaMarkerSize()
  401. def updateCoaMarkerSize(self, coaDist = None):
  402. if not coaDist:
  403. coaDist = Vec3(self.coaMarker.getPos(base.direct.camera)).length()
  404. # Nominal size based on default 30 degree vertical FOV
  405. # Need to adjust size based on distance and current FOV
  406. sf = COA_MARKER_SF * coaDist * (base.direct.drList.getCurrentDr().fovV/30.0)
  407. if sf == 0.0:
  408. sf = 0.1
  409. self.coaMarker.setScale(sf)
  410. # Lerp color to fade out
  411. if self.coaMarkerColorIval:
  412. self.coaMarkerColorIval.finish()
  413. self.coaMarkerColorIval = Sequence(
  414. Func(self.coaMarker.unstash),
  415. self.coaMarker.colorInterval(1.5, Vec4(1, 0, 0, 0),
  416. startColor = Vec4(1, 0, 0, 1),
  417. blendType = 'easeInOut'),
  418. Func(self.coaMarker.stash)
  419. )
  420. self.coaMarkerColorIval.start()
  421. def homeCam(self):
  422. # Record undo point
  423. base.direct.pushUndo([base.direct.camera])
  424. base.direct.camera.reparentTo(render)
  425. base.direct.camera.clearMat()
  426. # Resize coa marker
  427. self.updateCoaMarkerSize()
  428. def uprightCam(self):
  429. taskMgr.remove('manipulateCamera')
  430. # Record undo point
  431. base.direct.pushUndo([base.direct.camera])
  432. # Pitch camera till upright
  433. currH = base.direct.camera.getH()
  434. base.direct.camera.lerpHpr(currH, 0, 0,
  435. CAM_MOVE_DURATION,
  436. other = render,
  437. blendType = 'easeInOut',
  438. task = 'manipulateCamera')
  439. def orbitUprightCam(self):
  440. taskMgr.remove('manipulateCamera')
  441. # Record undo point
  442. base.direct.pushUndo([base.direct.camera])
  443. # Transform camera z axis to render space
  444. mCam2Render = Mat4(Mat4.identMat()) # [gjeon] fixed to give required argument
  445. mCam2Render.assign(base.direct.camera.getMat(render))
  446. zAxis = Vec3(mCam2Render.xformVec(Z_AXIS))
  447. zAxis.normalize()
  448. # Compute rotation angle needed to upright cam
  449. orbitAngle = rad2Deg(math.acos(CLAMP(zAxis.dot(Z_AXIS), -1, 1)))
  450. # Check angle
  451. if orbitAngle < 0.1:
  452. # Already upright
  453. return
  454. # Compute orthogonal axis of rotation
  455. rotAxis = Vec3(zAxis.cross(Z_AXIS))
  456. rotAxis.normalize()
  457. # Find angle between rot Axis and render X_AXIS
  458. rotAngle = rad2Deg(math.acos(CLAMP(rotAxis.dot(X_AXIS), -1, 1)))
  459. # Determine sign or rotation angle
  460. if rotAxis[1] < 0:
  461. rotAngle *= -1
  462. # Position ref CS at coa marker with xaxis aligned with rot axis
  463. self.camManipRef.setPos(self.coaMarker, Vec3(0))
  464. self.camManipRef.setHpr(render, rotAngle, 0, 0)
  465. # Reparent Cam to ref Coordinate system
  466. parent = base.direct.camera.getParent()
  467. base.direct.camera.wrtReparentTo(self.camManipRef)
  468. # Rotate ref CS to final orientation
  469. t = self.camManipRef.lerpHpr(rotAngle, orbitAngle, 0,
  470. CAM_MOVE_DURATION,
  471. other = render,
  472. blendType = 'easeInOut',
  473. task = 'manipulateCamera')
  474. # Upon death, reparent Cam to parent
  475. t.parent = parent
  476. t.uponDeath = self.reparentCam
  477. def centerCam(self):
  478. self.centerCamIn(1.0)
  479. def centerCamNow(self):
  480. self.centerCamIn(0.)
  481. def centerCamIn(self, t):
  482. taskMgr.remove('manipulateCamera')
  483. # Record undo point
  484. base.direct.pushUndo([base.direct.camera])
  485. # Determine marker location
  486. markerToCam = self.coaMarker.getPos(base.direct.camera)
  487. dist = Vec3(markerToCam - ZERO_POINT).length()
  488. scaledCenterVec = Y_AXIS * dist
  489. delta = markerToCam - scaledCenterVec
  490. self.camManipRef.setPosHpr(base.direct.camera, Point3(0), Point3(0))
  491. t = base.direct.camera.lerpPos(Point3(delta),
  492. CAM_MOVE_DURATION,
  493. other = self.camManipRef,
  494. blendType = 'easeInOut',
  495. task = 'manipulateCamera')
  496. t.uponDeath = self.updateCoaMarkerSizeOnDeath
  497. def zoomCam(self, zoomFactor, t):
  498. taskMgr.remove('manipulateCamera')
  499. # Record undo point
  500. base.direct.pushUndo([base.direct.camera])
  501. # Find a point zoom factor times the current separation
  502. # of the widget and cam
  503. zoomPtToCam = self.coaMarker.getPos(base.direct.camera) * zoomFactor
  504. # Put a target nodePath there
  505. self.camManipRef.setPos(base.direct.camera, zoomPtToCam)
  506. # Move to that point
  507. t = base.direct.camera.lerpPos(ZERO_POINT,
  508. CAM_MOVE_DURATION,
  509. other = self.camManipRef,
  510. blendType = 'easeInOut',
  511. task = 'manipulateCamera')
  512. t.uponDeath = self.updateCoaMarkerSizeOnDeath
  513. def spawnMoveToView(self, view):
  514. # Kill any existing tasks
  515. taskMgr.remove('manipulateCamera')
  516. # Record undo point
  517. base.direct.pushUndo([base.direct.camera])
  518. # Calc hprOffset
  519. hprOffset = VBase3()
  520. if view == 8:
  521. # Try the next roll angle
  522. self.orthoViewRoll = (self.orthoViewRoll + 90.0) % 360.0
  523. # but use the last view
  524. view = self.lastView
  525. else:
  526. self.orthoViewRoll = 0.0
  527. # Adjust offset based on specified view
  528. if view == 1:
  529. hprOffset.set(180., 0., 0.)
  530. elif view == 2:
  531. hprOffset.set(0., 0., 0.)
  532. elif view == 3:
  533. hprOffset.set(90., 0., 0.)
  534. elif view == 4:
  535. hprOffset.set(-90., 0., 0.)
  536. elif view == 5:
  537. hprOffset.set(0., -90., 0.)
  538. elif view == 6:
  539. hprOffset.set(0., 90., 0.)
  540. elif view == 7:
  541. hprOffset.set(135., -35.264, 0.)
  542. # Position target
  543. self.camManipRef.setPosHpr(self.coaMarker, ZERO_VEC,
  544. hprOffset)
  545. # Scale center vec by current distance to target
  546. offsetDistance = Vec3(base.direct.camera.getPos(self.camManipRef) -
  547. ZERO_POINT).length()
  548. scaledCenterVec = Y_AXIS * (-1.0 * offsetDistance)
  549. # Now put the camManipRef at that point
  550. self.camManipRef.setPosHpr(self.camManipRef,
  551. scaledCenterVec,
  552. ZERO_VEC)
  553. # Record view for next time around
  554. self.lastView = view
  555. t = base.direct.camera.lerpPosHpr(ZERO_POINT,
  556. VBase3(0, 0, self.orthoViewRoll),
  557. CAM_MOVE_DURATION,
  558. other = self.camManipRef,
  559. blendType = 'easeInOut',
  560. task = 'manipulateCamera')
  561. t.uponDeath = self.updateCoaMarkerSizeOnDeath
  562. def swingCamAboutWidget(self, degrees, t):
  563. # Remove existing camera manipulation task
  564. taskMgr.remove('manipulateCamera')
  565. # Record undo point
  566. base.direct.pushUndo([base.direct.camera])
  567. # Coincident with widget
  568. self.camManipRef.setPos(self.coaMarker, ZERO_POINT)
  569. # But aligned with render space
  570. self.camManipRef.setHpr(ZERO_POINT)
  571. parent = base.direct.camera.getParent()
  572. base.direct.camera.wrtReparentTo(self.camManipRef)
  573. manipTask = self.camManipRef.lerpHpr(VBase3(degrees, 0, 0),
  574. CAM_MOVE_DURATION,
  575. blendType = 'easeInOut',
  576. task = 'manipulateCamera')
  577. # Upon death, reparent Cam to parent
  578. manipTask.parent = parent
  579. manipTask.uponDeath = self.reparentCam
  580. def reparentCam(self, state):
  581. base.direct.camera.wrtReparentTo(state.parent)
  582. self.updateCoaMarkerSize()
  583. def fitOnWidget(self, nodePath = 'None Given'):
  584. # Fit the node on the screen
  585. # stop any ongoing tasks
  586. taskMgr.remove('manipulateCamera')
  587. # How big is the node?
  588. nodeScale = base.direct.widget.scalingNode.getScale(render)
  589. maxScale = max(nodeScale[0], nodeScale[1], nodeScale[2])
  590. maxDim = min(base.direct.dr.nearWidth, base.direct.dr.nearHeight)
  591. # At what distance does the object fill 30% of the screen?
  592. # Assuming radius of 1 on widget
  593. camY = base.direct.dr.near * (2.0 * maxScale)/(0.3 * maxDim)
  594. # What is the vector through the center of the screen?
  595. centerVec = Y_AXIS * camY
  596. # Where is the node relative to the viewpoint
  597. vWidget2Camera = base.direct.widget.getPos(base.direct.camera)
  598. # How far do you move the camera to be this distance from the node?
  599. deltaMove = vWidget2Camera - centerVec
  600. # Move a target there
  601. self.camManipRef.setPos(base.direct.camera, deltaMove)
  602. parent = base.direct.camera.getParent()
  603. base.direct.camera.wrtReparentTo(self.camManipRef)
  604. fitTask = base.direct.camera.lerpPos(Point3(0, 0, 0),
  605. CAM_MOVE_DURATION,
  606. blendType = 'easeInOut',
  607. task = 'manipulateCamera')
  608. # Upon death, reparent Cam to parent
  609. fitTask.parent = parent
  610. fitTask.uponDeath = self.reparentCam
  611. def moveToFit(self):
  612. # How bit is the active widget?
  613. widgetScale = base.direct.widget.scalingNode.getScale(render)
  614. maxScale = max(widgetScale[0], widgetScale[1], widgetScale[2])
  615. # At what distance does the widget fill 50% of the screen?
  616. camY = ((2 * base.direct.dr.near * (1.5 * maxScale)) /
  617. min(base.direct.dr.nearWidth, base.direct.dr.nearHeight))
  618. # Find a point this distance along the Y axis
  619. # MRM: This needs to be generalized to support non uniform frusta
  620. centerVec = Y_AXIS * camY
  621. # Before moving, record the relationship between the selected nodes
  622. # and the widget, so that this can be maintained
  623. base.direct.selected.getWrtAll()
  624. # Push state onto undo stack
  625. base.direct.pushUndo(base.direct.selected)
  626. # Remove the task to keep the widget attached to the object
  627. taskMgr.remove('followSelectedNodePath')
  628. # Spawn a task to keep the selected objects with the widget
  629. taskMgr.add(self.stickToWidgetTask, 'stickToWidget')
  630. # Spawn a task to move the widget
  631. t = base.direct.widget.lerpPos(Point3(centerVec),
  632. CAM_MOVE_DURATION,
  633. other = base.direct.camera,
  634. blendType = 'easeInOut',
  635. task = 'moveToFitTask')
  636. t.uponDeath = lambda state: taskMgr.remove('stickToWidget')
  637. def stickToWidgetTask(self, state):
  638. # Move the objects with the widget
  639. base.direct.selected.moveWrtWidgetAll()
  640. # Continue
  641. return Task.cont
  642. def enableMouseFly(self, fKeyEvents = 1):
  643. # disable C++ fly interface
  644. base.disableMouse()
  645. # Enable events
  646. for event in self.actionEvents:
  647. self.accept(event[0], event[1], extraArgs = event[2:])
  648. if fKeyEvents:
  649. for event in self.keyEvents:
  650. self.accept(event[0], event[1], extraArgs = event[2:])
  651. # Show marker
  652. self.coaMarker.reparentTo(base.direct.group)
  653. def disableMouseFly(self):
  654. # Hide the marker
  655. self.coaMarker.reparentTo(hidden)
  656. # Ignore events
  657. for event in self.actionEvents:
  658. self.ignore(event[0])
  659. for event in self.keyEvents:
  660. self.ignore(event[0])
  661. # Kill tasks
  662. self.removeManipulateCameraTask()
  663. taskMgr.remove('stickToWidget')
  664. base.enableMouse()
  665. def removeManipulateCameraTask(self):
  666. taskMgr.remove('manipulateCamera')