DirectSession.py 35 KB

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