seSession.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991
  1. #################################################################
  2. # seSession.py
  3. # Originally from DirectSession.py
  4. # Altered by Yi-Hong Lin, [email protected], 2004
  5. #
  6. # We took out a lot of stuff we don't need in the sceneeditor.
  7. # This is also the main reason we didn't just inherite the original directSession.
  8. # Also, the way of selecting, renaming and some hot-key controls are changed.
  9. #
  10. #################################################################
  11. from direct.showbase.DirectObject import *
  12. from direct.directtools.DirectGlobals import *
  13. from direct.directtools.DirectUtil import*
  14. from direct.interval.IntervalGlobal import *
  15. from seCameraControl import *
  16. from seManipulation import *
  17. from seSelection import *
  18. from seGrid import *
  19. from seGeometry import *
  20. from direct.tkpanels import Placer
  21. from direct.tkwidgets import Slider
  22. from direct.gui import OnscreenText
  23. import types
  24. import string
  25. from direct.showbase import Loader
  26. class SeSession(DirectObject): ### Customized DirectSession
  27. def __init__(self):
  28. # Establish a global pointer to the direct object early on
  29. # so dependant classes can access it in their code
  30. __builtins__["SEditor"] = self
  31. # These come early since they are used later on
  32. self.group = render.attachNewNode('SEditor')
  33. self.font = TextNode.getDefaultFont()
  34. self.fEnabled = 0
  35. self.drList = DisplayRegionList()
  36. self.iRayList = map(lambda x: x.iRay, self.drList)
  37. self.dr = self.drList[0]
  38. self.camera = base.camera
  39. self.trueCamera = self.camera
  40. self.iRay = self.dr.iRay
  41. self.coaMode = COA_ORIGIN
  42. self.enableAutoCamera = True
  43. self.cameraControl = DirectCameraControl()
  44. self.manipulationControl = DirectManipulationControl()
  45. self.useObjectHandles()
  46. self.grid = DirectGrid()
  47. self.grid.disable()
  48. # Initialize the collection of selected nodePaths
  49. self.selected = SelectedNodePaths()
  50. # Ancestry of currently selected object
  51. self.ancestry = []
  52. self.ancestryIndex = 0
  53. self.activeParent = None
  54. self.selectedNPReadout = OnscreenText.OnscreenText(
  55. pos = (-1.0, -0.9), bg=Vec4(1,1,1,1),
  56. scale = 0.05, align = TextNode.ALeft,
  57. mayChange = 1, font = self.font)
  58. # Make sure readout is never lit or drawn in wireframe
  59. useDirectRenderStyle(self.selectedNPReadout)
  60. self.selectedNPReadout.reparentTo(hidden)
  61. self.activeParentReadout = OnscreenText.OnscreenText(
  62. pos = (-1.0, -0.975), bg=Vec4(1,1,1,1),
  63. scale = 0.05, align = TextNode.ALeft,
  64. mayChange = 1, font = self.font)
  65. # Make sure readout is never lit or drawn in wireframe
  66. useDirectRenderStyle(self.activeParentReadout)
  67. self.activeParentReadout.reparentTo(hidden)
  68. self.directMessageReadout = OnscreenText.OnscreenText(
  69. pos = (-1.0, 0.9), bg=Vec4(1,1,1,1),
  70. scale = 0.05, align = TextNode.ALeft,
  71. mayChange = 1, font = self.font)
  72. # Make sure readout is never lit or drawn in wireframe
  73. useDirectRenderStyle(self.directMessageReadout)
  74. self.directMessageReadout.reparentTo(hidden)
  75. self.fControl = 0
  76. self.fAlt = 0
  77. self.fShift = 0
  78. self.pos = VBase3()
  79. self.hpr = VBase3()
  80. self.scale = VBase3()
  81. self.hitPt = Point3(0.0)
  82. # Lists for managing undo/redo operations
  83. self.undoList = []
  84. self.redoList = []
  85. # One run through the context task to init everything
  86. self.drList.updateContext()
  87. for dr in self.drList:
  88. dr.camUpdate()
  89. self.modifierEvents = ['control', 'control-up',
  90. 'shift', 'shift-up',
  91. 'alt', 'alt-up',
  92. ]
  93. self.keyEvents = ['escape', 'delete', 'page_up', 'page_down',
  94. '[', '{', ']', '}',
  95. 'shift-a', 'b', 'control-f',
  96. 'l', 'shift-l', 'o', 'p', 'r',
  97. 'shift-r', 's', 't', 'v', 'w']
  98. self.mouseEvents = ['mouse1', 'mouse1-up',
  99. 'shift-mouse1', 'shift-mouse1-up',
  100. 'control-mouse1', 'control-mouse1-up',
  101. 'alt-mouse1', 'alt-mouse1-up',
  102. 'mouse2', 'mouse2-up',
  103. 'shift-mouse2', 'shift-mouse2-up',
  104. 'control-mouse2', 'control-mouse2-up',
  105. 'alt-mouse2', 'alt-mouse2-up',
  106. 'mouse3', 'mouse3-up',
  107. 'shift-mouse3', 'shift-mouse3-up',
  108. 'control-mouse3', 'control-mouse3-up',
  109. 'alt-mouse3', 'alt-mouse3-up',
  110. ]
  111. def enable(self):
  112. if self.fEnabled:
  113. return
  114. # Make sure old tasks are shut down
  115. self.disable()
  116. # Start all display region context tasks
  117. self.drList.spawnContextTask()
  118. # Turn on mouse Flying
  119. self.cameraControl.enableMouseFly()
  120. # Turn on object manipulation
  121. self.manipulationControl.enableManipulation()
  122. # Make sure list of selected items is reset
  123. self.selected.reset()
  124. # Accept appropriate hooks
  125. self.enableKeyEvents()
  126. self.enableModifierEvents()
  127. self.enableMouseEvents()
  128. # Set flag
  129. self.fEnabled = 1
  130. if self.enableAutoCamera:
  131. self.accept('DH_LoadingComplete', self.autoCameraMove)
  132. def disable(self):
  133. # Shut down all display region context tasks
  134. self.drList.removeContextTask()
  135. # Turn off camera fly
  136. self.cameraControl.disableMouseFly()
  137. # Turn off object manipulation
  138. self.manipulationControl.disableManipulation()
  139. self.disableKeyEvents()
  140. self.disableModifierEvents()
  141. self.disableMouseEvents()
  142. self.ignore('DH_LoadingComplete')
  143. # Kill tasks
  144. taskMgr.remove('flashNodePath')
  145. taskMgr.remove('hideDirectMessage')
  146. taskMgr.remove('hideDirectMessageLater')
  147. # Set flag
  148. self.fEnabled = 0
  149. def minimumConfiguration(self):
  150. # Remove context task
  151. self.drList.removeContextTask()
  152. # Turn off camera fly
  153. self.cameraControl.disableMouseFly()
  154. # Ignore keyboard and action events
  155. self.disableKeyEvents()
  156. self.disableActionEvents()
  157. # But let mouse events pass through
  158. self.enableMouseEvents()
  159. self.enableModifierEvents()
  160. def oobe(self):
  161. # If oobeMode was never set, set it to false and create the
  162. # structures we need to implement OOBE.
  163. try:
  164. self.oobeMode
  165. except:
  166. self.oobeMode = 0
  167. self.oobeCamera = hidden.attachNewNode('oobeCamera')
  168. self.oobeVis = loader.loadModelOnce('models/misc/camera')
  169. if self.oobeVis:
  170. self.oobeVis.node().setFinal(1)
  171. if self.oobeMode:
  172. # Position a target point to lerp the oobe camera to
  173. self.cameraControl.camManipRef.iPosHpr(self.trueCamera)
  174. t = self.oobeCamera.lerpPosHpr(
  175. Point3(0), Vec3(0), 2.0,
  176. other = self.cameraControl.camManipRef,
  177. task = 'manipulateCamera',
  178. blendType = 'easeInOut')
  179. # When move is done, switch to oobe mode
  180. t.uponDeath = self.endOOBE
  181. else:
  182. # Place camera marker at true camera location
  183. self.oobeVis.reparentTo(self.trueCamera)
  184. # Remove any transformation on the models arc
  185. self.oobeVis.clearMat()
  186. # Make oobeCamera be a sibling of wherever camera is now.
  187. cameraParent = self.camera.getParent()
  188. # Prepare oobe camera
  189. self.oobeCamera.reparentTo(cameraParent)
  190. self.oobeCamera.iPosHpr(self.trueCamera)
  191. # Put camera under new oobe camera
  192. base.cam.reparentTo(self.oobeCamera)
  193. # Position a target point to lerp the oobe camera to
  194. self.cameraControl.camManipRef.setPos(
  195. self.trueCamera, Vec3(-2,-20, 5))
  196. self.cameraControl.camManipRef.lookAt(self.trueCamera)
  197. t = self.oobeCamera.lerpPosHpr(
  198. Point3(0), Vec3(0), 2.0,
  199. other = self.cameraControl.camManipRef,
  200. task = 'manipulateCamera',
  201. blendType = 'easeInOut')
  202. # When move is done, switch to oobe mode
  203. t.uponDeath = self.beginOOBE
  204. def beginOOBE(self, state):
  205. # Make sure we've reached our final destination
  206. self.oobeCamera.iPosHpr(self.cameraControl.camManipRef)
  207. self.camera = self.oobeCamera
  208. self.oobeMode = 1
  209. def endOOBE(self, state):
  210. # Make sure we've reached our final destination
  211. self.oobeCamera.iPosHpr(self.trueCamera)
  212. # Disable OOBE mode.
  213. base.cam.reparentTo(self.trueCamera)
  214. self.camera = self.trueCamera
  215. # Get rid of ancillary node paths
  216. self.oobeVis.reparentTo(hidden)
  217. self.oobeCamera.reparentTo(hidden)
  218. self.oobeMode = 0
  219. def destroy(self):
  220. self.disable()
  221. def reset(self):
  222. self.enable()
  223. # EVENT FUNCTIONS
  224. def enableModifierEvents(self):
  225. for event in self.modifierEvents:
  226. self.accept(event, self.inputHandler, [event])
  227. def enableKeyEvents(self):
  228. for event in self.keyEvents:
  229. self.accept(event, self.inputHandler, [event])
  230. def enableMouseEvents(self):
  231. for event in self.mouseEvents:
  232. self.accept(event, self.inputHandler, [event])
  233. def disableModifierEvents(self):
  234. for event in self.modifierEvents:
  235. self.ignore(event)
  236. def disableKeyEvents(self):
  237. for event in self.keyEvents:
  238. self.ignore(event)
  239. def disableMouseEvents(self):
  240. for event in self.mouseEvents:
  241. self.ignore(event)
  242. def inputHandler(self, input):
  243. # Deal with keyboard and mouse input
  244. if input == 'mouse1-up':
  245. messenger.send('DIRECT-mouse1Up')
  246. if SEditor.widget.fActive:
  247. messenger.send('shift-f')
  248. elif input.find('mouse1') != -1:
  249. modifiers = self.getModifiers(input, 'mouse1')
  250. messenger.send('DIRECT-mouse1', sentArgs = [modifiers])
  251. elif input == 'mouse2-up':
  252. messenger.send('DIRECT-mouse2Up')
  253. if SEditor.widget.fActive:
  254. messenger.send('shift-f')
  255. elif input.find('mouse2') != -1:
  256. modifiers = self.getModifiers(input, 'mouse2')
  257. messenger.send('DIRECT-mouse2', sentArgs = [modifiers])
  258. elif input == 'mouse3-up':
  259. messenger.send('DIRECT-mouse3Up')
  260. if SEditor.widget.fActive:
  261. messenger.send('shift-f')
  262. elif input.find('mouse3') != -1:
  263. modifiers = self.getModifiers(input, 'mouse3')
  264. messenger.send('DIRECT-mouse3', sentArgs = [modifiers])
  265. elif input == 'shift':
  266. self.fShift = 1
  267. elif input == 'shift-up':
  268. self.fShift = 0
  269. elif input == 'control':
  270. self.fControl = 1
  271. elif input == 'control-up':
  272. self.fControl = 0
  273. elif input == 'alt':
  274. self.fAlt = 1
  275. elif input == 'alt-up':
  276. self.fAlt = 0
  277. elif input == 'page_up':
  278. self.upAncestry()
  279. elif input == 'page_down':
  280. self.downAncestry()
  281. elif input == 'escape':
  282. self.deselectAll()
  283. elif input == 'delete':
  284. taskMgr.remove('followSelectedNodePath')
  285. #self.removeAllSelected()
  286. messenger.send('SGE_Remove',[None])
  287. self.deselectAll()
  288. elif input == 'v':
  289. messenger.send('SEditor-ToggleWidgetVis')
  290. self.toggleWidgetVis()
  291. if SEditor.widget.fActive:
  292. messenger.send('shift-f')
  293. elif input == 'b':
  294. messenger.send('SEditor-ToggleBackface')
  295. base.toggleBackface()
  296. #elif input == 'control-f':
  297. # self.flash(last)
  298. elif input == 'shift-l':
  299. self.cameraControl.toggleCOALock()
  300. elif input == 'o':
  301. self.oobe()
  302. elif input == 'p':
  303. if self.selected.last:
  304. self.setActiveParent(self.selected.last)
  305. elif input == 'r':
  306. # Do wrt reparent
  307. if self.selected.last:
  308. self.reparent(self.selected.last, fWrt = 1)
  309. elif input == 'shift-r':
  310. # Do regular reparent
  311. if self.selected.last:
  312. self.reparent(self.selected.last)
  313. elif input == 's':
  314. if self.selected.last:
  315. self.select(self.selected.last)
  316. elif input == 't':
  317. messenger.send('SEditor-ToggleTexture')
  318. base.toggleTexture()
  319. elif input == 'shift-a':
  320. self.selected.toggleVisAll()
  321. elif input == 'w':
  322. messenger.send('SEditor-ToggleWireframe')
  323. base.toggleWireframe()
  324. elif (input == '[') or (input == '{'):
  325. self.undo()
  326. elif (input == ']') or (input == '}'):
  327. self.redo()
  328. def getModifiers(self, input, base):
  329. modifiers = DIRECT_NO_MOD
  330. modifierString = input[: input.find(base)]
  331. if modifierString.find('shift') != -1:
  332. modifiers |= DIRECT_SHIFT_MOD
  333. if modifierString.find('control') != -1:
  334. modifiers |= DIRECT_CONTROL_MOD
  335. if modifierString.find('alt') != -1:
  336. modifiers |= DIRECT_ALT_MOD
  337. return modifiers
  338. def gotShift(self, modifiers):
  339. return modifiers & DIRECT_SHIFT_MOD
  340. def gotControl(self, modifiers):
  341. return modifiers & DIRECT_CONTROL_MOD
  342. def gotAlt(self, modifiers):
  343. return modifiers & DIRECT_ALT_MOD
  344. def select(self, nodePath, fMultiSelect = 0, fResetAncestry = 1, callback=False):
  345. dnp = self.selected.select(nodePath, fMultiSelect)
  346. if dnp:
  347. messenger.send('DIRECT_preSelectNodePath', [dnp])
  348. if fResetAncestry:
  349. # Update ancestry
  350. self.ancestry = dnp.getAncestors()
  351. self.ancestry.reverse()
  352. self.ancestryIndex = 0
  353. # Update the selectedNPReadout
  354. self.selectedNPReadout.reparentTo(aspect2d)
  355. self.selectedNPReadout.setText(
  356. 'Selected:' + dnp.getName())
  357. # Show the manipulation widget
  358. self.widget.showWidget()
  359. # Update camera controls coa to this point
  360. # Coa2Camera = Coa2Dnp * Dnp2Camera
  361. mCoa2Camera = dnp.mCoa2Dnp * dnp.getMat(self.camera)
  362. row = mCoa2Camera.getRow(3)
  363. coa = Vec3(row[0], row[1], row[2])
  364. self.cameraControl.updateCoa(coa)
  365. # Adjust widgets size
  366. # This uses the additional scaling factor used to grow and
  367. # shrink the widget
  368. self.widget.setScalingFactor(dnp.getRadius())
  369. # Spawn task to have object handles follow the selected object
  370. taskMgr.remove('followSelectedNodePath')
  371. t = Task.Task(self.followSelectedNodePathTask)
  372. t.dnp = dnp
  373. taskMgr.add(t, 'followSelectedNodePath')
  374. # Send an message marking the event
  375. messenger.send('DIRECT_selectedNodePath', [dnp])
  376. if callback:
  377. messenger.send('se_selectedNodePath', [dnp, False])
  378. else:
  379. messenger.send('se_selectedNodePath', [dnp])
  380. self.upAncestry()
  381. if SEditor.widget.fActive:
  382. messenger.send('shift-f')
  383. def followSelectedNodePathTask(self, state):
  384. mCoa2Render = state.dnp.mCoa2Dnp * state.dnp.getMat(render)
  385. decomposeMatrix(mCoa2Render,
  386. self.scale,self.hpr,self.pos,
  387. CSDefault)
  388. self.widget.setPosHpr(self.pos,self.hpr)
  389. return Task.cont
  390. def deselect(self, nodePath):
  391. dnp = self.selected.deselect(nodePath)
  392. if dnp:
  393. # Hide the manipulation widget
  394. self.widget.hideWidget()
  395. self.selectedNPReadout.reparentTo(hidden)
  396. self.selectedNPReadout.setText(' ')
  397. taskMgr.remove('followSelectedNodePath')
  398. self.ancestry = []
  399. # Send an message marking the event
  400. messenger.send('DIRECT_deselectedNodePath', [dnp])
  401. def deselectAll(self):
  402. self.selected.deselectAll()
  403. # Hide the manipulation widget
  404. self.widget.hideWidget()
  405. self.selectedNPReadout.reparentTo(hidden)
  406. self.selectedNPReadout.setText(' ')
  407. taskMgr.remove('followSelectedNodePath')
  408. messenger.send('se_deselectedAll')
  409. def setActiveParent(self, nodePath = None):
  410. # Record new parent
  411. self.activeParent = nodePath
  412. # Update the activeParentReadout
  413. self.activeParentReadout.reparentTo(aspect2d)
  414. self.activeParentReadout.setText(
  415. 'Active Reparent Target:' + nodePath.getName())
  416. # Alert everyone else
  417. self.activeParentReadout.show()
  418. def reparent(self, nodePath = None, fWrt = 0):
  419. if (nodePath and self.activeParent and
  420. self.isNotCycle(nodePath, self.activeParent)):
  421. oldParent = nodePath.getParent()
  422. if fWrt:
  423. nodePath.wrtReparentTo(self.activeParent)
  424. else:
  425. nodePath.reparentTo(self.activeParent)
  426. # Alert everyone else
  427. messenger.send('DIRECT_reparent',
  428. [nodePath, oldParent, self.activeParent])
  429. messenger.send('SGE_Update Explorer',[render])
  430. self.activeParentReadout.hide()
  431. def isNotCycle(self, nodePath, parent):
  432. if nodePath.id() == parent.id():
  433. print 'DIRECT.reparent: Invalid parent'
  434. return 0
  435. elif parent.hasParent():
  436. return self.isNotCycle(nodePath, parent.getParent())
  437. else:
  438. return 1
  439. def fitOnNodePath(self, nodePath = 'None Given'):
  440. if nodePath == 'None Given':
  441. # If nothing specified, try selected node path
  442. nodePath = self.selected.last
  443. SEditor.select(nodePath)
  444. def fitTask(state, self = self):
  445. self.cameraControl.fitOnWidget()
  446. return Task.done
  447. taskMgr.doMethodLater(0.1, fitTask, 'manipulateCamera')
  448. def isolate(self, nodePath = 'None Given'):
  449. """ Show a node path and hide its siblings """
  450. # First kill the flashing task to avoid complications
  451. taskMgr.remove('flashNodePath')
  452. # Use currently selected node path if node selected
  453. if nodePath == 'None Given':
  454. nodePath = self.selected.last
  455. # Do we have a node path?
  456. if nodePath:
  457. # Yes, show everything in level
  458. self.showAllDescendants(nodePath.getParent())
  459. # Now hide all of this node path's siblings
  460. nodePath.hideSiblings()
  461. def toggleVis(self, nodePath = 'None Given'):
  462. """ Toggle visibility of node path """
  463. # First kill the flashing task to avoid complications
  464. taskMgr.remove('flashNodePath')
  465. if nodePath == 'None Given':
  466. # If nothing specified, try selected node path
  467. nodePath = self.selected.last
  468. if nodePath:
  469. # Now toggle node path's visibility state
  470. nodePath.toggleVis()
  471. def removeNodePath(self, nodePath = 'None Given'):
  472. if nodePath == 'None Given':
  473. # If nothing specified, try selected node path
  474. nodePath = self.selected.last
  475. if nodePath:
  476. nodePath.remove()
  477. def removeAllSelected(self):
  478. self.selected.removeAll()
  479. def showAllDescendants(self, nodePath = render):
  480. """ Show the level and its descendants """
  481. nodePath.showAllDescendants()
  482. nodePath.hideCS()
  483. def upAncestry(self):
  484. if self.ancestry:
  485. l = len(self.ancestry)
  486. i = self.ancestryIndex + 1
  487. if i < l:
  488. np = self.ancestry[i]
  489. name = np.getName()
  490. if i>0:
  491. type = self.ancestry[i-1].node().getType().getName()
  492. else:
  493. type = self.ancestry[0].node().getType().getName()
  494. ntype = np.node().getType().getName()
  495. if (name != 'render') and (name != 'renderTop')and(self.checkTypeNameForAncestry(type, ntype)):
  496. self.ancestryIndex = i
  497. self.select(np, 0, 0, True)
  498. def checkTypeNameForAncestry(self, type, nextType ):
  499. if (type=='ModelRoot'):
  500. if (nextType=='AmbientLight')or(nextType=='PointLight')or(nextType=='DirectionalLight')or(nextType=='Spotlight'):
  501. return True
  502. return False
  503. elif (type=='ModelNode'):
  504. if (nextType=='ModelNode'):
  505. return True
  506. return False
  507. elif (type=='CollisionNode'):
  508. return False
  509. elif (type=='ActorNode'):
  510. return False
  511. elif (type=='AmbientLight')or(type=='PointLight')or(type=='DirectionalLight')or(type=='Spotlight'):
  512. return False
  513. else:
  514. return True
  515. def downAncestry(self):
  516. if self.ancestry:
  517. l = len(self.ancestry)
  518. i = self.ancestryIndex - 1
  519. if i >= 0:
  520. np = self.ancestry[i]
  521. name = np.getName()
  522. if (name != 'render') and (name != 'renderTop'):
  523. self.ancestryIndex = i
  524. self.select(np, 0, 0, True)
  525. def getAndSetName(self, nodePath):
  526. """ Prompt user for new node path name """
  527. from tkSimpleDialog import askstring
  528. newName = askstring('Node Path: ' + nodePath.getName(),
  529. 'Enter new name:')
  530. if newName:
  531. nodePath.setName(newName)
  532. messenger.send('DIRECT_nodePathSetName', [nodePath, newName])
  533. # UNDO REDO FUNCTIONS
  534. def pushUndo(self, nodePathList, fResetRedo = 1):
  535. # Assemble group of changes
  536. undoGroup = []
  537. for nodePath in nodePathList:
  538. t = nodePath.getTransform()
  539. undoGroup.append([nodePath, t])
  540. # Now record group
  541. self.undoList.append(undoGroup)
  542. # Truncate list
  543. self.undoList = self.undoList[-25:]
  544. # Alert anyone who cares
  545. messenger.send('DIRECT_pushUndo')
  546. if fResetRedo and (nodePathList != []):
  547. self.redoList = []
  548. messenger.send('DIRECT_redoListEmpty')
  549. def popUndoGroup(self):
  550. # Get last item
  551. undoGroup = self.undoList[-1]
  552. # Strip last item off of undo list
  553. self.undoList = self.undoList[:-1]
  554. # Update state of undo button
  555. if not self.undoList:
  556. messenger.send('DIRECT_undoListEmpty')
  557. # Return last item
  558. return undoGroup
  559. def pushRedo(self, nodePathList):
  560. # Assemble group of changes
  561. redoGroup = []
  562. for nodePath in nodePathList:
  563. t = nodePath.getTransform()
  564. redoGroup.append([nodePath, t])
  565. # Now record redo group
  566. self.redoList.append(redoGroup)
  567. # Truncate list
  568. self.redoList = self.redoList[-25:]
  569. # Alert anyone who cares
  570. messenger.send('DIRECT_pushRedo')
  571. def popRedoGroup(self):
  572. # Get last item
  573. redoGroup = self.redoList[-1]
  574. # Strip last item off of redo list
  575. self.redoList = self.redoList[:-1]
  576. # Update state of redo button
  577. if not self.redoList:
  578. messenger.send('DIRECT_redoListEmpty')
  579. # Return last item
  580. return redoGroup
  581. def undo(self):
  582. if self.undoList:
  583. # Get last item off of redo list
  584. undoGroup = self.popUndoGroup()
  585. # Record redo information
  586. nodePathList = map(lambda x: x[0], undoGroup)
  587. self.pushRedo(nodePathList)
  588. # Now undo xform for group
  589. for pose in undoGroup:
  590. # Undo xform
  591. pose[0].setTransform(pose[1])
  592. # Alert anyone who cares
  593. messenger.send('DIRECT_undo')
  594. def redo(self):
  595. if self.redoList:
  596. # Get last item off of redo list
  597. redoGroup = self.popRedoGroup()
  598. # Record undo information
  599. nodePathList = map(lambda x: x[0], redoGroup)
  600. self.pushUndo(nodePathList, fResetRedo = 0)
  601. # Redo xform
  602. for pose in redoGroup:
  603. pose[0].setTransform(pose[1])
  604. # Alert anyone who cares
  605. messenger.send('DIRECT_redo')
  606. # UTILITY FUNCTIONS
  607. def message(self, text):
  608. taskMgr.remove('hideDirectMessage')
  609. taskMgr.remove('hideDirectMessageLater')
  610. self.directMessageReadout.reparentTo(aspect2d)
  611. self.directMessageReadout.setText(text)
  612. self.hideDirectMessageLater()
  613. def hideDirectMessageLater(self):
  614. taskMgr.doMethodLater(3.0, self.hideDirectMessage, 'hideDirectMessage')
  615. def hideDirectMessage(self, state):
  616. self.directMessageReadout.reparentTo(hidden)
  617. return Task.done
  618. def useObjectHandles(self):
  619. self.widget = self.manipulationControl.objectHandles
  620. self.widget.reparentTo(SEditor.group)
  621. def hideSelectedNPReadout(self):
  622. self.selectedNPReadout.reparentTo(hidden)
  623. def hideActiveParentReadout(self):
  624. self.activeParentReadout.reparentTo(hidden)
  625. def toggleWidgetVis(self):
  626. self.widget.toggleWidget()
  627. def setCOAMode(self, mode):
  628. self.coaMode = mode
  629. def isEnabled(self):
  630. return self.fEnabled
  631. def addUnpickable(self, item):
  632. for iRay in self.iRayList:
  633. iRay.addUnpickable(item)
  634. def removeUnpickable(self, item):
  635. for iRay in self.iRayList:
  636. iRay.removeUnpickable(item)
  637. def toggleAutoCamera(self):
  638. self.enableAutoCamera = (self.enableAutoCamera+1)%2
  639. if self.enableAutoCamera==1:
  640. self.accept('DH_LoadingComplete', self.autoCameraMove)
  641. else:
  642. self.ignore('DH_LoadingComplete')
  643. return
  644. def autoCameraMove(self, nodePath):
  645. time = 1
  646. node = DirectNodePath(nodePath)
  647. radius = node.getRadius()
  648. center = node.getCenter()
  649. node.dehighlight()
  650. posB = base.camera.getPos()
  651. hprB = base.camera.getHpr()
  652. posE = Point3((radius*-1.41)+center.getX(), (radius*-1.41)+center.getY(), (radius*1.41)+center.getZ())
  653. hprE = Point3(-45, -38, 0)
  654. print posB, hprB
  655. print posE, hprE
  656. posInterval1 = base.camera.posInterval(time, posE, bakeInStart = 1)
  657. posInterval2 = base.camera.posInterval(time, posB, bakeInStart = 1)
  658. hprInterval1 = base.camera.hprInterval(time, hprE, bakeInStart = 1)
  659. hprInterval2 = base.camera.hprInterval(time, hprB, bakeInStart = 1)
  660. parallel1 = Parallel(posInterval1, hprInterval1)
  661. parallel2 = Parallel(posInterval2, hprInterval2)
  662. Sequence(Wait(7), parallel1, Wait(1), parallel2).start()
  663. return
  664. class DisplayRegionContext(DirectObject):
  665. regionCount = 0
  666. def __init__(self, cam):
  667. self.cam = cam
  668. self.camNode = self.cam.node()
  669. self.camLens = self.camNode.getLens()
  670. # set lens change callback
  671. changeEvent = 'dr%d-change-event' % DisplayRegionContext.regionCount
  672. DisplayRegionContext.regionCount += 1
  673. self.camLens.setChangeEvent(changeEvent)
  674. self.accept(changeEvent, self.camUpdate)
  675. self.iRay = SelectionRay(self.cam)
  676. self.nearVec = Vec3(0)
  677. self.mouseX = 0.0
  678. self.mouseY = 0.0
  679. # A Camera node can have more than one display region
  680. # associated with it. Here I assume that there is only
  681. # one display region per camera, since we are defining a
  682. # display region on a per-camera basis. See note in
  683. # DisplayRegionList.__init__()
  684. try:
  685. self.dr = self.camNode.getDr(0)
  686. except:
  687. self.dr = self.camNode.getDisplayRegion(0)
  688. left = self.dr.getLeft()
  689. right = self.dr.getRight()
  690. bottom = self.dr.getBottom()
  691. top = self.dr.getTop()
  692. self.originX = left+right-1
  693. self.originY = top+bottom-1
  694. self.scaleX = 1.0/(right-left)
  695. self.scaleY = 1.0/(top-bottom)
  696. self.setOrientation()
  697. self.camUpdate()
  698. def __getitem__(self,key):
  699. return self.__dict__[key]
  700. def setOrientation(self):
  701. # MRM This assumes orientation is set on transform above cam
  702. hpr = self.cam.getHpr()
  703. if hpr[2] < 135 and hpr[2]>45 or hpr[2]>225 and hpr[2]<315:
  704. self.isSideways = 1
  705. elif hpr[2] > -135 and hpr[2] < -45 or hpr[2] < -225 and hpr[2] > -315:
  706. self.isSideways = 1
  707. else:
  708. self.isSideways = 0
  709. # The following take into consideration sideways displays
  710. def getHfov(self):
  711. if self.isSideways:
  712. return self.camLens.getVfov()
  713. else:
  714. return self.camLens.getHfov()
  715. def getVfov(self):
  716. if self.isSideways:
  717. return self.camLens.getHfov()
  718. else:
  719. return self.camLens.getVfov()
  720. def setHfov(self,hfov):
  721. if self.isSideways:
  722. self.camLens.setFov(self.camLens.getHfov(), hfov)
  723. else:
  724. self.camLens.setFov(hfov, self.camLens.getVfov())
  725. def setVfov(self,vfov):
  726. if self.isSideways:
  727. self.camLens.setFov(vfov, self.camLens.getVfov())
  728. else:
  729. self.camLens.setFov(self.camLens.getHfov(), vfov)
  730. def setFov(self,hfov,vfov):
  731. if self.isSideways:
  732. self.camLens.setFov(vfov, hfov)
  733. else:
  734. self.camLens.setFov(hfov, vfov)
  735. def getWidth(self):
  736. prop = base.win.getProperties()
  737. if prop.hasSize():
  738. return prop.getXSize()
  739. else:
  740. return 640
  741. def getHeight(self):
  742. prop = base.win.getProperties()
  743. if prop.hasSize():
  744. return prop.getYSize()
  745. else:
  746. return 480
  747. def camUpdate(self, lens = None):
  748. # Window Data
  749. self.near = self.camLens.getNear()
  750. self.far = self.camLens.getFar()
  751. self.fovH = self.camLens.getHfov()
  752. self.fovV = self.camLens.getVfov()
  753. self.nearWidth = math.tan(deg2Rad(self.fovH * 0.5)) * self.near * 2.0
  754. self.nearHeight = math.tan(deg2Rad(self.fovV * 0.5)) * self.near * 2.0
  755. self.left = -self.nearWidth * 0.5
  756. self.right = self.nearWidth * 0.5
  757. self.top = self.nearHeight * 0.5
  758. self.bottom = -self.nearHeight * 0.5
  759. def mouseUpdate(self):
  760. # Mouse Data
  761. # Last frame
  762. self.mouseLastX = self.mouseX
  763. self.mouseLastY = self.mouseY
  764. # Values for this frame
  765. # This ranges from -1 to 1
  766. if (base.mouseWatcherNode.hasMouse()):
  767. self.mouseX = base.mouseWatcherNode.getMouseX()
  768. self.mouseY = base.mouseWatcherNode.getMouseY()
  769. self.mouseX = (self.mouseX-self.originX)*self.scaleX
  770. self.mouseY = (self.mouseY-self.originY)*self.scaleY
  771. # Delta percent of window the mouse moved
  772. self.mouseDeltaX = self.mouseX - self.mouseLastX
  773. self.mouseDeltaY = self.mouseY - self.mouseLastY
  774. self.nearVec.set((self.nearWidth*0.5) * self.mouseX,
  775. self.near,
  776. (self.nearHeight*0.5) * self.mouseY)
  777. class DisplayRegionList(DirectObject):
  778. def __init__(self):
  779. self.displayRegionList = []
  780. i = 0
  781. # Things are funky if we are oobe
  782. if (hasattr(base, 'oobeMode') and base.oobeMode):
  783. # assume we only have one cam at this point
  784. drc = DisplayRegionContext(base.cam)
  785. self.displayRegionList.append(drc)
  786. else:
  787. # MRM: Doesn't properly handle multiple camera groups anymore
  788. # Assumes everything is under main camera
  789. # This is following the old way of setting up
  790. # display regions. A display region is set up for
  791. # each camera node in the scene graph. This was done
  792. # so that only display regions in the scene graph are
  793. # considered. The right way to do this is to set up
  794. # a display region for each real display region, and then
  795. # keep track of which are currently active (e.g. use a flag)
  796. # processing only them.
  797. for camIndex in range(len(base.camList)):
  798. cam = base.camList[camIndex]
  799. if cam.getName()=='<noname>':
  800. cam.setName('Camera%d' % camIndex)
  801. drc = DisplayRegionContext(cam)
  802. self.displayRegionList.append(drc)
  803. self.accept("DIRECT-mouse1",self.mouseUpdate)
  804. self.accept("DIRECT-mouse2",self.mouseUpdate)
  805. self.accept("DIRECT-mouse3",self.mouseUpdate)
  806. self.accept("DIRECT-mouse1Up",self.mouseUpdate)
  807. self.accept("DIRECT-mouse2Up",self.mouseUpdate)
  808. self.accept("DIRECT-mouse3Up",self.mouseUpdate)
  809. def __getitem__(self, index):
  810. return self.displayRegionList[index]
  811. def __len__(self):
  812. return len(self.displayRegionList)
  813. def updateContext(self):
  814. self.contextTask(None)
  815. def setNearFar(self, near, far):
  816. for dr in self.displayRegionList:
  817. dr.camLens.setNearFar(near, far)
  818. def setNear(self, near):
  819. for dr in self.displayRegionList:
  820. dr.camLens.setNear(near)
  821. def setFar(self, far):
  822. for dr in self.displayRegionList:
  823. dr.camLens.setFar(far)
  824. def setFov(self, hfov, vfov):
  825. for dr in self.displayRegionList:
  826. dr.setFov(hfov, vfov)
  827. def setHfov(self, fov):
  828. for dr in self.displayRegionList:
  829. dr.setHfov(fov)
  830. def setVfov(self, fov):
  831. for dr in self.displayRegionList:
  832. dr.setVfov(fov)
  833. def mouseUpdate(self, modifiers = DIRECT_NO_MOD):
  834. for dr in self.displayRegionList:
  835. dr.mouseUpdate()
  836. SEditor.dr = self.getCurrentDr()
  837. def getCurrentDr(self):
  838. for dr in self.displayRegionList:
  839. if (dr.mouseX >= -1.0 and dr.mouseX <= 1.0 and
  840. dr.mouseY >= -1.0 and dr.mouseY <= 1.0):
  841. return dr
  842. return self.displayRegionList[0]
  843. def start(self):
  844. # First shutdown any existing task
  845. self.stop()
  846. # Start a new context task
  847. self.spawnContextTask()
  848. def stop(self):
  849. # Kill the existing context task
  850. taskMgr.remove('DIRECTContextTask')
  851. def spawnContextTask(self):
  852. taskMgr.add(self.contextTask, 'DIRECTContextTask')
  853. def removeContextTask(self):
  854. taskMgr.remove('DIRECTContextTask')
  855. def contextTask(self, state):
  856. # Window Data
  857. self.mouseUpdate()
  858. # hack to test movement
  859. return Task.cont
  860. # Create one
  861. #__builtins__['direct'] = base.direct = DirectSession()