seMopathRecorder.py 84 KB

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