seMopathRecorder.py 82 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. # Import Tkinter, Pmw, and the dial code from this directory tree.
  15. from PandaObject import *
  16. from TkGlobal import *
  17. from AppShell import *
  18. from DirectGlobals import *
  19. from DirectUtil import *
  20. from SeGeometry import *
  21. from SeSelection import *
  22. from tkFileDialog import *
  23. import os
  24. import string
  25. import Dial
  26. import Floater
  27. import Slider
  28. import EntryScale
  29. import VectorWidgets
  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, PandaObject):
  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 = 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 = LEFT, expand = 0)
  231. self.bind(self.redoButton, 'Redo last operation')
  232. # Record button
  233. mainFrame = Frame(interior, relief = 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 = LEFT, expand = 0)
  241. widget['relief'] = 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 = LEFT, fill = 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 = 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 = LEFT, fill = BOTH, expand = 1)
  278. widget.configure(foreground = 'Red', relief = RAISED, borderwidth = 2,
  279. anchor = CENTER, width = 16)
  280. widget = self.createButton(frame, 'Recording', 'Add Keyframe',
  281. 'Add Keyframe To Current Path',
  282. self.addKeyframe,
  283. side = 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 = LEFT, expand = 1)
  288. frame.pack(fill = X, expand = 1)
  289. mainFrame.pack(expand = 1, fill = X, pady = 3)
  290. # Playback controls
  291. playbackFrame = Frame(interior, relief = SUNKEN,
  292. borderwidth = 2)
  293. Label(playbackFrame, text = 'PLAYBACK CONTROLS',
  294. font=('MSSansSerif', 12, 'bold')).pack(fill = 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 = TOP)
  299. widget.component('hull')['relief'] = 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 = 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 = 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 = LEFT, fill = BOTH, expand = 1)
  318. widget.configure(anchor = 'center', justify = 'center',
  319. relief = RAISED, font = ('MSSansSerif', 12, 'bold'))
  320. widget = self.createButton(frame, 'Playback', '>>',
  321. 'Jump to end of playback',
  322. self.jumpToEndOfPlayback,
  323. side = 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 = LEFT, fill = BOTH, expand = 0)
  329. frame.pack(fill = X, expand = 1)
  330. # Speed control
  331. frame = Frame(playbackFrame)
  332. widget = Button(frame, text = 'PB Speed Vernier', relief = FLAT,
  333. command = lambda s = self: s.setSpeedScale(1.0))
  334. widget.pack(side = 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 = LEFT, fill = 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 = LEFT, expand = 0)
  349. frame.pack(fill = X, expand = 1)
  350. playbackFrame.pack(fill = X, pady = 2)
  351. # Create notebook pages
  352. self.mainNotebook = Pmw.NoteBook(interior)
  353. self.mainNotebook.pack(fill = 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 = X)
  364. # Resample
  365. resampleFrame = Frame(
  366. self.resamplePage, relief = 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'] = 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 = LEFT, fill = 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 = LEFT, fill = X, expand = 1)
  384. frame.pack(fill = X, expand = 0)
  385. resampleFrame.pack(fill = X, expand = 0, pady = 2)
  386. # Desample
  387. desampleFrame = Frame(
  388. self.resamplePage, relief = 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'] = RIDGE
  397. widget['postCallback'] = self.desampleCurve
  398. desampleFrame.pack(fill = X, expand = 0, pady = 2)
  399. ## REFINE PAGE ##
  400. refineFrame = Frame(self.refinePage, relief = SUNKEN,
  401. borderwidth = 2)
  402. label = Label(refineFrame, text = 'REFINE CURVE',
  403. font=('MSSansSerif', 12, 'bold'))
  404. label.pack(fill = 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 = X)
  433. ## EXTEND PAGE ##
  434. extendFrame = Frame(self.extendPage, relief = SUNKEN,
  435. borderwidth = 2)
  436. label = Label(extendFrame, text = 'EXTEND CURVE',
  437. font=('MSSansSerif', 12, 'bold'))
  438. label.pack(fill = 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 = X)
  454. ## CROP PAGE ##
  455. cropFrame = Frame(self.cropPage, relief = SUNKEN,
  456. borderwidth = 2)
  457. label = Label(cropFrame, text = 'CROP CURVE',
  458. font=('MSSansSerif', 12, 'bold'))
  459. label.pack(fill = 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 = NONE)
  475. cropFrame.pack(fill = X)
  476. ## DRAW PAGE ##
  477. drawFrame = Frame(self.drawPage, relief = 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 = X)
  485. frame = Frame(sfFrame)
  486. Label(frame, text = 'SHOW:').pack(side = LEFT, expand = 0)
  487. widget = self.createCheckbutton(
  488. frame, 'Style', 'Path',
  489. 'On: path is visible', self.setPathVis, 1,
  490. side = LEFT, fill = X, expand = 1)
  491. widget = self.createCheckbutton(
  492. frame, 'Style', 'Knots',
  493. 'On: path knots are visible', self.setKnotVis, 1,
  494. side = LEFT, fill = X, expand = 1)
  495. widget = self.createCheckbutton(
  496. frame, 'Style', 'CVs',
  497. 'On: path CVs are visible', self.setCvVis, 0,
  498. side = LEFT, fill = X, expand = 1)
  499. widget = self.createCheckbutton(
  500. frame, 'Style', 'Hull',
  501. 'On: path hull is visible', self.setHullVis, 0,
  502. side = LEFT, fill = X, expand = 1)
  503. widget = self.createCheckbutton(
  504. frame, 'Style', 'Trace',
  505. 'On: record is visible', self.setTraceVis, 0,
  506. side = LEFT, fill = X, expand = 1)
  507. widget = self.createCheckbutton(
  508. frame, 'Style', 'Marker',
  509. 'On: playback marker is visible', self.setMarkerVis, 0,
  510. side = LEFT, fill = X, expand = 1)
  511. frame.pack(fill = 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 = TOP)
  519. widget.component('hull')['relief'] = 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 = TOP)
  526. widget.component('hull')['relief'] = 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 = TOP)
  533. widget.component('hull')['relief'] = 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 = X)
  560. ## OPTIONS PAGE ##
  561. optionsFrame = Frame(self.optionsPage, relief = SUNKEN,
  562. borderwidth = 2)
  563. label = Label(optionsFrame, text = 'RECORDING OPTIONS',
  564. font=('MSSansSerif', 12, 'bold'))
  565. label.pack(fill = 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 = 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 = W)
  583. self.setKeyframeHook()
  584. frame.pack(expand = 1, fill = 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 = 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 = LEFT, fill = BOTH, expand = 0)
  600. frame.pack(expand = 1, fill = X)
  601. # Pack record frame
  602. optionsFrame.pack(fill = 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.getChildrenAsList()
  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. hitPtDist = Vec3(entry.getFromIntersectionPoint()).length()
  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 = 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 = LEFT, fill = 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 = LEFT, fill = 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 = X, expand = expand)
  1615. return (frame, label, entry)
  1616. def createButton(self, parent, category, text, balloonHelp, command,
  1617. side = 'top', expand = 0, fill = 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 = X, expand = 0):
  1628. bool = BooleanVar()
  1629. bool.set(initialState)
  1630. widget = Checkbutton(parent, text = text, anchor = 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 = X, expand = 0):
  1642. widget = Radiobutton(parent, text = text, anchor = 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.Floater, (parent,), kw)
  1658. # Do this after the widget so command isn't called on creation
  1659. widget['command'] = command
  1660. widget.pack(fill = 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(Dial.AngleDial,(parent,), kw)
  1668. # Do this after the widget so command isn't called on creation
  1669. widget['command'] = command
  1670. widget.pack(fill = 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 = TOP, fill = X, expand = 1, **kw):
  1678. kw['text'] = text
  1679. kw['min'] = min
  1680. kw['max'] = max
  1681. kw['resolution'] = resolution
  1682. #widget = apply(EntryScale.EntryScale, (parent,), kw)
  1683. import Slider
  1684. widget = apply(Slider.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 = TOP, fill = X, expand = 1, **kw):
  1695. kw['text'] = text
  1696. kw['min'] = min
  1697. kw['max'] = max
  1698. kw['resolution'] = resolution
  1699. widget = apply(EntryScale.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(VectorWidgets.Vector2Entry, (parent,), kw)
  1711. # Do this after the widget so command isn't called on creation
  1712. widget['command'] = command
  1713. widget.pack(fill = 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(VectorWidgets.Vector3Entry, (parent,), kw)
  1722. # Do this after the widget so command isn't called on creation
  1723. widget['command'] = command
  1724. widget.pack(fill = 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(VectorWidgets.ColorEntry, (parent,) ,kw)
  1733. # Do this after the widget so command isn't called on creation
  1734. widget['command'] = command
  1735. widget.pack(fill = 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 = 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 = 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 = LEFT, expand = 0, fill = X):
  1758. widget = Pmw.ComboBox(parent,
  1759. labelpos = 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 = TOP, expand = 0, fill = X)
  1872. dataFrame.pack(side = TOP, expand = 0, fill = 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 = LEFT, fill=X,expand=0)
  1880. self.button_ok = Button(dataFrame, text="OK", command=self.ok_press,width=10)
  1881. self.button_ok.pack(fill=X,expand=0,side=LEFT, padx = 3)
  1882. dataFrame.pack(side = TOP, expand = 0, fill = X, padx=10, pady=10)
  1883. mainFrame.pack(expand = 1, fill = 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