DirectSession.py 34 KB

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