sePlacer.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. """ DIRECT Nine DoF Manipulation Panel """
  2. from direct.showbase.DirectObject import DirectObject
  3. from direct.directtools.DirectGlobals import *
  4. from direct.tkwidgets.AppShell import AppShell
  5. from direct.tkwidgets.Dial import AngleDial
  6. from direct.tkwidgets.Floater import Floater
  7. from Tkinter import Button, Menubutton, Menu, StringVar
  8. from pandac.PandaModules import *
  9. import Tkinter, Pmw
  10. """
  11. TODO:
  12. Task to monitor pose
  13. """
  14. class Placer(AppShell):
  15. # Override class variables here
  16. appname = 'Placer Panel'
  17. frameWidth = 625
  18. frameHeight = 215
  19. usecommandarea = 0
  20. usestatusarea = 0
  21. def __init__(self, parent = None, **kw):
  22. INITOPT = Pmw.INITOPT
  23. optiondefs = (
  24. ('title', self.appname, None),
  25. ('nodePath', SEditor.camera, None),
  26. )
  27. self.defineoptions(kw, optiondefs)
  28. # Call superclass initialization function
  29. AppShell.__init__(self)
  30. self.initialiseoptions(Placer)
  31. # Accept the message from sceneEditor to update the information about the target nodePath
  32. self.accept('placerUpdate', self.updatePlacer)
  33. def appInit(self):
  34. # Initialize state
  35. self.tempCS = SEditor.group.attachNewNode('placerTempCS')
  36. self.orbitFromCS = SEditor.group.attachNewNode(
  37. 'placerOrbitFromCS')
  38. self.orbitToCS = SEditor.group.attachNewNode('placerOrbitToCS')
  39. self.refCS = self.tempCS
  40. # Dictionary keeping track of all node paths manipulated so far
  41. self.nodePathDict = {}
  42. self.nodePathDict['camera'] = SEditor.camera
  43. self.nodePathDict['widget'] = SEditor.widget
  44. self.nodePathNames = ['camera', 'widget', 'selected']
  45. self.refNodePathDict = {}
  46. self.refNodePathDict['parent'] = self['nodePath'].getParent()
  47. self.refNodePathDict['render'] = render
  48. self.refNodePathDict['camera'] = SEditor.camera
  49. self.refNodePathDict['widget'] = SEditor.widget
  50. self.refNodePathNames = ['parent', 'self', 'render',
  51. 'camera', 'widget', 'selected']
  52. # Initial state
  53. self.initPos = Vec3(0)
  54. self.initHpr = Vec3(0)
  55. self.initScale = Vec3(1)
  56. self.deltaHpr = Vec3(0)
  57. # Offset for orbital mode
  58. self.posOffset = Vec3(0)
  59. # Set up event hooks
  60. self.undoEvents = [('DIRECT_undo', self.undoHook),
  61. ('DIRECT_pushUndo', self.pushUndoHook),
  62. ('DIRECT_undoListEmpty', self.undoListEmptyHook),
  63. ('DIRECT_redo', self.redoHook),
  64. ('DIRECT_pushRedo', self.pushRedoHook),
  65. ('DIRECT_redoListEmpty', self.redoListEmptyHook)]
  66. for event, method in self.undoEvents:
  67. self.accept(event, method)
  68. # Init movement mode
  69. self.movementMode = 'Relative To:'
  70. def createInterface(self):
  71. # The interior of the toplevel panel
  72. interior = self.interior()
  73. interior['relief'] = Tkinter.FLAT
  74. # Add placer commands to menubar
  75. self.menuBar.addmenu('Placer', 'Placer Panel Operations')
  76. self.menuBar.addmenuitem('Placer', 'command',
  77. 'Zero Node Path',
  78. label = 'Zero All',
  79. command = self.zeroAll)
  80. self.menuBar.addmenuitem('Placer', 'command',
  81. 'Reset Node Path',
  82. label = 'Reset All',
  83. command = self.resetAll)
  84. self.menuBar.addmenuitem('Placer', 'command',
  85. 'Print Node Path Info',
  86. label = 'Print Info',
  87. command = self.printNodePathInfo)
  88. self.menuBar.addmenuitem(
  89. 'Placer', 'command',
  90. 'Toggle widget visability',
  91. label = 'Toggle Widget Vis',
  92. command = SEditor.toggleWidgetVis)
  93. self.menuBar.addmenuitem(
  94. 'Placer', 'command',
  95. 'Toggle widget manipulation mode',
  96. label = 'Toggle Widget Mode',
  97. command = SEditor.manipulationControl.toggleObjectHandlesMode)
  98. # Get a handle to the menu frame
  99. menuFrame = self.menuFrame
  100. self.nodePathMenu = Pmw.ComboBox(
  101. menuFrame, labelpos = Tkinter.W, label_text = 'Node Path:',
  102. entry_width = 20,
  103. selectioncommand = self.selectNodePathNamed,
  104. scrolledlist_items = self.nodePathNames)
  105. self.nodePathMenu.selectitem('selected')
  106. self.nodePathMenuEntry = (
  107. self.nodePathMenu.component('entryfield_entry'))
  108. self.nodePathMenuBG = (
  109. self.nodePathMenuEntry.configure('background')[3])
  110. self.nodePathMenu.pack(side = 'left', fill = 'x', expand = 1)
  111. self.bind(self.nodePathMenu, 'Select node path to manipulate')
  112. modeMenu = Pmw.OptionMenu(menuFrame,
  113. items = ('Relative To:',
  114. 'Orbit:'),
  115. initialitem = 'Relative To:',
  116. command = self.setMovementMode,
  117. menubutton_width = 8)
  118. modeMenu.pack(side = 'left', expand = 0)
  119. self.bind(modeMenu, 'Select manipulation mode')
  120. self.refNodePathMenu = Pmw.ComboBox(
  121. menuFrame, entry_width = 16,
  122. selectioncommand = self.selectRefNodePathNamed,
  123. scrolledlist_items = self.refNodePathNames)
  124. self.refNodePathMenu.selectitem('parent')
  125. self.refNodePathMenuEntry = (
  126. self.refNodePathMenu.component('entryfield_entry'))
  127. self.refNodePathMenu.pack(side = 'left', fill = 'x', expand = 1)
  128. self.bind(self.refNodePathMenu, 'Select relative node path')
  129. self.undoButton = Button(menuFrame, text = 'Undo',
  130. command = SEditor.undo)
  131. if SEditor.undoList:
  132. self.undoButton['state'] = 'normal'
  133. else:
  134. self.undoButton['state'] = 'disabled'
  135. self.undoButton.pack(side = 'left', expand = 0)
  136. self.bind(self.undoButton, 'Undo last operation')
  137. self.redoButton = Button(menuFrame, text = 'Redo',
  138. command = SEditor.redo)
  139. if SEditor.redoList:
  140. self.redoButton['state'] = 'normal'
  141. else:
  142. self.redoButton['state'] = 'disabled'
  143. self.redoButton.pack(side = 'left', expand = 0)
  144. self.bind(self.redoButton, 'Redo last operation')
  145. # Create and pack the Pos Controls
  146. posGroup = Pmw.Group(interior,
  147. tag_pyclass = Menubutton,
  148. tag_text = 'Position',
  149. tag_font=('MSSansSerif', 14),
  150. tag_activebackground = '#909090',
  151. ring_relief = Tkinter.RIDGE)
  152. posMenubutton = posGroup.component('tag')
  153. self.bind(posMenubutton, 'Position menu operations')
  154. posMenu = Menu(posMenubutton, tearoff = 0)
  155. posMenu.add_command(label = 'Set to zero', command = self.zeroPos)
  156. posMenu.add_command(label = 'Reset initial',
  157. command = self.resetPos)
  158. posMenubutton['menu'] = posMenu
  159. posGroup.pack(side='left', fill = 'both', expand = 1)
  160. posInterior = posGroup.interior()
  161. # Create the dials
  162. self.posX = self.createcomponent('posX', (), None,
  163. Floater, (posInterior,),
  164. text = 'X', relief = Tkinter.FLAT,
  165. value = 0.0,
  166. label_foreground = 'Red')
  167. self.posX['commandData'] = ['x']
  168. self.posX['preCallback'] = self.xformStart
  169. self.posX['postCallback'] = self.xformStop
  170. self.posX['callbackData'] = ['x']
  171. self.posX.pack(expand=1,fill='both')
  172. self.posY = self.createcomponent('posY', (), None,
  173. Floater, (posInterior,),
  174. text = 'Y', relief = Tkinter.FLAT,
  175. value = 0.0,
  176. label_foreground = '#00A000')
  177. self.posY['commandData'] = ['y']
  178. self.posY['preCallback'] = self.xformStart
  179. self.posY['postCallback'] = self.xformStop
  180. self.posY['callbackData'] = ['y']
  181. self.posY.pack(expand=1,fill='both')
  182. self.posZ = self.createcomponent('posZ', (), None,
  183. Floater, (posInterior,),
  184. text = 'Z', relief = Tkinter.FLAT,
  185. value = 0.0,
  186. label_foreground = 'Blue')
  187. self.posZ['commandData'] = ['z']
  188. self.posZ['preCallback'] = self.xformStart
  189. self.posZ['postCallback'] = self.xformStop
  190. self.posZ['callbackData'] = ['z']
  191. self.posZ.pack(expand=1,fill='both')
  192. # Create and pack the Hpr Controls
  193. hprGroup = Pmw.Group(interior,
  194. tag_pyclass = Menubutton,
  195. tag_text = 'Orientation',
  196. tag_font=('MSSansSerif', 14),
  197. tag_activebackground = '#909090',
  198. ring_relief = Tkinter.RIDGE)
  199. hprMenubutton = hprGroup.component('tag')
  200. self.bind(hprMenubutton, 'Orientation menu operations')
  201. hprMenu = Menu(hprMenubutton, tearoff = 0)
  202. hprMenu.add_command(label = 'Set to zero', command = self.zeroHpr)
  203. hprMenu.add_command(label = 'Reset initial', command = self.resetHpr)
  204. hprMenubutton['menu'] = hprMenu
  205. hprGroup.pack(side='left',fill = 'both', expand = 1)
  206. hprInterior = hprGroup.interior()
  207. # Create the dials
  208. self.hprH = self.createcomponent('hprH', (), None,
  209. AngleDial, (hprInterior,),
  210. style = 'mini',
  211. text = 'H', value = 0.0,
  212. relief = Tkinter.FLAT,
  213. label_foreground = 'blue')
  214. self.hprH['commandData'] = ['h']
  215. self.hprH['preCallback'] = self.xformStart
  216. self.hprH['postCallback'] = self.xformStop
  217. self.hprH['callbackData'] = ['h']
  218. self.hprH.pack(expand=1,fill='both')
  219. self.hprP = self.createcomponent('hprP', (), None,
  220. AngleDial, (hprInterior,),
  221. style = 'mini',
  222. text = 'P', value = 0.0,
  223. relief = Tkinter.FLAT,
  224. label_foreground = 'red')
  225. self.hprP['commandData'] = ['p']
  226. self.hprP['preCallback'] = self.xformStart
  227. self.hprP['postCallback'] = self.xformStop
  228. self.hprP['callbackData'] = ['p']
  229. self.hprP.pack(expand=1,fill='both')
  230. self.hprR = self.createcomponent('hprR', (), None,
  231. AngleDial, (hprInterior,),
  232. style = 'mini',
  233. text = 'R', value = 0.0,
  234. relief = Tkinter.FLAT,
  235. label_foreground = '#00A000')
  236. self.hprR['commandData'] = ['r']
  237. self.hprR['preCallback'] = self.xformStart
  238. self.hprR['postCallback'] = self.xformStop
  239. self.hprR['callbackData'] = ['r']
  240. self.hprR.pack(expand=1,fill='both')
  241. # Create and pack the Scale Controls
  242. # The available scaling modes
  243. self.scalingMode = StringVar()
  244. self.scalingMode.set('Scale Uniform')
  245. # The scaling widgets
  246. scaleGroup = Pmw.Group(interior,
  247. tag_text = 'Scale Uniform',
  248. tag_pyclass = Menubutton,
  249. tag_font=('MSSansSerif', 14),
  250. tag_activebackground = '#909090',
  251. ring_relief = Tkinter.RIDGE)
  252. self.scaleMenubutton = scaleGroup.component('tag')
  253. self.bind(self.scaleMenubutton, 'Scale menu operations')
  254. self.scaleMenubutton['textvariable'] = self.scalingMode
  255. # Scaling menu
  256. scaleMenu = Menu(self.scaleMenubutton, tearoff = 0)
  257. scaleMenu.add_command(label = 'Set to unity',
  258. command = self.unitScale)
  259. scaleMenu.add_command(label = 'Reset initial',
  260. command = self.resetScale)
  261. scaleMenu.add_radiobutton(label = 'Scale Free',
  262. variable = self.scalingMode)
  263. scaleMenu.add_radiobutton(label = 'Scale Uniform',
  264. variable = self.scalingMode)
  265. scaleMenu.add_radiobutton(label = 'Scale Proportional',
  266. variable = self.scalingMode)
  267. self.scaleMenubutton['menu'] = scaleMenu
  268. # Pack group widgets
  269. scaleGroup.pack(side='left',fill = 'both', expand = 1)
  270. scaleInterior = scaleGroup.interior()
  271. # Create the dials
  272. self.scaleX = self.createcomponent('scaleX', (), None,
  273. Floater, (scaleInterior,),
  274. text = 'X Scale',
  275. relief = Tkinter.FLAT,
  276. min = 0.0001, value = 1.0,
  277. resetValue = 1.0,
  278. label_foreground = 'Red')
  279. self.scaleX['commandData'] = ['sx']
  280. self.scaleX['callbackData'] = ['sx']
  281. self.scaleX['preCallback'] = self.xformStart
  282. self.scaleX['postCallback'] = self.xformStop
  283. self.scaleX.pack(expand=1,fill='both')
  284. self.scaleY = self.createcomponent('scaleY', (), None,
  285. Floater, (scaleInterior,),
  286. text = 'Y Scale',
  287. relief = Tkinter.FLAT,
  288. min = 0.0001, value = 1.0,
  289. resetValue = 1.0,
  290. label_foreground = '#00A000')
  291. self.scaleY['commandData'] = ['sy']
  292. self.scaleY['callbackData'] = ['sy']
  293. self.scaleY['preCallback'] = self.xformStart
  294. self.scaleY['postCallback'] = self.xformStop
  295. self.scaleY.pack(expand=1,fill='both')
  296. self.scaleZ = self.createcomponent('scaleZ', (), None,
  297. Floater, (scaleInterior,),
  298. text = 'Z Scale',
  299. relief = Tkinter.FLAT,
  300. min = 0.0001, value = 1.0,
  301. resetValue = 1.0,
  302. label_foreground = 'Blue')
  303. self.scaleZ['commandData'] = ['sz']
  304. self.scaleZ['callbackData'] = ['sz']
  305. self.scaleZ['preCallback'] = self.xformStart
  306. self.scaleZ['postCallback'] = self.xformStop
  307. self.scaleZ.pack(expand=1,fill='both')
  308. # Make sure appropriate labels are showing
  309. self.setMovementMode('Relative To:')
  310. # Set up placer for inital node path
  311. self.selectNodePathNamed('init')
  312. self.selectRefNodePathNamed('parent')
  313. # Update place to reflect initial state
  314. self.updatePlacer()
  315. # Now that you're done setting up, attach commands
  316. self.posX['command'] = self.xform
  317. self.posY['command'] = self.xform
  318. self.posZ['command'] = self.xform
  319. self.hprH['command'] = self.xform
  320. self.hprP['command'] = self.xform
  321. self.hprR['command'] = self.xform
  322. self.scaleX['command'] = self.xform
  323. self.scaleY['command'] = self.xform
  324. self.scaleZ['command'] = self.xform
  325. ### WIDGET OPERATIONS ###
  326. def setMovementMode(self, movementMode):
  327. # Set prefix
  328. namePrefix = ''
  329. self.movementMode = movementMode
  330. if (movementMode == 'Relative To:'):
  331. namePrefix = 'Relative '
  332. elif (movementMode == 'Orbit:'):
  333. namePrefix = 'Orbit '
  334. # Update pos widgets
  335. self.posX['text'] = namePrefix + 'X'
  336. self.posY['text'] = namePrefix + 'Y'
  337. self.posZ['text'] = namePrefix + 'Z'
  338. # Update hpr widgets
  339. if (movementMode == 'Orbit:'):
  340. namePrefix = 'Orbit delta '
  341. self.hprH['text'] = namePrefix + 'H'
  342. self.hprP['text'] = namePrefix + 'P'
  343. self.hprR['text'] = namePrefix + 'R'
  344. # Update temp cs and initialize widgets
  345. self.updatePlacer()
  346. def setScalingMode(self):
  347. if self['nodePath']:
  348. scale = self['nodePath'].getScale()
  349. if ((scale[0] != scale[1]) or
  350. (scale[0] != scale[2]) or
  351. (scale[1] != scale[2])):
  352. self.scalingMode.set('Scale Free')
  353. def selectNodePathNamed(self, name):
  354. nodePath = None
  355. if name == 'init':
  356. nodePath = self['nodePath']
  357. # Add Combo box entry for the initial node path
  358. self.addNodePath(nodePath)
  359. elif name == 'selected':
  360. nodePath = SEditor.selected.last
  361. # Add Combo box entry for this selected object
  362. self.addNodePath(nodePath)
  363. else:
  364. nodePath = self.nodePathDict.get(name, None)
  365. if (nodePath == None):
  366. # See if this evaluates into a node path
  367. try:
  368. nodePath = eval(name)
  369. if isinstance(nodePath, NodePath):
  370. self.addNodePath(nodePath)
  371. else:
  372. # Good eval but not a node path, give up
  373. nodePath = None
  374. except:
  375. # Bogus eval
  376. nodePath = None
  377. # Clear bogus entry from listbox
  378. listbox = self.nodePathMenu.component('scrolledlist')
  379. listbox.setlist(self.nodePathNames)
  380. else:
  381. if name == 'widget':
  382. # Record relationship between selected nodes and widget
  383. SEditor.selected.getWrtAll()
  384. # Update active node path
  385. self.setActiveNodePath(nodePath)
  386. def setActiveNodePath(self, nodePath):
  387. self['nodePath'] = nodePath
  388. if self['nodePath']:
  389. self.nodePathMenuEntry.configure(
  390. background = self.nodePathMenuBG)
  391. # Check to see if node path and ref node path are the same
  392. if ((self.refCS != None) and
  393. (self.refCS.id() == self['nodePath'].id())):
  394. # Yes they are, use temp CS as ref
  395. # This calls updatePlacer
  396. self.setReferenceNodePath(self.tempCS)
  397. # update listbox accordingly
  398. self.refNodePathMenu.selectitem('parent')
  399. else:
  400. # Record initial value and initialize the widgets
  401. self.updatePlacer()
  402. # Record initial position
  403. self.updateResetValues(self['nodePath'])
  404. # Set scaling mode based on node path's current scale
  405. self.setScalingMode()
  406. else:
  407. # Flash entry
  408. self.nodePathMenuEntry.configure(background = 'Pink')
  409. def selectRefNodePathNamed(self, name):
  410. nodePath = None
  411. if name == 'self':
  412. nodePath = self.tempCS
  413. elif name == 'selected':
  414. nodePath = SEditor.selected.last
  415. # Add Combo box entry for this selected object
  416. self.addRefNodePath(nodePath)
  417. elif name == 'parent':
  418. nodePath = self['nodePath'].getParent()
  419. else:
  420. nodePath = self.refNodePathDict.get(name, None)
  421. if (nodePath == None):
  422. # See if this evaluates into a node path
  423. try:
  424. nodePath = eval(name)
  425. if isinstance(nodePath, NodePath):
  426. self.addRefNodePath(nodePath)
  427. else:
  428. # Good eval but not a node path, give up
  429. nodePath = None
  430. except:
  431. # Bogus eval
  432. nodePath = None
  433. # Clear bogus entry from listbox
  434. listbox = self.refNodePathMenu.component('scrolledlist')
  435. listbox.setlist(self.refNodePathNames)
  436. # Check to see if node path and ref node path are the same
  437. if (nodePath != None) and (nodePath.id() == self['nodePath'].id()):
  438. # Yes they are, use temp CS and update listbox accordingly
  439. nodePath = self.tempCS
  440. self.refNodePathMenu.selectitem('parent')
  441. # Update ref node path
  442. self.setReferenceNodePath(nodePath)
  443. def setReferenceNodePath(self, nodePath):
  444. self.refCS = nodePath
  445. if self.refCS:
  446. self.refNodePathMenuEntry.configure(
  447. background = self.nodePathMenuBG)
  448. # Update placer to reflect new state
  449. self.updatePlacer()
  450. else:
  451. # Flash entry
  452. self.refNodePathMenuEntry.configure(background = 'Pink')
  453. def addNodePath(self, nodePath):
  454. self.addNodePathToDict(nodePath, self.nodePathNames,
  455. self.nodePathMenu, self.nodePathDict)
  456. def addRefNodePath(self, nodePath):
  457. self.addNodePathToDict(nodePath, self.refNodePathNames,
  458. self.refNodePathMenu, self.refNodePathDict)
  459. def addNodePathToDict(self, nodePath, names, menu, dict):
  460. if not nodePath:
  461. return
  462. # Get node path's name
  463. name = nodePath.getName()
  464. if name in ['parent', 'render', 'camera']:
  465. dictName = name
  466. else:
  467. # Generate a unique name for the dict
  468. dictName = name + '-' + `nodePath.id()`
  469. if not dict.has_key(dictName):
  470. # Update combo box to include new item
  471. names.append(dictName)
  472. listbox = menu.component('scrolledlist')
  473. listbox.setlist(names)
  474. # Add new item to dictionary
  475. dict[dictName] = nodePath
  476. menu.selectitem(dictName)
  477. def updatePlacer(self):
  478. pos = Vec3(0)
  479. hpr = Vec3(0)
  480. scale = Vec3(1)
  481. np = self['nodePath']
  482. if (np != None) and isinstance(np, NodePath):
  483. # Update temp CS
  484. self.updateAuxiliaryCoordinateSystems()
  485. # Update widgets
  486. if self.movementMode == 'Orbit:':
  487. pos.assign(self.posOffset)
  488. hpr.assign(ZERO_VEC)
  489. scale.assign(np.getScale())
  490. elif self.refCS:
  491. pos.assign(np.getPos(self.refCS))
  492. hpr.assign(np.getHpr(self.refCS))
  493. scale.assign(np.getScale())
  494. self.updatePosWidgets(pos)
  495. self.updateHprWidgets(hpr)
  496. self.updateScaleWidgets(scale)
  497. def updateAuxiliaryCoordinateSystems(self):
  498. # Temp CS
  499. self.tempCS.setPosHpr(self['nodePath'], 0,0,0,0,0,0)
  500. # Orbit CS
  501. # At reference
  502. self.orbitFromCS.setPos(self.refCS, 0,0,0)
  503. # But aligned with target
  504. self.orbitFromCS.setHpr(self['nodePath'], 0,0,0)
  505. # Also update to CS
  506. self.orbitToCS.setPosHpr(self.orbitFromCS, 0,0,0,0,0,0)
  507. # Get offset from origin
  508. self.posOffset.assign(self['nodePath'].getPos(self.orbitFromCS))
  509. ### NODE PATH TRANSFORMATION OPERATIONS ###
  510. def xform(self, value, axis):
  511. if axis in ['sx', 'sy', 'sz']:
  512. self.xformScale(value,axis)
  513. elif self.movementMode == 'Relative To:':
  514. self.xformRelative(value, axis)
  515. elif self.movementMode == 'Orbit:':
  516. self.xformOrbit(value, axis)
  517. if self.nodePathMenu.get() == 'widget':
  518. if SEditor.manipulationControl.fSetCoa:
  519. # Update coa based on current widget position
  520. SEditor.selected.last.mCoa2Dnp.assign(
  521. SEditor.widget.getMat(SEditor.selected.last))
  522. else:
  523. # Move the objects with the widget
  524. SEditor.selected.moveWrtWidgetAll()
  525. def xformStart(self, data):
  526. # Record undo point
  527. self.pushUndo()
  528. # If moving widget kill follow task and update wrts
  529. if self.nodePathMenu.get() == 'widget':
  530. taskMgr.remove('followSelectedNodePath')
  531. # Record relationship between selected nodes and widget
  532. SEditor.selected.getWrtAll()
  533. # Record initial state
  534. self.deltaHpr = self['nodePath'].getHpr(self.refCS)
  535. # Update placer to reflect new state
  536. self.updatePlacer()
  537. def xformStop(self, data):
  538. # Throw event to signal manipulation done
  539. # Send nodepath as a list
  540. messenger.send('DIRECT_manipulateObjectCleanup', [[self['nodePath']]])
  541. # Update placer to reflect new state
  542. self.updatePlacer()
  543. # If moving widget restart follow task
  544. if self.nodePathMenu.get() == 'widget':
  545. # Restart followSelectedNodePath task
  546. SEditor.manipulationControl.spawnFollowSelectedNodePathTask()
  547. def xformRelative(self, value, axis):
  548. nodePath = self['nodePath']
  549. if (nodePath != None) and (self.refCS != None):
  550. if axis == 'x':
  551. nodePath.setX(self.refCS, value)
  552. elif axis == 'y':
  553. nodePath.setY(self.refCS, value)
  554. elif axis == 'z':
  555. nodePath.setZ(self.refCS, value)
  556. else:
  557. if axis == 'h':
  558. self.deltaHpr.setX(value)
  559. elif axis == 'p':
  560. self.deltaHpr.setY(value)
  561. elif axis == 'r':
  562. self.deltaHpr.setZ(value)
  563. # Put node path at new hpr
  564. nodePath.setHpr(self.refCS, self.deltaHpr)
  565. def xformOrbit(self, value, axis):
  566. nodePath = self['nodePath']
  567. if ((nodePath != None) and (self.refCS != None) and
  568. (self.orbitFromCS != None) and (self.orbitToCS != None)):
  569. if axis == 'x':
  570. self.posOffset.setX(value)
  571. elif axis == 'y':
  572. self.posOffset.setY(value)
  573. elif axis == 'z':
  574. self.posOffset.setZ(value)
  575. elif axis == 'h':
  576. self.orbitToCS.setH(self.orbitFromCS, value)
  577. elif axis == 'p':
  578. self.orbitToCS.setP(self.orbitFromCS, value)
  579. elif axis == 'r':
  580. self.orbitToCS.setR(self.orbitFromCS, value)
  581. nodePath.setPosHpr(self.orbitToCS, self.posOffset, ZERO_VEC)
  582. def xformScale(self, value, axis):
  583. if self['nodePath']:
  584. mode = self.scalingMode.get()
  585. scale = self['nodePath'].getScale()
  586. if mode == 'Scale Free':
  587. if axis == 'sx':
  588. scale.setX(value)
  589. elif axis == 'sy':
  590. scale.setY(value)
  591. elif axis == 'sz':
  592. scale.setZ(value)
  593. elif mode == 'Scale Uniform':
  594. scale.set(value,value,value)
  595. elif mode == 'Scale Proportional':
  596. if axis == 'sx':
  597. sf = value/scale[0]
  598. elif axis == 'sy':
  599. sf = value/scale[1]
  600. elif axis == 'sz':
  601. sf = value/scale[2]
  602. scale = scale * sf
  603. self['nodePath'].setScale(scale)
  604. def updatePosWidgets(self, pos):
  605. self.posX.set(pos[0])
  606. self.posY.set(pos[1])
  607. self.posZ.set(pos[2])
  608. def updateHprWidgets(self, hpr):
  609. self.hprH.set(hpr[0])
  610. self.hprP.set(hpr[1])
  611. self.hprR.set(hpr[2])
  612. def updateScaleWidgets(self, scale):
  613. self.scaleX.set(scale[0])
  614. self.scaleY.set(scale[1])
  615. self.scaleZ.set(scale[2])
  616. def zeroAll(self):
  617. self.xformStart(None)
  618. self.updatePosWidgets(ZERO_VEC)
  619. self.updateHprWidgets(ZERO_VEC)
  620. self.updateScaleWidgets(UNIT_VEC)
  621. self.xformStop(None)
  622. def zeroPos(self):
  623. self.xformStart(None)
  624. self.updatePosWidgets(ZERO_VEC)
  625. self.xformStop(None)
  626. def zeroHpr(self):
  627. self.xformStart(None)
  628. self.updateHprWidgets(ZERO_VEC)
  629. self.xformStop(None)
  630. def unitScale(self):
  631. self.xformStart(None)
  632. self.updateScaleWidgets(UNIT_VEC)
  633. self.xformStop(None)
  634. def updateResetValues(self, nodePath):
  635. self.initPos.assign(nodePath.getPos())
  636. self.posX['resetValue'] = self.initPos[0]
  637. self.posY['resetValue'] = self.initPos[1]
  638. self.posZ['resetValue'] = self.initPos[2]
  639. self.initHpr.assign(nodePath.getHpr())
  640. self.hprH['resetValue'] = self.initHpr[0]
  641. self.hprP['resetValue'] = self.initHpr[1]
  642. self.hprR['resetValue'] = self.initHpr[2]
  643. self.initScale.assign(nodePath.getScale())
  644. self.scaleX['resetValue'] = self.initScale[0]
  645. self.scaleY['resetValue'] = self.initScale[1]
  646. self.scaleZ['resetValue'] = self.initScale[2]
  647. def resetAll(self):
  648. if self['nodePath']:
  649. self.xformStart(None)
  650. self['nodePath'].setPosHprScale(
  651. self.initPos, self.initHpr, self.initScale)
  652. self.xformStop(None)
  653. def resetPos(self):
  654. if self['nodePath']:
  655. self.xformStart(None)
  656. self['nodePath'].setPos(self.initPos)
  657. self.xformStop(None)
  658. def resetHpr(self):
  659. if self['nodePath']:
  660. self.xformStart(None)
  661. self['nodePath'].setHpr(self.initHpr)
  662. self.xformStop(None)
  663. def resetScale(self):
  664. if self['nodePath']:
  665. self.xformStart(None)
  666. self['nodePath'].setScale(self.initScale)
  667. self.xformStop(None)
  668. def pushUndo(self, fResetRedo = 1):
  669. SEditor.pushUndo([self['nodePath']])
  670. def undoHook(self, nodePathList = []):
  671. # Reflect new changes
  672. self.updatePlacer()
  673. def pushUndoHook(self):
  674. # Make sure button is reactivated
  675. self.undoButton.configure(state = 'normal')
  676. def undoListEmptyHook(self):
  677. # Make sure button is deactivated
  678. self.undoButton.configure(state = 'disabled')
  679. def pushRedo(self):
  680. SEditor.pushRedo([self['nodePath']])
  681. def redoHook(self, nodePathList = []):
  682. # Reflect new changes
  683. self.updatePlacer()
  684. def pushRedoHook(self):
  685. # Make sure button is reactivated
  686. self.redoButton.configure(state = 'normal')
  687. def redoListEmptyHook(self):
  688. # Make sure button is deactivated
  689. self.redoButton.configure(state = 'disabled')
  690. def printNodePathInfo(self):
  691. np = self['nodePath']
  692. if np:
  693. name = np.getName()
  694. pos = np.getPos()
  695. hpr = np.getHpr()
  696. scale = np.getScale()
  697. posString = '%.2f, %.2f, %.2f' % (pos[0], pos[1], pos[2])
  698. hprString = '%.2f, %.2f, %.2f' % (hpr[0], hpr[1], hpr[2])
  699. scaleString = '%.2f, %.2f, %.2f' % (scale[0], scale[1], scale[2])
  700. print 'NodePath: %s' % name
  701. print 'Pos: %s' % posString
  702. print 'Hpr: %s' % hprString
  703. print 'Scale: %s' % scaleString
  704. print ('%s.setPosHprScale(%s, %s, %s)' %
  705. (name, posString, hprString, scaleString))
  706. def onDestroy(self, event):
  707. # Remove hooks
  708. for event, method in self.undoEvents:
  709. self.ignore(event)
  710. self.tempCS.removeNode()
  711. self.orbitFromCS.removeNode()
  712. self.orbitToCS.removeNode()
  713. # send out a message to let sceneEditor know that placer panel has been closed.
  714. # Also, stop accepting the updata message from sceneEditor
  715. messenger.send('Placer_close')
  716. self.ignore('placerUpdate')
  717. def place(nodePath):
  718. return Placer(nodePath = nodePath)
  719. ######################################################################
  720. # Create demo in root window for testing.
  721. if __name__ == '__main__':
  722. root = Pmw.initialise()
  723. widget = Placer()