DirectCameraControl.py 37 KB

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