DirectCameraControl.py 30 KB

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