seMopathRecorder.py 84 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072
  1. #################################################################
  2. # seMopathRecorder.py
  3. # Originally from MopathRecorder.py
  4. # Altered by Yi-Hong Lin, [email protected], 2004
  5. #
  6. # We have to change something about data flow and UI
  7. # so the curves data can be saved into our dataHolder.
  8. # Other things we have changed here is that we have added a copy model
  9. # of target nodePath under the render when the recording begins.
  10. # And, of course, we have removed it right after the recording ends.
  11. # You can find it in toggleRecord function.
  12. #
  13. #################################################################
  14. from direct.showbase.DirectObject import DirectObject
  15. from direct.tkwidgets.AppShell import AppShell
  16. #from direct.directtools.DirectGlobals import *
  17. #from direct.directtools.DirectUtil import *
  18. from seGeometry import *
  19. from seSelection import *
  20. from direct.tkwidgets.Dial import AngleDial
  21. from direct.tkwidgets.Floater import Floater
  22. from direct.tkwidgets.Slider import Slider
  23. from direct.tkwidgets.EntryScale import EntryScale
  24. from direct.tkwidgets.VectorWidgets import Vector2Entry, Vector3Entry
  25. from direct.tkwidgets.VectorWidgets import ColorEntry
  26. from Tkinter import Button, Frame, Radiobutton, Checkbutton, Label
  27. from Tkinter import StringVar, BooleanVar, Entry, Scale
  28. import os, string, Tkinter, Pmw
  29. import __builtin__
  30. PRF_UTILITIES = [
  31. 'lambda: camera.lookAt(render)',
  32. 'lambda: camera.setZ(render, 0.0)',
  33. 'lambda s = self: s.playbackMarker.lookAt(render)',
  34. 'lambda s = self: s.playbackMarker.setZ(render, 0.0)',
  35. 'lambda s = self: s.followTerrain(10.0)']
  36. class MopathRecorder(AppShell, DirectObject):
  37. # Override class variables here
  38. appname = 'Mopath Recorder Panel'
  39. frameWidth = 450
  40. frameHeight = 550
  41. usecommandarea = 0
  42. usestatusarea = 0
  43. count = 0
  44. def __init__(self, parent = None, **kw):
  45. INITOPT = Pmw.INITOPT
  46. name = 'recorder-%d' % MopathRecorder.count
  47. MopathRecorder.count += 1
  48. optiondefs = (
  49. ('title', self.appname, None),
  50. ('nodePath', None, None),
  51. ('name', name, None)
  52. )
  53. self.defineoptions(kw, optiondefs)
  54. # Call superclass initialization function
  55. AppShell.__init__(self)
  56. self.initialiseoptions(MopathRecorder)
  57. self.selectNodePathNamed('camera')
  58. self.parent.resizable(False,False) ## Disable the ability to resize for this Window.
  59. def appInit(self):
  60. self.mopathRecorderNode = render.attachNewNode("MopathRecorder")
  61. self.name = self['name']
  62. # Dictionary of widgets
  63. self.widgetDict = {}
  64. self.variableDict = {}
  65. # Initialize state
  66. # The active node path
  67. self.nodePath = self['nodePath']
  68. self.playbackNodePath = self.nodePath
  69. # The active node path's parent
  70. self.nodePathParent = render
  71. # Top level node path
  72. self.recorderNodePath = self.mopathRecorderNode.attachNewNode(self.name)
  73. # Temp CS for use in refinement/path extension
  74. self.tempCS = self.recorderNodePath.attachNewNode(
  75. 'mopathRecorderTempCS')
  76. # Marker for use in playback
  77. self.playbackMarker = loader.loadModel('models/misc/sphere') ###
  78. self.playbackMarker.setName('Playback Marker')
  79. self.playbackMarker.reparentTo(self.recorderNodePath)
  80. self.playbackMarkerIds = self.getChildIds(
  81. self.playbackMarker.getChild(0))
  82. self.playbackMarker.hide()
  83. # Tangent marker
  84. self.tangentGroup = self.playbackMarker.attachNewNode('Tangent Group')
  85. self.tangentGroup.hide()
  86. self.tangentMarker = loader.loadModel('models/misc/sphere')
  87. self.tangentMarker.reparentTo(self.tangentGroup)
  88. self.tangentMarker.setScale(0.5)
  89. self.tangentMarker.setColor(1,0,1,1)
  90. self.tangentMarker.setName('Tangent Marker')
  91. self.tangentMarkerIds = self.getChildIds(
  92. self.tangentMarker.getChild(0))
  93. self.tangentLines = LineNodePath(self.tangentGroup)
  94. self.tangentLines.setColor(VBase4(1,0,1,1))
  95. self.tangentLines.setThickness(1)
  96. self.tangentLines.moveTo(0,0,0)
  97. self.tangentLines.drawTo(0,0,0)
  98. self.tangentLines.create()
  99. # Active node path dictionary
  100. self.nodePathDict = {}
  101. self.nodePathDict['marker'] = self.playbackMarker
  102. self.nodePathDict['camera'] = camera
  103. self.nodePathDict['widget'] = SEditor.widget
  104. self.nodePathDict['mopathRecorderTempCS'] = self.tempCS
  105. self.nodePathNames = ['marker', 'camera', 'selected']
  106. # ID of selected object
  107. self.manipulandumId = None
  108. self.trace = LineNodePath(self.recorderNodePath)
  109. self.oldPlaybackNodePath = None
  110. # Count of point sets recorded
  111. self.pointSet = []
  112. self.prePoints = []
  113. self.postPoints = []
  114. self.pointSetDict = {}
  115. self.pointSetCount = 0
  116. self.pointSetName = self.name + '-ps-' + `self.pointSetCount`
  117. # User callback to call before recording point
  118. self.samplingMode = 'Continuous'
  119. self.preRecordFunc = None
  120. # Hook to start/stop recording
  121. self.startStopHook = 'f6'
  122. self.keyframeHook = 'f10'
  123. # Curve fitter object
  124. self.lastPos = Point3(0)
  125. self.curveFitter = CurveFitter()
  126. # Curve variables
  127. # Number of ticks per parametric unit
  128. self.numTicks = 1
  129. # Number of segments to represent each parametric unit
  130. # This just affects the visual appearance of the curve
  131. self.numSegs = 40
  132. # The nurbs curves
  133. self.curveCollection = None
  134. # Curve drawers
  135. self.nurbsCurveDrawer = NurbsCurveDrawer()
  136. self.nurbsCurveDrawer.setCurves(ParametricCurveCollection())
  137. self.nurbsCurveDrawer.setNumSegs(self.numSegs)
  138. self.nurbsCurveDrawer.setShowHull(0)
  139. self.nurbsCurveDrawer.setShowCvs(0)
  140. self.nurbsCurveDrawer.setNumTicks(0)
  141. self.nurbsCurveDrawer.setTickScale(5.0)
  142. self.curveNodePath = self.recorderNodePath.attachNewNode(
  143. self.nurbsCurveDrawer.getGeomNode())
  144. useDirectRenderStyle(self.curveNodePath)
  145. # Playback variables
  146. self.maxT = 0.0
  147. self.playbackTime = 0.0
  148. self.loopPlayback = 1
  149. self.playbackSF = 1.0
  150. # Sample variables
  151. self.desampleFrequency = 1
  152. self.numSamples = 100
  153. self.recordStart = 0.0
  154. self.deltaTime = 0.0
  155. self.controlStart = 0.0
  156. self.controlStop = 0.0
  157. self.recordStop = 0.0
  158. self.cropFrom = 0.0
  159. self.cropTo = 0.0
  160. self.fAdjustingValues = 0
  161. # For terrain following
  162. self.iRayCS = self.recorderNodePath.attachNewNode(
  163. 'mopathRecorderIRayCS')
  164. self.iRay = SelectionRay(self.iRayCS)
  165. # Set up event hooks
  166. self.actionEvents = [
  167. ('DIRECT_undo', self.undoHook),
  168. ('DIRECT_pushUndo', self.pushUndoHook),
  169. ('DIRECT_undoListEmpty', self.undoListEmptyHook),
  170. ('DIRECT_redo', self.redoHook),
  171. ('DIRECT_pushRedo', self.pushRedoHook),
  172. ('DIRECT_redoListEmpty', self.redoListEmptyHook),
  173. ('DIRECT_selectedNodePath', self.selectedNodePathHook),
  174. ('DIRECT_deselectedNodePath', self.deselectedNodePathHook),
  175. ('DIRECT_manipulateObjectStart', self.manipulateObjectStartHook),
  176. ('DIRECT_manipulateObjectCleanup',
  177. self.manipulateObjectCleanupHook),
  178. ]
  179. for event, method in self.actionEvents:
  180. self.accept(event, method)
  181. def createInterface(self):
  182. interior = self.interior()
  183. # FILE MENU
  184. # Get a handle on the file menu so commands can be inserted
  185. # before quit item
  186. fileMenu = self.menuBar.component('File-menu')
  187. fileMenu.insert_command(
  188. fileMenu.index('Quit'),
  189. label = 'Load Curve',
  190. command = self.loadCurveFromFile)
  191. fileMenu.insert_command(
  192. fileMenu.index('Quit'),
  193. label = 'Save Curve',
  194. command = self.saveCurveToFile)
  195. # Add mopath recorder commands to menubar
  196. self.menuBar.addmenu('Recorder', 'Mopath Recorder Panel Operations')
  197. self.menuBar.addmenuitem(
  198. 'Recorder', 'command',
  199. 'Save current curve as a new point set',
  200. label = 'Save Point Set',
  201. command = self.extractPointSetFromCurveCollection)
  202. self.menuBar.addmenuitem(
  203. 'Recorder', 'command',
  204. 'Toggle widget visability',
  205. label = 'Toggle Widget Vis',
  206. command = self.toggleWidgetVis)
  207. self.menuBar.addmenuitem(
  208. 'Recorder', 'command',
  209. 'Toggle widget manipulation mode',
  210. label = 'Toggle Widget Mode',
  211. command = SEditor.manipulationControl.toggleObjectHandlesMode)
  212. self.historyWidget = self.createComboBox(self.menuFrame, 'Mopath', 'Path:',
  213. 'Select input points to fit curve to', '',
  214. self.selectPointSetNamed, expand = 1)
  215. self.undoButton = Button(self.menuFrame, text = 'Undo',
  216. command = SEditor.undo)
  217. if SEditor.undoList:
  218. self.undoButton['state'] = 'normal'
  219. else:
  220. self.undoButton['state'] = 'disabled'
  221. self.undoButton.pack(side = Tkinter.LEFT, expand = 0)
  222. self.bind(self.undoButton, 'Undo last operation')
  223. self.redoButton = Button(self.menuFrame, text = 'Redo',
  224. command = SEditor.redo)
  225. if SEditor.redoList:
  226. self.redoButton['state'] = 'normal'
  227. else:
  228. self.redoButton['state'] = 'disabled'
  229. self.redoButton.pack(side = Tkinter.LEFT, expand = 0)
  230. self.bind(self.redoButton, 'Redo last operation')
  231. # Record button
  232. mainFrame = Frame(interior, relief = Tkinter.SUNKEN, borderwidth = 2)
  233. frame = Frame(mainFrame)
  234. # Active node path
  235. # Button to select active node path
  236. widget = self.createButton(frame, 'Recording', 'Node Path:',
  237. 'Select Active Mopath Node Path',
  238. lambda s = self: SEditor.select(s.nodePath),
  239. side = Tkinter.LEFT, expand = 0)
  240. widget['relief'] = Tkinter.FLAT
  241. self.nodePathMenu = Pmw.ComboBox(
  242. frame, entry_width = 20,
  243. selectioncommand = self.selectNodePathNamed,
  244. scrolledlist_items = self.nodePathNames)
  245. self.nodePathMenu.selectitem('camera')
  246. self.nodePathMenuEntry = (
  247. self.nodePathMenu.component('entryfield_entry'))
  248. self.nodePathMenuBG = (
  249. self.nodePathMenuEntry.configure('background')[3])
  250. self.nodePathMenu.pack(side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
  251. self.bind(self.nodePathMenu,
  252. 'Select active node path used for recording and playback')
  253. # Recording type
  254. self.recordingType = StringVar()
  255. self.recordingType.set('New Curve')
  256. widget = self.createRadiobutton(
  257. frame, 'left',
  258. 'Recording', 'New Curve',
  259. ('Next record session records a new path'),
  260. self.recordingType, 'New Curve',expand = 0)
  261. widget = self.createRadiobutton(
  262. frame, 'left',
  263. 'Recording', 'Refine',
  264. ('Next record session refines existing path'),
  265. self.recordingType, 'Refine', expand = 0)
  266. widget = self.createRadiobutton(
  267. frame, 'left',
  268. 'Recording', 'Extend',
  269. ('Next record session extends existing path'),
  270. self.recordingType, 'Extend', expand = 0)
  271. frame.pack(fill = Tkinter.X, expand = 1)
  272. frame = Frame(mainFrame)
  273. widget = self.createCheckbutton(
  274. frame, 'Recording', 'Record',
  275. 'On: path is being recorded', self.toggleRecord, 0,
  276. side = Tkinter.LEFT, fill = Tkinter.BOTH, expand = 1)
  277. widget.configure(foreground = 'Red', relief = Tkinter.RAISED, borderwidth = 2,
  278. anchor = Tkinter.CENTER, width = 16)
  279. widget = self.createButton(frame, 'Recording', 'Add Keyframe',
  280. 'Add Keyframe To Current Path',
  281. self.addKeyframe,
  282. side = Tkinter.LEFT, expand = 1)
  283. widget = self.createButton(frame, 'Recording', 'Bind Path to Node',
  284. 'Bind Motion Path to selected Object',
  285. self.bindMotionPathToNode,
  286. side = Tkinter.LEFT, expand = 1)
  287. frame.pack(fill = Tkinter.X, expand = 1)
  288. mainFrame.pack(expand = 1, fill = Tkinter.X, pady = 3)
  289. # Playback controls
  290. playbackFrame = Frame(interior, relief = Tkinter.SUNKEN,
  291. borderwidth = 2)
  292. Label(playbackFrame, text = 'PLAYBACK CONTROLS',
  293. font=('MSSansSerif', 12, 'bold')).pack(fill = Tkinter.X)
  294. # Main playback control slider
  295. widget = self.createEntryScale(
  296. playbackFrame, 'Playback', 'Time', 'Set current playback time',
  297. resolution = 0.01, command = self.playbackGoTo, side = Tkinter.TOP)
  298. widget.component('hull')['relief'] = Tkinter.RIDGE
  299. # Kill playback task if drag slider
  300. widget['preCallback'] = self.stopPlayback
  301. # Jam duration entry into entry scale
  302. self.createLabeledEntry(widget.labelFrame, 'Resample', 'Path Duration',
  303. 'Set total curve duration',
  304. command = self.setPathDuration,
  305. side = Tkinter.LEFT, expand = 0)
  306. # Start stop buttons
  307. frame = Frame(playbackFrame)
  308. widget = self.createButton(frame, 'Playback', '<<',
  309. 'Jump to start of playback',
  310. self.jumpToStartOfPlayback,
  311. side = Tkinter.LEFT, expand = 1)
  312. widget['font'] = (('MSSansSerif', 12, 'bold'))
  313. widget = self.createCheckbutton(frame, 'Playback', 'Play',
  314. 'Start/Stop playback',
  315. self.startStopPlayback, 0,
  316. side = Tkinter.LEFT, fill = Tkinter.BOTH, expand = 1)
  317. widget.configure(anchor = 'center', justify = 'center',
  318. relief = Tkinter.RAISED, font = ('MSSansSerif', 12, 'bold'))
  319. widget = self.createButton(frame, 'Playback', '>>',
  320. 'Jump to end of playback',
  321. self.jumpToEndOfPlayback,
  322. side = Tkinter.LEFT, expand = 1)
  323. widget['font'] = (('MSSansSerif', 12, 'bold'))
  324. self.createCheckbutton(frame, 'Playback', 'Loop',
  325. 'On: loop playback',
  326. self.setLoopPlayback, self.loopPlayback,
  327. side = Tkinter.LEFT, fill = Tkinter.BOTH, expand = 0)
  328. frame.pack(fill = Tkinter.X, expand = 1)
  329. # Speed control
  330. frame = Frame(playbackFrame)
  331. widget = Button(frame, text = 'PB Speed Vernier', relief = Tkinter.FLAT,
  332. command = lambda s = self: s.setSpeedScale(1.0))
  333. widget.pack(side = Tkinter.LEFT, expand = 0)
  334. self.speedScale = Scale(frame, from_ = -1, to = 1,
  335. resolution = 0.01, showvalue = 0,
  336. width = 10, orient = 'horizontal',
  337. command = self.setPlaybackSF)
  338. self.speedScale.pack(side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
  339. self.speedVar = StringVar()
  340. self.speedVar.set("0.00")
  341. self.speedEntry = Entry(frame, textvariable = self.speedVar,
  342. width = 8)
  343. self.speedEntry.bind(
  344. '<Return>',
  345. lambda e = None, s = self: s.setSpeedScale(
  346. string.atof(s.speedVar.get())))
  347. self.speedEntry.pack(side = Tkinter.LEFT, expand = 0)
  348. frame.pack(fill = Tkinter.X, expand = 1)
  349. playbackFrame.pack(fill = Tkinter.X, pady = 2)
  350. # Create notebook pages
  351. self.mainNotebook = Pmw.NoteBook(interior)
  352. self.mainNotebook.pack(fill = Tkinter.BOTH, expand = 1)
  353. self.resamplePage = self.mainNotebook.add('Resample')
  354. self.refinePage = self.mainNotebook.add('Refine')
  355. self.extendPage = self.mainNotebook.add('Extend')
  356. self.cropPage = self.mainNotebook.add('Crop')
  357. self.drawPage = self.mainNotebook.add('Draw')
  358. self.optionsPage = self.mainNotebook.add('Options')
  359. ## RESAMPLE PAGE
  360. label = Label(self.resamplePage, text = 'RESAMPLE CURVE',
  361. font=('MSSansSerif', 12, 'bold'))
  362. label.pack(fill = Tkinter.X)
  363. # Resample
  364. resampleFrame = Frame(
  365. self.resamplePage, relief = Tkinter.SUNKEN, borderwidth = 2)
  366. label = Label(resampleFrame, text = 'RESAMPLE CURVE',
  367. font=('MSSansSerif', 12, 'bold')).pack()
  368. widget = self.createSlider(
  369. resampleFrame, 'Resample', 'Num. Samples',
  370. 'Number of samples in resampled curve',
  371. resolution = 1, min = 2, max = 1000, command = self.setNumSamples)
  372. widget.component('hull')['relief'] = Tkinter.RIDGE
  373. widget['postCallback'] = self.sampleCurve
  374. frame = Frame(resampleFrame)
  375. self.createButton(
  376. frame, 'Resample', 'Make Even',
  377. 'Apply timewarp so resulting path has constant velocity',
  378. self.makeEven, side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
  379. self.createButton(
  380. frame, 'Resample', 'Face Forward',
  381. 'Compute HPR so resulting hpr curve faces along xyz tangent',
  382. self.faceForward, side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
  383. frame.pack(fill = Tkinter.X, expand = 0)
  384. resampleFrame.pack(fill = Tkinter.X, expand = 0, pady = 2)
  385. # Desample
  386. desampleFrame = Frame(
  387. self.resamplePage, relief = Tkinter.SUNKEN, borderwidth = 2)
  388. Label(desampleFrame, text = 'DESAMPLE CURVE',
  389. font=('MSSansSerif', 12, 'bold')).pack()
  390. widget = self.createSlider(
  391. desampleFrame, 'Resample', 'Points Between Samples',
  392. 'Specify number of points to skip between samples',
  393. min = 1, max = 100, resolution = 1,
  394. command = self.setDesampleFrequency)
  395. widget.component('hull')['relief'] = Tkinter.RIDGE
  396. widget['postCallback'] = self.desampleCurve
  397. desampleFrame.pack(fill = Tkinter.X, expand = 0, pady = 2)
  398. ## REFINE PAGE ##
  399. refineFrame = Frame(self.refinePage, relief = Tkinter.SUNKEN,
  400. borderwidth = 2)
  401. label = Label(refineFrame, text = 'REFINE CURVE',
  402. font=('MSSansSerif', 12, 'bold'))
  403. label.pack(fill = Tkinter.X)
  404. widget = self.createSlider(refineFrame,
  405. 'Refine Page', 'Refine From',
  406. 'Begin time of refine pass',
  407. resolution = 0.01,
  408. command = self.setRecordStart)
  409. widget['preCallback'] = self.setRefineMode
  410. widget['postCallback'] = lambda s = self: s.getPrePoints('Refine')
  411. widget = self.createSlider(
  412. refineFrame, 'Refine Page',
  413. 'Control Start',
  414. 'Time when full control of node path is given during refine pass',
  415. resolution = 0.01,
  416. command = self.setControlStart)
  417. widget['preCallback'] = self.setRefineMode
  418. widget = self.createSlider(
  419. refineFrame, 'Refine Page',
  420. 'Control Stop',
  421. 'Time when node path begins transition back to original curve',
  422. resolution = 0.01,
  423. command = self.setControlStop)
  424. widget['preCallback'] = self.setRefineMode
  425. widget = self.createSlider(refineFrame, 'Refine Page', 'Refine To',
  426. 'Stop time of refine pass',
  427. resolution = 0.01,
  428. command = self.setRefineStop)
  429. widget['preCallback'] = self.setRefineMode
  430. widget['postCallback'] = self.getPostPoints
  431. refineFrame.pack(fill = Tkinter.X)
  432. ## EXTEND PAGE ##
  433. extendFrame = Frame(self.extendPage, relief = Tkinter.SUNKEN,
  434. borderwidth = 2)
  435. label = Label(extendFrame, text = 'EXTEND CURVE',
  436. font=('MSSansSerif', 12, 'bold'))
  437. label.pack(fill = Tkinter.X)
  438. widget = self.createSlider(extendFrame,
  439. 'Extend Page', 'Extend From',
  440. 'Begin time of extend pass',
  441. resolution = 0.01,
  442. command = self.setRecordStart)
  443. widget['preCallback'] = self.setExtendMode
  444. widget['postCallback'] = lambda s = self: s.getPrePoints('Extend')
  445. widget = self.createSlider(
  446. extendFrame, 'Extend Page',
  447. 'Control Start',
  448. 'Time when full control of node path is given during extend pass',
  449. resolution = 0.01,
  450. command = self.setControlStart)
  451. widget['preCallback'] = self.setExtendMode
  452. extendFrame.pack(fill = Tkinter.X)
  453. ## CROP PAGE ##
  454. cropFrame = Frame(self.cropPage, relief = Tkinter.SUNKEN,
  455. borderwidth = 2)
  456. label = Label(cropFrame, text = 'CROP CURVE',
  457. font=('MSSansSerif', 12, 'bold'))
  458. label.pack(fill = Tkinter.X)
  459. widget = self.createSlider(
  460. cropFrame,
  461. 'Crop Page', 'Crop From',
  462. 'Delete all curve points before this time',
  463. resolution = 0.01,
  464. command = self.setCropFrom)
  465. widget = self.createSlider(
  466. cropFrame,
  467. 'Crop Page', 'Crop To',
  468. 'Delete all curve points after this time',
  469. resolution = 0.01,
  470. command = self.setCropTo)
  471. self.createButton(cropFrame, 'Crop Page', 'Crop Curve',
  472. 'Crop curve to specified from to times',
  473. self.cropCurve, fill = Tkinter.NONE)
  474. cropFrame.pack(fill = Tkinter.X)
  475. ## DRAW PAGE ##
  476. drawFrame = Frame(self.drawPage, relief = Tkinter.SUNKEN,
  477. borderwidth = 2)
  478. self.sf = Pmw.ScrolledFrame(self.drawPage, horizflex = 'elastic')
  479. self.sf.pack(fill = 'both', expand = 1)
  480. sfFrame = self.sf.interior()
  481. label = Label(sfFrame, text = 'CURVE RENDERING STYLE',
  482. font=('MSSansSerif', 12, 'bold'))
  483. label.pack(fill = Tkinter.X)
  484. frame = Frame(sfFrame)
  485. Label(frame, text = 'SHOW:').pack(side = Tkinter.LEFT, expand = 0)
  486. widget = self.createCheckbutton(
  487. frame, 'Style', 'Path',
  488. 'On: path is visible', self.setPathVis, 1,
  489. side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
  490. widget = self.createCheckbutton(
  491. frame, 'Style', 'Knots',
  492. 'On: path knots are visible', self.setKnotVis, 1,
  493. side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
  494. widget = self.createCheckbutton(
  495. frame, 'Style', 'CVs',
  496. 'On: path CVs are visible', self.setCvVis, 0,
  497. side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
  498. widget = self.createCheckbutton(
  499. frame, 'Style', 'Hull',
  500. 'On: path hull is visible', self.setHullVis, 0,
  501. side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
  502. widget = self.createCheckbutton(
  503. frame, 'Style', 'Trace',
  504. 'On: record is visible', self.setTraceVis, 0,
  505. side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
  506. widget = self.createCheckbutton(
  507. frame, 'Style', 'Marker',
  508. 'On: playback marker is visible', self.setMarkerVis, 0,
  509. side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
  510. frame.pack(fill = Tkinter.X, expand = 1)
  511. # Sliders
  512. widget = self.createSlider(
  513. sfFrame, 'Style', 'Num Segs',
  514. 'Set number of segments used to approximate each parametric unit',
  515. min = 1.0, max = 400, resolution = 1.0,
  516. value = 40,
  517. command = self.setNumSegs, side = Tkinter.TOP)
  518. widget.component('hull')['relief'] = Tkinter.RIDGE
  519. widget = self.createSlider(
  520. sfFrame, 'Style', 'Num Ticks',
  521. 'Set number of tick marks drawn for each unit of time',
  522. min = 0.0, max = 10.0, resolution = 1.0,
  523. value = 0.0,
  524. command = self.setNumTicks, side = Tkinter.TOP)
  525. widget.component('hull')['relief'] = Tkinter.RIDGE
  526. widget = self.createSlider(
  527. sfFrame, 'Style', 'Tick Scale',
  528. 'Set visible size of time tick marks',
  529. min = 0.01, max = 100.0, resolution = 0.01,
  530. value = 5.0,
  531. command = self.setTickScale, side = Tkinter.TOP)
  532. widget.component('hull')['relief'] = Tkinter.RIDGE
  533. self.createColorEntry(
  534. sfFrame, 'Style', 'Path Color',
  535. 'Color of curve',
  536. command = self.setPathColor,
  537. value = [255.0,255.0,255.0,255.0])
  538. self.createColorEntry(
  539. sfFrame, 'Style', 'Knot Color',
  540. 'Color of knots',
  541. command = self.setKnotColor,
  542. value = [0,0,255.0,255.0])
  543. self.createColorEntry(
  544. sfFrame, 'Style', 'CV Color',
  545. 'Color of CVs',
  546. command = self.setCvColor,
  547. value = [255.0,0,0,255.0])
  548. self.createColorEntry(
  549. sfFrame, 'Style', 'Tick Color',
  550. 'Color of Ticks',
  551. command = self.setTickColor,
  552. value = [255.0,0,0,255.0])
  553. self.createColorEntry(
  554. sfFrame, 'Style', 'Hull Color',
  555. 'Color of Hull',
  556. command = self.setHullColor,
  557. value = [255.0,128.0,128.0,255.0])
  558. #drawFrame.pack(fill = Tkinter.X)
  559. ## OPTIONS PAGE ##
  560. optionsFrame = Frame(self.optionsPage, relief = Tkinter.SUNKEN,
  561. borderwidth = 2)
  562. label = Label(optionsFrame, text = 'RECORDING OPTIONS',
  563. font=('MSSansSerif', 12, 'bold'))
  564. label.pack(fill = Tkinter.X)
  565. # Hooks
  566. frame = Frame(optionsFrame)
  567. widget = self.createLabeledEntry(
  568. frame, 'Recording', 'Record Hook',
  569. 'Hook used to start/stop recording',
  570. value = self.startStopHook,
  571. command = self.setStartStopHook)[0]
  572. label = self.getWidget('Recording', 'Record Hook-Label')
  573. label.configure(width = 16, anchor = Tkinter.W)
  574. self.setStartStopHook()
  575. widget = self.createLabeledEntry(
  576. frame, 'Recording', 'Keyframe Hook',
  577. 'Hook used to add a new keyframe',
  578. value = self.keyframeHook,
  579. command = self.setKeyframeHook)[0]
  580. label = self.getWidget('Recording', 'Keyframe Hook-Label')
  581. label.configure(width = 16, anchor = Tkinter.W)
  582. self.setKeyframeHook()
  583. frame.pack(expand = 1, fill = Tkinter.X)
  584. # PreRecordFunc
  585. frame = Frame(optionsFrame)
  586. widget = self.createComboBox(
  587. frame, 'Recording', 'Pre-Record Func',
  588. 'Function called before sampling each point',
  589. PRF_UTILITIES, self.setPreRecordFunc,
  590. history = 1, expand = 1)
  591. widget.configure(label_width = 16, label_anchor = Tkinter.W)
  592. widget.configure(entryfield_entry_state = 'normal')
  593. # Initialize preRecordFunc
  594. self.preRecordFunc = eval(PRF_UTILITIES[0])
  595. self.createCheckbutton(frame, 'Recording', 'PRF Active',
  596. 'On: Pre Record Func enabled',
  597. None, 0,
  598. side = Tkinter.LEFT, fill = Tkinter.BOTH, expand = 0)
  599. frame.pack(expand = 1, fill = Tkinter.X)
  600. # Pack record frame
  601. optionsFrame.pack(fill = Tkinter.X, pady = 2)
  602. self.mainNotebook.setnaturalsize()
  603. def pushUndo(self, fResetRedo = 1):
  604. SEditor.pushUndo([self.nodePath])
  605. def undoHook(self):
  606. # Reflect new changes
  607. pass
  608. def pushUndoHook(self):
  609. # Make sure button is reactivated
  610. self.undoButton.configure(state = 'normal')
  611. def undoListEmptyHook(self):
  612. # Make sure button is deactivated
  613. self.undoButton.configure(state = 'disabled')
  614. def pushRedo(self):
  615. SEditor.pushRedo([self.nodePath])
  616. def redoHook(self):
  617. # Reflect new changes
  618. pass
  619. def pushRedoHook(self):
  620. # Make sure button is reactivated
  621. self.redoButton.configure(state = 'normal')
  622. def redoListEmptyHook(self):
  623. # Make sure button is deactivated
  624. self.redoButton.configure(state = 'disabled')
  625. def selectedNodePathHook(self, nodePath):
  626. """
  627. Hook called upon selection of a node path used to select playback
  628. marker if subnode selected
  629. """
  630. taskMgr.remove(self.name + '-curveEditTask')
  631. print nodePath.id()
  632. if nodePath.id() in self.playbackMarkerIds:
  633. SEditor.select(self.playbackMarker)
  634. elif nodePath.id() in self.tangentMarkerIds:
  635. SEditor.select(self.tangentMarker)
  636. elif nodePath.id() == self.playbackMarker.id():
  637. self.tangentGroup.show()
  638. taskMgr.add(self.curveEditTask,
  639. self.name + '-curveEditTask')
  640. elif nodePath.id() == self.tangentMarker.id():
  641. self.tangentGroup.show()
  642. taskMgr.add(self.curveEditTask,
  643. self.name + '-curveEditTask')
  644. else:
  645. self.tangentGroup.hide()
  646. def getChildIds(self, nodePath):
  647. ids = [nodePath.id()]
  648. kids = nodePath.getChildrenAsList()
  649. for kid in kids:
  650. ids += self.getChildIds(kid)
  651. return ids
  652. def deselectedNodePathHook(self, nodePath):
  653. """
  654. Hook called upon deselection of a node path used to select playback
  655. marker if subnode selected
  656. """
  657. if ((nodePath.id() == self.playbackMarker.id()) or
  658. (nodePath.id() == self.tangentMarker.id())):
  659. self.tangentGroup.hide()
  660. def curveEditTask(self,state):
  661. if self.curveCollection != None:
  662. # Update curve position
  663. if self.manipulandumId == self.playbackMarker.id():
  664. # Show playback marker
  665. self.playbackMarker.getChild(0).show()
  666. pos = Point3(0)
  667. hpr = Point3(0)
  668. pos = self.playbackMarker.getPos(self.nodePathParent)
  669. hpr = self.playbackMarker.getHpr(self.nodePathParent)
  670. self.curveCollection.adjustXyz(
  671. self.playbackTime, VBase3(pos[0], pos[1], pos[2]))
  672. self.curveCollection.adjustHpr(
  673. self.playbackTime, VBase3(hpr[0], hpr[1], hpr[2]))
  674. # Note: this calls recompute on the curves
  675. self.nurbsCurveDrawer.draw()
  676. # Update tangent
  677. if self.manipulandumId == self.tangentMarker.id():
  678. # If manipulating marker, update tangent
  679. # Hide playback marker
  680. self.playbackMarker.getChild(0).hide()
  681. # Where is tangent marker relative to playback marker
  682. tan = self.tangentMarker.getPos()
  683. # Transform this vector to curve space
  684. tan2Curve = Vec3(
  685. self.playbackMarker.getMat(
  686. self.nodePathParent).xformVec(tan))
  687. # Update nurbs curve
  688. self.curveCollection.getXyzCurve().adjustTangent(
  689. self.playbackTime,
  690. tan2Curve[0], tan2Curve[1], tan2Curve[2])
  691. # Note: this calls recompute on the curves
  692. self.nurbsCurveDrawer.draw()
  693. else:
  694. # Show playback marker
  695. self.playbackMarker.getChild(0).show()
  696. # Update tangent marker line
  697. tan = Point3(0)
  698. self.curveCollection.getXyzCurve().getTangent(
  699. self.playbackTime, tan)
  700. # Transform this point to playback marker space
  701. tan.assign(
  702. self.nodePathParent.getMat(
  703. self.playbackMarker).xformVec(tan))
  704. self.tangentMarker.setPos(tan)
  705. # In either case update tangent line
  706. self.tangentLines.setVertex(1, tan[0], tan[1], tan[2])
  707. return Task.cont
  708. def manipulateObjectStartHook(self):
  709. self.manipulandumId = None
  710. if SEditor.selected.last:
  711. if SEditor.selected.last.id() == self.playbackMarker.id():
  712. self.manipulandumId = self.playbackMarker.id()
  713. elif SEditor.selected.last.id() == self.tangentMarker.id():
  714. self.manipulandumId = self.tangentMarker.id()
  715. def manipulateObjectCleanupHook(self):
  716. # Clear flag
  717. self.manipulandumId = None
  718. def onDestroy(self, event):
  719. # Remove hooks
  720. for event, method in self.actionEvents:
  721. self.ignore(event)
  722. # remove start stop hook
  723. self.ignore(self.startStopHook)
  724. self.ignore(self.keyframeHook)
  725. self.curveNodePath.reparentTo(self.recorderNodePath)
  726. self.trace.reparentTo(self.recorderNodePath)
  727. self.recorderNodePath.removeNode()
  728. # Make sure markers are deselected
  729. SEditor.deselect(self.playbackMarker)
  730. SEditor.deselect(self.tangentMarker)
  731. # Remove tasks
  732. taskMgr.remove(self.name + '-recordTask')
  733. taskMgr.remove(self.name + '-playbackTask')
  734. taskMgr.remove(self.name + '-curveEditTask')
  735. self.mopathRecorderNode.removeChildren()
  736. self.mopathRecorderNode.removeNode()
  737. messenger.send('mPath_close')
  738. messenger.send('SGE_Update Explorer',[render])
  739. def createNewPointSet(self, curveName = None):
  740. if curveName == None:
  741. self.pointSetName = self.name + '-ps-' + `self.pointSetCount`
  742. else:
  743. self.pointSetName = curveName
  744. # Update dictionary and record pointer to new point set
  745. self.pointSet = self.pointSetDict[self.pointSetName] = []
  746. # Update combo box
  747. comboBox = self.getWidget('Mopath', 'Path:')
  748. scrolledList = comboBox.component('scrolledlist')
  749. listbox = scrolledList.component('listbox')
  750. names = list(listbox.get(0,'end'))
  751. names.append(self.pointSetName)
  752. scrolledList.setlist(names)
  753. comboBox.selectitem(self.pointSetName)
  754. # Update count
  755. self.pointSetCount += 1
  756. def extractPointSetFromCurveFitter(self, curveName = None):
  757. # Get new point set based on newly created curve
  758. self.createNewPointSet(curveName)
  759. for i in range(self.curveFitter.getNumSamples()):
  760. time = self.curveFitter.getSampleT(i)
  761. pos = Point3(self.curveFitter.getSampleXyz(i))
  762. hpr = Point3(self.curveFitter.getSampleHpr(i))
  763. self.pointSet.append([time, pos, hpr])
  764. def extractPointSetFromCurveCollection(self, curveName=None):
  765. # Use curve to compute new point set
  766. # Record maxT
  767. self.maxT = self.curveCollection.getMaxT()
  768. # Determine num samples
  769. # Limit point set to 1000 points and samples per second to 30
  770. samplesPerSegment = min(30.0, 1000.0/self.curveCollection.getMaxT())
  771. self.setNumSamples(self.maxT * samplesPerSegment)
  772. # Sample the curve but don't create a new curve collection
  773. self.sampleCurve(fCompute = 0, curveName = curveName)
  774. # Update widgets based on new data
  775. self.updateWidgets()
  776. def selectPointSetNamed(self, name):
  777. self.pointSet = self.pointSetDict.get(name, None)
  778. # Reload points into curve fitter
  779. # Reset curve fitters
  780. self.curveFitter.reset()
  781. for time, pos, hpr in self.pointSet:
  782. # Add it to the curve fitters
  783. self.curveFitter.addXyzHpr(time, pos, hpr)
  784. # Compute curve
  785. self.computeCurves()
  786. def setPathVis(self):
  787. if self.getVariable('Style', 'Path').get():
  788. self.curveNodePath.show()
  789. else:
  790. self.curveNodePath.hide()
  791. def setKnotVis(self):
  792. self.nurbsCurveDrawer.setShowKnots(
  793. self.getVariable('Style', 'Knots').get())
  794. def setCvVis(self):
  795. self.nurbsCurveDrawer.setShowCvs(
  796. self.getVariable('Style', 'CVs').get())
  797. def setHullVis(self):
  798. self.nurbsCurveDrawer.setShowHull(
  799. self.getVariable('Style', 'Hull').get())
  800. def setTraceVis(self):
  801. if self.getVariable('Style', 'Trace').get():
  802. self.trace.show()
  803. else:
  804. self.trace.hide()
  805. def setMarkerVis(self):
  806. if self.getVariable('Style', 'Marker').get():
  807. self.playbackMarker.reparentTo(self.recorderNodePath)
  808. else:
  809. self.playbackMarker.reparentTo(hidden)
  810. def setNumSegs(self, value):
  811. self.numSegs = int(value)
  812. self.nurbsCurveDrawer.setNumSegs(self.numSegs)
  813. def setNumTicks(self, value):
  814. self.nurbsCurveDrawer.setNumTicks(float(value))
  815. def setTickScale(self, value):
  816. self.nurbsCurveDrawer.setTickScale(float(value))
  817. def setPathColor(self, color):
  818. self.nurbsCurveDrawer.setColor(
  819. color[0]/255.0,color[1]/255.0,color[2]/255.0)
  820. self.nurbsCurveDrawer.draw()
  821. def setKnotColor(self, color):
  822. self.nurbsCurveDrawer.setKnotColor(
  823. color[0]/255.0,color[1]/255.0,color[2]/255.0)
  824. def setCvColor(self, color):
  825. self.nurbsCurveDrawer.setCvColor(
  826. color[0]/255.0,color[1]/255.0,color[2]/255.0)
  827. def setTickColor(self, color):
  828. self.nurbsCurveDrawer.setTickColor(
  829. color[0]/255.0,color[1]/255.0,color[2]/255.0)
  830. def setHullColor(self, color):
  831. self.nurbsCurveDrawer.setHullColor(
  832. color[0]/255.0,color[1]/255.0,color[2]/255.0)
  833. def setStartStopHook(self, event = None):
  834. # Clear out old hook
  835. self.ignore(self.startStopHook)
  836. # Record new one
  837. hook = self.getVariable('Recording', 'Record Hook').get()
  838. self.startStopHook = hook
  839. # Add new one
  840. self.accept(self.startStopHook, self.toggleRecordVar)
  841. def setKeyframeHook(self, event = None):
  842. # Clear out old hook
  843. self.ignore(self.keyframeHook)
  844. # Record new one
  845. hook = self.getVariable('Recording', 'Keyframe Hook').get()
  846. self.keyframeHook = hook
  847. # Add new one
  848. self.accept(self.keyframeHook, self.addKeyframe)
  849. def reset(self):
  850. self.pointSet = []
  851. self.hasPoints = 0
  852. self.curveCollection = None
  853. self.curveFitter.reset()
  854. self.nurbsCurveDrawer.hide()
  855. def setSamplingMode(self, mode):
  856. self.samplingMode = mode
  857. def disableKeyframeButton(self):
  858. self.getWidget('Recording', 'Add Keyframe')['state'] = 'disabled'
  859. def enableKeyframeButton(self):
  860. self.getWidget('Recording', 'Add Keyframe')['state'] = 'normal'
  861. def setRecordingType(self, type):
  862. self.recordingType.set(type)
  863. def setNewCurveMode(self):
  864. self.setRecordingType('New Curve')
  865. def setRefineMode(self):
  866. self.setRecordingType('Refine')
  867. def setExtendMode(self):
  868. self.setRecordingType('Extend')
  869. def toggleRecordVar(self):
  870. # Get recording variable
  871. v = self.getVariable('Recording', 'Record')
  872. # Toggle it
  873. v.set(1 - v.get())
  874. # Call the command
  875. self.toggleRecord()
  876. def toggleRecord(self):
  877. if self.getVariable('Recording', 'Record').get():
  878. # Reparent a Marker to target nodePath to show where the recording start
  879. self.markingNode = self.nodePath.getParent().attachNewNode('MopthMarkerNode')
  880. self.nodePath.copyTo(self.markingNode)
  881. self.markingNode.wrtReparentTo(render)
  882. # Kill old tasks
  883. taskMgr.remove(self.name + '-recordTask')
  884. taskMgr.remove(self.name + '-curveEditTask')
  885. # Remove old curve
  886. self.nurbsCurveDrawer.hide()
  887. # Reset curve fitters
  888. self.curveFitter.reset()
  889. # Update sampling mode button if necessary
  890. if self.samplingMode == 'Continuous':
  891. self.disableKeyframeButton()
  892. # Create a new point set to hold raw data
  893. self.createNewPointSet()
  894. # Clear out old trace, get ready to draw new
  895. self.initTrace()
  896. # Keyframe mode?
  897. if (self.samplingMode == 'Keyframe'):
  898. # Record first point
  899. self.lastPos.assign(Point3(
  900. self.nodePath.getPos(self.nodePathParent)))
  901. # Init delta time
  902. self.deltaTime = 0.0
  903. # Record first point
  904. self.recordPoint(self.recordStart)
  905. # Everything else
  906. else:
  907. if ((self.recordingType.get() == 'Refine') or
  908. (self.recordingType.get() == 'Extend')):
  909. # Turn off looping playback
  910. self.loopPlayback = 0
  911. # Update widget to reflect new value
  912. self.getVariable('Playback', 'Loop').set(0)
  913. # Select tempCS as playback nodepath
  914. self.oldPlaybackNodePath = self.playbackNodePath
  915. self.setPlaybackNodePath(self.tempCS)
  916. # Parent record node path to temp
  917. self.nodePath.reparentTo(self.playbackNodePath)
  918. # Align with temp
  919. self.nodePath.setPosHpr(0,0,0,0,0,0)
  920. # Set playback start to self.recordStart
  921. self.playbackGoTo(self.recordStart)
  922. # start flying nodePath along path
  923. self.startPlayback()
  924. # Start new task
  925. t = taskMgr.add(
  926. self.recordTask, self.name + '-recordTask')
  927. t.startTime = globalClock.getFrameTime()
  928. else:
  929. self.markingNode.removeNode() # Hide the marker in the end of recording
  930. if self.samplingMode == 'Continuous':
  931. # Kill old task
  932. taskMgr.remove(self.name + '-recordTask')
  933. if ((self.recordingType.get() == 'Refine') or
  934. (self.recordingType.get() == 'Extend')):
  935. # Reparent node path back to parent
  936. self.nodePath.wrtReparentTo(self.nodePathParent)
  937. # Restore playback Node Path
  938. self.setPlaybackNodePath(self.oldPlaybackNodePath)
  939. else:
  940. # Add last point
  941. self.addKeyframe(0)
  942. # Reset sampling mode
  943. self.setSamplingMode('Continuous')
  944. self.enableKeyframeButton()
  945. # Clean up after refine or extend
  946. if ((self.recordingType.get() == 'Refine') or
  947. (self.recordingType.get() == 'Extend')):
  948. # Merge prePoints, pointSet, postPoints
  949. self.mergePoints()
  950. # Clear out pre and post list
  951. self.prePoints = []
  952. self.postPoints = []
  953. # Reset recording mode
  954. self.setNewCurveMode()
  955. # Compute curve
  956. self.computeCurves()
  957. def recordTask(self, state):
  958. # Record raw data point
  959. time = self.recordStart + (
  960. globalClock.getFrameTime() - state.startTime)
  961. self.recordPoint(time)
  962. return Task.cont
  963. def addKeyframe(self, fToggleRecord = 1):
  964. # Make sure we're in a recording mode!
  965. if (fToggleRecord and
  966. (not self.getVariable('Recording', 'Record').get())):
  967. # Set sampling mode
  968. self.setSamplingMode('Keyframe')
  969. # This will automatically add the first point
  970. self.toggleRecordVar()
  971. else:
  972. # Use distance as a time
  973. pos = self.nodePath.getPos(self.nodePathParent)
  974. deltaPos = Vec3(pos - self.lastPos).length()
  975. if deltaPos != 0:
  976. # If we've moved at all, use delta Pos as time
  977. self.deltaTime = self.deltaTime + deltaPos
  978. else:
  979. # Otherwise add one second
  980. self.deltaTime = self.deltaTime + 1.0
  981. # Record point at new time
  982. self.recordPoint(self.recordStart + self.deltaTime)
  983. # Update last pos
  984. self.lastPos.assign(pos)
  985. def easeInOut(self, t):
  986. x = t * t
  987. return (3 * x) - (2 * t * x)
  988. def setPreRecordFunc(self, func):
  989. # Note: If func is one defined at command prompt, need to set
  990. # __builtins__.func = func at command line
  991. self.preRecordFunc = eval(func)
  992. # Update widget to reflect new value
  993. self.getVariable('Recording', 'PRF Active').set(1)
  994. def recordPoint(self, time):
  995. # Call user define callback before recording point
  996. if (self.getVariable('Recording', 'PRF Active').get() and
  997. (self.preRecordFunc != None)):
  998. self.preRecordFunc()
  999. # Get point
  1000. pos = self.nodePath.getPos(self.nodePathParent)
  1001. hpr = self.nodePath.getHpr(self.nodePathParent)
  1002. qNP = Quat()
  1003. qNP.setHpr(hpr)
  1004. # Blend between recordNodePath and self.nodePath
  1005. if ((self.recordingType.get() == 'Refine') or
  1006. (self.recordingType.get() == 'Extend')):
  1007. if ((time < self.controlStart) and
  1008. ((self.controlStart - self.recordStart) != 0.0)):
  1009. rPos = self.playbackNodePath.getPos(self.nodePathParent)
  1010. rHpr = self.playbackNodePath.getHpr(self.nodePathParent)
  1011. qR = Quat()
  1012. qR.setHpr(rHpr)
  1013. t = self.easeInOut(((time - self.recordStart)/
  1014. (self.controlStart - self.recordStart)))
  1015. # Transition between the recorded node path and the driven one
  1016. pos = (rPos * (1 - t)) + (pos * t)
  1017. q = qSlerp(qR, qNP, t)
  1018. hpr.assign(q.getHpr())
  1019. elif ((self.recordingType.get() == 'Refine') and
  1020. (time > self.controlStop) and
  1021. ((self.recordStop - self.controlStop) != 0.0)):
  1022. rPos = self.playbackNodePath.getPos(self.nodePathParent)
  1023. rHpr = self.playbackNodePath.getHpr(self.nodePathParent)
  1024. qR = Quat()
  1025. qR.setHpr(rHpr)
  1026. t = self.easeInOut(((time - self.controlStop)/
  1027. (self.recordStop - self.controlStop)))
  1028. # Transition between the recorded node path and the driven one
  1029. pos = (pos * (1 - t)) + (rPos * t)
  1030. q = qSlerp(qNP, qR, t)
  1031. hpr.assign(q.getHpr())
  1032. # Add it to the point set
  1033. self.pointSet.append([time, pos, hpr])
  1034. # Add it to the curve fitters
  1035. self.curveFitter.addXyzHpr(time, pos, hpr)
  1036. # Update trace now if recording keyframes
  1037. if (self.samplingMode == 'Keyframe'):
  1038. self.trace.reset()
  1039. for t, p, h in self.pointSet:
  1040. self.trace.drawTo(p[0], p[1], p[2])
  1041. self.trace.create()
  1042. def computeCurves(self):
  1043. # Check to make sure curve fitters have points
  1044. if (self.curveFitter.getNumSamples() == 0):
  1045. print 'MopathRecorder.computeCurves: Must define curve first'
  1046. return
  1047. # Create curves
  1048. # XYZ
  1049. self.curveFitter.sortPoints()
  1050. self.curveFitter.wrapHpr()
  1051. self.curveFitter.computeTangents(1)
  1052. # This is really a collection
  1053. self.curveCollection = self.curveFitter.makeNurbs()
  1054. self.nurbsCurveDrawer.setCurves(self.curveCollection)
  1055. self.nurbsCurveDrawer.draw()
  1056. # Update widget based on new curve
  1057. self.updateWidgets()
  1058. def initTrace(self):
  1059. self.trace.reset()
  1060. # Put trace line segs under node path's parent
  1061. self.trace.reparentTo(self.nodePathParent)
  1062. # Show it
  1063. self.trace.show()
  1064. def updateWidgets(self):
  1065. if not self.curveCollection:
  1066. return
  1067. self.fAdjustingValues = 1
  1068. # Widgets depending on max T
  1069. maxT = self.curveCollection.getMaxT()
  1070. maxT_text = '%0.2f' % maxT
  1071. # Playback controls
  1072. self.getWidget('Playback', 'Time').configure(max = maxT_text)
  1073. self.getVariable('Resample', 'Path Duration').set(maxT_text)
  1074. # Refine widgets
  1075. widget = self.getWidget('Refine Page', 'Refine From')
  1076. widget.configure(max = maxT)
  1077. widget.set(0.0)
  1078. widget = self.getWidget('Refine Page', 'Control Start')
  1079. widget.configure(max = maxT)
  1080. widget.set(0.0)
  1081. widget = self.getWidget('Refine Page', 'Control Stop')
  1082. widget.configure(max = maxT)
  1083. widget.set(float(maxT))
  1084. widget = self.getWidget('Refine Page', 'Refine To')
  1085. widget.configure(max = maxT)
  1086. widget.set(float(maxT))
  1087. # Extend widgets
  1088. widget = self.getWidget('Extend Page', 'Extend From')
  1089. widget.configure(max = maxT)
  1090. widget.set(float(0.0))
  1091. widget = self.getWidget('Extend Page', 'Control Start')
  1092. widget.configure(max = maxT)
  1093. widget.set(float(0.0))
  1094. # Crop widgets
  1095. widget = self.getWidget('Crop Page', 'Crop From')
  1096. widget.configure(max = maxT)
  1097. widget.set(float(0.0))
  1098. widget = self.getWidget('Crop Page', 'Crop To')
  1099. widget.configure(max = maxT)
  1100. widget.set(float(maxT))
  1101. self.maxT = float(maxT)
  1102. # Widgets depending on number of samples
  1103. numSamples = self.curveFitter.getNumSamples()
  1104. widget = self.getWidget('Resample', 'Points Between Samples')
  1105. widget.configure(max=numSamples)
  1106. widget = self.getWidget('Resample', 'Num. Samples')
  1107. widget.configure(max = 4 * numSamples)
  1108. widget.set(numSamples, 0)
  1109. self.fAdjustingValues = 0
  1110. def selectNodePathNamed(self, name):
  1111. nodePath = None
  1112. if name == 'init':
  1113. nodePath = self.nodePath
  1114. # Add Combo box entry for the initial node path
  1115. self.addNodePath(nodePath)
  1116. elif name == 'selected':
  1117. nodePath = SEditor.selected.last
  1118. # Add Combo box entry for this selected object
  1119. self.addNodePath(nodePath)
  1120. else:
  1121. nodePath = self.nodePathDict.get(name, None)
  1122. if (nodePath == None):
  1123. # See if this evaluates into a node path
  1124. try:
  1125. nodePath = eval(name)
  1126. if isinstance(nodePath, NodePath):
  1127. self.addNodePath(nodePath)
  1128. else:
  1129. # Good eval but not a node path, give up
  1130. nodePath = None
  1131. except:
  1132. # Bogus eval
  1133. nodePath = None
  1134. # Clear bogus entry from listbox
  1135. listbox = self.nodePathMenu.component('scrolledlist')
  1136. listbox.setlist(self.nodePathNames)
  1137. else:
  1138. if name == 'widget':
  1139. # Record relationship between selected nodes and widget
  1140. SEditor.selected.getWrtAll()
  1141. if name == 'marker':
  1142. self.playbackMarker.show()
  1143. # Initialize tangent marker position
  1144. tan = Point3(0)
  1145. if self.curveCollection != None:
  1146. self.curveCollection.getXyzCurve().getTangent(
  1147. self.playbackTime, tan)
  1148. self.tangentMarker.setPos(tan)
  1149. else:
  1150. self.playbackMarker.hide()
  1151. # Update active node path
  1152. self.setNodePath(nodePath)
  1153. messenger.send('mPath_requestCurveList',[nodePath,self.name])
  1154. self.accept('curveListFor'+self.name, self.addCurvesFromNodepath)
  1155. def setNodePath(self, nodePath):
  1156. self.playbackNodePath = self.nodePath = nodePath
  1157. if self.nodePath:
  1158. # Record nopath's parent
  1159. self.nodePathParent = self.nodePath.getParent()
  1160. # Put curve drawer under record node path's parent
  1161. self.curveNodePath.reparentTo(self.nodePathParent)
  1162. # Set entry color
  1163. self.nodePathMenuEntry.configure(
  1164. background = self.nodePathMenuBG)
  1165. else:
  1166. # Flash entry
  1167. self.nodePathMenuEntry.configure(background = 'Pink')
  1168. def setPlaybackNodePath(self, nodePath):
  1169. self.playbackNodePath = nodePath
  1170. def addNodePath(self, nodePath):
  1171. self.addNodePathToDict(nodePath, self.nodePathNames,
  1172. self.nodePathMenu, self.nodePathDict)
  1173. def addNodePathToDict(self, nodePath, names, menu, dict):
  1174. if not nodePath:
  1175. return
  1176. # Get node path's name
  1177. name = nodePath.getName()
  1178. if name in ['mopathRecorderTempCS', 'widget', 'camera', 'marker']:
  1179. dictName = name
  1180. else:
  1181. # Generate a unique name for the dict
  1182. dictName = name # + '-' + `nodePath.id()`
  1183. if not dict.has_key(dictName):
  1184. # Update combo box to include new item
  1185. names.append(dictName)
  1186. listbox = menu.component('scrolledlist')
  1187. listbox.setlist(names)
  1188. # Add new item to dictionary
  1189. dict[dictName] = nodePath
  1190. menu.selectitem(dictName)
  1191. def setLoopPlayback(self):
  1192. self.loopPlayback = self.getVariable('Playback', 'Loop').get()
  1193. def playbackGoTo(self, time):
  1194. if self.curveCollection == None:
  1195. return
  1196. self.playbackTime = CLAMP(time, 0.0, self.maxT)
  1197. if self.curveCollection != None:
  1198. pos = Point3(0)
  1199. hpr = Point3(0)
  1200. self.curveCollection.evaluate(self.playbackTime, pos, hpr)
  1201. self.playbackNodePath.setPosHpr(self.nodePathParent, pos, hpr)
  1202. def startPlayback(self):
  1203. if self.curveCollection == None:
  1204. return
  1205. # Kill any existing tasks
  1206. self.stopPlayback()
  1207. # Make sure checkbutton is set
  1208. self.getVariable('Playback', 'Play').set(1)
  1209. # Start new playback task
  1210. t = taskMgr.add(
  1211. self.playbackTask, self.name + '-playbackTask')
  1212. t.currentTime = self.playbackTime
  1213. t.lastTime = globalClock.getFrameTime()
  1214. def setSpeedScale(self, value):
  1215. self.speedScale.set(math.log10(value))
  1216. def setPlaybackSF(self, value):
  1217. self.playbackSF = pow(10.0, float(value))
  1218. self.speedVar.set('%0.2f' % self.playbackSF)
  1219. def playbackTask(self, state):
  1220. time = globalClock.getFrameTime()
  1221. dTime = self.playbackSF * (time - state.lastTime)
  1222. state.lastTime = time
  1223. if self.loopPlayback:
  1224. cTime = (state.currentTime + dTime) % self.maxT
  1225. else:
  1226. cTime = state.currentTime + dTime
  1227. # Stop task if not looping and at end of curve
  1228. # Or if refining curve and past recordStop
  1229. if ((self.recordingType.get() == 'Refine') and
  1230. (cTime > self.recordStop)):
  1231. # Go to recordStop
  1232. self.getWidget('Playback', 'Time').set(self.recordStop)
  1233. # Then stop playback
  1234. self.stopPlayback()
  1235. # Also kill record task
  1236. self.toggleRecordVar()
  1237. return Task.done
  1238. elif ((self.loopPlayback == 0) and (cTime > self.maxT)):
  1239. # Go to maxT
  1240. self.getWidget('Playback', 'Time').set(self.maxT)
  1241. # Then stop playback
  1242. self.stopPlayback()
  1243. return Task.done
  1244. elif ((self.recordingType.get() == 'Extend') and
  1245. (cTime > self.controlStart)):
  1246. # Go to final point
  1247. self.getWidget('Playback', 'Time').set(self.controlStart)
  1248. # Stop playback
  1249. self.stopPlayback()
  1250. return Task.done
  1251. # Otherwise go to specified time and continue
  1252. self.getWidget('Playback', 'Time').set(cTime)
  1253. state.currentTime = cTime
  1254. return Task.cont
  1255. def stopPlayback(self):
  1256. self.getVariable('Playback', 'Play').set(0)
  1257. taskMgr.remove(self.name + '-playbackTask')
  1258. def jumpToStartOfPlayback(self):
  1259. self.stopPlayback()
  1260. self.getWidget('Playback', 'Time').set(0.0)
  1261. def jumpToEndOfPlayback(self):
  1262. self.stopPlayback()
  1263. if self.curveCollection != None:
  1264. self.getWidget('Playback', 'Time').set(self.maxT)
  1265. def startStopPlayback(self):
  1266. if self.getVariable('Playback', 'Play').get():
  1267. self.startPlayback()
  1268. else:
  1269. self.stopPlayback()
  1270. def setDesampleFrequency(self, frequency):
  1271. self.desampleFrequency = frequency
  1272. def desampleCurve(self):
  1273. if (self.curveFitter.getNumSamples() == 0):
  1274. print 'MopathRecorder.desampleCurve: Must define curve first'
  1275. return
  1276. # NOTE: This is destructive, points will be deleted from curve fitter
  1277. self.curveFitter.desample(self.desampleFrequency)
  1278. # Compute new curve based on desampled data
  1279. self.computeCurves()
  1280. # Get point set from the curve fitter
  1281. self.extractPointSetFromCurveFitter()
  1282. def setNumSamples(self, numSamples):
  1283. self.numSamples = int(numSamples)
  1284. def sampleCurve(self, fCompute = 1, curveName = None):
  1285. if self.curveCollection == None:
  1286. print 'MopathRecorder.sampleCurve: Must define curve first'
  1287. return
  1288. # Reset curve fitters
  1289. self.curveFitter.reset()
  1290. # Sample curve using specified number of samples
  1291. self.curveFitter.sample(self.curveCollection, self.numSamples)
  1292. if fCompute:
  1293. # Now recompute curves
  1294. self.computeCurves()
  1295. # Get point set from the curve fitter
  1296. self.extractPointSetFromCurveFitter(curveName)
  1297. def makeEven(self):
  1298. # Note: segments_per_unit = 2 seems to give a good fit
  1299. self.curveCollection.makeEven(self.maxT, 2)
  1300. # Get point set from curve
  1301. self.extractPointSetFromCurveCollection()
  1302. def faceForward(self):
  1303. # Note: segments_per_unit = 2 seems to give a good fit
  1304. self.curveCollection.faceForward(2)
  1305. # Get point set from curve
  1306. self.extractPointSetFromCurveCollection()
  1307. def setPathDuration(self, event):
  1308. newMaxT = float(self.getWidget('Resample', 'Path Duration').get())
  1309. self.setPathDurationTo(newMaxT)
  1310. def setPathDurationTo(self, newMaxT):
  1311. # Compute scale factor
  1312. sf = newMaxT/self.maxT
  1313. # Scale curve collection
  1314. self.curveCollection.resetMaxT(newMaxT)
  1315. # Scale point set
  1316. # Save handle to old point set
  1317. oldPointSet = self.pointSet
  1318. # Create new point set
  1319. self.createNewPointSet()
  1320. # Reset curve fitters
  1321. self.curveFitter.reset()
  1322. # Now scale values
  1323. for time, pos, hpr in oldPointSet:
  1324. newTime = time * sf
  1325. # Update point set
  1326. self.pointSet.append([newTime, Point3(pos), Point3(hpr)])
  1327. # Add it to the curve fitters
  1328. self.curveFitter.addXyzHpr(newTime, pos, hpr)
  1329. # Update widgets
  1330. self.updateWidgets()
  1331. # Compute curve
  1332. #self.computeCurves()
  1333. def setRecordStart(self,value):
  1334. self.recordStart = value
  1335. # Someone else is adjusting values, let them take care of it
  1336. if self.fAdjustingValues:
  1337. return
  1338. self.fAdjustingValues = 1
  1339. # Adjust refine widgets
  1340. # Make sure we're in sync
  1341. self.getWidget('Refine Page', 'Refine From').set(
  1342. self.recordStart)
  1343. self.getWidget('Extend Page', 'Extend From').set(
  1344. self.recordStart)
  1345. # Check bounds
  1346. if self.recordStart > self.controlStart:
  1347. self.getWidget('Refine Page', 'Control Start').set(
  1348. self.recordStart)
  1349. self.getWidget('Extend Page', 'Control Start').set(
  1350. self.recordStart)
  1351. if self.recordStart > self.controlStop:
  1352. self.getWidget('Refine Page', 'Control Stop').set(
  1353. self.recordStart)
  1354. if self.recordStart > self.recordStop:
  1355. self.getWidget('Refine Page', 'Refine To').set(self.recordStart)
  1356. # Move playback node path to specified time
  1357. self.getWidget('Playback', 'Time').set(value)
  1358. self.fAdjustingValues = 0
  1359. def getPrePoints(self, type = 'Refine'):
  1360. # Switch to appropriate recording type
  1361. self.setRecordingType(type)
  1362. # Reset prePoints
  1363. self.prePoints = []
  1364. # See if we need to save any points before recordStart
  1365. for i in range(len(self.pointSet)):
  1366. # Have we passed recordStart?
  1367. if self.recordStart < self.pointSet[i][0]:
  1368. # Get a copy of the points prior to recordStart
  1369. self.prePoints = self.pointSet[:i-1]
  1370. break
  1371. def setControlStart(self, value):
  1372. self.controlStart = value
  1373. # Someone else is adjusting values, let them take care of it
  1374. if self.fAdjustingValues:
  1375. return
  1376. self.fAdjustingValues = 1
  1377. # Adjust refine widgets
  1378. # Make sure both pages are in sync
  1379. self.getWidget('Refine Page', 'Control Start').set(
  1380. self.controlStart)
  1381. self.getWidget('Extend Page', 'Control Start').set(
  1382. self.controlStart)
  1383. # Check bounds on other widgets
  1384. if self.controlStart < self.recordStart:
  1385. self.getWidget('Refine Page', 'Refine From').set(
  1386. self.controlStart)
  1387. self.getWidget('Extend Page', 'Extend From').set(
  1388. self.controlStart)
  1389. if self.controlStart > self.controlStop:
  1390. self.getWidget('Refine Page', 'Control Stop').set(
  1391. self.controlStart)
  1392. if self.controlStart > self.recordStop:
  1393. self.getWidget('Refine Page', 'Refine To').set(
  1394. self.controlStart)
  1395. # Move playback node path to specified time
  1396. self.getWidget('Playback', 'Time').set(value)
  1397. self.fAdjustingValues = 0
  1398. def setControlStop(self, value):
  1399. self.controlStop = value
  1400. # Someone else is adjusting values, let them take care of it
  1401. if self.fAdjustingValues:
  1402. return
  1403. self.fAdjustingValues = 1
  1404. if self.controlStop < self.recordStart:
  1405. self.getWidget('Refine Page', 'Refine From').set(
  1406. self.controlStop)
  1407. if self.controlStop < self.controlStart:
  1408. self.getWidget('Refine Page', 'Control Start').set(
  1409. self.controlStop)
  1410. if self.controlStop > self.recordStop:
  1411. self.getWidget('Refine Page', 'Refine To').set(
  1412. self.controlStop)
  1413. # Move playback node path to specified time
  1414. self.getWidget('Playback', 'Time').set(value)
  1415. self.fAdjustingValues = 0
  1416. def setRefineStop(self, value):
  1417. self.recordStop = value
  1418. # Someone else is adjusting values, let them take care of it
  1419. if self.fAdjustingValues:
  1420. return
  1421. self.fAdjustingValues = 1
  1422. if self.recordStop < self.recordStart:
  1423. self.getWidget('Refine Page', 'Refine From').set(
  1424. self.recordStop)
  1425. if self.recordStop < self.controlStart:
  1426. self.getWidget('Refine Page', 'Control Start').set(
  1427. self.recordStop)
  1428. if self.recordStop < self.controlStop:
  1429. self.getWidget('Refine Page', 'Control Stop').set(
  1430. self.recordStop)
  1431. # Move playback node path to specified time
  1432. self.getWidget('Playback', 'Time').set(value)
  1433. self.fAdjustingValues = 0
  1434. def getPostPoints(self):
  1435. # Set flag so we know to do a refine pass
  1436. self.setRefineMode()
  1437. # Reset postPoints
  1438. self.postPoints = []
  1439. # See if we need to save any points after recordStop
  1440. for i in range(len(self.pointSet)):
  1441. # Have we reached recordStop?
  1442. if self.recordStop < self.pointSet[i][0]:
  1443. # Get a copy of the points after recordStop
  1444. self.postPoints = self.pointSet[i:]
  1445. break
  1446. def mergePoints(self):
  1447. # prepend pre points
  1448. self.pointSet[0:0] = self.prePoints
  1449. for time, pos, hpr in self.prePoints:
  1450. # Add it to the curve fitters
  1451. self.curveFitter.addXyzHpr(time, pos, hpr)
  1452. # And post points
  1453. # What is end time of pointSet?
  1454. endTime = self.pointSet[-1][0]
  1455. for time, pos, hpr in self.postPoints:
  1456. adjustedTime = endTime + (time - self.recordStop)
  1457. # Add it to point set
  1458. self.pointSet.append([adjustedTime, pos, hpr])
  1459. # Add it to the curve fitters
  1460. self.curveFitter.addXyzHpr(adjustedTime, pos, hpr)
  1461. def setCropFrom(self,value):
  1462. self.cropFrom = value
  1463. # Someone else is adjusting values, let them take care of it
  1464. if self.fAdjustingValues:
  1465. return
  1466. self.fAdjustingValues = 1
  1467. if self.cropFrom > self.cropTo:
  1468. self.getWidget('Crop Page', 'Crop To').set(
  1469. self.cropFrom)
  1470. # Move playback node path to specified time
  1471. self.getWidget('Playback', 'Time').set(value)
  1472. self.fAdjustingValues = 0
  1473. def setCropTo(self,value):
  1474. self.cropTo = value
  1475. # Someone else is adjusting values, let them take care of it
  1476. if self.fAdjustingValues:
  1477. return
  1478. self.fAdjustingValues = 1
  1479. if self.cropTo < self.cropFrom:
  1480. self.getWidget('Crop Page', 'Crop From').set(
  1481. self.cropTo)
  1482. # Move playback node path to specified time
  1483. self.getWidget('Playback', 'Time').set(value)
  1484. self.fAdjustingValues = 0
  1485. def cropCurve(self):
  1486. if self.pointSet == None:
  1487. print 'Empty Point Set'
  1488. return
  1489. # Keep handle on old points
  1490. oldPoints = self.pointSet
  1491. # Create new point set
  1492. self.createNewPointSet()
  1493. # Copy over points between from/to
  1494. # Reset curve fitters
  1495. self.curveFitter.reset()
  1496. # Add start point
  1497. pos = Point3(0)
  1498. hpr = Point3(0)
  1499. self.curveCollection.evaluate(self.cropFrom, pos, hpr)
  1500. self.curveFitter.addXyzHpr(0.0, pos, hpr)
  1501. # Get points within bounds
  1502. for time, pos, hpr in oldPoints:
  1503. # Is it within the time?
  1504. if ((time > self.cropFrom) and
  1505. (time < self.cropTo)):
  1506. # Add it to the curve fitters
  1507. t = time - self.cropFrom
  1508. self.curveFitter.addXyzHpr(t, pos, hpr)
  1509. # And the point set
  1510. self.pointSet.append([t, pos, hpr])
  1511. # Add last point
  1512. pos = Vec3(0)
  1513. hpr = Vec3(0)
  1514. self.curveCollection.evaluate(self.cropTo, pos, hpr)
  1515. self.curveFitter.addXyzHpr(self.cropTo - self.cropFrom, pos, hpr)
  1516. # Compute curve
  1517. self.computeCurves()
  1518. def loadCurveFromFile(self):
  1519. # Use first directory in model path
  1520. mPath = getModelPath()
  1521. if mPath.getNumDirectories() > 0:
  1522. if `mPath.getDirectory(0)` == '.':
  1523. path = '.'
  1524. else:
  1525. path = mPath.getDirectory(0).toOsSpecific()
  1526. else:
  1527. path = '.'
  1528. if not os.path.isdir(path):
  1529. print 'MopathRecorder Info: Empty Model Path!'
  1530. print 'Using current directory'
  1531. path = '.'
  1532. mopathFilename = askopenfilename(
  1533. defaultextension = '.egg',
  1534. filetypes = (('Egg Files', '*.egg'),
  1535. ('Bam Files', '*.bam'),
  1536. ('All files', '*')),
  1537. initialdir = path,
  1538. title = 'Load Nurbs Curve',
  1539. parent = self.parent)
  1540. if mopathFilename:
  1541. self.reset()
  1542. nodePath = loader.loadModel(
  1543. Filename.fromOsSpecific(mopathFilename))
  1544. self.curveCollection = ParametricCurveCollection()
  1545. # MRM: Add error check
  1546. self.curveCollection.addCurves(nodePath.node())
  1547. nodePath.removeNode()
  1548. if self.curveCollection:
  1549. # Draw the curve
  1550. self.nurbsCurveDrawer.setCurves(self.curveCollection)
  1551. self.nurbsCurveDrawer.draw()
  1552. # Save a pointset for this curve
  1553. self.extractPointSetFromCurveCollection()
  1554. else:
  1555. self.reset()
  1556. def saveCurveToFile(self):
  1557. # Use first directory in model path
  1558. mPath = getModelPath()
  1559. if mPath.getNumDirectories() > 0:
  1560. if `mPath.getDirectory(0)` == '.':
  1561. path = '.'
  1562. else:
  1563. path = mPath.getDirectory(0).toOsSpecific()
  1564. else:
  1565. path = '.'
  1566. if not os.path.isdir(path):
  1567. print 'MopathRecorder Info: Empty Model Path!'
  1568. print 'Using current directory'
  1569. path = '.'
  1570. mopathFilename = asksaveasfilename(
  1571. defaultextension = '.egg',
  1572. filetypes = (('Egg Files', '*.egg'),
  1573. ('Bam Files', '*.bam'),
  1574. ('All files', '*')),
  1575. initialdir = path,
  1576. title = 'Save Nurbs Curve as',
  1577. parent = self.parent)
  1578. if mopathFilename:
  1579. self.curveCollection.writeEgg(Filename(mopathFilename))
  1580. def followTerrain(self, height = 1.0):
  1581. self.iRay.rayCollisionNodePath.reparentTo(self.nodePath)
  1582. entry = self.iRay.pickGeom3D()
  1583. if entry:
  1584. fromNodePath = entry.getFromNodePath()
  1585. hitPtDist = Vec3(entry.getSurfacePoint(fromNodePath))
  1586. self.nodePath.setZ(self.nodePath, height - hitPtDist)
  1587. self.iRay.rayCollisionNodePath.reparentTo(self.recorderNodePath)
  1588. ## WIDGET UTILITY FUNCTIONS ##
  1589. def addWidget(self, widget, category, text):
  1590. self.widgetDict[category + '-' + text] = widget
  1591. def getWidget(self, category, text):
  1592. return self.widgetDict[category + '-' + text]
  1593. def getVariable(self, category, text):
  1594. return self.variableDict[category + '-' + text]
  1595. def createLabeledEntry(self, parent, category, text, balloonHelp,
  1596. value = '', command = None,
  1597. relief = 'sunken', side = Tkinter.LEFT,
  1598. expand = 1, width = 12):
  1599. frame = Frame(parent)
  1600. variable = StringVar()
  1601. variable.set(value)
  1602. label = Label(frame, text = text)
  1603. label.pack(side = Tkinter.LEFT, fill = Tkinter.X)
  1604. self.bind(label, balloonHelp)
  1605. self.widgetDict[category + '-' + text + '-Label'] = label
  1606. entry = Entry(frame, width = width, relief = relief,
  1607. textvariable = variable)
  1608. entry.pack(side = Tkinter.LEFT, fill = Tkinter.X, expand = expand)
  1609. self.bind(entry, balloonHelp)
  1610. self.widgetDict[category + '-' + text] = entry
  1611. self.variableDict[category + '-' + text] = variable
  1612. if command:
  1613. entry.bind('<Return>', command)
  1614. frame.pack(side = side, fill = Tkinter.X, expand = expand)
  1615. return (frame, label, entry)
  1616. def createButton(self, parent, category, text, balloonHelp, command,
  1617. side = 'top', expand = 0, fill = Tkinter.X):
  1618. widget = Button(parent, text = text)
  1619. # Do this after the widget so command isn't called on creation
  1620. widget['command'] = command
  1621. widget.pack(side = side, fill = fill, expand = expand)
  1622. self.bind(widget, balloonHelp)
  1623. self.widgetDict[category + '-' + text] = widget
  1624. return widget
  1625. def createCheckbutton(self, parent, category, text,
  1626. balloonHelp, command, initialState,
  1627. side = 'top', fill = Tkinter.X, expand = 0):
  1628. bool = BooleanVar()
  1629. bool.set(initialState)
  1630. widget = Checkbutton(parent, text = text, anchor = Tkinter.W,
  1631. variable = bool)
  1632. # Do this after the widget so command isn't called on creation
  1633. widget['command'] = command
  1634. widget.pack(side = side, fill = fill, expand = expand)
  1635. self.bind(widget, balloonHelp)
  1636. self.widgetDict[category + '-' + text] = widget
  1637. self.variableDict[category + '-' + text] = bool
  1638. return widget
  1639. def createRadiobutton(self, parent, side, category, text,
  1640. balloonHelp, variable, value,
  1641. command = None, fill = Tkinter.X, expand = 0):
  1642. widget = Radiobutton(parent, text = text, anchor = Tkinter.W,
  1643. variable = variable, value = value)
  1644. # Do this after the widget so command isn't called on creation
  1645. widget['command'] = command
  1646. widget.pack(side = side, fill = fill, expand = expand)
  1647. self.bind(widget, balloonHelp)
  1648. self.widgetDict[category + '-' + text] = widget
  1649. return widget
  1650. def createFloater(self, parent, category, text, balloonHelp,
  1651. command = None, min = 0.0, resolution = None,
  1652. maxVelocity = 10.0, **kw):
  1653. kw['text'] = text
  1654. kw['min'] = min
  1655. kw['maxVelocity'] = maxVelocity
  1656. kw['resolution'] = resolution
  1657. widget = apply(Floater, (parent,), kw)
  1658. # Do this after the widget so command isn't called on creation
  1659. widget['command'] = command
  1660. widget.pack(fill = Tkinter.X)
  1661. self.bind(widget, balloonHelp)
  1662. self.widgetDict[category + '-' + text] = widget
  1663. return widget
  1664. def createAngleDial(self, parent, category, text, balloonHelp,
  1665. command = None, **kw):
  1666. kw['text'] = text
  1667. widget = apply(AngleDial,(parent,), kw)
  1668. # Do this after the widget so command isn't called on creation
  1669. widget['command'] = command
  1670. widget.pack(fill = Tkinter.X)
  1671. self.bind(widget, balloonHelp)
  1672. self.widgetDict[category + '-' + text] = widget
  1673. return widget
  1674. def createSlider(self, parent, category, text, balloonHelp,
  1675. command = None, min = 0.0, max = 1.0,
  1676. resolution = None,
  1677. side = Tkinter.TOP, fill = Tkinter.X, expand = 1, **kw):
  1678. kw['text'] = text
  1679. kw['min'] = min
  1680. kw['max'] = max
  1681. kw['resolution'] = resolution
  1682. #widget = apply(EntryScale, (parent,), kw)
  1683. widget = apply(Slider, (parent,), kw)
  1684. # Do this after the widget so command isn't called on creation
  1685. widget['command'] = command
  1686. widget.pack(side = side, fill = fill, expand = expand)
  1687. self.bind(widget, balloonHelp)
  1688. self.widgetDict[category + '-' + text] = widget
  1689. return widget
  1690. def createEntryScale(self, parent, category, text, balloonHelp,
  1691. command = None, min = 0.0, max = 1.0,
  1692. resolution = None,
  1693. side = Tkinter.TOP, fill = Tkinter.X, expand = 1, **kw):
  1694. kw['text'] = text
  1695. kw['min'] = min
  1696. kw['max'] = max
  1697. kw['resolution'] = resolution
  1698. widget = apply(EntryScale, (parent,), kw)
  1699. # Do this after the widget so command isn't called on creation
  1700. widget['command'] = command
  1701. widget.pack(side = side, fill = fill, expand = expand)
  1702. self.bind(widget, balloonHelp)
  1703. self.widgetDict[category + '-' + text] = widget
  1704. return widget
  1705. def createVector2Entry(self, parent, category, text, balloonHelp,
  1706. command = None, **kw):
  1707. # Set label's text
  1708. kw['text'] = text
  1709. widget = apply(Vector2Entry, (parent,), kw)
  1710. # Do this after the widget so command isn't called on creation
  1711. widget['command'] = command
  1712. widget.pack(fill = Tkinter.X)
  1713. self.bind(widget, balloonHelp)
  1714. self.widgetDict[category + '-' + text] = widget
  1715. return widget
  1716. def createVector3Entry(self, parent, category, text, balloonHelp,
  1717. command = None, **kw):
  1718. # Set label's text
  1719. kw['text'] = text
  1720. widget = apply(Vector3Entry, (parent,), kw)
  1721. # Do this after the widget so command isn't called on creation
  1722. widget['command'] = command
  1723. widget.pack(fill = Tkinter.X)
  1724. self.bind(widget, balloonHelp)
  1725. self.widgetDict[category + '-' + text] = widget
  1726. return widget
  1727. def createColorEntry(self, parent, category, text, balloonHelp,
  1728. command = None, **kw):
  1729. # Set label's text
  1730. kw['text'] = text
  1731. widget = apply(ColorEntry, (parent,) ,kw)
  1732. # Do this after the widget so command isn't called on creation
  1733. widget['command'] = command
  1734. widget.pack(fill = Tkinter.X)
  1735. self.bind(widget, balloonHelp)
  1736. self.widgetDict[category + '-' + text] = widget
  1737. return widget
  1738. def createOptionMenu(self, parent, category, text, balloonHelp,
  1739. items, command):
  1740. optionVar = StringVar()
  1741. if len(items) > 0:
  1742. optionVar.set(items[0])
  1743. widget = Pmw.OptionMenu(parent, labelpos = Tkinter.W, label_text = text,
  1744. label_width = 12, menu_tearoff = 1,
  1745. menubutton_textvariable = optionVar,
  1746. items = items)
  1747. # Do this after the widget so command isn't called on creation
  1748. widget['command'] = command
  1749. widget.pack(fill = Tkinter.X)
  1750. self.bind(widget.component('menubutton'), balloonHelp)
  1751. self.widgetDict[category + '-' + text] = widget
  1752. self.variableDict[category + '-' + text] = optionVar
  1753. return optionVar
  1754. def createComboBox(self, parent, category, text, balloonHelp,
  1755. items, command, history = 0,
  1756. side = Tkinter.LEFT, expand = 0, fill = Tkinter.X):
  1757. widget = Pmw.ComboBox(parent,
  1758. labelpos = Tkinter.W,
  1759. label_text = text,
  1760. label_anchor = 'e',
  1761. label_width = 12,
  1762. entry_width = 16,
  1763. history = history,
  1764. scrolledlist_items = items)
  1765. # Don't allow user to edit entryfield
  1766. widget.configure(entryfield_entry_state = 'disabled')
  1767. # Select first item if it exists
  1768. if len(items) > 0:
  1769. widget.selectitem(items[0])
  1770. # Bind selection command
  1771. widget['selectioncommand'] = command
  1772. widget.pack(side = side, fill = fill, expand = expand)
  1773. # Bind help
  1774. self.bind(widget, balloonHelp)
  1775. # Record widget
  1776. self.widgetDict[category + '-' + text] = widget
  1777. return widget
  1778. def makeCameraWindow(self):
  1779. # First, we need to make a new layer on the window.
  1780. chan = base.win.getChannel(0)
  1781. self.cLayer = chan.makeLayer(1)
  1782. self.layerIndex = 1
  1783. self.cDr = self.cLayer.makeDisplayRegion(0.6, 1.0, 0, 0.4)
  1784. self.cDr.setClearDepthActive(1)
  1785. self.cDr.setClearColorActive(1)
  1786. self.cDr.setClearColor(Vec4(0))
  1787. # It gets its own camera
  1788. self.cCamera = render.attachNewNode('cCamera')
  1789. self.cCamNode = Camera('cCam')
  1790. self.cLens = PerspectiveLens()
  1791. self.cLens.setFov(40,40)
  1792. self.cLens.setNear(0.1)
  1793. self.cLens.setFar(100.0)
  1794. self.cCamNode.setLens(self.cLens)
  1795. self.cCamNode.setScene(render)
  1796. self.cCam = self.cCamera.attachNewNode(self.cCamNode)
  1797. self.cDr.setCamera(self.cCam)
  1798. def toggleWidgetVis(self):
  1799. ## In order to make sure everything is going on right way...
  1800. messenger.send('SEditor-ToggleWidgetVis')
  1801. SEditor.toggleWidgetVis()
  1802. def bindMotionPathToNode(self):
  1803. if self.curveCollection == None:
  1804. print '----Error: you need to select or create a curve first!'
  1805. return
  1806. self.accept('MP_checkName', self.bindMotionPath)
  1807. self.askName = namePathPanel(MopathRecorder.count)
  1808. return
  1809. def bindMotionPath(self,name=None,test=None):
  1810. print test
  1811. self.ignore('MP_checkName')
  1812. del self.askName
  1813. self.curveCollection.getCurve(0).setName(name)
  1814. comboBox = self.getWidget('Mopath', 'Path:')
  1815. oldName = comboBox.get()
  1816. self.pointSetDict[name] = self.pointSetDict[oldName]
  1817. del self.pointSetDict[oldName]
  1818. scrolledList = comboBox.component('scrolledlist')
  1819. listbox = scrolledList.component('listbox')
  1820. names = list(listbox.get(0,'end'))
  1821. num = names.index(oldName)
  1822. names.pop(num)
  1823. names.append(name)
  1824. scrolledList.setlist(names)
  1825. comboBox.selectitem(name)
  1826. messenger.send('mPath_bindPathToNode',[self.playbackNodePath, self.curveCollection])
  1827. return
  1828. def addCurvesFromNodepath(self,curveList):
  1829. '''addCurvesFromNodepath(self,curveList)
  1830. This function will take a curveCollection list as a input.
  1831. If the list is not None, it will put the vurve back into the curve list.
  1832. else, do nothing.
  1833. '''
  1834. print curveList
  1835. self.ignore('curveListFor'+self.name)
  1836. if curveList != None:
  1837. for collection in curveList:
  1838. self.curveCollection = collection
  1839. self.extractPointSetFromCurveCollection(curveName=self.curveCollection.getCurve(0).getName())
  1840. else:
  1841. pass
  1842. return
  1843. class namePathPanel(AppShell):
  1844. # Override class variables
  1845. appname = 'Name the Path'
  1846. frameWidth = 575
  1847. frameHeight = 200
  1848. usecommandarea = 0
  1849. usestatusarea = 0
  1850. index = 0
  1851. def __init__(self, count, parent = None, **kw):
  1852. INITOPT = Pmw.INITOPT
  1853. self.id = 'Name the Path'
  1854. self.appname = self.id
  1855. optiondefs = (
  1856. ('title', self.appname, None),
  1857. )
  1858. self.defineoptions(kw, optiondefs)
  1859. # Initialize the superclass
  1860. AppShell.__init__(self)
  1861. self.parent.resizable(False,False)
  1862. # Execute option callbacks
  1863. self.initialiseoptions(namePathPanel)
  1864. def createInterface(self):
  1865. self.menuBar.destroy()
  1866. interior = self.interior()
  1867. mainFrame = Frame(interior)
  1868. dataFrame = Frame(mainFrame)
  1869. label = Label(dataFrame, text='This name will be used as a reference for this Path.',font=('MSSansSerif', 10))
  1870. label.pack(side = Tkinter.TOP, expand = 0, fill = Tkinter.X)
  1871. dataFrame.pack(side = Tkinter.TOP, expand = 0, fill = Tkinter.X, padx=5, pady=10)
  1872. dataFrame = Frame(mainFrame)
  1873. self.inputZone = Pmw.EntryField(dataFrame, labelpos='w', label_text = 'Name Selected Path: ',
  1874. entry_font=('MSSansSerif', 10),
  1875. label_font=('MSSansSerif', 10),
  1876. validate = None,
  1877. entry_width = 20)
  1878. self.inputZone.pack(side = Tkinter.LEFT, fill=Tkinter.X,expand=0)
  1879. self.button_ok = Button(dataFrame, text="OK", command=self.ok_press,width=10)
  1880. self.button_ok.pack(fill=Tkinter.X,expand=0,side=Tkinter.LEFT, padx = 3)
  1881. dataFrame.pack(side = Tkinter.TOP, expand = 0, fill = Tkinter.X, padx=10, pady=10)
  1882. mainFrame.pack(expand = 1, fill = Tkinter.BOTH)
  1883. def onDestroy(self, event):
  1884. '''
  1885. If you have open any thing, please rewrite here!
  1886. '''
  1887. pass
  1888. def ok_press(self):
  1889. name = self.inputZone.getvalue()
  1890. messenger.send('MP_checkName',[name])
  1891. self.quit()
  1892. return