seCameraControl.py 27 KB

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