#!BPY # """ # Name: 'OGRE Meshes' # Blender: 242 # Group: 'Export' # Tooltip: 'Export meshes and animations to OGRE' # """ # Copyright (C) 2005 Michael Reimpell # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA __author__ = 'Michael Reimpell' __version__ = 'n/a' __url__ = ["Help, http://www.ogre3d.org/phpBB2/search.php", "Ogre3D, http://www.ogre3d.org"] __bpydoc__ = "Please see the external documentation that comes with the script." # epydoc doc format __docformat__ = "javadoc en" import sys try: import Blender except ImportError: sys.exit("Please run this script from within Blender!\nSee the script's manual for more information.") class OgrePackageImporter: """Imports ogrepkg. If ogrepkg fails to load, the user can be requested to point to the parent directory of the 'ogrepkg' directory. The PYTHONPATH is then set accordingly and the package location is stored in Blender's registry. Requires modules sys and Blender. """ def __init__(self, packagePath=None): """Constructor. @param packagePath Optional packagePath to include into PYTHONPATH. """ # import sys self.packagePath = packagePath return def importPackage(self): """Tries to import ogrepkg. Tries to import ogrepkg with the PYTHONPATH based on the optional constructor argument and the contents of Blender's registry entry for "OgrePackage". Raises an ImportError on failure. """ if not self.packagePath: # load registry if packagePath is not given to constructor. self._loadRegistry() if self.packagePath: # append patckagePath to PYTHONPATH sys.path.append(self.packagePath) # try importing ogrepkg import ogrepkg #raises ImportError on failure return def requestPackagePath(self): """Requests path to the ogrepkg package from the user. """ # gui path = Blender.Get('uscriptsdir') if not path: path = Blender.Get('scriptsdir') if not path: path = Blender.Get('filename') if not path: path = '' self.pathButton = Blender.Draw.Create(path) self.importSuccess = False Blender.Draw.Register(self._draw, self._eventHandler, self._buttonHandler) return def _loadRegistry(self): registryDict = Blender.Registry.GetKey('OgrePackage', True) if registryDict: if registryDict.has_key('packagePath'): if registryDict['packagePath']: self.packagePath = registryDict['packagePath'] return def _saveRegistry(self): if self.packagePath: registryDict = Blender.Registry.GetKey('OgrePackage', True) if registryDict is None: registryDict = {} registryDict['packagePath'] = self.packagePath Blender.Registry.SetKey('OgrePackage', registryDict, True) return def _checkPath(self, path): # Sets self.pathButton, self.importSuccess dirName = Blender.sys.dirname(path) if Blender.sys.exists(dirName): self.pathButton = Blender.Draw.Create(dirName) sys.path.append(dirName) try: import ogrepkg except ImportError: self.importSuccess = False else: self.importSuccess = True sys.path.remove(dirName) Blender.Draw.Redraw(1) return def _pathSelector(self, filename): self._checkPath(filename) return def _draw(self): # clear background theme = Blender.Window.Theme.Get()[0] bgColor = [color/255.0 for color in theme.get('buts').back] Blender.BGL.glClearColor(*bgColor) Blender.BGL.glClear(Blender.BGL.GL_COLOR_BUFFER_BIT) size = list(Blender.Window.GetAreaSize()) if size[0] < 350: size[0] = 350 if size[1] < 165: size[1] = 165 # 10 px border rect = [10, 10, size[0]-11, size[1]-11] # text color Blender.BGL.glColor4ub(*theme.get('text').text) # title "Settings", large, bold, underlined Blender.BGL.glRasterPos2i(rect[0], rect[3] - 9) Blender.Draw.Text("Settings", 'large') Blender.BGL.glRasterPos2i(rect[0] + 1, rect[3] - 9) Blender.Draw.Text("Settings", 'large') # 14 px font size, 2 px line spacing, 5 px shift to baseline rect[3] -= 16 # 2 px underline Blender.BGL.glRecti(rect[0], \ rect[3]-2, \ rect[0] + Blender.Draw.GetStringWidth("Settings", 'large') + 1, \ rect[3]) # 10 px skip rect[3] -= 12 # message 12 px font size, 2 px line spacing, 4 px shift to baseline if not self.importSuccess: Blender.BGL.glRasterPos2i(rect[0], rect[3] - 8) Blender.Draw.Text("The script can't find the 'ogrepkg' package!") Blender.BGL.glRasterPos2i(rect[0], rect[3] - 22) Blender.Draw.Text("In order to run the script, please provide the path to the") Blender.BGL.glRasterPos2i(rect[0], rect[3] - 36) Blender.Draw.Text("directory that contains the 'ogrepkg' directory.") else: Blender.BGL.glRasterPos2i(rect[0], rect[3] - 8) Blender.Draw.Text("Package 'ogrepkg' found!") Blender.BGL.glRasterPos2i(rect[0], rect[3] - 22) Blender.Draw.Text("Click 'Ok' to store the package location permanently") Blender.BGL.glRasterPos2i(rect[0], rect[3] - 36) Blender.Draw.Text("and run the script again.") # You can cange the location any time using Blender's script registry editor. # 10 px skip rect[3] -= 52 # path string self.pathButton = Blender.Draw.String("Path: ", 101, rect[0], rect[3] - 21, rect[2] - rect[1] - 71, 20, self.pathButton.val, 255, "Package location") # select button Blender.Draw.Button("Select", 102, rect[2] - 70, rect[3] - 21, 70, 20, "Select package location") rect[3] -= 31 # Ok button if self.importSuccess: Blender.Draw.Button("Ok", 103, rect[0], rect[1], 100, 30, "Quit, accepting package location") # Abort button Blender.Draw.Button("Abort", 104, rect[2] - 101, rect[1], 100, 30, "Quit, discarding package location") return def _eventHandler(self, event, value): return def _buttonHandler(self, event): # 101: PathString # 102: Select # 103: Ok # 104: Abort if (event == 101): # PathString self._checkPath(self.pathButton.val) elif (event == 102): # Select Blender.Window.FileSelector(self._pathSelector, "Select Package Location", self.pathButton.val) elif (event == 103): # Ok # assign packagePath self.packagePath = self.pathButton.val self._saveRegistry() Blender.Draw.Exit() elif(event == 104): # Abort Blender.Draw.Exit() return try: import pickle except ImportError: choice = Blender.Draw.PupMenu("Error: Python installation not found!%t" \ + "|This script needs a full Python %d.%d installation." % tuple(sys.version_info[0:2]) \ + "|Please refer to the Blender website how to install Python for Blender.|%l" \ + "|www.blender.org" \ + "|www.python.org") try: import webbrowser except ImportError: pass else: if (choice == 4): webbrowser.open("www.blender.org", 1, 1) elif (choice == 5): webbrowser.open("www.python.org", 1 , 1) else: # force reload of modules for name in sys.modules.keys()[:]: if name[0:7] == 'ogrepkg': del sys.modules[name] opi = OgrePackageImporter() try: opi.importPackage() # import ogrepkg except ImportError: opi.requestPackagePath() else: import webbrowser # ogrepkg successfully imported from ogrepkg import * from ogrepkg.base import * from ogrepkg.gui import * from ogrepkg.meshexport import * from ogrepkg.materialexport import * PackageSettings.getSingleton().load() # modules stay in memory, therefore update settings and clear log manually ##Log.getSingleton().clear() class PreferencesScreen(Screen): def __init__(self): Screen.__init__(self) # none or converter self.converter = ValueModel(OgreXMLConverter.getSingleton().getConverter()) # always valid string '' if no additional arguments self.converterArgs = ValueModel(OgreXMLConverter.getSingleton().getAdditionalArguments()) frame = OgreFrame(self, "Preferences") layout = VerticalLayout(frame) cbox = Box(layout, L("OgreXMLConverter"), 0 , 10) cbLayout = VerticalLayout(cbox) # converter location self.locationView = PreferencesScreen.ConverterLocationView(cbLayout, self.converter) # additional arguments Spacer(cbLayout, Size([0, 20])) LabelView(cbLayout, L("Additional arguments:")) StringView(cbLayout, Size([Size.INFINITY, 20], [200, 20]), self.converterArgs, T("Arguments: "), \ T("Additional arguments in call to OgreXMLConverter.")) Spacer(layout, Size([0, Size.INFINITY], [0, 0])) # button row Spacer(layout, Size([0, 10])) bhLayout = HorizontalLayout(layout) bSize = Size([Size.INFINITY, 30], [Blender.Draw.GetStringWidth('Preferences')+10, 30]) Button(bhLayout, bSize, PreferencesScreen.OkAction(self), T("Ok"), \ T("Apply and save settings.")) Spacer(bhLayout, bSize) Spacer(bhLayout, bSize) Button(bhLayout, bSize, PreferencesScreen.CancelAction(self), T("Cancel"), \ T("Restore old settings.")) return def activate(self): self.converter.setValue(OgreXMLConverter.getSingleton().getConverter()) self.converterArgs.setValue(OgreXMLConverter.getSingleton().getAdditionalArguments()) self.locationView.activate() Screen.activate(self) return def applySettings(self): """Apply settings. """ OgreXMLConverter.getSingleton().setConverter(self.converter.getValue()) OgreXMLConverter.getSingleton().setAdditionalArguments(self.converterArgs.getValue()) return def discardSettings(self): self.locationView.discardSettings() return class ConverterLocationView(View): def __init__(self, layout, converterModel): self.converter = converterModel # "Location:" LabelView(layout, L("Location:")) # [ Auto ] [ Manual ] ctg = ToggleGroup() self.cAutoToggle = ToggleModel(True) self.cManualToggle = ToggleModel(False) ctg.addToggle(self.cAutoToggle) ctg.addToggle(self.cManualToggle) cthLayout = HorizontalLayout(layout) ToggleView(cthLayout, Size([Size.INFINITY, 20], [100, 20]), self.cAutoToggle, T("Auto"), \ T("Use OgreXMLConverter in Path.")) ToggleView(cthLayout, Size([Size.INFINITY, 20], [100, 20]), self.cManualToggle, T("Manual"), \ T("Specifiy OgreXMLConverter location manually.")) # save choosen path temporarily, # so clicking on Auto and back on Manual does not clear it self.lastPath = ValueModel('') # Nothing or [Converter: ............ ][select] self.alternatives = AlternativesLayout(layout) self.altAuto = Spacer(self.alternatives, Size([0, 20])) self.altManual = HorizontalLayout(self.alternatives) StringView(self.altManual, Size([Size.INFINITY, 20], [200, 20]), \ PreferencesScreen.ConverterLocationView.ConverterAdapter(self.converter), T("Converter: "), \ T("OgreXMLConverter executable.")) Button(self.altManual, Size([70, 20]), PreferencesScreen.ConverterLocationView.SelectAction(self.converter), \ T("Select"), T("Select the converter location.")) self.cAutoToggle.addView(self) self.activate() return def activate(self): """Reset to saved configuration. """ if (self.converter.getValue() == None): # restore last path self.converter.setValue(self.lastPath.getValue()) # select auto (sets converter back to "None") self.cAutoToggle.setValue(True) self.alternatives.setCurrent(self.altAuto) else: self.cManualToggle.setValue(True) self.alternatives.setCurrent(self.altManual) return def update(self): """Called by cAutoToggle. """ if self.cAutoToggle.getValue(): self.alternatives.setCurrent(self.altAuto) self.lastPath.setValue(self.converter.getValue()) self.converter.setValue(None) else: self.converter.setValue(self.lastPath.getValue()) self.alternatives.setCurrent(self.altManual) return def discardSettings(self): self.lastPath.setValue(self.converter.getValue()) return class ConverterAdapter(ValueModel): def __init__(self, model): Model.__init__(self) self.model = model return def setValue(self, value): self.model.setValue(value) self._notify() return def getValue(self): value = self.model.getValue() if value is None: value = '' return value class SelectAction(Action): def __init__(self, converterModel): self.converter = converterModel return def execute(self): if( self.converter.getValue() == None): self.converter.setValue('') Blender.Window.FileSelector(self.converter.setValue, "Select Converter", self.converter.getValue()) return class OkAction(Action): def __init__(self, screen): self.screen = screen return def execute(self): self.screen.applySettings() self.screen.deactivate() return class CancelAction(Action): def __init__(self, screen): self.screen = screen return def execute(self): self.screen.discardSettings() self.screen.deactivate() return class ArmatureAction: """Wrapper for Blender actions. """ def __init__(self, bAction): self.bAction = bAction self.firstFrame = None self.lastFrame = None self.animationProxyList = [] self.update() def invalidate(self): """Invalidate all ArmatureAnimationProxys as Blender actions does no longer exist or affect the mesh. Called by ActionManager. Calls ArmatureAnimationProxy.invalidate() on all attached ArmatureAnimationProxies. """ # copy list as proxy.invalidate() calls detachAnimationProxy(proxy) for proxy in self.animationProxyList[:]: proxy.invalidate() return def attachAnimationProxy(self, proxy): """Attaches ArmatureAnimationProxy to this action. All attached actions are notified to invalidate themselves if this ArmatureAction gets invalidated. This method is called by the ArmatureAnimationProxy constructor. @see ArmatureAnimationProxy """ self.animationProxyList.append(proxy) return def detachAnimationProxy(self, proxy): """Detaches ArmatureAnimationProxy from this action. The proxy is removed from the list of attached animations and thus no longer invalidated if this ArmatureAction gets invalidated. This method is called by the ArmatureAnimationProxyManager.removeProxy() method. @see attachAnimationProxy() @see ArmatureAnimationProxyManager """ if proxy in self.animationProxyList: self.animationProxyList.remove(proxy) return def getName(self): return self.bAction.getName() def getFirstFrame(self): return self.firstFrame def getLastFrame(self): return self.lastFrame def getBlenderAction(self): return self.bAction def hasEffectOn(self, bArmature): hasEffect = False actionIpoDict = self.bAction.getAllChannelIpos() armatureBoneIter = iter(bArmature.bones.keys()) try: while not hasEffect: boneName = armatureBoneIter.next() if actionIpoDict.has_key(boneName): # TODO: check for curves hasEffect = True except StopIteration: pass return hasEffect def update(self): """Updates firstKeyFrame and lastKeyFrame considering the current IpoCurves. """ firstKeyFrame = None lastKeyFrame = None ipoDict = self.bAction.getAllChannelIpos() if ipoDict is not None: # check all bone Ipos # ipoDict[boneName] = Blender.Ipo for ipo in ipoDict.values(): if ipo is not None: # check all IpoCurves for ipoCurve in ipo.getCurves(): # check first and last keyframe for bezTriple in ipoCurve.getPoints(): iFrame = bezTriple.getPoints()[0] if ((iFrame < firstKeyFrame) or (firstKeyFrame is None)): firstKeyFrame = iFrame if ((iFrame > lastKeyFrame) or (lastKeyFrame is None)): lastKeyFrame = iFrame if firstKeyFrame == None: firstKeyFrame = 1 if lastKeyFrame == None: lastKeyFrame = 1 self.firstFrame = firstKeyFrame self.lastFrame = lastKeyFrame return class ActionManager: """Manages Blender actions. """ def __init__(self): # key: name, value: ArmatureAction self.armatureActionDict = {} self.update() return def update(self): """Synchronizes with Blender. Updates available actions and default keyframe ranges. Invalidates actions that are no longer available. update() calls Armature.Action.invalidate(), which in turn calls ArmatureAnimationProxy.invalidate(), which in turn calls ArmatureAnimationProxyMangaer.removeProxy(). """ # popultate armatureActionDict # get dictionary (name, Blender Action) of valid blender actions bActionDict = Blender.Armature.NLA.GetActions() #TODO: ?Check for curve and beztriple? # invalidate and remove deleted actions for actionName in [name for name in self.armatureActionDict.keys() if name not in bActionDict.keys()]: self.armatureActionDict[actionName].invalidate() del self.armatureActionDict[actionName] # update remaining actions for action in self.armatureActionDict.values(): action.update() # create new actions for bAction in [action for (name, action) in bActionDict.iteritems() if name not in self.armatureActionDict.keys()]: self.armatureActionDict[bAction.getName()] = ArmatureAction(bAction) return def getAction(self, name): """Returns ArmatureAction for Blender action with given name. """ action = None if name in self.armatureActionDict.keys(): action = self.armatureActionDict[name] return action def getActions(self, bObject): """Returns list of actions that have effect on the given Blender mesh object. If the mesh is linked to an action that has an effect on the meshes armature, this action is the first element of the returned action list. @param bObject Blender mesh object. @return list of ArmatureActions that have an effect on the given mesh object. """ actionList = [] # get available actions parent = GetArmatureObject(bObject) if (parent is not None): actionList = [action for action in self.armatureActionDict.values() if action.hasEffectOn(parent.getData())] # move linked action to first position in returned list bAction = bObject.getAction() if bAction is not None: # linked if self.armatureActionDict.has_key(bAction.getName()): # valid linkedAction = self.armatureActionDict[bAction.getName()] if linkedAction in actionList: # has effect on mesh, move to first position in list actionList.remove(linkedAction) actionList.insert(0, linkedAction) return actionList class AnimationProxy(Model, View): """Base class that represents an animation. """ def __init__(self, name='', startFrame=1, endFrame=1): Model.__init__(self) # Model self.nameModel = ValueModel(name) self.nameModel.addView(self) self.startFrameModel = BoundedValueModel(1, 30000, startFrame) self.startFrameModel.addView(self) self.endFrameModel = BoundedValueModel(1, 30000, endFrame) self.endFrameModel.addView(self) return def getName(self): return self.nameModel.getValue() def getStartFrame(self): return self.startFrameModel.getValue() def getEndFrame(self): return self.endFrameModel.getValue() def update(self): self._notify() return def toAnimation(self): """Returns a corresponding animation. """ raise NotImplementedError return class PoseAnimationProxy(AnimationProxy): def toAnimation(self): return PoseAnimation(self.nameModel.getValue(), self.startFrameModel.getValue(), self.endFrameModel.getValue()) class MorphAnimationProxy(AnimationProxy): def toAnimation(self): return MorphAnimation(self.nameModel.getValue(), self.startFrameModel.getValue(), self.endFrameModel.getValue()) class AnimationProxyView(HorizontalLayout, View): def __init__(self, parent, model, deleteAction): HorizontalLayout.__init__(self, parent) View.__init__(self, model) NumberView(self, Size([100, 20]), self.model.startFrameModel, T("Start: "), T("Start frame")) NumberView(self, Size([100, 20]), self.model.endFrameModel, T("End: "), T("End frame")) StringView(self, Size([Size.INFINITY, 20], [80, 20]), self.model.nameModel, tooltip=T("Animation name in Ogre3D")) Button(self, Size([60, 20]), deleteAction, T("Delete"), T("Delete animation from export")) return class ArmatureAnimationProxy(AnimationProxy): """Represents an ArmatureAnimation. """ def __init__(self, manager, action=None, name='', startFrame=-1, endFrame=-1): """Constructor. @param manager ArmatureAnimationProxyManager @param action Armature action. If None the default action is used. In this case, the manager must provide at least one action. @param name Animation name. If an empty string is passed, the name is set to the name of the action. @param startFrame Start frame of animation. If set to -1, it is set to the first keyframe for the action . @param endFrame End frame of animation. If set to -1, it is set to the last keyframe for the action. """ self.manager = manager # action if action is None: self.actionModel = ArmatureAnimationProxy.ActionValueModel(self, self.manager.getActions()[0]) else: self.actionModel = ArmatureAnimationProxy.ActionValueModel(self, action) self.actionModel.addView(self) if (name == ''): name = self.actionModel.getValue().getName() if (startFrame == -1): startFrame = self.actionModel.getValue().getFirstFrame() if (endFrame == -1): endFrame = self.actionModel.getValue().getLastFrame() AnimationProxy.__init__(self, name, startFrame, endFrame) return def invalidate(self): """Invalidate animation as action does no longer exist or affect the mesh. Called by ArmatureAction. It invalidates itself by calling ArmatureAnimationProxyManager.removeProxy(). """ self.manager.removeProxy(self.manager.getKey(proxy)) return def getAction(self): return self.actionModel.getValue() def setAction(self, action): self.actionModel.setValue(action) return def getActionChoices(self): return self.manager.getActions() def toAnimation(self): """Returns the corresponding armature animation. """ return ArmatureAnimation(self.actionModel.getValue().getBlenderAction(), \ self.nameModel.getValue(), self.startFrameModel.getValue(), self.endFrameModel.getValue()) class ActionValueModel(ValueModel): def __init__(self, armatureAnimationProxy, armatureAction): """Constructor. Attaches ArmatureAnimationProxy to ArmatureAction. """ Model.__init__(self) self.proxy = armatureAnimationProxy # do not call setValue() in constructor as it relies on other proxy models # and it doesn't have a view yet. self.value = armatureAction self.value.attachAnimationProxy(self.proxy) return def setValue(self, value): """Set to new action. Detaches proxy on old action and attaches proxy on new action. """ if (value != self.value): self.value.detachAnimationProxy(self.proxy) value.attachAnimationProxy(self.proxy) ValueModel.setValue(self, value) # reset animation properties to action's default. self.proxy.nameModel.setValue(value.getName()) self.proxy.startFrameModel.setValue(value.getFirstFrame()) self.proxy.endFrameModel.setValue(value.getLastFrame()) return class ArmatureAnimationProxyView(HorizontalLayout, View): def __init__(self, parent, model, deleteAction): HorizontalLayout.__init__(self, parent) View.__init__(self, model) # action menu self.menu = Menu(self, Size([100, 20]), T("Blender Action")) NumberView(self, Size([100, 20]), self.model.startFrameModel, T("Start: "), T("Start frame")) NumberView(self, Size([100, 20]), self.model.endFrameModel, T("End: "), T("End frame")) StringView(self, Size([Size.INFINITY, 20], [80, 20]), self.model.nameModel, tooltip=T("Animation name in Ogre3D")) Button(self, Size([60, 20]), deleteAction, T("Delete"), T("Delete animation from export")) self.update() return def update(self): # rebuild action menu self.menu.removeAll() self.menu.appendItem(MenuTitle("Action")) # current available actions choices = self.model.getActionChoices() current = self.model.getAction() for action in choices: if action == current: isSelected = True else: isSelected = False self.menu.appendItem(MenuItem(action.getName(), \ ArmatureAnimationProxyView.SelectAction(self, action)), isSelected) return class SelectAction(Action): def __init__(self, view, action): self.view = view self.action = action return def execute(self): self.view.model.setAction(self.action) return class AnimationProxyManager(Model): """Base class for managing animations of a given Blender mesh object. """ def __init__(self, bObject): Model.__init__(self) self.bObject = bObject # list of keys determines ordering self.animationProxyKeyList = [] # all keys < maxKey self.maxKey = 0 # key: key, value: xAnimationProxy self.animationProxyDict = {} return def addProxy(self, proxy): """Adds an AnimationProxy to the manager. @param proxy AnimationProxy. @return Key. """ if (len(self.animationProxyKeyList) == self.maxKey): self.maxKey += 1 key = self.maxKey else: # first unused key = [(x + 1) for x in range(self.maxKey) if (x + 1) not in self.animationProxyDict.keys()][0] self.animationProxyDict[key] = proxy self.animationProxyKeyList.append(key) for view in self.viewList: view.notifyAdded(key) return key def removeProxy(self, key): if self.animationProxyDict.has_key(key): del self.animationProxyDict[key] self.animationProxyKeyList.remove(key) for view in self.viewList: view.notifyRemoved(key) return def getBlenderObject(self): return self.bObject def getKey(self, proxy): return def getKeyList(self): return self.animationProxyKeyList def getProxy(self, key): proxy = None if key in self.animationProxyDict.keys(): proxy = self.animationProxyDict[key] return proxy def toAnimations(self, animationExporter): for key in self.animationProxyKeyList: animationExporter.addAnimation(self.animationProxyDict[key].toAnimation()) return def update(self): """Synchronize with Blender. @param True if animations can still be exported, else False. """ return def loadPackageSettings(self): return def savePackageSettings(self): return # @staticmethod # def create( ... ): # """Factory method. # """ class PoseAnimationProxyManager(AnimationProxyManager): """Manages pose animations of a given Blender mesh object. """ def __init__(self, bMeshObject): AnimationProxyManager.__init__(self, bMeshObject) # load package settings if available self.loadPackageSettings() return def toAnimations(self, animationExporter): for key in self.animationProxyKeyList: animationExporter.addPoseAnimation(self.animationProxyDict[key].toAnimation()) return def loadPackageSettings(self): # clear old self.animationProxyKeyList = [] self.maxKey = 0 self.animationProxyDict = {} # get package settings poseAnimationList = PackageSettings.getSingleton().getObjectSetting(self.bObject.getName(), 'PoseAnimationList') # old naming convention if not poseAnimationList: poseAnimationList = PackageSettings.getSingleton().getObjectSetting(self.bObject.getName(), 'poseAnimationList') if poseAnimationList: for poseAnimation in poseAnimationList: # skip old single frame pose animations if (len(poseAnimation) == 3): self.addProxy(PoseAnimationProxy(poseAnimation[0], poseAnimation[1], poseAnimation[2])) self._notify() return def savePackageSettings(self): poseAnimationList = [] for key in self.animationProxyKeyList: proxy = self.animationProxyDict[key] poseAnimationList.append([proxy.getName(), proxy.getStartFrame(), proxy.getEndFrame()]) PackageSettings.getSingleton().setObjectSetting(self.bObject.getName(), 'PoseAnimationList', poseAnimationList) return def update(self): """Checks if Blender object has still shape keys. @return True if shape keys still present, else False. """ isValid = False bKey = self.bObject.getData().getKey() if bKey is not None: if (len(bKey.blocks) > 0): isValid = True return isValid # @staticmethod def create(bMeshObject): """Factory method: @return instance or None if object has no shape key. """ manager = None bKey = bMeshObject.getData().getKey() if bKey is not None: if (len(bKey.blocks) > 0): manager = PoseAnimationProxyManager(bMeshObject) return manager create = staticmethod(create) class MorphAnimationProxyManager(AnimationProxyManager): """Manages morph animations of a given Blender mesh object. """ def __init__(self, bMeshObject): AnimationProxyManager.__init__(self, bMeshObject) # load package settings if available self.loadPackageSettings() return def toAnimations(self, animationExporter): for key in self.animationProxyKeyList: animationExporter.addMorphAnimation(self.animationProxyDict[key].toAnimation()) return def loadPackageSettings(self): # clear old self.animationProxyKeyList = [] self.maxKey = 0 self.animationProxyDict = {} # get package settings morphAnimationList = PackageSettings.getSingleton().getObjectSetting(self.bObject.getName(), 'MorphAnimationList') # old naming convention if not morphAnimationList: morphAnimationList = PackageSettings.getSingleton().getObjectSetting(self.bObject.getName(), 'morphAnimationList') if morphAnimationList: for morphAnimation in morphAnimationList: self.addProxy(MorphAnimationProxy(morphAnimation[0], morphAnimation[1], morphAnimation[2])) self._notify() return def savePackageSettings(self): morphAnimationList = [] for key in self.animationProxyKeyList: proxy = self.animationProxyDict[key] morphAnimationList.append([proxy.getName(), proxy.getStartFrame(), proxy.getEndFrame()]) PackageSettings.getSingleton().setObjectSetting(self.bObject.getName(), 'MorphAnimationList', morphAnimationList) return def update(self): """Checks if Blender object has still shape keys. @return True if shape keys still present, else False. """ isValid = False bKey = self.bObject.getData().getKey() if bKey is not None: if (len(bKey.blocks) > 0): isValid = True return isValid # @staticmethod def create(bMeshObject): """Factory method: @return instance or None if object has no shape key. """ manager = None bKey = bMeshObject.getData().getKey() if bKey is not None: if (len(bKey.blocks) > 0): manager = MorphAnimationProxyManager(bMeshObject) return manager create = staticmethod(create) class AnimationProxyManagerView(AddWidgetListLayout, View): """Base class for view for AnimationProxyMangager. """ def __init__(self, parent, size, model, addAction, addTooltip): # Model View.__init__(self, model) # View AddWidgetListLayout.__init__(self, parent, size) Button(self, Size([60, 20]), addAction, T("Add"), addTooltip) # list of displayed keys self.displayedKeyList = [] # key: key, value: AnimationProxyView self.proxyViewDict = {} self.update() self.setAutoScroll(True) return def notifyAdded(self, key): proxyView = AnimationProxyView(self, self.model.getProxy(key), AnimationProxyManagerView.DeleteAction(self.model, key)) self.proxyViewDict[key] = proxyView self.displayedKeyList.append(key) Blender.Draw.Redraw(1) return def update(self): self.removeAll() self.displayedKeyList = [] for key in self.model.getKeyList(): self.notifyAdded(key) Blender.Draw.Redraw(1) return def notifyRemoved(self, key): self.displayedKeyList.remove(key) self.proxyViewDict[key].removeFromParent() del self.proxyViewDict[key] Blender.Draw.Redraw(1) return class DeleteAction(Action): def __init__(self, model, key): self.model = model self.key = key return def execute(self): self.model.removeProxy(self.key) return class PoseAnimationProxyManagerView(AnimationProxyManagerView): """View for PoseAnimationProxyManager. Set size greater than Size([350,100]). """ def __init__(self, parent, size, model): AnimationProxyManagerView.__init__(self, parent, size, model, \ PoseAnimationProxyManagerView.AddAction(model), \ T("Add new pose animation.")) return class AddAction(Action): def __init__(self, model): self.model = model return def execute(self): key = self.model.addProxy(PoseAnimationProxy('name', 1, 1)) return class MorphAnimationProxyManagerView(AnimationProxyManagerView): """View for MorphAnimationProxyManager. Set size greater than Size([350,100]). """ def __init__(self, parent, size, model): AnimationProxyManagerView.__init__(self, parent, size, model, \ MorphAnimationProxyManagerView.AddAction(model), \ T("Add new morph animation.")) return class AddAction(Action): def __init__(self, model): self.model = model return def execute(self): key = self.model.addProxy(MorphAnimationProxy('name', 1, 1)) return class ArmatureAnimationProxyManager(AnimationProxyManager): """Manages armature animations of a given Blender mesh object. """ def __init__(self, bMeshObject, actionManager): AnimationProxyManager.__init__(self, bMeshObject) # actions self.actionManager = actionManager self.actionList = [] self.update() # load package settings if available self.loadPackageSettings() return def removeProxy(self, key): """Removes proxy from ProxyManager as well as detaches it from ActionManager. """ if self.animationProxyDict.has_key(key): proxy = self.animationProxyDict[key] proxy.getAction().detachAnimationProxy(proxy) del self.animationProxyDict[key] self.animationProxyKeyList.remove(key) for view in self.viewList: view.notifyRemoved(key) return def removeProxies(self): """Removes all animation proxies. """ for proxy in self.animationProxyDict.values(): proxy.getAction().detachAnimationProxy(proxy) self.animationProxyDict = {} return def update(self): """Updates available actions and default action keyframe ranges. Only notifies views if it is still a valid manager. @return True if armature and actions are still present, else False. """ isValid = False self.actionList = [] # check if mesh object has (still) an armature is done by getActions self.actionList = self.actionManager.getActions(self.bObject) if (len(self.actionList) > 0): isValid = True return isValid def loadPackageSettings(self): # clear old for proxy in self.animationProxyDict.values(): proxy.invalidate() self.animationProxyKeyList = [] self.maxKey = 0 self.animationProxyDict = {} # get package settings animationList = PackageSettings.getSingleton().getObjectSetting(self.bObject.getName(), 'ArmatureAnimationList') if not animationList: # load old configuration text animationList = self._loadOldSettings() if animationList and len(animationList): validActionNames = [action.getName() for action in self.actionList] for animation in animationList: # animation = [action name, animation name, start frame, end frame] # check if action is still available if animation[0] in validActionNames: # add animiation action = self.actionManager.getAction(animation[0]) if action: self.addProxy(ArmatureAnimationProxy(self, action, animation[1], animation[2], animation[3])) self._notify() return def savePackageSettings(self): animationList = [] for key in self.animationProxyKeyList: proxy = self.animationProxyDict[key] animationList.append([proxy.getAction().getName(), proxy.getName(), proxy.getStartFrame(), proxy.getEndFrame()]) PackageSettings.getSingleton().setObjectSetting(self.bObject.getName(), 'ArmatureAnimationList', animationList) return def toAnimations(self, animationExporter): for key in self.animationProxyKeyList: animationExporter.addAnimation(self.animationProxyDict[key].toAnimation()) return def getActions(self): return self.actionList def _loadOldSettings(self): """Load animation settings from the old exporter. @return list of animations in the new format or None. """ animationList = None # try open 'ogreexport.cfg' text configTextName = 'ogreexport.cfg' if configTextName in [text.getName() for text in Blender.Text.Get()]: configText = Blender.Text.Get(configTextName) # compose string from text and unpickle try: # unpickle settingsDict = pickle.loads(string.join(configText.asLines()[4:],'\n')) except (PickleError): pass else: # read configuration if settingsDict.has_key('armatureAnimationDictListDict'): armatureAnimationDictListDict = settingsDict['armatureAnimationDictListDict'] parent = GetArmatureObject(self.bObject) if (parent is not None): if armatureAnimationDictListDict.has_key(parent.getName()): armatureAnimationDictList = armatureAnimationDictListDict[parent.getName()] animationList = [] for animationDict in armatureAnimationDictList: actionName = animationDict['actionKey'] name = animationDict['name'] startFrame = animationDict['startFrame'] endFrame = animationDict['endFrame'] animationList.append([actionName, name, startFrame, endFrame]) return animationList # @staticmethod def create(bMeshObject, actionManager): """Factory method: @return instance or None if object has no shape key. """ manager = None if (len(actionManager.getActions(bMeshObject)) > 0): manager = ArmatureAnimationProxyManager(bMeshObject, actionManager) return manager create = staticmethod(create) class ArmatureAnimationProxyManagerView(AnimationProxyManagerView): """View for MorphAnimationProxyManager. Set size greater than Size([350,100]). """ def __init__(self, parent, size, model): AnimationProxyManagerView.__init__(self, parent, size, model, \ ArmatureAnimationProxyManagerView.AddAction(model), \ T("Add new skeleton animation.")) return def notifyAdded(self, key): proxyView = ArmatureAnimationProxyView(self, self.model.getProxy(key), AnimationProxyManagerView.DeleteAction(self.model, key)) self.proxyViewDict[key] = proxyView self.displayedKeyList.append(key) Blender.Draw.Redraw(1) return class AddAction(Action): def __init__(self, model): self.model = model return def execute(self): key = self.model.addProxy(ArmatureAnimationProxy(self.model)) return class SelectedObjectManager(Model): """Manages mesh objects selected for export. Views have to provide an updateSelection() method. """ def __init__(self): Model.__init__(self) # ActionManager takes care of actions for all objects self.actionManager = ActionManager() # object names self.selectedList = [] # key: name, value: Blender Object of type Mesh self.selectedDict = {} # name of current selected object self.current = None # key: name; value: MorphAnimationProxyManager self.morphAnimationProxyManagerDict = {} # key: name; value: PoseAnimationProxyMangager self.poseAnimationProxyManagerDict = {} # key: name; value: ArmatureAnimationProxyManager self.armatureAnimationProxyManagerDict = {} self.updateSelection() return def updateSelection(self): """Update the list of objects selected for export and the available actions for these objects. """ # update available actions self.actionManager.update() self.selectedList = [] self.selectedDict = {} # for every currently selected mesh object for bMeshObject in [bObject for bObject in Blender.Object.GetSelected() if (bObject.getType() == 'Mesh')]: name = bMeshObject.getName() self.selectedList.append(name) self.selectedDict[name] = bMeshObject # morph animation if self.morphAnimationProxyManagerDict.has_key(name): # update if not self.morphAnimationProxyManagerDict[name].update(): # manager no longer valid del self.morphAnimationProxyManagerDict[name] else: # create new manager manager = MorphAnimationProxyManager.create(bMeshObject) if manager: self.morphAnimationProxyManagerDict[name] = manager # pose animation if self.poseAnimationProxyManagerDict.has_key(name): # update if not self.poseAnimationProxyManagerDict[name].update(): # manager no longer valid del self.poseAnimationProxyManagerDict[name] else: # create new manager manager = PoseAnimationProxyManager.create(bMeshObject) if manager: self.poseAnimationProxyManagerDict[name] = manager # armature animation if self.armatureAnimationProxyManagerDict.has_key(name): # update if not self.armatureAnimationProxyManagerDict[name].update(): # manager no longer valid self.armatureAnimationProxyManagerDict[name].removeProxies() del self.armatureAnimationProxyManagerDict[name] else: # create new manager manager = ArmatureAnimationProxyManager.create(bMeshObject, self.actionManager) if manager: self.armatureAnimationProxyManagerDict[name] = manager # == remove AnimationProxyManagers of unselected objects == # save animations of unselected objects # remove MorphAnimationProxyManagers of unselected objects for (bObjectName, proxyManager) in [(name, self.morphAnimationProxyManagerDict[name]) \ for name in self.morphAnimationProxyManagerDict.keys()[:] if name not in self.selectedList]: proxyManager.savePackageSettings() del self.morphAnimationProxyManagerDict[bObjectName] # remove PoseAnimationProxyManagers of unselected objects for (bObjectName, proxyManager) in [(name, self.poseAnimationProxyManagerDict[name]) \ for name in self.poseAnimationProxyManagerDict.keys()[:] if name not in self.selectedList]: proxyManager.savePackageSettings() del self.poseAnimationProxyManagerDict[bObjectName] # remove ArmatureAnimationProxyMangers of unselected objects for (bObjectName, proxyManager) in [(name, self.armatureAnimationProxyManagerDict[name]) \ for name in self.armatureAnimationProxyManagerDict.keys()[:] if name not in self.selectedList]: proxyManager.savePackageSettings() # detach animations from actions proxyManager.removeProxies() del self.armatureAnimationProxyManagerDict[bObjectName] # update current selected if len(self.selectedList): self.current = self.selectedList[0] else: self.current = None # notify views for view in self.viewList: view.updateSelection() return def setCurrentObjectName(self, name): if name in self.selectedDict.keys(): self.current = name self._notify() return def getCurrentObjectName(self): return self.current def getObject(self, name): bObject = None if name in self.selectedDict.keys(): bObject = self.selectedDict[name] return bObject def getMorphAnimationProxyManager(self, name): proxyManager = None if name in self.morphAnimationProxyManagerDict.keys(): proxyManager = self.morphAnimationProxyManagerDict[name] return proxyManager def getPoseAnimationProxyManager(self, name): proxyManager = None if name in self.poseAnimationProxyManagerDict.keys(): proxyManager = self.poseAnimationProxyManagerDict[name] return proxyManager def getArmatureAnimationProxyManager(self, name): proxyManger = None if name in self.armatureAnimationProxyManagerDict.keys(): proxyManger = self.armatureAnimationProxyManagerDict[name] return proxyManger def getObjectNameList(self): return self.selectedList def savePackageSettings(self): for proxyManager in self.morphAnimationProxyManagerDict.values(): proxyManager.savePackageSettings() for proxyManager in self.poseAnimationProxyManagerDict.values(): proxyManager.savePackageSettings() for proxyManager in self.armatureAnimationProxyManagerDict.values(): proxyManager.savePackageSettings() return def export(self, exportPath, materialScriptName, colouredAmbient, gameEngineMaterials, convertXML, copyTextures): # create MaterialManager if len(self.selectedList): materialManager = MaterialManager(exportPath, materialScriptName) Log.getSingleton().logInfo("Output to directory \"%s\"" % exportPath) for name in self.selectedList: Log.getSingleton().logInfo("Processing Object \"%s\"" % name) # create MeshExporter meshExporter = MeshExporter(self.selectedDict[name]) if self.morphAnimationProxyManagerDict.has_key(name): self.morphAnimationProxyManagerDict[name].toAnimations(meshExporter.getVertexAnimationExporter()) if self.poseAnimationProxyManagerDict.has_key(name): self.poseAnimationProxyManagerDict[name].toAnimations(meshExporter.getVertexAnimationExporter()) if self.armatureAnimationProxyManagerDict.has_key(name): self.armatureAnimationProxyManagerDict[name].toAnimations(meshExporter.getArmatureExporter()) # export meshExporter.export(exportPath, materialManager, Matrix(*matrixOne), colouredAmbient, gameEngineMaterials, convertXML) # export materials materialManager.export(exportPath, materialScriptName, copyTextures) else: Log.getSingleton().logWarning("No mesh object selected for export!") return class SelectedObjectManagerView(VerticalLayout, View): def __init__(self, parent, model): VerticalLayout.__init__(self, parent) View.__init__(self, model) hLayout = HorizontalLayout(self) LabelView(hLayout, L("Selected: ")) self.menu = Menu(hLayout, Size([Size.INFINITY, 20], [150, 20]), T("Objects selected for export.")) Button(hLayout, Size([70, 20]), SelectedObjectManagerView.UpdateAction(self.model), T("Update"), \ T("Update list of objects selected for export.")) Spacer(self, Size([0, 10])) self.alternatives = AlternativesLayout(self) self.noneSelectedWidget = Spacer(self.alternatives, Size([0, Size.INFINITY], [0, 0])) self.alternatives.setCurrent(self.noneSelectedWidget) # key: object name, value: AnimationProxyManagersView self.animationProxyManagersViewDict = {} self.updateSelection() return def updateSelection(self): """Called from model if list of selected mesh objects changes. """ self.menu.removeAll() self.alternatives.setCurrent(self.noneSelectedWidget) for name in self.model.getObjectNameList(): # set current object to first in list setCurrent = False if (name == self.model.getObjectNameList()[0]): setCurrent = True self.menu.appendItem(MenuItem(name, SelectedObjectManagerView.SelectAction(self.model, name)), setCurrent) if not self.animationProxyManagersViewDict.has_key(name): poseManager = self.model.getPoseAnimationProxyManager(name) morphManager = self.model.getMorphAnimationProxyManager(name) armatureManager = self.model.getArmatureAnimationProxyManager(name) self.animationProxyManagersViewDict[name] = SelectedObjectManagerView.AnimationProxyManagersView( \ self.alternatives, self.model, name) else: # udpate existing self.animationProxyManagersViewDict[name].updateSelection() # set current object to first in list if (name == self.model.getObjectNameList()[0]): self.alternatives.setCurrent(self.animationProxyManagersViewDict[name]) Blender.Draw.Redraw(1) return def update(self): """Update current selected. """ self.alternatives.setCurrent(self.animationProxyManagersViewDict[self.model.getCurrentObjectName()]) Blender.Draw.Redraw(1) return class SelectAction(Action): def __init__(self, model, name): self.model = model self.name = name return def execute(self): self.model.setCurrentObjectName(self.name) return class UpdateAction(Action): def __init__(self, model): self.model = model return def execute(self): self.model.updateSelection() return class AnimationProxyManagersView(Box, View): """View for the different animation proxy managers. Provides toggles to switch between morph, pose and armature animation proxy managers. """ def __init__(self, parent, manager, name): """Constructor. @manager SelectedObjectManager. @name Blender mesh object name. """ Box.__init__(self, parent, L("Animation Settings of \"%s\"" % name), 0, 10) # mesh object name self.name = name # SelectedObjectManager self.manager = manager # AnimationProxyManagers # [Armature, Pose, Morph] self.managerList = [None, None, None] # Toggle Bar [Skeleton] [ Pose ] [ Morph ] # If corresponding manager does not exist # the toggle is replaced with a Spacer that has size [0, 0]. # Therefore ToggleView and Spacer are alternatives # # Only active toggles, i.e. toggles that correspond to an existing # AnimationProxyManager are currently in the group self.toggleList = [None, None, None] # models self.toggleList[0] = ToggleModel(False) self.toggleList[1] = ToggleModel(False) self.toggleList[2] = ToggleModel(False) # self.model = ToggleGroup() View.__init__(self, ToggleGroup()) vLayout = VerticalLayout(self) hLayout = HorizontalLayout(vLayout) self.alternativesList = [None, None, None] self.alternativesList[0] = AlternativesLayout(hLayout) self.alternativesList[1] = AlternativesLayout(hLayout) self.alternativesList[2] = AlternativesLayout(hLayout) self.toggleViewList = [None, None, None] self.toggleViewList[0] = ToggleView(self.alternativesList[0], \ Size([Size.INFINITY, 20], [150, 20]), \ self.toggleList[0], \ T("Skeleton"), T("Armature animation to Ogre skeleton animation.")) self.toggleViewList[1] = ToggleView(self.alternativesList[1], \ Size([Size.INFINITY, 20], [150, 20]), \ self.toggleList[1], \ T("Pose"),T("Shape animation to Ogre pose animation.")) self.toggleViewList[2] = ToggleView(self.alternativesList[2], \ Size([Size.INFINITY, 20], [150, 20]), \ self.toggleList[2], \ T("Morph"),T("Shape animation to Ogre morph animation.")) self.spacerList = [None, None, None] self.spacerList[0] = Spacer(self.alternativesList[0], Size([0,0])) self.alternativesList[0].setCurrent(self.spacerList[0]) self.spacerList[1] = Spacer(self.alternativesList[1], Size([0,0])) self.alternativesList[1].setCurrent(self.spacerList[1]) self.spacerList[2] = Spacer(self.alternativesList[2], Size([0,0])) self.alternativesList[2].setCurrent(self.spacerList[2]) # lower part of screen shows either the selected AnimationProxyManagerView # or a spacer self.alternatives = AlternativesLayout(vLayout) # AnimationProxyManagerViews self.managerViewList = [None, None, None] self.spacer = Spacer(self.alternatives, Size([Size.INFINITY, Size.INFINITY], [350, 120])) self.alternatives.setCurrent(self.spacer) # update managers self.updateSelection() return def update(self): """Updates togglegroup status. """ if (self.managerList.count(None) != 3): toggleModel = self.model.getValue() # TODO: test if toggle is enabled should be unnecessary if toggleModel and toggleModel.getValue(): # raises ValueError if model is not in list index = self.toggleList.index(toggleModel) if self.managerViewList[index] is not None: # display current selected AnimationProxyMana self.alternatives.setCurrent(self.managerViewList[index]) else: # forgot to remove toggle from ToggleGroup # as manager is no longer present raise RuntimeError return def updateSelection(self): """Checks if action managers are still or newly available. """ newManager = self.manager.getArmatureAnimationProxyManager(self.name) if not (self.managerList[0] == newManager): # armature manager changed if self.managerList[0] is None: # create new # add manager self.managerList[0] = newManager # add manager view self.managerViewList[0] = ArmatureAnimationProxyManagerView( \ self.alternatives, Size([Size.INFINITY, Size.INFINITY], [350, 120]), \ self.managerList[0]) # add toggle to toggleGroup, calls update() if selected self.model.addToggle(self.toggleList[0]) # show ToggleView self.alternativesList[0].setCurrent(self.toggleViewList[0]) elif newManager is None: # remove old # hide ToggleView self.alternativesList[0].setCurrent(self.spacerList[0]) # remove toggle from ToggleGroup self.model.removeToggle(self.toggleList[0]) # set spacer in lower part of the window if no manager left if (self.managerList.count(None) >= 2): self.alternatives.setCurrent(self.spacer) # remove old manager in list self.managerList[0] = None # remove old managerView self.managerViewList[0].removeFromParent() self.managerViewList[0] = None else: # swap # manager self.managerList[0] = newManager # view oldView = self.managerViewList[0] # add new to alternatives layout self.managerViewList[0] = ArmatureAnimationProxyanagerView( \ self.alternatives, Size([Size.INFINITY, Size.INFINITY], [350, 120]), \ self.managerList[0]) # remove old from alternatives layout if (self.alternatives.getCurrent() == oldView): # show view self.alternatives.setCurrent(self.managerViewList[0]) oldView.removeFromParent() newManager = self.manager.getPoseAnimationProxyManager(self.name) if not (self.managerList[1] == newManager): # pose manager changed if self.managerList[1] is None: # create new # add manager self.managerList[1] = newManager # add manager view self.managerViewList[1] = PoseAnimationProxyManagerView( \ self.alternatives, Size([Size.INFINITY, Size.INFINITY], [350, 120]), \ self.managerList[1]) # add toggle to toggleGroup, calls update() if selected self.model.addToggle(self.toggleList[1]) # show ToggleView self.alternativesList[1].setCurrent(self.toggleViewList[1]) elif newManager is None: # remove old # hide ToggleView self.alternativesList[1].setCurrent(self.spacerList[1]) # remove toggle from ToggleGroup self.model.removeToggle(self.toggleList[1]) # set spacer in lower part of the window if no manager left if (self.managerList.count(None) >= 2): self.alternatives.setCurrent(self.spacer) # remove old manager in list self.managerList[1] = None # remove old managerView self.managerViewList[1].removeFromParent() self.managerViewList[1] = None else: # swap # manager self.managerList[0] = newManager # view oldView = self.managerViewList[1] # add new to alternatives layout self.managerViewList[1] = PoseAnimationProxyanagerView( \ self.alternatives, Size([Size.INFINITY, Size.INFINITY], [350, 120]), \ self.managerList[1]) # remove old from alternatives layout if (self.alternatives.getCurrent() == oldView): # show view self.alternatives.setCurrent(self.managerViewList[1]) oldView.removeFromParent() newManager = self.manager.getMorphAnimationProxyManager(self.name) if not (self.managerList[2] == newManager): # morph manager changed if self.managerList[2] is None: # create new # add manager self.managerList[2] = newManager # add manager view self.managerViewList[2] = MorphAnimationProxyManagerView( \ self.alternatives, Size([Size.INFINITY, Size.INFINITY], [350, 120]), \ self.managerList[2]) # add toggle to toggleGroup, calls update() if selected self.model.addToggle(self.toggleList[2]) # show ToggleView self.alternativesList[2].setCurrent(self.toggleViewList[2]) elif newManager is None: # remove old # hide ToggleView self.alternativesList[2].setCurrent(self.spacerList[2]) # remove toggle from ToggleGroup self.model.removeToggle(self.toggleList[2]) # set spacer in lower part of the window if no manager left if (self.managerList.count(None) >= 2): self.alternatives.setCurrent(self.spacer) # remove old manager in list self.managerList[2] = None # remove old managerView self.managerViewList[2].removeFromParent() self.managerViewList[2] = None else: # swap # manager self.managerList[2] = newManager # view oldView = self.managerViewList[2] # add new to alternatives layout self.managerViewList[2] = PoseAnimationProxyanagerView( \ self.alternatives, Size([Size.INFINITY, Size.INFINITY], [350, 120]), \ self.managerList[2]) # remove old from alternatives layout if (self.alternatives.getCurrent() == oldView): # show view self.alternatives.setCurrent(self.managerViewList[2]) oldView.removeFromParent() return class MeshExporterApplication: def __init__(self): # initialize global settings to default value self.materalScriptName = BasenameModel(Blender.Scene.GetCurrent().getName() + '.material') self.exportPath = DirnameModel(Blender.Get('filename')) self.colouredAmbient = ToggleModel(0) self.gameEngineMaterials = ToggleModel(0) self.convertXML = ToggleModel(0) self.copyTextures = ToggleModel(0) # load package settings if applicable self._loadPackageSettings() # manager for selected objects self.selectedObjectManager = SelectedObjectManager() self.preferencesScreen = PreferencesScreen() # create main screen # material settings self.mainScreen = Screen() frame = OgreFrame(self.mainScreen, "Meshes Exporter") vLayout = VerticalLayout(frame) # SelectedObjectManagerView SelectedObjectManagerView(vLayout, self.selectedObjectManager) ## material settings Spacer(vLayout, Size([0, 10])) mbox = Box(vLayout, L("Material Settings"), 0 , 10) mvLayout = VerticalLayout(mbox) StringView(mvLayout, Size([200, 20]), self.materalScriptName, T("Material File: "), \ T("All material definitions go in this file (relative to the export path).")) mvhLayout = HorizontalLayout(mvLayout) ToggleView(mvhLayout, Size([Size.INFINITY, 20], [150, 20]), self.colouredAmbient, \ T("Coloured Ambient"), \ T("Use Amb factor times diffuse colour as ambient instead of Amb factor times white.")) ToggleView(mvhLayout, Size([Size.INFINITY, 20], [150, 20]), self.gameEngineMaterials, \ T("Game Engine Materials"), \ T("Export game engine materials instead of rendering materials.")) ToggleView(mvLayout, Size([Size.INFINITY, 20], [150, 20]), self.copyTextures, \ T("Copy Textures"), \ T("Copy texture files into export path.")) ## global settings Spacer(vLayout, Size([0, 10])) ToggleView(vLayout, Size([Size.INFINITY, 20], [150, 20]), self.convertXML, T("OgreXMLConverter"), \ T("Run OgreXMLConverter on the exported XML files.")) # path panel phLayout = HorizontalLayout(vLayout) StringView(phLayout, Size([Size.INFINITY, 20], [200, 20]), self.exportPath, T("Path: "), \ T("The directory where the exported files are saved.")) Button(phLayout, Size([70, 20]), MeshExporterApplication.SelectAction(self), T("Select"), \ T("Select the export directory.")) ## buttons Spacer(vLayout, Size([0, 10])) bhLayout = HorizontalLayout(vLayout) bSize = Size([Size.INFINITY, 30], [Blender.Draw.GetStringWidth('Preferences')+10, 30]) Button(bhLayout, bSize, MeshExporterApplication.ExportAction(self), T("Export"), \ T("Export selected mesh objects.")) Button(bhLayout, bSize, MeshExporterApplication.PreferencesAction(self), T("Preferences"), \ T("Exporter preferences.")) Button(bhLayout, bSize, MeshExporterApplication.HelpAction(), T("Help"), \ T("Get help.")) Button(bhLayout, bSize, MeshExporterApplication.QuitAction(self), T("Quit"), \ T("Quit without exporting.")) return def go(self): self.mainScreen.activate() return def _loadPackageSettings(self): materalScriptName = PackageSettings.getSingleton().getSetting('materalScriptName') if materalScriptName is not None: self.materalScriptName.setValue(materalScriptName) colouredAmbient = PackageSettings.getSingleton().getSetting('colouredAmbient') if colouredAmbient is not None: self.colouredAmbient.setValue(colouredAmbient) gameEngineMaterials = PackageSettings.getSingleton().getSetting('gameEngineMaterials') if gameEngineMaterials is not None: self.gameEngineMaterials.setValue(gameEngineMaterials) copyTextures = PackageSettings.getSingleton().getSetting('copyTextures') if copyTextures is not None: self.copyTextures.setValue(copyTextures) convertXML = PackageSettings.getSingleton().getSetting('convertXML') if convertXML is not None: self.convertXML.setValue(convertXML) exportPath = PackageSettings.getSingleton().getSetting('exportPath') if exportPath is not None: self.exportPath.setValue(exportPath) return def _savePackageSettings(self): self.selectedObjectManager.savePackageSettings() PackageSettings.getSingleton().setSetting('materalScriptName', self.materalScriptName.getValue()) PackageSettings.getSingleton().setSetting('colouredAmbient', self.colouredAmbient.getValue()) PackageSettings.getSingleton().setSetting('gameEngineMaterials', self.gameEngineMaterials.getValue()) PackageSettings.getSingleton().setSetting('copyTextures', self.copyTextures.getValue()) PackageSettings.getSingleton().setSetting('convertXML', self.convertXML.getValue()) PackageSettings.getSingleton().setSetting('exportPath', self.exportPath.getValue()) PackageSettings.getSingleton().save() return class PreferencesAction(Action): def __init__(self, app): self.app = app return def execute(self): self.app.preferencesScreen.activate() return class HelpAction(Action): def execute(self): dirList = [Blender.Get('datadir'), Blender.Get('udatadir'), Blender.Get('scriptsdir'), Blender.Get('uscriptsdir')] stack = [dir for dir in dirList if dir is not None] found = False helpFile = '' while not(found) and (len(stack) > 0): dir = stack.pop(0) helpFile = Blender.sys.join(dir, Blender.sys.join('ogrehelp','ogremeshesexporter.html')) if Blender.sys.exists(helpFile): found = True if found: webbrowser.open(helpFile, 1, 1) else: webbrowser.open("http://www.ogre3d.org/phpBB2/search.php", 1, 1) return class UpdateAction(Action): def __init__(self, app): self.app = app return class SelectAction(Action): def __init__(self, app): self.app = app return def execute(self): Blender.Window.FileSelector(self.app.exportPath.setValue, "Export Directory", self.app.exportPath.getValue()) return class ExportAction(Action): def __init__(self, app): self.app = app self.exportScreen = None self.logView = None return def execute(self): # create export screen self.exportScreen = Screen() frame = OgreFrame(self.exportScreen, "Meshes Exporter") vLayout = VerticalLayout(frame) LabelView(vLayout, L('Export Log:', 'large')) self.logView = LogView(vLayout, Size([Size.INFINITY, Size.INFINITY], [300, 200]), 20, False) Spacer(vLayout, Size([0, 10])) activator = Activator(vLayout, False) bhLayout = HorizontalLayout(activator) Button(bhLayout, Size([Size.INFINITY, 30], [120, 30]), MeshExporterApplication.ExportAction.OkAction(self), \ T("Ok"), T("Close export log.")) Spacer(bhLayout, Size([Size.INFINITY, 30], [120, 30])) Spacer(bhLayout, Size([Size.INFINITY, 30], [120, 30])) Button(bhLayout, Size([Size.INFINITY, 30], [120, 30]), MeshExporterApplication.QuitAction(self.app), \ T("Quit"), T("Quit exporter.")) self.exportScreen.activate() # save package settings on every export self.app._savePackageSettings() Log.getSingleton().logInfo("Exporting...") self.app.selectedObjectManager.export(self.app.exportPath.getValue(), \ self.app.materalScriptName.getValue(), \ self.app.colouredAmbient.getValue(), \ self.app.gameEngineMaterials.getValue(), \ self.app.convertXML.getValue(), \ self.app.copyTextures.getValue()) Log.getSingleton().logInfo("Done.") activator.setEnabled(True) Blender.Draw.Redraw(1) return class OkAction(Action): def __init__(self, exportAction): self.action = exportAction return def execute(self): self.action.exportScreen.deactivate() # remove view from model self.action.logView.detachModel() return class QuitAction(QuitAction): def __init__(self, app): self.app = app return def execute(self): self.app._savePackageSettings() QuitAction.execute(self) return application = MeshExporterApplication() application.go()