1 | #!BPY |
---|
2 | |
---|
3 | # """ |
---|
4 | # Name: 'OGRE Meshes' |
---|
5 | # Blender: 242 |
---|
6 | # Group: 'Export' |
---|
7 | # Tooltip: 'Export meshes and animations to OGRE' |
---|
8 | # """ |
---|
9 | |
---|
10 | # Copyright (C) 2005 Michael Reimpell |
---|
11 | # |
---|
12 | # This library is free software; you can redistribute it and/or |
---|
13 | # modify it under the terms of the GNU Lesser General Public |
---|
14 | # License as published by the Free Software Foundation; either |
---|
15 | # version 2.1 of the License, or (at your option) any later version. |
---|
16 | # |
---|
17 | # This library is distributed in the hope that it will be useful, |
---|
18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
20 | # Lesser General Public License for more details. |
---|
21 | # |
---|
22 | # You should have received a copy of the GNU Lesser General Public |
---|
23 | # License along with this library; if not, write to the Free Software |
---|
24 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
---|
25 | |
---|
26 | __author__ = 'Michael Reimpell' |
---|
27 | __version__ = 'n/a' |
---|
28 | __url__ = ["Help, http://www.ogre3d.org/phpBB2/search.php", "Ogre3D, http://www.ogre3d.org"] |
---|
29 | __bpydoc__ = "Please see the external documentation that comes with the script." |
---|
30 | |
---|
31 | # epydoc doc format |
---|
32 | __docformat__ = "javadoc en" |
---|
33 | |
---|
34 | import sys |
---|
35 | |
---|
36 | try: |
---|
37 | import Blender |
---|
38 | except ImportError: |
---|
39 | sys.exit("Please run this script from within Blender!\nSee the script's manual for more information.") |
---|
40 | |
---|
41 | class OgrePackageImporter: |
---|
42 | """Imports ogrepkg. |
---|
43 | |
---|
44 | If ogrepkg fails to load, the user can be requested to |
---|
45 | point to the parent directory of the 'ogrepkg' directory. |
---|
46 | The PYTHONPATH is then set accordingly and the package |
---|
47 | location is stored in Blender's registry. |
---|
48 | |
---|
49 | Requires modules sys and Blender. |
---|
50 | """ |
---|
51 | def __init__(self, packagePath=None): |
---|
52 | """Constructor. |
---|
53 | |
---|
54 | @param packagePath Optional packagePath to include into PYTHONPATH. |
---|
55 | """ |
---|
56 | # import sys |
---|
57 | self.packagePath = packagePath |
---|
58 | return |
---|
59 | def importPackage(self): |
---|
60 | """Tries to import ogrepkg. |
---|
61 | |
---|
62 | Tries to import ogrepkg with the PYTHONPATH based |
---|
63 | on the optional constructor argument and the contents |
---|
64 | of Blender's registry entry for "OgrePackage". |
---|
65 | Raises an ImportError on failure. |
---|
66 | """ |
---|
67 | if not self.packagePath: |
---|
68 | # load registry if packagePath is not given to constructor. |
---|
69 | self._loadRegistry() |
---|
70 | if self.packagePath: |
---|
71 | # append patckagePath to PYTHONPATH |
---|
72 | sys.path.append(self.packagePath) |
---|
73 | # try importing ogrepkg |
---|
74 | import ogrepkg #raises ImportError on failure |
---|
75 | return |
---|
76 | def requestPackagePath(self): |
---|
77 | """Requests path to the ogrepkg package from the user. |
---|
78 | """ |
---|
79 | # gui |
---|
80 | path = Blender.Get('uscriptsdir') |
---|
81 | if not path: |
---|
82 | path = Blender.Get('scriptsdir') |
---|
83 | if not path: |
---|
84 | path = Blender.Get('filename') |
---|
85 | if not path: |
---|
86 | path = '' |
---|
87 | self.pathButton = Blender.Draw.Create(path) |
---|
88 | self.importSuccess = False |
---|
89 | Blender.Draw.Register(self._draw, self._eventHandler, self._buttonHandler) |
---|
90 | return |
---|
91 | def _loadRegistry(self): |
---|
92 | registryDict = Blender.Registry.GetKey('OgrePackage', True) |
---|
93 | if registryDict: |
---|
94 | if registryDict.has_key('packagePath'): |
---|
95 | if registryDict['packagePath']: |
---|
96 | self.packagePath = registryDict['packagePath'] |
---|
97 | return |
---|
98 | def _saveRegistry(self): |
---|
99 | if self.packagePath: |
---|
100 | registryDict = Blender.Registry.GetKey('OgrePackage', True) |
---|
101 | if registryDict is None: |
---|
102 | registryDict = {} |
---|
103 | registryDict['packagePath'] = self.packagePath |
---|
104 | Blender.Registry.SetKey('OgrePackage', registryDict, True) |
---|
105 | return |
---|
106 | def _checkPath(self, path): |
---|
107 | # Sets self.pathButton, self.importSuccess |
---|
108 | dirName = Blender.sys.dirname(path) |
---|
109 | if Blender.sys.exists(dirName): |
---|
110 | self.pathButton = Blender.Draw.Create(dirName) |
---|
111 | sys.path.append(dirName) |
---|
112 | try: |
---|
113 | import ogrepkg |
---|
114 | except ImportError: |
---|
115 | self.importSuccess = False |
---|
116 | else: |
---|
117 | self.importSuccess = True |
---|
118 | sys.path.remove(dirName) |
---|
119 | Blender.Draw.Redraw(1) |
---|
120 | return |
---|
121 | def _pathSelector(self, filename): |
---|
122 | self._checkPath(filename) |
---|
123 | return |
---|
124 | def _draw(self): |
---|
125 | # clear background |
---|
126 | theme = Blender.Window.Theme.Get()[0] |
---|
127 | bgColor = [color/255.0 for color in theme.get('buts').back] |
---|
128 | Blender.BGL.glClearColor(*bgColor) |
---|
129 | Blender.BGL.glClear(Blender.BGL.GL_COLOR_BUFFER_BIT) |
---|
130 | size = list(Blender.Window.GetAreaSize()) |
---|
131 | if size[0] < 350: |
---|
132 | size[0] = 350 |
---|
133 | if size[1] < 165: |
---|
134 | size[1] = 165 |
---|
135 | # 10 px border |
---|
136 | rect = [10, 10, size[0]-11, size[1]-11] |
---|
137 | # text color |
---|
138 | Blender.BGL.glColor4ub(*theme.get('text').text) |
---|
139 | # title "Settings", large, bold, underlined |
---|
140 | Blender.BGL.glRasterPos2i(rect[0], rect[3] - 9) |
---|
141 | Blender.Draw.Text("Settings", 'large') |
---|
142 | Blender.BGL.glRasterPos2i(rect[0] + 1, rect[3] - 9) |
---|
143 | Blender.Draw.Text("Settings", 'large') |
---|
144 | # 14 px font size, 2 px line spacing, 5 px shift to baseline |
---|
145 | rect[3] -= 16 |
---|
146 | # 2 px underline |
---|
147 | Blender.BGL.glRecti(rect[0], \ |
---|
148 | rect[3]-2, \ |
---|
149 | rect[0] + Blender.Draw.GetStringWidth("Settings", 'large') + 1, \ |
---|
150 | rect[3]) |
---|
151 | # 10 px skip |
---|
152 | rect[3] -= 12 |
---|
153 | # message 12 px font size, 2 px line spacing, 4 px shift to baseline |
---|
154 | if not self.importSuccess: |
---|
155 | Blender.BGL.glRasterPos2i(rect[0], rect[3] - 8) |
---|
156 | Blender.Draw.Text("The script can't find the 'ogrepkg' package!") |
---|
157 | Blender.BGL.glRasterPos2i(rect[0], rect[3] - 22) |
---|
158 | Blender.Draw.Text("In order to run the script, please provide the path to the") |
---|
159 | Blender.BGL.glRasterPos2i(rect[0], rect[3] - 36) |
---|
160 | Blender.Draw.Text("directory that contains the 'ogrepkg' directory.") |
---|
161 | else: |
---|
162 | Blender.BGL.glRasterPos2i(rect[0], rect[3] - 8) |
---|
163 | Blender.Draw.Text("Package 'ogrepkg' found!") |
---|
164 | Blender.BGL.glRasterPos2i(rect[0], rect[3] - 22) |
---|
165 | Blender.Draw.Text("Click 'Ok' to store the package location permanently") |
---|
166 | Blender.BGL.glRasterPos2i(rect[0], rect[3] - 36) |
---|
167 | Blender.Draw.Text("and run the script again.") |
---|
168 | # You can cange the location any time using Blender's script registry editor. |
---|
169 | # 10 px skip |
---|
170 | rect[3] -= 52 |
---|
171 | # path string |
---|
172 | self.pathButton = Blender.Draw.String("Path: ", 101, rect[0], rect[3] - 21, rect[2] - rect[1] - 71, 20, self.pathButton.val, 255, "Package location") |
---|
173 | # select button |
---|
174 | Blender.Draw.Button("Select", 102, rect[2] - 70, rect[3] - 21, 70, 20, "Select package location") |
---|
175 | rect[3] -= 31 |
---|
176 | # Ok button |
---|
177 | if self.importSuccess: |
---|
178 | Blender.Draw.Button("Ok", 103, rect[0], rect[1], 100, 30, "Quit, accepting package location") |
---|
179 | # Abort button |
---|
180 | Blender.Draw.Button("Abort", 104, rect[2] - 101, rect[1], 100, 30, "Quit, discarding package location") |
---|
181 | return |
---|
182 | def _eventHandler(self, event, value): |
---|
183 | return |
---|
184 | def _buttonHandler(self, event): |
---|
185 | # 101: PathString |
---|
186 | # 102: Select |
---|
187 | # 103: Ok |
---|
188 | # 104: Abort |
---|
189 | if (event == 101): # PathString |
---|
190 | self._checkPath(self.pathButton.val) |
---|
191 | elif (event == 102): # Select |
---|
192 | Blender.Window.FileSelector(self._pathSelector, "Select Package Location", self.pathButton.val) |
---|
193 | elif (event == 103): # Ok |
---|
194 | # assign packagePath |
---|
195 | self.packagePath = self.pathButton.val |
---|
196 | self._saveRegistry() |
---|
197 | Blender.Draw.Exit() |
---|
198 | elif(event == 104): # Abort |
---|
199 | Blender.Draw.Exit() |
---|
200 | return |
---|
201 | |
---|
202 | try: |
---|
203 | import pickle |
---|
204 | except ImportError: |
---|
205 | choice = Blender.Draw.PupMenu("Error: Python installation not found!%t" \ |
---|
206 | + "|This script needs a full Python %d.%d installation." % tuple(sys.version_info[0:2]) \ |
---|
207 | + "|Please refer to the Blender website how to install Python for Blender.|%l" \ |
---|
208 | + "|www.blender.org" \ |
---|
209 | + "|www.python.org") |
---|
210 | try: |
---|
211 | import webbrowser |
---|
212 | except ImportError: |
---|
213 | pass |
---|
214 | else: |
---|
215 | if (choice == 4): |
---|
216 | webbrowser.open("www.blender.org", 1, 1) |
---|
217 | elif (choice == 5): |
---|
218 | webbrowser.open("www.python.org", 1 , 1) |
---|
219 | else: |
---|
220 | # force reload of modules |
---|
221 | for name in sys.modules.keys()[:]: |
---|
222 | if name[0:7] == 'ogrepkg': |
---|
223 | del sys.modules[name] |
---|
224 | opi = OgrePackageImporter() |
---|
225 | try: |
---|
226 | opi.importPackage() # import ogrepkg |
---|
227 | except ImportError: |
---|
228 | opi.requestPackagePath() |
---|
229 | else: |
---|
230 | import webbrowser |
---|
231 | # ogrepkg successfully imported |
---|
232 | from ogrepkg import * |
---|
233 | from ogrepkg.base import * |
---|
234 | from ogrepkg.gui import * |
---|
235 | from ogrepkg.meshexport import * |
---|
236 | from ogrepkg.materialexport import * |
---|
237 | |
---|
238 | PackageSettings.getSingleton().load() |
---|
239 | # modules stay in memory, therefore update settings and clear log manually |
---|
240 | ##Log.getSingleton().clear() |
---|
241 | |
---|
242 | class PreferencesScreen(Screen): |
---|
243 | def __init__(self): |
---|
244 | Screen.__init__(self) |
---|
245 | # none or converter |
---|
246 | self.converter = ValueModel(OgreXMLConverter.getSingleton().getConverter()) |
---|
247 | # always valid string '' if no additional arguments |
---|
248 | self.converterArgs = ValueModel(OgreXMLConverter.getSingleton().getAdditionalArguments()) |
---|
249 | frame = OgreFrame(self, "Preferences") |
---|
250 | layout = VerticalLayout(frame) |
---|
251 | cbox = Box(layout, L("OgreXMLConverter"), 0 , 10) |
---|
252 | cbLayout = VerticalLayout(cbox) |
---|
253 | # converter location |
---|
254 | self.locationView = PreferencesScreen.ConverterLocationView(cbLayout, self.converter) |
---|
255 | # additional arguments |
---|
256 | Spacer(cbLayout, Size([0, 20])) |
---|
257 | LabelView(cbLayout, L("Additional arguments:")) |
---|
258 | StringView(cbLayout, Size([Size.INFINITY, 20], [200, 20]), self.converterArgs, T("Arguments: "), \ |
---|
259 | T("Additional arguments in call to OgreXMLConverter.")) |
---|
260 | Spacer(layout, Size([0, Size.INFINITY], [0, 0])) |
---|
261 | # button row |
---|
262 | Spacer(layout, Size([0, 10])) |
---|
263 | bhLayout = HorizontalLayout(layout) |
---|
264 | bSize = Size([Size.INFINITY, 30], [Blender.Draw.GetStringWidth('Preferences')+10, 30]) |
---|
265 | Button(bhLayout, bSize, PreferencesScreen.OkAction(self), T("Ok"), \ |
---|
266 | T("Apply and save settings.")) |
---|
267 | Spacer(bhLayout, bSize) |
---|
268 | Spacer(bhLayout, bSize) |
---|
269 | Button(bhLayout, bSize, PreferencesScreen.CancelAction(self), T("Cancel"), \ |
---|
270 | T("Restore old settings.")) |
---|
271 | return |
---|
272 | def activate(self): |
---|
273 | self.converter.setValue(OgreXMLConverter.getSingleton().getConverter()) |
---|
274 | self.converterArgs.setValue(OgreXMLConverter.getSingleton().getAdditionalArguments()) |
---|
275 | self.locationView.activate() |
---|
276 | Screen.activate(self) |
---|
277 | return |
---|
278 | def applySettings(self): |
---|
279 | """Apply settings. |
---|
280 | """ |
---|
281 | OgreXMLConverter.getSingleton().setConverter(self.converter.getValue()) |
---|
282 | OgreXMLConverter.getSingleton().setAdditionalArguments(self.converterArgs.getValue()) |
---|
283 | return |
---|
284 | def discardSettings(self): |
---|
285 | self.locationView.discardSettings() |
---|
286 | return |
---|
287 | class ConverterLocationView(View): |
---|
288 | def __init__(self, layout, converterModel): |
---|
289 | self.converter = converterModel |
---|
290 | # "Location:" |
---|
291 | LabelView(layout, L("Location:")) |
---|
292 | # [ Auto ] [ Manual ] |
---|
293 | ctg = ToggleGroup() |
---|
294 | self.cAutoToggle = ToggleModel(True) |
---|
295 | self.cManualToggle = ToggleModel(False) |
---|
296 | ctg.addToggle(self.cAutoToggle) |
---|
297 | ctg.addToggle(self.cManualToggle) |
---|
298 | cthLayout = HorizontalLayout(layout) |
---|
299 | ToggleView(cthLayout, Size([Size.INFINITY, 20], [100, 20]), self.cAutoToggle, T("Auto"), \ |
---|
300 | T("Use OgreXMLConverter in Path.")) |
---|
301 | ToggleView(cthLayout, Size([Size.INFINITY, 20], [100, 20]), self.cManualToggle, T("Manual"), \ |
---|
302 | T("Specifiy OgreXMLConverter location manually.")) |
---|
303 | # save choosen path temporarily, |
---|
304 | # so clicking on Auto and back on Manual does not clear it |
---|
305 | self.lastPath = ValueModel('') |
---|
306 | # Nothing or [Converter: ............ ][select] |
---|
307 | self.alternatives = AlternativesLayout(layout) |
---|
308 | self.altAuto = Spacer(self.alternatives, Size([0, 20])) |
---|
309 | self.altManual = HorizontalLayout(self.alternatives) |
---|
310 | StringView(self.altManual, Size([Size.INFINITY, 20], [200, 20]), \ |
---|
311 | PreferencesScreen.ConverterLocationView.ConverterAdapter(self.converter), T("Converter: "), \ |
---|
312 | T("OgreXMLConverter executable.")) |
---|
313 | Button(self.altManual, Size([70, 20]), PreferencesScreen.ConverterLocationView.SelectAction(self.converter), \ |
---|
314 | T("Select"), T("Select the converter location.")) |
---|
315 | self.cAutoToggle.addView(self) |
---|
316 | self.activate() |
---|
317 | return |
---|
318 | def activate(self): |
---|
319 | """Reset to saved configuration. |
---|
320 | """ |
---|
321 | if (self.converter.getValue() == None): |
---|
322 | # restore last path |
---|
323 | self.converter.setValue(self.lastPath.getValue()) |
---|
324 | # select auto (sets converter back to "None") |
---|
325 | self.cAutoToggle.setValue(True) |
---|
326 | self.alternatives.setCurrent(self.altAuto) |
---|
327 | else: |
---|
328 | self.cManualToggle.setValue(True) |
---|
329 | self.alternatives.setCurrent(self.altManual) |
---|
330 | return |
---|
331 | def update(self): |
---|
332 | """Called by cAutoToggle. |
---|
333 | """ |
---|
334 | if self.cAutoToggle.getValue(): |
---|
335 | self.alternatives.setCurrent(self.altAuto) |
---|
336 | self.lastPath.setValue(self.converter.getValue()) |
---|
337 | self.converter.setValue(None) |
---|
338 | else: |
---|
339 | self.converter.setValue(self.lastPath.getValue()) |
---|
340 | self.alternatives.setCurrent(self.altManual) |
---|
341 | return |
---|
342 | def discardSettings(self): |
---|
343 | self.lastPath.setValue(self.converter.getValue()) |
---|
344 | return |
---|
345 | class ConverterAdapter(ValueModel): |
---|
346 | def __init__(self, model): |
---|
347 | Model.__init__(self) |
---|
348 | self.model = model |
---|
349 | return |
---|
350 | def setValue(self, value): |
---|
351 | self.model.setValue(value) |
---|
352 | self._notify() |
---|
353 | return |
---|
354 | def getValue(self): |
---|
355 | value = self.model.getValue() |
---|
356 | if value is None: |
---|
357 | value = '' |
---|
358 | return value |
---|
359 | class SelectAction(Action): |
---|
360 | def __init__(self, converterModel): |
---|
361 | self.converter = converterModel |
---|
362 | return |
---|
363 | def execute(self): |
---|
364 | if( self.converter.getValue() == None): |
---|
365 | self.converter.setValue('') |
---|
366 | Blender.Window.FileSelector(self.converter.setValue, "Select Converter", self.converter.getValue()) |
---|
367 | return |
---|
368 | class OkAction(Action): |
---|
369 | def __init__(self, screen): |
---|
370 | self.screen = screen |
---|
371 | return |
---|
372 | def execute(self): |
---|
373 | self.screen.applySettings() |
---|
374 | self.screen.deactivate() |
---|
375 | return |
---|
376 | class CancelAction(Action): |
---|
377 | def __init__(self, screen): |
---|
378 | self.screen = screen |
---|
379 | return |
---|
380 | def execute(self): |
---|
381 | self.screen.discardSettings() |
---|
382 | self.screen.deactivate() |
---|
383 | return |
---|
384 | |
---|
385 | class ArmatureAction: |
---|
386 | """Wrapper for Blender actions. |
---|
387 | """ |
---|
388 | def __init__(self, bAction): |
---|
389 | self.bAction = bAction |
---|
390 | self.firstFrame = None |
---|
391 | self.lastFrame = None |
---|
392 | self.animationProxyList = [] |
---|
393 | self.update() |
---|
394 | def invalidate(self): |
---|
395 | """Invalidate all ArmatureAnimationProxys as Blender actions does no longer exist or affect the mesh. |
---|
396 | |
---|
397 | Called by ActionManager. Calls ArmatureAnimationProxy.invalidate() on all attached |
---|
398 | ArmatureAnimationProxies. |
---|
399 | """ |
---|
400 | # copy list as proxy.invalidate() calls detachAnimationProxy(proxy) |
---|
401 | for proxy in self.animationProxyList[:]: |
---|
402 | proxy.invalidate() |
---|
403 | return |
---|
404 | def attachAnimationProxy(self, proxy): |
---|
405 | """Attaches ArmatureAnimationProxy to this action. |
---|
406 | |
---|
407 | All attached actions are notified to invalidate themselves if this |
---|
408 | ArmatureAction gets invalidated. |
---|
409 | |
---|
410 | This method is called by the ArmatureAnimationProxy constructor. |
---|
411 | @see ArmatureAnimationProxy |
---|
412 | """ |
---|
413 | self.animationProxyList.append(proxy) |
---|
414 | return |
---|
415 | def detachAnimationProxy(self, proxy): |
---|
416 | """Detaches ArmatureAnimationProxy from this action. |
---|
417 | |
---|
418 | The proxy is removed from the list of attached animations and thus no |
---|
419 | longer invalidated if this ArmatureAction gets invalidated. |
---|
420 | |
---|
421 | This method is called by the ArmatureAnimationProxyManager.removeProxy() method. |
---|
422 | |
---|
423 | @see attachAnimationProxy() |
---|
424 | @see ArmatureAnimationProxyManager |
---|
425 | """ |
---|
426 | if proxy in self.animationProxyList: |
---|
427 | self.animationProxyList.remove(proxy) |
---|
428 | return |
---|
429 | def getName(self): |
---|
430 | return self.bAction.getName() |
---|
431 | def getFirstFrame(self): |
---|
432 | return self.firstFrame |
---|
433 | def getLastFrame(self): |
---|
434 | return self.lastFrame |
---|
435 | def getBlenderAction(self): |
---|
436 | return self.bAction |
---|
437 | def hasEffectOn(self, bArmature): |
---|
438 | hasEffect = False |
---|
439 | actionIpoDict = self.bAction.getAllChannelIpos() |
---|
440 | armatureBoneIter = iter(bArmature.bones.keys()) |
---|
441 | try: |
---|
442 | while not hasEffect: |
---|
443 | boneName = armatureBoneIter.next() |
---|
444 | if actionIpoDict.has_key(boneName): |
---|
445 | # TODO: check for curves |
---|
446 | hasEffect = True |
---|
447 | except StopIteration: |
---|
448 | pass |
---|
449 | return hasEffect |
---|
450 | def update(self): |
---|
451 | """Updates firstKeyFrame and lastKeyFrame considering the current IpoCurves. |
---|
452 | """ |
---|
453 | firstKeyFrame = None |
---|
454 | lastKeyFrame = None |
---|
455 | ipoDict = self.bAction.getAllChannelIpos() |
---|
456 | if ipoDict is not None: |
---|
457 | # check all bone Ipos |
---|
458 | # ipoDict[boneName] = Blender.Ipo |
---|
459 | for ipo in ipoDict.values(): |
---|
460 | if ipo is not None: |
---|
461 | # check all IpoCurves |
---|
462 | for ipoCurve in ipo.getCurves(): |
---|
463 | # check first and last keyframe |
---|
464 | for bezTriple in ipoCurve.getPoints(): |
---|
465 | iFrame = bezTriple.getPoints()[0] |
---|
466 | if ((iFrame < firstKeyFrame) or (firstKeyFrame is None)): |
---|
467 | firstKeyFrame = iFrame |
---|
468 | if ((iFrame > lastKeyFrame) or (lastKeyFrame is None)): |
---|
469 | lastKeyFrame = iFrame |
---|
470 | if firstKeyFrame == None: |
---|
471 | firstKeyFrame = 1 |
---|
472 | if lastKeyFrame == None: |
---|
473 | lastKeyFrame = 1 |
---|
474 | self.firstFrame = firstKeyFrame |
---|
475 | self.lastFrame = lastKeyFrame |
---|
476 | return |
---|
477 | |
---|
478 | class ActionManager: |
---|
479 | """Manages Blender actions. |
---|
480 | """ |
---|
481 | def __init__(self): |
---|
482 | # key: name, value: ArmatureAction |
---|
483 | self.armatureActionDict = {} |
---|
484 | self.update() |
---|
485 | return |
---|
486 | def update(self): |
---|
487 | """Synchronizes with Blender. |
---|
488 | |
---|
489 | Updates available actions and default keyframe ranges. |
---|
490 | Invalidates actions that are no longer available. |
---|
491 | |
---|
492 | update() calls Armature.Action.invalidate(), which in turn calls |
---|
493 | ArmatureAnimationProxy.invalidate(), which in turn calls |
---|
494 | ArmatureAnimationProxyMangaer.removeProxy(). |
---|
495 | """ |
---|
496 | # popultate armatureActionDict |
---|
497 | # get dictionary (name, Blender Action) of valid blender actions |
---|
498 | bActionDict = Blender.Armature.NLA.GetActions() |
---|
499 | #TODO: ?Check for curve and beztriple? |
---|
500 | # invalidate and remove deleted actions |
---|
501 | for actionName in [name for name in self.armatureActionDict.keys() if name not in bActionDict.keys()]: |
---|
502 | self.armatureActionDict[actionName].invalidate() |
---|
503 | del self.armatureActionDict[actionName] |
---|
504 | # update remaining actions |
---|
505 | for action in self.armatureActionDict.values(): |
---|
506 | action.update() |
---|
507 | # create new actions |
---|
508 | for bAction in [action for (name, action) in bActionDict.iteritems() if name not in self.armatureActionDict.keys()]: |
---|
509 | self.armatureActionDict[bAction.getName()] = ArmatureAction(bAction) |
---|
510 | return |
---|
511 | def getAction(self, name): |
---|
512 | """Returns ArmatureAction for Blender action with given name. |
---|
513 | """ |
---|
514 | action = None |
---|
515 | if name in self.armatureActionDict.keys(): |
---|
516 | action = self.armatureActionDict[name] |
---|
517 | return action |
---|
518 | def getActions(self, bObject): |
---|
519 | """Returns list of actions that have effect on the given Blender mesh object. |
---|
520 | |
---|
521 | If the mesh is linked to an action that has an effect on the meshes armature, |
---|
522 | this action is the first element of the returned action list. |
---|
523 | |
---|
524 | @param bObject Blender mesh object. |
---|
525 | @return list of ArmatureActions that have an effect on the given mesh object. |
---|
526 | """ |
---|
527 | actionList = [] |
---|
528 | # get available actions |
---|
529 | parent = GetArmatureObject(bObject) |
---|
530 | if (parent is not None): |
---|
531 | actionList = [action for action in self.armatureActionDict.values() if action.hasEffectOn(parent.getData())] |
---|
532 | # move linked action to first position in returned list |
---|
533 | bAction = bObject.getAction() |
---|
534 | if bAction is not None: |
---|
535 | # linked |
---|
536 | if self.armatureActionDict.has_key(bAction.getName()): |
---|
537 | # valid |
---|
538 | linkedAction = self.armatureActionDict[bAction.getName()] |
---|
539 | if linkedAction in actionList: |
---|
540 | # has effect on mesh, move to first position in list |
---|
541 | actionList.remove(linkedAction) |
---|
542 | actionList.insert(0, linkedAction) |
---|
543 | return actionList |
---|
544 | |
---|
545 | class AnimationProxy(Model, View): |
---|
546 | """Base class that represents an animation. |
---|
547 | """ |
---|
548 | def __init__(self, name='', startFrame=1, endFrame=1): |
---|
549 | Model.__init__(self) |
---|
550 | # Model |
---|
551 | self.nameModel = ValueModel(name) |
---|
552 | self.nameModel.addView(self) |
---|
553 | self.startFrameModel = BoundedValueModel(1, 30000, startFrame) |
---|
554 | self.startFrameModel.addView(self) |
---|
555 | self.endFrameModel = BoundedValueModel(1, 30000, endFrame) |
---|
556 | self.endFrameModel.addView(self) |
---|
557 | return |
---|
558 | def getName(self): |
---|
559 | return self.nameModel.getValue() |
---|
560 | def getStartFrame(self): |
---|
561 | return self.startFrameModel.getValue() |
---|
562 | def getEndFrame(self): |
---|
563 | return self.endFrameModel.getValue() |
---|
564 | def update(self): |
---|
565 | self._notify() |
---|
566 | return |
---|
567 | def toAnimation(self): |
---|
568 | """Returns a corresponding animation. |
---|
569 | """ |
---|
570 | raise NotImplementedError |
---|
571 | return |
---|
572 | |
---|
573 | class PoseAnimationProxy(AnimationProxy): |
---|
574 | def toAnimation(self): |
---|
575 | return PoseAnimation(self.nameModel.getValue(), self.startFrameModel.getValue(), self.endFrameModel.getValue()) |
---|
576 | |
---|
577 | class MorphAnimationProxy(AnimationProxy): |
---|
578 | def toAnimation(self): |
---|
579 | return MorphAnimation(self.nameModel.getValue(), self.startFrameModel.getValue(), self.endFrameModel.getValue()) |
---|
580 | |
---|
581 | class AnimationProxyView(HorizontalLayout, View): |
---|
582 | def __init__(self, parent, model, deleteAction): |
---|
583 | HorizontalLayout.__init__(self, parent) |
---|
584 | View.__init__(self, model) |
---|
585 | NumberView(self, Size([100, 20]), self.model.startFrameModel, T("Start: "), T("Start frame")) |
---|
586 | NumberView(self, Size([100, 20]), self.model.endFrameModel, T("End: "), T("End frame")) |
---|
587 | StringView(self, Size([Size.INFINITY, 20], [80, 20]), self.model.nameModel, tooltip=T("Animation name in Ogre3D")) |
---|
588 | Button(self, Size([60, 20]), deleteAction, T("Delete"), T("Delete animation from export")) |
---|
589 | return |
---|
590 | |
---|
591 | class ArmatureAnimationProxy(AnimationProxy): |
---|
592 | """Represents an ArmatureAnimation. |
---|
593 | """ |
---|
594 | def __init__(self, manager, action=None, name='', startFrame=-1, endFrame=-1): |
---|
595 | """Constructor. |
---|
596 | |
---|
597 | @param manager ArmatureAnimationProxyManager |
---|
598 | @param action Armature action. If <code>None</code> the default action is used. |
---|
599 | In this case, the manager must provide at least one action. |
---|
600 | @param name Animation name. If an empty string is passed, the name is set to |
---|
601 | the name of the action. |
---|
602 | @param startFrame Start frame of animation. If set to -1, it is set to the |
---|
603 | first keyframe for the action . |
---|
604 | @param endFrame End frame of animation. If set to -1, it is set to the last |
---|
605 | keyframe for the action. |
---|
606 | """ |
---|
607 | self.manager = manager |
---|
608 | # action |
---|
609 | if action is None: |
---|
610 | self.actionModel = ArmatureAnimationProxy.ActionValueModel(self, self.manager.getActions()[0]) |
---|
611 | else: |
---|
612 | self.actionModel = ArmatureAnimationProxy.ActionValueModel(self, action) |
---|
613 | self.actionModel.addView(self) |
---|
614 | if (name == ''): |
---|
615 | name = self.actionModel.getValue().getName() |
---|
616 | if (startFrame == -1): |
---|
617 | startFrame = self.actionModel.getValue().getFirstFrame() |
---|
618 | if (endFrame == -1): |
---|
619 | endFrame = self.actionModel.getValue().getLastFrame() |
---|
620 | AnimationProxy.__init__(self, name, startFrame, endFrame) |
---|
621 | return |
---|
622 | def invalidate(self): |
---|
623 | """Invalidate animation as action does no longer exist or affect the mesh. |
---|
624 | |
---|
625 | Called by ArmatureAction. It invalidates itself by calling |
---|
626 | ArmatureAnimationProxyManager.removeProxy(). |
---|
627 | """ |
---|
628 | self.manager.removeProxy(self.manager.getKey(proxy)) |
---|
629 | return |
---|
630 | def getAction(self): |
---|
631 | return self.actionModel.getValue() |
---|
632 | def setAction(self, action): |
---|
633 | self.actionModel.setValue(action) |
---|
634 | return |
---|
635 | def getActionChoices(self): |
---|
636 | return self.manager.getActions() |
---|
637 | def toAnimation(self): |
---|
638 | """Returns the corresponding armature animation. |
---|
639 | """ |
---|
640 | return ArmatureAnimation(self.actionModel.getValue().getBlenderAction(), \ |
---|
641 | self.nameModel.getValue(), self.startFrameModel.getValue(), self.endFrameModel.getValue()) |
---|
642 | class ActionValueModel(ValueModel): |
---|
643 | def __init__(self, armatureAnimationProxy, armatureAction): |
---|
644 | """Constructor. |
---|
645 | |
---|
646 | Attaches ArmatureAnimationProxy to ArmatureAction. |
---|
647 | """ |
---|
648 | Model.__init__(self) |
---|
649 | self.proxy = armatureAnimationProxy |
---|
650 | # do not call setValue() in constructor as it relies on other proxy models |
---|
651 | # and it doesn't have a view yet. |
---|
652 | self.value = armatureAction |
---|
653 | self.value.attachAnimationProxy(self.proxy) |
---|
654 | return |
---|
655 | def setValue(self, value): |
---|
656 | """Set to new action. |
---|
657 | |
---|
658 | Detaches proxy on old action and attaches proxy on new action. |
---|
659 | """ |
---|
660 | if (value != self.value): |
---|
661 | self.value.detachAnimationProxy(self.proxy) |
---|
662 | value.attachAnimationProxy(self.proxy) |
---|
663 | ValueModel.setValue(self, value) |
---|
664 | # reset animation properties to action's default. |
---|
665 | self.proxy.nameModel.setValue(value.getName()) |
---|
666 | self.proxy.startFrameModel.setValue(value.getFirstFrame()) |
---|
667 | self.proxy.endFrameModel.setValue(value.getLastFrame()) |
---|
668 | return |
---|
669 | |
---|
670 | class ArmatureAnimationProxyView(HorizontalLayout, View): |
---|
671 | def __init__(self, parent, model, deleteAction): |
---|
672 | HorizontalLayout.__init__(self, parent) |
---|
673 | View.__init__(self, model) |
---|
674 | # action menu |
---|
675 | self.menu = Menu(self, Size([100, 20]), T("Blender Action")) |
---|
676 | NumberView(self, Size([100, 20]), self.model.startFrameModel, T("Start: "), T("Start frame")) |
---|
677 | NumberView(self, Size([100, 20]), self.model.endFrameModel, T("End: "), T("End frame")) |
---|
678 | StringView(self, Size([Size.INFINITY, 20], [80, 20]), self.model.nameModel, tooltip=T("Animation name in Ogre3D")) |
---|
679 | Button(self, Size([60, 20]), deleteAction, T("Delete"), T("Delete animation from export")) |
---|
680 | self.update() |
---|
681 | return |
---|
682 | def update(self): |
---|
683 | # rebuild action menu |
---|
684 | self.menu.removeAll() |
---|
685 | self.menu.appendItem(MenuTitle("Action")) |
---|
686 | # current available actions |
---|
687 | choices = self.model.getActionChoices() |
---|
688 | current = self.model.getAction() |
---|
689 | for action in choices: |
---|
690 | if action == current: |
---|
691 | isSelected = True |
---|
692 | else: |
---|
693 | isSelected = False |
---|
694 | self.menu.appendItem(MenuItem(action.getName(), \ |
---|
695 | ArmatureAnimationProxyView.SelectAction(self, action)), isSelected) |
---|
696 | return |
---|
697 | class SelectAction(Action): |
---|
698 | def __init__(self, view, action): |
---|
699 | self.view = view |
---|
700 | self.action = action |
---|
701 | return |
---|
702 | def execute(self): |
---|
703 | self.view.model.setAction(self.action) |
---|
704 | return |
---|
705 | |
---|
706 | class AnimationProxyManager(Model): |
---|
707 | """Base class for managing animations of a given Blender mesh object. |
---|
708 | """ |
---|
709 | def __init__(self, bObject): |
---|
710 | Model.__init__(self) |
---|
711 | self.bObject = bObject |
---|
712 | # list of keys determines ordering |
---|
713 | self.animationProxyKeyList = [] |
---|
714 | # all keys < maxKey |
---|
715 | self.maxKey = 0 |
---|
716 | # key: key, value: xAnimationProxy |
---|
717 | self.animationProxyDict = {} |
---|
718 | return |
---|
719 | def addProxy(self, proxy): |
---|
720 | """Adds an AnimationProxy to the manager. |
---|
721 | |
---|
722 | @param proxy AnimationProxy. |
---|
723 | @return Key. |
---|
724 | """ |
---|
725 | if (len(self.animationProxyKeyList) == self.maxKey): |
---|
726 | self.maxKey += 1 |
---|
727 | key = self.maxKey |
---|
728 | else: |
---|
729 | # first unused |
---|
730 | key = [(x + 1) for x in range(self.maxKey) if (x + 1) not in self.animationProxyDict.keys()][0] |
---|
731 | self.animationProxyDict[key] = proxy |
---|
732 | self.animationProxyKeyList.append(key) |
---|
733 | for view in self.viewList: |
---|
734 | view.notifyAdded(key) |
---|
735 | return key |
---|
736 | def removeProxy(self, key): |
---|
737 | if self.animationProxyDict.has_key(key): |
---|
738 | del self.animationProxyDict[key] |
---|
739 | self.animationProxyKeyList.remove(key) |
---|
740 | for view in self.viewList: |
---|
741 | view.notifyRemoved(key) |
---|
742 | return |
---|
743 | def getBlenderObject(self): |
---|
744 | return self.bObject |
---|
745 | def getKey(self, proxy): |
---|
746 | |
---|
747 | return |
---|
748 | def getKeyList(self): |
---|
749 | return self.animationProxyKeyList |
---|
750 | def getProxy(self, key): |
---|
751 | proxy = None |
---|
752 | if key in self.animationProxyDict.keys(): |
---|
753 | proxy = self.animationProxyDict[key] |
---|
754 | return proxy |
---|
755 | def toAnimations(self, animationExporter): |
---|
756 | for key in self.animationProxyKeyList: |
---|
757 | animationExporter.addAnimation(self.animationProxyDict[key].toAnimation()) |
---|
758 | return |
---|
759 | def update(self): |
---|
760 | """Synchronize with Blender. |
---|
761 | |
---|
762 | @param <code>True</code> if animations can still be exported, else <code>False</code>. |
---|
763 | """ |
---|
764 | return |
---|
765 | def loadPackageSettings(self): |
---|
766 | return |
---|
767 | def savePackageSettings(self): |
---|
768 | return |
---|
769 | # @staticmethod |
---|
770 | # def create( ... ): |
---|
771 | # """Factory method. |
---|
772 | # """ |
---|
773 | |
---|
774 | class PoseAnimationProxyManager(AnimationProxyManager): |
---|
775 | """Manages pose animations of a given Blender mesh object. |
---|
776 | """ |
---|
777 | def __init__(self, bMeshObject): |
---|
778 | AnimationProxyManager.__init__(self, bMeshObject) |
---|
779 | # load package settings if available |
---|
780 | self.loadPackageSettings() |
---|
781 | return |
---|
782 | def toAnimations(self, animationExporter): |
---|
783 | for key in self.animationProxyKeyList: |
---|
784 | animationExporter.addPoseAnimation(self.animationProxyDict[key].toAnimation()) |
---|
785 | return |
---|
786 | def loadPackageSettings(self): |
---|
787 | # clear old |
---|
788 | self.animationProxyKeyList = [] |
---|
789 | self.maxKey = 0 |
---|
790 | self.animationProxyDict = {} |
---|
791 | # get package settings |
---|
792 | poseAnimationList = PackageSettings.getSingleton().getObjectSetting(self.bObject.getName(), 'PoseAnimationList') |
---|
793 | # old naming convention |
---|
794 | if not poseAnimationList: |
---|
795 | poseAnimationList = PackageSettings.getSingleton().getObjectSetting(self.bObject.getName(), 'poseAnimationList') |
---|
796 | if poseAnimationList: |
---|
797 | for poseAnimation in poseAnimationList: |
---|
798 | # skip old single frame pose animations |
---|
799 | if (len(poseAnimation) == 3): |
---|
800 | self.addProxy(PoseAnimationProxy(poseAnimation[0], poseAnimation[1], poseAnimation[2])) |
---|
801 | self._notify() |
---|
802 | return |
---|
803 | def savePackageSettings(self): |
---|
804 | poseAnimationList = [] |
---|
805 | for key in self.animationProxyKeyList: |
---|
806 | proxy = self.animationProxyDict[key] |
---|
807 | poseAnimationList.append([proxy.getName(), proxy.getStartFrame(), proxy.getEndFrame()]) |
---|
808 | PackageSettings.getSingleton().setObjectSetting(self.bObject.getName(), 'PoseAnimationList', poseAnimationList) |
---|
809 | return |
---|
810 | def update(self): |
---|
811 | """Checks if Blender object has still shape keys. |
---|
812 | |
---|
813 | @return <code>True</code> if shape keys still present, else <code>False</code>. |
---|
814 | """ |
---|
815 | isValid = False |
---|
816 | bKey = self.bObject.getData().getKey() |
---|
817 | if bKey is not None: |
---|
818 | if (len(bKey.blocks) > 0): |
---|
819 | isValid = True |
---|
820 | return isValid |
---|
821 | # @staticmethod |
---|
822 | def create(bMeshObject): |
---|
823 | """Factory method: |
---|
824 | |
---|
825 | @return instance or <code>None</code> if object has no shape key. |
---|
826 | """ |
---|
827 | manager = None |
---|
828 | bKey = bMeshObject.getData().getKey() |
---|
829 | if bKey is not None: |
---|
830 | if (len(bKey.blocks) > 0): |
---|
831 | manager = PoseAnimationProxyManager(bMeshObject) |
---|
832 | return manager |
---|
833 | create = staticmethod(create) |
---|
834 | |
---|
835 | class MorphAnimationProxyManager(AnimationProxyManager): |
---|
836 | """Manages morph animations of a given Blender mesh object. |
---|
837 | """ |
---|
838 | def __init__(self, bMeshObject): |
---|
839 | AnimationProxyManager.__init__(self, bMeshObject) |
---|
840 | # load package settings if available |
---|
841 | self.loadPackageSettings() |
---|
842 | return |
---|
843 | def toAnimations(self, animationExporter): |
---|
844 | for key in self.animationProxyKeyList: |
---|
845 | animationExporter.addMorphAnimation(self.animationProxyDict[key].toAnimation()) |
---|
846 | return |
---|
847 | def loadPackageSettings(self): |
---|
848 | # clear old |
---|
849 | self.animationProxyKeyList = [] |
---|
850 | self.maxKey = 0 |
---|
851 | self.animationProxyDict = {} |
---|
852 | # get package settings |
---|
853 | morphAnimationList = PackageSettings.getSingleton().getObjectSetting(self.bObject.getName(), 'MorphAnimationList') |
---|
854 | # old naming convention |
---|
855 | if not morphAnimationList: |
---|
856 | morphAnimationList = PackageSettings.getSingleton().getObjectSetting(self.bObject.getName(), 'morphAnimationList') |
---|
857 | if morphAnimationList: |
---|
858 | for morphAnimation in morphAnimationList: |
---|
859 | self.addProxy(MorphAnimationProxy(morphAnimation[0], morphAnimation[1], morphAnimation[2])) |
---|
860 | self._notify() |
---|
861 | return |
---|
862 | def savePackageSettings(self): |
---|
863 | morphAnimationList = [] |
---|
864 | for key in self.animationProxyKeyList: |
---|
865 | proxy = self.animationProxyDict[key] |
---|
866 | morphAnimationList.append([proxy.getName(), proxy.getStartFrame(), proxy.getEndFrame()]) |
---|
867 | PackageSettings.getSingleton().setObjectSetting(self.bObject.getName(), 'MorphAnimationList', morphAnimationList) |
---|
868 | return |
---|
869 | def update(self): |
---|
870 | """Checks if Blender object has still shape keys. |
---|
871 | |
---|
872 | @return <code>True</code> if shape keys still present, else <code>False</code>. |
---|
873 | """ |
---|
874 | isValid = False |
---|
875 | bKey = self.bObject.getData().getKey() |
---|
876 | if bKey is not None: |
---|
877 | if (len(bKey.blocks) > 0): |
---|
878 | isValid = True |
---|
879 | return isValid |
---|
880 | # @staticmethod |
---|
881 | def create(bMeshObject): |
---|
882 | """Factory method: |
---|
883 | |
---|
884 | @return instance or <code>None</code> if object has no shape key. |
---|
885 | """ |
---|
886 | manager = None |
---|
887 | bKey = bMeshObject.getData().getKey() |
---|
888 | if bKey is not None: |
---|
889 | if (len(bKey.blocks) > 0): |
---|
890 | manager = MorphAnimationProxyManager(bMeshObject) |
---|
891 | return manager |
---|
892 | create = staticmethod(create) |
---|
893 | |
---|
894 | class AnimationProxyManagerView(AddWidgetListLayout, View): |
---|
895 | """Base class for view for AnimationProxyMangager. |
---|
896 | """ |
---|
897 | def __init__(self, parent, size, model, addAction, addTooltip): |
---|
898 | # Model |
---|
899 | View.__init__(self, model) |
---|
900 | # View |
---|
901 | AddWidgetListLayout.__init__(self, parent, size) |
---|
902 | Button(self, Size([60, 20]), addAction, T("Add"), addTooltip) |
---|
903 | # list of displayed keys |
---|
904 | self.displayedKeyList = [] |
---|
905 | # key: key, value: AnimationProxyView |
---|
906 | self.proxyViewDict = {} |
---|
907 | self.update() |
---|
908 | self.setAutoScroll(True) |
---|
909 | return |
---|
910 | def notifyAdded(self, key): |
---|
911 | proxyView = AnimationProxyView(self, self.model.getProxy(key), AnimationProxyManagerView.DeleteAction(self.model, key)) |
---|
912 | self.proxyViewDict[key] = proxyView |
---|
913 | self.displayedKeyList.append(key) |
---|
914 | Blender.Draw.Redraw(1) |
---|
915 | return |
---|
916 | def update(self): |
---|
917 | self.removeAll() |
---|
918 | self.displayedKeyList = [] |
---|
919 | for key in self.model.getKeyList(): |
---|
920 | self.notifyAdded(key) |
---|
921 | Blender.Draw.Redraw(1) |
---|
922 | return |
---|
923 | def notifyRemoved(self, key): |
---|
924 | self.displayedKeyList.remove(key) |
---|
925 | self.proxyViewDict[key].removeFromParent() |
---|
926 | del self.proxyViewDict[key] |
---|
927 | Blender.Draw.Redraw(1) |
---|
928 | return |
---|
929 | class DeleteAction(Action): |
---|
930 | def __init__(self, model, key): |
---|
931 | self.model = model |
---|
932 | self.key = key |
---|
933 | return |
---|
934 | def execute(self): |
---|
935 | self.model.removeProxy(self.key) |
---|
936 | return |
---|
937 | |
---|
938 | class PoseAnimationProxyManagerView(AnimationProxyManagerView): |
---|
939 | """View for PoseAnimationProxyManager. |
---|
940 | |
---|
941 | Set size greater than <code>Size([350,100])</code>. |
---|
942 | """ |
---|
943 | def __init__(self, parent, size, model): |
---|
944 | AnimationProxyManagerView.__init__(self, parent, size, model, \ |
---|
945 | PoseAnimationProxyManagerView.AddAction(model), \ |
---|
946 | T("Add new pose animation.")) |
---|
947 | return |
---|
948 | class AddAction(Action): |
---|
949 | def __init__(self, model): |
---|
950 | self.model = model |
---|
951 | return |
---|
952 | def execute(self): |
---|
953 | key = self.model.addProxy(PoseAnimationProxy('name', 1, 1)) |
---|
954 | return |
---|
955 | |
---|
956 | class MorphAnimationProxyManagerView(AnimationProxyManagerView): |
---|
957 | """View for MorphAnimationProxyManager. |
---|
958 | |
---|
959 | Set size greater than <code>Size([350,100])</code>. |
---|
960 | """ |
---|
961 | def __init__(self, parent, size, model): |
---|
962 | AnimationProxyManagerView.__init__(self, parent, size, model, \ |
---|
963 | MorphAnimationProxyManagerView.AddAction(model), \ |
---|
964 | T("Add new morph animation.")) |
---|
965 | return |
---|
966 | class AddAction(Action): |
---|
967 | def __init__(self, model): |
---|
968 | self.model = model |
---|
969 | return |
---|
970 | def execute(self): |
---|
971 | key = self.model.addProxy(MorphAnimationProxy('name', 1, 1)) |
---|
972 | return |
---|
973 | |
---|
974 | class ArmatureAnimationProxyManager(AnimationProxyManager): |
---|
975 | """Manages armature animations of a given Blender mesh object. |
---|
976 | """ |
---|
977 | def __init__(self, bMeshObject, actionManager): |
---|
978 | AnimationProxyManager.__init__(self, bMeshObject) |
---|
979 | # actions |
---|
980 | self.actionManager = actionManager |
---|
981 | self.actionList = [] |
---|
982 | self.update() |
---|
983 | # load package settings if available |
---|
984 | self.loadPackageSettings() |
---|
985 | return |
---|
986 | def removeProxy(self, key): |
---|
987 | """Removes proxy from ProxyManager as well as detaches it from ActionManager. |
---|
988 | """ |
---|
989 | if self.animationProxyDict.has_key(key): |
---|
990 | proxy = self.animationProxyDict[key] |
---|
991 | proxy.getAction().detachAnimationProxy(proxy) |
---|
992 | del self.animationProxyDict[key] |
---|
993 | self.animationProxyKeyList.remove(key) |
---|
994 | for view in self.viewList: |
---|
995 | view.notifyRemoved(key) |
---|
996 | return |
---|
997 | def removeProxies(self): |
---|
998 | """Removes all animation proxies. |
---|
999 | """ |
---|
1000 | for proxy in self.animationProxyDict.values(): |
---|
1001 | proxy.getAction().detachAnimationProxy(proxy) |
---|
1002 | self.animationProxyDict = {} |
---|
1003 | return |
---|
1004 | def update(self): |
---|
1005 | """Updates available actions and default action keyframe ranges. |
---|
1006 | |
---|
1007 | Only notifies views if it is still a valid manager. |
---|
1008 | @return <code>True</code> if armature and actions are still present, |
---|
1009 | else <code>False</code>. |
---|
1010 | """ |
---|
1011 | isValid = False |
---|
1012 | self.actionList = [] |
---|
1013 | # check if mesh object has (still) an armature is done by getActions |
---|
1014 | self.actionList = self.actionManager.getActions(self.bObject) |
---|
1015 | if (len(self.actionList) > 0): |
---|
1016 | isValid = True |
---|
1017 | return isValid |
---|
1018 | def loadPackageSettings(self): |
---|
1019 | # clear old |
---|
1020 | for proxy in self.animationProxyDict.values(): |
---|
1021 | proxy.invalidate() |
---|
1022 | self.animationProxyKeyList = [] |
---|
1023 | self.maxKey = 0 |
---|
1024 | self.animationProxyDict = {} |
---|
1025 | # get package settings |
---|
1026 | animationList = PackageSettings.getSingleton().getObjectSetting(self.bObject.getName(), 'ArmatureAnimationList') |
---|
1027 | if not animationList: |
---|
1028 | # load old configuration text |
---|
1029 | animationList = self._loadOldSettings() |
---|
1030 | if animationList and len(animationList): |
---|
1031 | validActionNames = [action.getName() for action in self.actionList] |
---|
1032 | for animation in animationList: |
---|
1033 | # animation = [action name, animation name, start frame, end frame] |
---|
1034 | # check if action is still available |
---|
1035 | if animation[0] in validActionNames: |
---|
1036 | # add animiation |
---|
1037 | action = self.actionManager.getAction(animation[0]) |
---|
1038 | if action: |
---|
1039 | self.addProxy(ArmatureAnimationProxy(self, action, animation[1], animation[2], animation[3])) |
---|
1040 | self._notify() |
---|
1041 | return |
---|
1042 | def savePackageSettings(self): |
---|
1043 | animationList = [] |
---|
1044 | for key in self.animationProxyKeyList: |
---|
1045 | proxy = self.animationProxyDict[key] |
---|
1046 | animationList.append([proxy.getAction().getName(), proxy.getName(), proxy.getStartFrame(), proxy.getEndFrame()]) |
---|
1047 | PackageSettings.getSingleton().setObjectSetting(self.bObject.getName(), 'ArmatureAnimationList', animationList) |
---|
1048 | return |
---|
1049 | def toAnimations(self, animationExporter): |
---|
1050 | for key in self.animationProxyKeyList: |
---|
1051 | animationExporter.addAnimation(self.animationProxyDict[key].toAnimation()) |
---|
1052 | return |
---|
1053 | def getActions(self): |
---|
1054 | return self.actionList |
---|
1055 | def _loadOldSettings(self): |
---|
1056 | """Load animation settings from the old exporter. |
---|
1057 | |
---|
1058 | @return list of animations in the new format or <code>None</code>. |
---|
1059 | """ |
---|
1060 | animationList = None |
---|
1061 | # try open 'ogreexport.cfg' text |
---|
1062 | configTextName = 'ogreexport.cfg' |
---|
1063 | if configTextName in [text.getName() for text in Blender.Text.Get()]: |
---|
1064 | configText = Blender.Text.Get(configTextName) |
---|
1065 | # compose string from text and unpickle |
---|
1066 | try: |
---|
1067 | # unpickle |
---|
1068 | settingsDict = pickle.loads(string.join(configText.asLines()[4:],'\n')) |
---|
1069 | except (PickleError): |
---|
1070 | pass |
---|
1071 | else: |
---|
1072 | # read configuration |
---|
1073 | if settingsDict.has_key('armatureAnimationDictListDict'): |
---|
1074 | armatureAnimationDictListDict = settingsDict['armatureAnimationDictListDict'] |
---|
1075 | parent = GetArmatureObject(self.bObject) |
---|
1076 | if (parent is not None): |
---|
1077 | if armatureAnimationDictListDict.has_key(parent.getName()): |
---|
1078 | armatureAnimationDictList = armatureAnimationDictListDict[parent.getName()] |
---|
1079 | animationList = [] |
---|
1080 | for animationDict in armatureAnimationDictList: |
---|
1081 | actionName = animationDict['actionKey'] |
---|
1082 | name = animationDict['name'] |
---|
1083 | startFrame = animationDict['startFrame'] |
---|
1084 | endFrame = animationDict['endFrame'] |
---|
1085 | animationList.append([actionName, name, startFrame, endFrame]) |
---|
1086 | return animationList |
---|
1087 | # @staticmethod |
---|
1088 | def create(bMeshObject, actionManager): |
---|
1089 | """Factory method: |
---|
1090 | |
---|
1091 | @return instance or <code>None</code> if object has no shape key. |
---|
1092 | """ |
---|
1093 | manager = None |
---|
1094 | if (len(actionManager.getActions(bMeshObject)) > 0): |
---|
1095 | manager = ArmatureAnimationProxyManager(bMeshObject, actionManager) |
---|
1096 | return manager |
---|
1097 | create = staticmethod(create) |
---|
1098 | |
---|
1099 | class ArmatureAnimationProxyManagerView(AnimationProxyManagerView): |
---|
1100 | """View for MorphAnimationProxyManager. |
---|
1101 | |
---|
1102 | Set size greater than <code>Size([350,100])</code>. |
---|
1103 | """ |
---|
1104 | def __init__(self, parent, size, model): |
---|
1105 | AnimationProxyManagerView.__init__(self, parent, size, model, \ |
---|
1106 | ArmatureAnimationProxyManagerView.AddAction(model), \ |
---|
1107 | T("Add new skeleton animation.")) |
---|
1108 | return |
---|
1109 | def notifyAdded(self, key): |
---|
1110 | proxyView = ArmatureAnimationProxyView(self, self.model.getProxy(key), AnimationProxyManagerView.DeleteAction(self.model, key)) |
---|
1111 | self.proxyViewDict[key] = proxyView |
---|
1112 | self.displayedKeyList.append(key) |
---|
1113 | Blender.Draw.Redraw(1) |
---|
1114 | return |
---|
1115 | class AddAction(Action): |
---|
1116 | def __init__(self, model): |
---|
1117 | self.model = model |
---|
1118 | return |
---|
1119 | def execute(self): |
---|
1120 | key = self.model.addProxy(ArmatureAnimationProxy(self.model)) |
---|
1121 | return |
---|
1122 | |
---|
1123 | class SelectedObjectManager(Model): |
---|
1124 | """Manages mesh objects selected for export. |
---|
1125 | |
---|
1126 | Views have to provide an updateSelection() method. |
---|
1127 | """ |
---|
1128 | def __init__(self): |
---|
1129 | Model.__init__(self) |
---|
1130 | # ActionManager takes care of actions for all objects |
---|
1131 | self.actionManager = ActionManager() |
---|
1132 | # object names |
---|
1133 | self.selectedList = [] |
---|
1134 | # key: name, value: Blender Object of type Mesh |
---|
1135 | self.selectedDict = {} |
---|
1136 | # name of current selected object |
---|
1137 | self.current = None |
---|
1138 | # key: name; value: MorphAnimationProxyManager |
---|
1139 | self.morphAnimationProxyManagerDict = {} |
---|
1140 | # key: name; value: PoseAnimationProxyMangager |
---|
1141 | self.poseAnimationProxyManagerDict = {} |
---|
1142 | # key: name; value: ArmatureAnimationProxyManager |
---|
1143 | self.armatureAnimationProxyManagerDict = {} |
---|
1144 | self.updateSelection() |
---|
1145 | return |
---|
1146 | def updateSelection(self): |
---|
1147 | """Update the list of objects selected for export and the available actions for these objects. |
---|
1148 | """ |
---|
1149 | # update available actions |
---|
1150 | self.actionManager.update() |
---|
1151 | self.selectedList = [] |
---|
1152 | self.selectedDict = {} |
---|
1153 | # for every currently selected mesh object |
---|
1154 | for bMeshObject in [bObject for bObject in Blender.Object.GetSelected() if (bObject.getType() == 'Mesh')]: |
---|
1155 | name = bMeshObject.getName() |
---|
1156 | self.selectedList.append(name) |
---|
1157 | self.selectedDict[name] = bMeshObject |
---|
1158 | # morph animation |
---|
1159 | if self.morphAnimationProxyManagerDict.has_key(name): |
---|
1160 | # update |
---|
1161 | if not self.morphAnimationProxyManagerDict[name].update(): |
---|
1162 | # manager no longer valid |
---|
1163 | del self.morphAnimationProxyManagerDict[name] |
---|
1164 | else: |
---|
1165 | # create new manager |
---|
1166 | manager = MorphAnimationProxyManager.create(bMeshObject) |
---|
1167 | if manager: |
---|
1168 | self.morphAnimationProxyManagerDict[name] = manager |
---|
1169 | # pose animation |
---|
1170 | if self.poseAnimationProxyManagerDict.has_key(name): |
---|
1171 | # update |
---|
1172 | if not self.poseAnimationProxyManagerDict[name].update(): |
---|
1173 | # manager no longer valid |
---|
1174 | del self.poseAnimationProxyManagerDict[name] |
---|
1175 | else: |
---|
1176 | # create new manager |
---|
1177 | manager = PoseAnimationProxyManager.create(bMeshObject) |
---|
1178 | if manager: |
---|
1179 | self.poseAnimationProxyManagerDict[name] = manager |
---|
1180 | # armature animation |
---|
1181 | if self.armatureAnimationProxyManagerDict.has_key(name): |
---|
1182 | # update |
---|
1183 | if not self.armatureAnimationProxyManagerDict[name].update(): |
---|
1184 | # manager no longer valid |
---|
1185 | self.armatureAnimationProxyManagerDict[name].removeProxies() |
---|
1186 | del self.armatureAnimationProxyManagerDict[name] |
---|
1187 | else: |
---|
1188 | # create new manager |
---|
1189 | manager = ArmatureAnimationProxyManager.create(bMeshObject, self.actionManager) |
---|
1190 | if manager: |
---|
1191 | self.armatureAnimationProxyManagerDict[name] = manager |
---|
1192 | # == remove AnimationProxyManagers of unselected objects == |
---|
1193 | # save animations of unselected objects |
---|
1194 | # remove MorphAnimationProxyManagers of unselected objects |
---|
1195 | for (bObjectName, proxyManager) in [(name, self.morphAnimationProxyManagerDict[name]) \ |
---|
1196 | for name in self.morphAnimationProxyManagerDict.keys()[:] if name not in self.selectedList]: |
---|
1197 | proxyManager.savePackageSettings() |
---|
1198 | del self.morphAnimationProxyManagerDict[bObjectName] |
---|
1199 | # remove PoseAnimationProxyManagers of unselected objects |
---|
1200 | for (bObjectName, proxyManager) in [(name, self.poseAnimationProxyManagerDict[name]) \ |
---|
1201 | for name in self.poseAnimationProxyManagerDict.keys()[:] if name not in self.selectedList]: |
---|
1202 | proxyManager.savePackageSettings() |
---|
1203 | del self.poseAnimationProxyManagerDict[bObjectName] |
---|
1204 | # remove ArmatureAnimationProxyMangers of unselected objects |
---|
1205 | for (bObjectName, proxyManager) in [(name, self.armatureAnimationProxyManagerDict[name]) \ |
---|
1206 | for name in self.armatureAnimationProxyManagerDict.keys()[:] if name not in self.selectedList]: |
---|
1207 | proxyManager.savePackageSettings() |
---|
1208 | # detach animations from actions |
---|
1209 | proxyManager.removeProxies() |
---|
1210 | del self.armatureAnimationProxyManagerDict[bObjectName] |
---|
1211 | # update current selected |
---|
1212 | if len(self.selectedList): |
---|
1213 | self.current = self.selectedList[0] |
---|
1214 | else: |
---|
1215 | self.current = None |
---|
1216 | # notify views |
---|
1217 | for view in self.viewList: |
---|
1218 | view.updateSelection() |
---|
1219 | return |
---|
1220 | def setCurrentObjectName(self, name): |
---|
1221 | if name in self.selectedDict.keys(): |
---|
1222 | self.current = name |
---|
1223 | self._notify() |
---|
1224 | return |
---|
1225 | def getCurrentObjectName(self): |
---|
1226 | return self.current |
---|
1227 | def getObject(self, name): |
---|
1228 | bObject = None |
---|
1229 | if name in self.selectedDict.keys(): |
---|
1230 | bObject = self.selectedDict[name] |
---|
1231 | return bObject |
---|
1232 | def getMorphAnimationProxyManager(self, name): |
---|
1233 | proxyManager = None |
---|
1234 | if name in self.morphAnimationProxyManagerDict.keys(): |
---|
1235 | proxyManager = self.morphAnimationProxyManagerDict[name] |
---|
1236 | return proxyManager |
---|
1237 | def getPoseAnimationProxyManager(self, name): |
---|
1238 | proxyManager = None |
---|
1239 | if name in self.poseAnimationProxyManagerDict.keys(): |
---|
1240 | proxyManager = self.poseAnimationProxyManagerDict[name] |
---|
1241 | return proxyManager |
---|
1242 | def getArmatureAnimationProxyManager(self, name): |
---|
1243 | proxyManger = None |
---|
1244 | if name in self.armatureAnimationProxyManagerDict.keys(): |
---|
1245 | proxyManger = self.armatureAnimationProxyManagerDict[name] |
---|
1246 | return proxyManger |
---|
1247 | def getObjectNameList(self): |
---|
1248 | return self.selectedList |
---|
1249 | def savePackageSettings(self): |
---|
1250 | for proxyManager in self.morphAnimationProxyManagerDict.values(): |
---|
1251 | proxyManager.savePackageSettings() |
---|
1252 | for proxyManager in self.poseAnimationProxyManagerDict.values(): |
---|
1253 | proxyManager.savePackageSettings() |
---|
1254 | for proxyManager in self.armatureAnimationProxyManagerDict.values(): |
---|
1255 | proxyManager.savePackageSettings() |
---|
1256 | return |
---|
1257 | def export(self, exportPath, materialScriptName, colouredAmbient, gameEngineMaterials, convertXML, copyTextures): |
---|
1258 | # create MaterialManager |
---|
1259 | if len(self.selectedList): |
---|
1260 | materialManager = MaterialManager(exportPath, materialScriptName) |
---|
1261 | Log.getSingleton().logInfo("Output to directory \"%s\"" % exportPath) |
---|
1262 | for name in self.selectedList: |
---|
1263 | Log.getSingleton().logInfo("Processing Object \"%s\"" % name) |
---|
1264 | # create MeshExporter |
---|
1265 | meshExporter = MeshExporter(self.selectedDict[name]) |
---|
1266 | if self.morphAnimationProxyManagerDict.has_key(name): |
---|
1267 | self.morphAnimationProxyManagerDict[name].toAnimations(meshExporter.getVertexAnimationExporter()) |
---|
1268 | if self.poseAnimationProxyManagerDict.has_key(name): |
---|
1269 | self.poseAnimationProxyManagerDict[name].toAnimations(meshExporter.getVertexAnimationExporter()) |
---|
1270 | if self.armatureAnimationProxyManagerDict.has_key(name): |
---|
1271 | self.armatureAnimationProxyManagerDict[name].toAnimations(meshExporter.getArmatureExporter()) |
---|
1272 | # export |
---|
1273 | meshExporter.export(exportPath, materialManager, Matrix(*matrixOne), colouredAmbient, gameEngineMaterials, convertXML) |
---|
1274 | # export materials |
---|
1275 | materialManager.export(exportPath, materialScriptName, copyTextures) |
---|
1276 | else: |
---|
1277 | Log.getSingleton().logWarning("No mesh object selected for export!") |
---|
1278 | return |
---|
1279 | |
---|
1280 | class SelectedObjectManagerView(VerticalLayout, View): |
---|
1281 | def __init__(self, parent, model): |
---|
1282 | VerticalLayout.__init__(self, parent) |
---|
1283 | View.__init__(self, model) |
---|
1284 | hLayout = HorizontalLayout(self) |
---|
1285 | LabelView(hLayout, L("Selected: ")) |
---|
1286 | self.menu = Menu(hLayout, Size([Size.INFINITY, 20], [150, 20]), T("Objects selected for export.")) |
---|
1287 | Button(hLayout, Size([70, 20]), SelectedObjectManagerView.UpdateAction(self.model), T("Update"), \ |
---|
1288 | T("Update list of objects selected for export.")) |
---|
1289 | Spacer(self, Size([0, 10])) |
---|
1290 | self.alternatives = AlternativesLayout(self) |
---|
1291 | self.noneSelectedWidget = Spacer(self.alternatives, Size([0, Size.INFINITY], [0, 0])) |
---|
1292 | self.alternatives.setCurrent(self.noneSelectedWidget) |
---|
1293 | # key: object name, value: AnimationProxyManagersView |
---|
1294 | self.animationProxyManagersViewDict = {} |
---|
1295 | self.updateSelection() |
---|
1296 | return |
---|
1297 | def updateSelection(self): |
---|
1298 | """Called from model if list of selected mesh objects changes. |
---|
1299 | """ |
---|
1300 | self.menu.removeAll() |
---|
1301 | self.alternatives.setCurrent(self.noneSelectedWidget) |
---|
1302 | for name in self.model.getObjectNameList(): |
---|
1303 | # set current object to first in list |
---|
1304 | setCurrent = False |
---|
1305 | if (name == self.model.getObjectNameList()[0]): |
---|
1306 | setCurrent = True |
---|
1307 | self.menu.appendItem(MenuItem(name, SelectedObjectManagerView.SelectAction(self.model, name)), setCurrent) |
---|
1308 | if not self.animationProxyManagersViewDict.has_key(name): |
---|
1309 | poseManager = self.model.getPoseAnimationProxyManager(name) |
---|
1310 | morphManager = self.model.getMorphAnimationProxyManager(name) |
---|
1311 | armatureManager = self.model.getArmatureAnimationProxyManager(name) |
---|
1312 | self.animationProxyManagersViewDict[name] = SelectedObjectManagerView.AnimationProxyManagersView( \ |
---|
1313 | self.alternatives, self.model, name) |
---|
1314 | else: |
---|
1315 | # udpate existing |
---|
1316 | self.animationProxyManagersViewDict[name].updateSelection() |
---|
1317 | # set current object to first in list |
---|
1318 | if (name == self.model.getObjectNameList()[0]): |
---|
1319 | self.alternatives.setCurrent(self.animationProxyManagersViewDict[name]) |
---|
1320 | Blender.Draw.Redraw(1) |
---|
1321 | return |
---|
1322 | def update(self): |
---|
1323 | """Update current selected. |
---|
1324 | """ |
---|
1325 | self.alternatives.setCurrent(self.animationProxyManagersViewDict[self.model.getCurrentObjectName()]) |
---|
1326 | Blender.Draw.Redraw(1) |
---|
1327 | return |
---|
1328 | class SelectAction(Action): |
---|
1329 | def __init__(self, model, name): |
---|
1330 | self.model = model |
---|
1331 | self.name = name |
---|
1332 | return |
---|
1333 | def execute(self): |
---|
1334 | self.model.setCurrentObjectName(self.name) |
---|
1335 | return |
---|
1336 | class UpdateAction(Action): |
---|
1337 | def __init__(self, model): |
---|
1338 | self.model = model |
---|
1339 | return |
---|
1340 | def execute(self): |
---|
1341 | self.model.updateSelection() |
---|
1342 | return |
---|
1343 | class AnimationProxyManagersView(Box, View): |
---|
1344 | """View for the different animation proxy managers. |
---|
1345 | |
---|
1346 | Provides toggles to switch between morph, pose and armature animation proxy managers. |
---|
1347 | """ |
---|
1348 | def __init__(self, parent, manager, name): |
---|
1349 | """Constructor. |
---|
1350 | |
---|
1351 | @manager SelectedObjectManager. |
---|
1352 | @name Blender mesh object name. |
---|
1353 | """ |
---|
1354 | Box.__init__(self, parent, L("Animation Settings of \"%s\"" % name), 0, 10) |
---|
1355 | # mesh object name |
---|
1356 | self.name = name |
---|
1357 | # SelectedObjectManager |
---|
1358 | self.manager = manager |
---|
1359 | # AnimationProxyManagers |
---|
1360 | # [Armature, Pose, Morph] |
---|
1361 | self.managerList = [None, None, None] |
---|
1362 | # Toggle Bar [Skeleton] [ Pose ] [ Morph ] |
---|
1363 | # If corresponding manager does not exist |
---|
1364 | # the toggle is replaced with a Spacer that has size [0, 0]. |
---|
1365 | # Therefore ToggleView and Spacer are alternatives |
---|
1366 | # |
---|
1367 | # Only active toggles, i.e. toggles that correspond to an existing |
---|
1368 | # AnimationProxyManager are currently in the group |
---|
1369 | self.toggleList = [None, None, None] # models |
---|
1370 | self.toggleList[0] = ToggleModel(False) |
---|
1371 | self.toggleList[1] = ToggleModel(False) |
---|
1372 | self.toggleList[2] = ToggleModel(False) |
---|
1373 | # self.model = ToggleGroup() |
---|
1374 | View.__init__(self, ToggleGroup()) |
---|
1375 | vLayout = VerticalLayout(self) |
---|
1376 | hLayout = HorizontalLayout(vLayout) |
---|
1377 | self.alternativesList = [None, None, None] |
---|
1378 | self.alternativesList[0] = AlternativesLayout(hLayout) |
---|
1379 | self.alternativesList[1] = AlternativesLayout(hLayout) |
---|
1380 | self.alternativesList[2] = AlternativesLayout(hLayout) |
---|
1381 | self.toggleViewList = [None, None, None] |
---|
1382 | self.toggleViewList[0] = ToggleView(self.alternativesList[0], \ |
---|
1383 | Size([Size.INFINITY, 20], [150, 20]), \ |
---|
1384 | self.toggleList[0], \ |
---|
1385 | T("Skeleton"), T("Armature animation to Ogre skeleton animation.")) |
---|
1386 | self.toggleViewList[1] = ToggleView(self.alternativesList[1], \ |
---|
1387 | Size([Size.INFINITY, 20], [150, 20]), \ |
---|
1388 | self.toggleList[1], \ |
---|
1389 | T("Pose"),T("Shape animation to Ogre pose animation.")) |
---|
1390 | self.toggleViewList[2] = ToggleView(self.alternativesList[2], \ |
---|
1391 | Size([Size.INFINITY, 20], [150, 20]), \ |
---|
1392 | self.toggleList[2], \ |
---|
1393 | T("Morph"),T("Shape animation to Ogre morph animation.")) |
---|
1394 | self.spacerList = [None, None, None] |
---|
1395 | self.spacerList[0] = Spacer(self.alternativesList[0], Size([0,0])) |
---|
1396 | self.alternativesList[0].setCurrent(self.spacerList[0]) |
---|
1397 | self.spacerList[1] = Spacer(self.alternativesList[1], Size([0,0])) |
---|
1398 | self.alternativesList[1].setCurrent(self.spacerList[1]) |
---|
1399 | self.spacerList[2] = Spacer(self.alternativesList[2], Size([0,0])) |
---|
1400 | self.alternativesList[2].setCurrent(self.spacerList[2]) |
---|
1401 | # lower part of screen shows either the selected AnimationProxyManagerView |
---|
1402 | # or a spacer |
---|
1403 | self.alternatives = AlternativesLayout(vLayout) |
---|
1404 | # AnimationProxyManagerViews |
---|
1405 | self.managerViewList = [None, None, None] |
---|
1406 | self.spacer = Spacer(self.alternatives, Size([Size.INFINITY, Size.INFINITY], [350, 120])) |
---|
1407 | self.alternatives.setCurrent(self.spacer) |
---|
1408 | # update managers |
---|
1409 | self.updateSelection() |
---|
1410 | return |
---|
1411 | def update(self): |
---|
1412 | """Updates togglegroup status. |
---|
1413 | """ |
---|
1414 | if (self.managerList.count(None) != 3): |
---|
1415 | toggleModel = self.model.getValue() |
---|
1416 | # TODO: test if toggle is enabled should be unnecessary |
---|
1417 | if toggleModel and toggleModel.getValue(): |
---|
1418 | # raises ValueError if model is not in list |
---|
1419 | index = self.toggleList.index(toggleModel) |
---|
1420 | if self.managerViewList[index] is not None: |
---|
1421 | # display current selected AnimationProxyMana |
---|
1422 | self.alternatives.setCurrent(self.managerViewList[index]) |
---|
1423 | else: |
---|
1424 | # forgot to remove toggle from ToggleGroup |
---|
1425 | # as manager is no longer present |
---|
1426 | raise RuntimeError |
---|
1427 | return |
---|
1428 | def updateSelection(self): |
---|
1429 | """Checks if action managers are still or newly available. |
---|
1430 | """ |
---|
1431 | newManager = self.manager.getArmatureAnimationProxyManager(self.name) |
---|
1432 | if not (self.managerList[0] == newManager): |
---|
1433 | # armature manager changed |
---|
1434 | if self.managerList[0] is None: |
---|
1435 | # create new |
---|
1436 | # add manager |
---|
1437 | self.managerList[0] = newManager |
---|
1438 | # add manager view |
---|
1439 | self.managerViewList[0] = ArmatureAnimationProxyManagerView( \ |
---|
1440 | self.alternatives, Size([Size.INFINITY, Size.INFINITY], [350, 120]), \ |
---|
1441 | self.managerList[0]) |
---|
1442 | # add toggle to toggleGroup, calls update() if selected |
---|
1443 | self.model.addToggle(self.toggleList[0]) |
---|
1444 | # show ToggleView |
---|
1445 | self.alternativesList[0].setCurrent(self.toggleViewList[0]) |
---|
1446 | elif newManager is None: |
---|
1447 | # remove old |
---|
1448 | # hide ToggleView |
---|
1449 | self.alternativesList[0].setCurrent(self.spacerList[0]) |
---|
1450 | # remove toggle from ToggleGroup |
---|
1451 | self.model.removeToggle(self.toggleList[0]) |
---|
1452 | # set spacer in lower part of the window if no manager left |
---|
1453 | if (self.managerList.count(None) >= 2): |
---|
1454 | self.alternatives.setCurrent(self.spacer) |
---|
1455 | # remove old manager in list |
---|
1456 | self.managerList[0] = None |
---|
1457 | # remove old managerView |
---|
1458 | self.managerViewList[0].removeFromParent() |
---|
1459 | self.managerViewList[0] = None |
---|
1460 | else: |
---|
1461 | # swap |
---|
1462 | # manager |
---|
1463 | self.managerList[0] = newManager |
---|
1464 | # view |
---|
1465 | oldView = self.managerViewList[0] |
---|
1466 | # add new to alternatives layout |
---|
1467 | self.managerViewList[0] = ArmatureAnimationProxyanagerView( \ |
---|
1468 | self.alternatives, Size([Size.INFINITY, Size.INFINITY], [350, 120]), \ |
---|
1469 | self.managerList[0]) |
---|
1470 | # remove old from alternatives layout |
---|
1471 | if (self.alternatives.getCurrent() == oldView): |
---|
1472 | # show view |
---|
1473 | self.alternatives.setCurrent(self.managerViewList[0]) |
---|
1474 | oldView.removeFromParent() |
---|
1475 | newManager = self.manager.getPoseAnimationProxyManager(self.name) |
---|
1476 | if not (self.managerList[1] == newManager): |
---|
1477 | # pose manager changed |
---|
1478 | if self.managerList[1] is None: |
---|
1479 | # create new |
---|
1480 | # add manager |
---|
1481 | self.managerList[1] = newManager |
---|
1482 | # add manager view |
---|
1483 | self.managerViewList[1] = PoseAnimationProxyManagerView( \ |
---|
1484 | self.alternatives, Size([Size.INFINITY, Size.INFINITY], [350, 120]), \ |
---|
1485 | self.managerList[1]) |
---|
1486 | # add toggle to toggleGroup, calls update() if selected |
---|
1487 | self.model.addToggle(self.toggleList[1]) |
---|
1488 | # show ToggleView |
---|
1489 | self.alternativesList[1].setCurrent(self.toggleViewList[1]) |
---|
1490 | elif newManager is None: |
---|
1491 | # remove old |
---|
1492 | # hide ToggleView |
---|
1493 | self.alternativesList[1].setCurrent(self.spacerList[1]) |
---|
1494 | # remove toggle from ToggleGroup |
---|
1495 | self.model.removeToggle(self.toggleList[1]) |
---|
1496 | # set spacer in lower part of the window if no manager left |
---|
1497 | if (self.managerList.count(None) >= 2): |
---|
1498 | self.alternatives.setCurrent(self.spacer) |
---|
1499 | # remove old manager in list |
---|
1500 | self.managerList[1] = None |
---|
1501 | # remove old managerView |
---|
1502 | self.managerViewList[1].removeFromParent() |
---|
1503 | self.managerViewList[1] = None |
---|
1504 | else: |
---|
1505 | # swap |
---|
1506 | # manager |
---|
1507 | self.managerList[0] = newManager |
---|
1508 | # view |
---|
1509 | oldView = self.managerViewList[1] |
---|
1510 | # add new to alternatives layout |
---|
1511 | self.managerViewList[1] = PoseAnimationProxyanagerView( \ |
---|
1512 | self.alternatives, Size([Size.INFINITY, Size.INFINITY], [350, 120]), \ |
---|
1513 | self.managerList[1]) |
---|
1514 | # remove old from alternatives layout |
---|
1515 | if (self.alternatives.getCurrent() == oldView): |
---|
1516 | # show view |
---|
1517 | self.alternatives.setCurrent(self.managerViewList[1]) |
---|
1518 | oldView.removeFromParent() |
---|
1519 | newManager = self.manager.getMorphAnimationProxyManager(self.name) |
---|
1520 | if not (self.managerList[2] == newManager): |
---|
1521 | # morph manager changed |
---|
1522 | if self.managerList[2] is None: |
---|
1523 | # create new |
---|
1524 | # add manager |
---|
1525 | self.managerList[2] = newManager |
---|
1526 | # add manager view |
---|
1527 | self.managerViewList[2] = MorphAnimationProxyManagerView( \ |
---|
1528 | self.alternatives, Size([Size.INFINITY, Size.INFINITY], [350, 120]), \ |
---|
1529 | self.managerList[2]) |
---|
1530 | # add toggle to toggleGroup, calls update() if selected |
---|
1531 | self.model.addToggle(self.toggleList[2]) |
---|
1532 | # show ToggleView |
---|
1533 | self.alternativesList[2].setCurrent(self.toggleViewList[2]) |
---|
1534 | elif newManager is None: |
---|
1535 | # remove old |
---|
1536 | # hide ToggleView |
---|
1537 | self.alternativesList[2].setCurrent(self.spacerList[2]) |
---|
1538 | # remove toggle from ToggleGroup |
---|
1539 | self.model.removeToggle(self.toggleList[2]) |
---|
1540 | # set spacer in lower part of the window if no manager left |
---|
1541 | if (self.managerList.count(None) >= 2): |
---|
1542 | self.alternatives.setCurrent(self.spacer) |
---|
1543 | # remove old manager in list |
---|
1544 | self.managerList[2] = None |
---|
1545 | # remove old managerView |
---|
1546 | self.managerViewList[2].removeFromParent() |
---|
1547 | self.managerViewList[2] = None |
---|
1548 | else: |
---|
1549 | # swap |
---|
1550 | # manager |
---|
1551 | self.managerList[2] = newManager |
---|
1552 | # view |
---|
1553 | oldView = self.managerViewList[2] |
---|
1554 | # add new to alternatives layout |
---|
1555 | self.managerViewList[2] = PoseAnimationProxyanagerView( \ |
---|
1556 | self.alternatives, Size([Size.INFINITY, Size.INFINITY], [350, 120]), \ |
---|
1557 | self.managerList[2]) |
---|
1558 | # remove old from alternatives layout |
---|
1559 | if (self.alternatives.getCurrent() == oldView): |
---|
1560 | # show view |
---|
1561 | self.alternatives.setCurrent(self.managerViewList[2]) |
---|
1562 | oldView.removeFromParent() |
---|
1563 | return |
---|
1564 | |
---|
1565 | class MeshExporterApplication: |
---|
1566 | def __init__(self): |
---|
1567 | # initialize global settings to default value |
---|
1568 | self.materalScriptName = BasenameModel(Blender.Scene.GetCurrent().getName() + '.material') |
---|
1569 | self.exportPath = DirnameModel(Blender.Get('filename')) |
---|
1570 | self.colouredAmbient = ToggleModel(0) |
---|
1571 | self.gameEngineMaterials = ToggleModel(0) |
---|
1572 | self.convertXML = ToggleModel(0) |
---|
1573 | self.copyTextures = ToggleModel(0) |
---|
1574 | # load package settings if applicable |
---|
1575 | self._loadPackageSettings() |
---|
1576 | # manager for selected objects |
---|
1577 | self.selectedObjectManager = SelectedObjectManager() |
---|
1578 | self.preferencesScreen = PreferencesScreen() |
---|
1579 | # create main screen |
---|
1580 | # material settings |
---|
1581 | self.mainScreen = Screen() |
---|
1582 | frame = OgreFrame(self.mainScreen, "Meshes Exporter") |
---|
1583 | vLayout = VerticalLayout(frame) |
---|
1584 | # SelectedObjectManagerView |
---|
1585 | SelectedObjectManagerView(vLayout, self.selectedObjectManager) |
---|
1586 | ## material settings |
---|
1587 | Spacer(vLayout, Size([0, 10])) |
---|
1588 | mbox = Box(vLayout, L("Material Settings"), 0 , 10) |
---|
1589 | mvLayout = VerticalLayout(mbox) |
---|
1590 | StringView(mvLayout, Size([200, 20]), self.materalScriptName, T("Material File: "), \ |
---|
1591 | T("All material definitions go in this file (relative to the export path).")) |
---|
1592 | mvhLayout = HorizontalLayout(mvLayout) |
---|
1593 | ToggleView(mvhLayout, Size([Size.INFINITY, 20], [150, 20]), self.colouredAmbient, \ |
---|
1594 | T("Coloured Ambient"), \ |
---|
1595 | T("Use Amb factor times diffuse colour as ambient instead of Amb factor times white.")) |
---|
1596 | ToggleView(mvhLayout, Size([Size.INFINITY, 20], [150, 20]), self.gameEngineMaterials, \ |
---|
1597 | T("Game Engine Materials"), \ |
---|
1598 | T("Export game engine materials instead of rendering materials.")) |
---|
1599 | ToggleView(mvLayout, Size([Size.INFINITY, 20], [150, 20]), self.copyTextures, \ |
---|
1600 | T("Copy Textures"), \ |
---|
1601 | T("Copy texture files into export path.")) |
---|
1602 | ## global settings |
---|
1603 | Spacer(vLayout, Size([0, 10])) |
---|
1604 | ToggleView(vLayout, Size([Size.INFINITY, 20], [150, 20]), self.convertXML, T("OgreXMLConverter"), \ |
---|
1605 | T("Run OgreXMLConverter on the exported XML files.")) |
---|
1606 | # path panel |
---|
1607 | phLayout = HorizontalLayout(vLayout) |
---|
1608 | StringView(phLayout, Size([Size.INFINITY, 20], [200, 20]), self.exportPath, T("Path: "), \ |
---|
1609 | T("The directory where the exported files are saved.")) |
---|
1610 | Button(phLayout, Size([70, 20]), MeshExporterApplication.SelectAction(self), T("Select"), \ |
---|
1611 | T("Select the export directory.")) |
---|
1612 | ## buttons |
---|
1613 | Spacer(vLayout, Size([0, 10])) |
---|
1614 | bhLayout = HorizontalLayout(vLayout) |
---|
1615 | bSize = Size([Size.INFINITY, 30], [Blender.Draw.GetStringWidth('Preferences')+10, 30]) |
---|
1616 | Button(bhLayout, bSize, MeshExporterApplication.ExportAction(self), T("Export"), \ |
---|
1617 | T("Export selected mesh objects.")) |
---|
1618 | Button(bhLayout, bSize, MeshExporterApplication.PreferencesAction(self), T("Preferences"), \ |
---|
1619 | T("Exporter preferences.")) |
---|
1620 | Button(bhLayout, bSize, MeshExporterApplication.HelpAction(), T("Help"), \ |
---|
1621 | T("Get help.")) |
---|
1622 | Button(bhLayout, bSize, MeshExporterApplication.QuitAction(self), T("Quit"), \ |
---|
1623 | T("Quit without exporting.")) |
---|
1624 | return |
---|
1625 | def go(self): |
---|
1626 | self.mainScreen.activate() |
---|
1627 | return |
---|
1628 | def _loadPackageSettings(self): |
---|
1629 | materalScriptName = PackageSettings.getSingleton().getSetting('materalScriptName') |
---|
1630 | if materalScriptName is not None: |
---|
1631 | self.materalScriptName.setValue(materalScriptName) |
---|
1632 | colouredAmbient = PackageSettings.getSingleton().getSetting('colouredAmbient') |
---|
1633 | if colouredAmbient is not None: |
---|
1634 | self.colouredAmbient.setValue(colouredAmbient) |
---|
1635 | gameEngineMaterials = PackageSettings.getSingleton().getSetting('gameEngineMaterials') |
---|
1636 | if gameEngineMaterials is not None: |
---|
1637 | self.gameEngineMaterials.setValue(gameEngineMaterials) |
---|
1638 | copyTextures = PackageSettings.getSingleton().getSetting('copyTextures') |
---|
1639 | if copyTextures is not None: |
---|
1640 | self.copyTextures.setValue(copyTextures) |
---|
1641 | convertXML = PackageSettings.getSingleton().getSetting('convertXML') |
---|
1642 | if convertXML is not None: |
---|
1643 | self.convertXML.setValue(convertXML) |
---|
1644 | exportPath = PackageSettings.getSingleton().getSetting('exportPath') |
---|
1645 | if exportPath is not None: |
---|
1646 | self.exportPath.setValue(exportPath) |
---|
1647 | return |
---|
1648 | def _savePackageSettings(self): |
---|
1649 | self.selectedObjectManager.savePackageSettings() |
---|
1650 | PackageSettings.getSingleton().setSetting('materalScriptName', self.materalScriptName.getValue()) |
---|
1651 | PackageSettings.getSingleton().setSetting('colouredAmbient', self.colouredAmbient.getValue()) |
---|
1652 | PackageSettings.getSingleton().setSetting('gameEngineMaterials', self.gameEngineMaterials.getValue()) |
---|
1653 | PackageSettings.getSingleton().setSetting('copyTextures', self.copyTextures.getValue()) |
---|
1654 | PackageSettings.getSingleton().setSetting('convertXML', self.convertXML.getValue()) |
---|
1655 | PackageSettings.getSingleton().setSetting('exportPath', self.exportPath.getValue()) |
---|
1656 | PackageSettings.getSingleton().save() |
---|
1657 | return |
---|
1658 | class PreferencesAction(Action): |
---|
1659 | def __init__(self, app): |
---|
1660 | self.app = app |
---|
1661 | return |
---|
1662 | def execute(self): |
---|
1663 | self.app.preferencesScreen.activate() |
---|
1664 | return |
---|
1665 | class HelpAction(Action): |
---|
1666 | def execute(self): |
---|
1667 | dirList = [Blender.Get('datadir'), Blender.Get('udatadir'), Blender.Get('scriptsdir'), Blender.Get('uscriptsdir')] |
---|
1668 | stack = [dir for dir in dirList if dir is not None] |
---|
1669 | found = False |
---|
1670 | helpFile = '' |
---|
1671 | while not(found) and (len(stack) > 0): |
---|
1672 | dir = stack.pop(0) |
---|
1673 | helpFile = Blender.sys.join(dir, Blender.sys.join('ogrehelp','ogremeshesexporter.html')) |
---|
1674 | if Blender.sys.exists(helpFile): |
---|
1675 | found = True |
---|
1676 | if found: |
---|
1677 | webbrowser.open(helpFile, 1, 1) |
---|
1678 | else: |
---|
1679 | webbrowser.open("http://www.ogre3d.org/phpBB2/search.php", 1, 1) |
---|
1680 | return |
---|
1681 | class UpdateAction(Action): |
---|
1682 | def __init__(self, app): |
---|
1683 | self.app = app |
---|
1684 | return |
---|
1685 | class SelectAction(Action): |
---|
1686 | def __init__(self, app): |
---|
1687 | self.app = app |
---|
1688 | return |
---|
1689 | def execute(self): |
---|
1690 | Blender.Window.FileSelector(self.app.exportPath.setValue, "Export Directory", self.app.exportPath.getValue()) |
---|
1691 | return |
---|
1692 | class ExportAction(Action): |
---|
1693 | def __init__(self, app): |
---|
1694 | self.app = app |
---|
1695 | self.exportScreen = None |
---|
1696 | self.logView = None |
---|
1697 | return |
---|
1698 | def execute(self): |
---|
1699 | # create export screen |
---|
1700 | self.exportScreen = Screen() |
---|
1701 | frame = OgreFrame(self.exportScreen, "Meshes Exporter") |
---|
1702 | vLayout = VerticalLayout(frame) |
---|
1703 | LabelView(vLayout, L('Export Log:', 'large')) |
---|
1704 | self.logView = LogView(vLayout, Size([Size.INFINITY, Size.INFINITY], [300, 200]), 20, False) |
---|
1705 | Spacer(vLayout, Size([0, 10])) |
---|
1706 | activator = Activator(vLayout, False) |
---|
1707 | bhLayout = HorizontalLayout(activator) |
---|
1708 | Button(bhLayout, Size([Size.INFINITY, 30], [120, 30]), MeshExporterApplication.ExportAction.OkAction(self), \ |
---|
1709 | T("Ok"), T("Close export log.")) |
---|
1710 | Spacer(bhLayout, Size([Size.INFINITY, 30], [120, 30])) |
---|
1711 | Spacer(bhLayout, Size([Size.INFINITY, 30], [120, 30])) |
---|
1712 | Button(bhLayout, Size([Size.INFINITY, 30], [120, 30]), MeshExporterApplication.QuitAction(self.app), \ |
---|
1713 | T("Quit"), T("Quit exporter.")) |
---|
1714 | self.exportScreen.activate() |
---|
1715 | # save package settings on every export |
---|
1716 | self.app._savePackageSettings() |
---|
1717 | Log.getSingleton().logInfo("Exporting...") |
---|
1718 | self.app.selectedObjectManager.export(self.app.exportPath.getValue(), \ |
---|
1719 | self.app.materalScriptName.getValue(), \ |
---|
1720 | self.app.colouredAmbient.getValue(), \ |
---|
1721 | self.app.gameEngineMaterials.getValue(), \ |
---|
1722 | self.app.convertXML.getValue(), \ |
---|
1723 | self.app.copyTextures.getValue()) |
---|
1724 | Log.getSingleton().logInfo("Done.") |
---|
1725 | activator.setEnabled(True) |
---|
1726 | Blender.Draw.Redraw(1) |
---|
1727 | return |
---|
1728 | class OkAction(Action): |
---|
1729 | def __init__(self, exportAction): |
---|
1730 | self.action = exportAction |
---|
1731 | return |
---|
1732 | def execute(self): |
---|
1733 | self.action.exportScreen.deactivate() |
---|
1734 | # remove view from model |
---|
1735 | self.action.logView.detachModel() |
---|
1736 | return |
---|
1737 | class QuitAction(QuitAction): |
---|
1738 | def __init__(self, app): |
---|
1739 | self.app = app |
---|
1740 | return |
---|
1741 | def execute(self): |
---|
1742 | self.app._savePackageSettings() |
---|
1743 | QuitAction.execute(self) |
---|
1744 | return |
---|
1745 | |
---|
1746 | application = MeshExporterApplication() |
---|
1747 | application.go() |
---|