DirectSession.py 37 KB

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