Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/Tools/BlenderExport/ogrepkg/armatureexport.py @ 6

Last change on this file since 6 was 6, checked in by anonymous, 17 years ago

=…

File size: 21.7 KB
RevLine 
[6]1"""Armature and armature animation export classes.
2
3   @author Michael Reimpell
4"""
5# Copyright (C) 2005  Michael Reimpell
6#
7# This library is free software; you can redistribute it and/or
8# modify it under the terms of the GNU Lesser General Public
9# License as published by the Free Software Foundation; either
10# version 2.1 of the License, or (at your option) any later version.
11#
12# This library is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15# Lesser General Public License for more details.
16#
17# You should have received a copy of the GNU Lesser General Public
18# License along with this library; if not, write to the Free Software
19# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21# epydoc doc format
22__docformat__ = "javadoc en"
23
24import base
25from base import *
26
27import math
28import Blender
29import Blender.Mathutils
30from Blender.Mathutils import *
31
32# TODO: Facade for Blender objects
33def GetArmatureObject(bObject):
34        """Returns Blender armature object of this Blender object or <code>None</code>.
35       
36           Armatures are either parented or listed in the modifier stack of the object to deform.
37        """
38        bArmatureObject = None
39        if (bObject.getType() == "Mesh"):
40                # parented armatures get preferred
41                bParentObject = bObject.getParent()
42                if (bParentObject and (bParentObject.getType() == "Armature")):
43                        bArmatureObject = bParentObject
44                else:
45                        # check modifier stack, use last armature modifier
46                        for modifier in bObject.modifiers:
47                                if ((modifier.type == Blender.Modifier.Type['ARMATURE'])
48                                        and modifier[Blender.Modifier.Settings.VERTGROUP]):
49                                        bArmatureObject = modifier[Blender.Modifier.Settings.OBJECT]
50        return bArmatureObject
51
52class ArmatureAction:
53        """Manages a Blender action.
54        """
55        def __init__(self, bAction, armatureExporter):
56                self.bAction = bAction
57                self.armatureExporter = armatureExporter
58                return
59        def getName(self):
60                return self.bAction.getName()
61        def hasEffect(self):
62                """If true, the action has an effect on at least one bone of the armature.
63                """
64                hasEffect = 0
65                channelIpoDict = self.bAction.getAllChannelIpos()
66                channelIterator = iter(channelIpoDict)
67                try:
68                        while not(hasEffect):
69                                channelName = channelIterator.next()
70                                if ((channelName in self.armatureExporter.boneIndices.keys())
71                                        and channelIpoDict[channelName].getNcurves()):
72                                        hasEffect = 1
73                except StopIteration:
74                        pass
75                return hasEffect
76       
77class ArmatureActionManager:
78        def __init__(self, armatureExporter):
79                self.armatureExporter = armatureExporter
80                self.actionList = []
81                for bAction in Blender.Armature.NLA.GetActions().values():
82                        action = ArmatureAction(bAction, self.armatureExporter)
83                        if action.hasEffect():
84                                self.actionList.append(action)
85                return
86        def __iter__(self):
87                return ArmatureActionManager.Iterator(self)
88        def getActions(self):
89                return self.actionList
90        class Iterator:
91                """Iterates over ArmatureActions.
92                """
93                def __init__(self, armatureActionManager):
94                        self.armatureActionManager = armatureActionManager
95                        self.listIndex = 0
96                        return
97                def next(self):
98                        if self.listIndex >= len(self.armatureActionManager.actionList):
99                                raise StopIteration
100                        self.listIndex = self.listIndex + 1
101                        return self.armatureActionManager.actionList[self.listIndex - 1]
102
103class ArmatureAnimation:
104        """Resembles Blender's action actuators.
105        """
106        def __init__(self, bAction, name, startFrame, endFrame):
107                """Constructor
108                   
109                   @param bAction        Blender action of the animation
110                   @param name           Animation name
111                   @param startFrame     first frame of the animation
112                   @param endFrame       last frame of the animation
113                """
114                self.bAction = bAction
115                self.name = name
116                self.startFrame = startFrame
117                self.endFrame = endFrame
118                # populated on export
119                self.length = None
120                self.trackList = None
121                return
122        def getName(self):
123                return self.name
124        def export(self, armatureExporter, fps):
125                """Export animation to OGRE.
126               
127                   @fps frames per second. Has to be larger than zero.
128                """
129                # store current settings
130                frameAtExportTime = Blender.Get('curframe')
131                actionAtExportTime = armatureExporter.getArmatureObject().getAction()
132                       
133                # total animation time
134                startFrame = int(self.startFrame)
135                endFrame = int(self.endFrame)
136                if (self.endFrame < self.startFrame):
137                        # TODO backward animation
138                        startFrame = self.endFrame
139                        endFrame = self.startFrame
140                        Log.getSingleton().logError("Start frame after end frame in animation \"%s\"" % self.name)
141                self.length = (endFrame - startFrame)/float(fps)
142               
143                # enable action
144                self.bAction.setActive(armatureExporter.getArmatureObject())
145                Blender.Window.Redraw()
146               
147                ## create track for all OGRE bones
148                stack = []
149                for bone in armatureExporter.getRootSkeletonBoneList():
150                        stack.append(SkeletonAnimationTrack(armatureExporter, bone))
151                rootTrackList = stack[:]
152                self.trackList = stack[:]
153                # precondition: stack contains all root tracks
154                while (len(stack) > 0):
155                        parentTrack = stack.pop(0)
156                        for bone in parentTrack.getSkeletonBone().getChildren():
157                                track = SkeletonAnimationTrack(armatureExporter, bone, parentTrack)
158                                stack.append(track)
159                                self.trackList.append(track)
160                # postcondition: every SkeletonBone has a corresponding track
161               
162                ## export pose frame by frame
163                bArmatureObject = armatureExporter.getArmatureObject()
164                meshObjectSpaceTransformation = armatureExporter.getAdditionalRootBoneTransformation()
165                for frame in range(startFrame, endFrame + 1):
166                        # frameTime
167                        frameTime = (frame - startFrame)/float(fps)
168                       
169                        # evaluate pose for current frame
170                        Blender.Set('curframe', frame)
171                        Blender.Window.Redraw()
172                        bArmatureObject.evaluatePose(frame)
173                        pose = bArmatureObject.getPose()
174                       
175                        # tracks on the stack do not have unprocessed parent tracks
176                        stack = rootTrackList[:]
177                        # set keyframes
178                        while (len(stack) > 0):
179                                track = stack.pop(0)
180                                track.addKeyframe(pose, frameTime, meshObjectSpaceTransformation)
181                                stack.extend(track.getChildren())
182               
183                # remove unused tracks
184                ## TODO
185               
186                # restore current settings
187                # FIXME: does not work with multiple actions
188                actionAtExportTime.setActive(armatureExporter.getArmatureObject())
189                Blender.Set('curframe', frameAtExportTime)
190                armatureExporter.getArmatureObject().evaluatePose(frameAtExportTime)
191                return
192        def write(self, f, indentation=0):
193                Log.getSingleton().logInfo("Writing skeleton animation \"%s\"." % self.name)
194                f.write(indent(indentation) + "<animation name=\"%s\" length=\"%f\">\n" % (self.name, self.length))
195                if (len(self.trackList) > 0):
196                        f.write(indent(indentation + 1) + "<tracks>\n")
197                        for track in self.trackList:
198                                track.write(f, indentation + 2)
199                        f.write(indent(indentation + 1) + "</tracks>\n")
200                f.write(indent(indentation) + "</animation>\n")
201                return
202
203class SkeletonBone:
204        """Bone of an Ogre sekeleton.
205        """
206        def __init__(self, armatureExporter, bBone, parent=None):
207                """Constructor.
208               
209                   @param armatureExporter ArmatureExporter, required for bone ids.
210                   @param bBone Blender bone.
211                   @param parent Parent SkeletonBone.
212                """
213                self.armatureExporter = armatureExporter
214                self.bBone = bBone
215                self.parent = parent
216                self.children = []
217                self.ogreRestMatrix = None
218                self.inverseTotalTransformation = None
219                # register as child of parent
220                if self.parent is not None:
221                        self.parent.addChild(self)
222                return
223        def addChild(self, child):
224                """Appends a child bone.
225               
226                   This method gets called from the constructor of SkeletonBone.
227               
228                   @param child SkeletonBone.
229                """
230                self.children.append(child)
231                return
232        def getName(self):
233                return self.bBone.name
234        def getParent(self):
235                return self.parent
236        def getChildren(self):
237                # return new list in order to avoid side effects
238                return self.children[:]
239        def getBlenderBone(self):
240                return self.bBone
241        def getOgreRestMatrix(self):
242                """Returns a copy of the rest matrix.
243                """
244                if self.ogreRestMatrix is None:
245                        raise AssertionError('OGRE rest matrix not set for this bone!')
246                return Blender.Mathutils.Matrix(*self.ogreRestMatrix)
247        def getInverseTotalTransformation(self):
248                return Blender.Mathutils.Matrix(*self.inverseTotalTransformation)
249        def setOgreRestMatrix(self):
250                """Sets rest transformation in OGRE.
251               
252                   Note that the parent bone rest matrix is required to be valid!
253                """
254                # Warning: Blender uses left multiplication vector*matrix
255                # get bone matrix of OGRE parent bone
256                inverseParentMatrix = Matrix([1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1])
257                if self.parent is not None:
258                        inverseParentMatrix = self.parent.getInverseTotalTransformation()
259               
260                # bone matrix relative to armature object
261                self.ogreRestMatrix = Blender.Mathutils.Matrix(*self.bBone.matrix['ARMATURESPACE'])
262                # relative to mesh object origin
263                self.ogreRestMatrix *= self.armatureExporter.getAdditionalRootBoneTransformation()
264                # store total transformation
265                self.inverseTotalTransformation = Blender.Mathutils.Matrix(*self.ogreRestMatrix)
266                self.inverseTotalTransformation.invert()
267                # relative to OGRE parent bone origin
268                self.ogreRestMatrix *= inverseParentMatrix
269                return
270        def writeBone(self, f, indentation=0):
271                f.write(indent(indentation) + "<bone id=\"%d\" name=\"%s\">\n"
272                        % (self.armatureExporter.getBoneIndex(self.getName()), self.getName()))
273                # get transformation values
274                positionTuple = tuple(self.ogreRestMatrix.translationPart())
275                rotationQuaternion = self.ogreRestMatrix.toQuat()
276                rotationQuaternion.normalize()
277                angle = float(rotationQuaternion.angle)/180*math.pi
278                axisTuple = tuple(rotationQuaternion.axis)
279                # Blender bones do not have scale in rest position
280                #scaleTuple = tuple(self.ogreRestMatrix.scalePart())
281                # write transformation values
282                f.write(indent(indentation + 1) + "<position x=\"%.6f\" y=\"%.6f\" z=\"%.6f\"/>\n" % positionTuple)
283                f.write(indent(indentation + 1) + "<rotation angle=\"%.6f\">\n" % angle)
284                f.write(indent(indentation + 2) + "<axis x=\"%.6f\" y=\"%.6f\" z=\"%.6f\"/>\n" % axisTuple)
285                f.write(indent(indentation + 1) + "</rotation>\n")
286                f.write(indent(indentation) + "</bone>\n")
287                return
288
289class SkeletonAnimationTrack:
290        def __init__(self, armatureExporter, skeletonBone, parent=None):
291                """Animation track for an OGRE bone.
292               
293                   @param parent parent track.
294                """
295                self.armatureExporter = armatureExporter
296                self.skeletonBone = skeletonBone
297                self.parent = parent
298                self.children = []
299                # key: time, value: keyframe matrix.
300                self.keyframeDict = {}
301                # cache name
302                self.name = self.skeletonBone.getName()
303                # cache restpose
304                self.ogreRestPose = self.skeletonBone.getOgreRestMatrix()
305                self.inverseOgreRestPose = self.skeletonBone.getOgreRestMatrix()
306                self.inverseOgreRestPose.invert()
307               
308                # (pose_matrix * additionalRootBoneTransformation)^(-1) of last keyframe
309                self.inverseLastKeyframeTotalTransformation = None
310               
311                # register as child of parent
312                if self.parent is not None:
313                        self.parent.addChild(self)             
314                return
315        def getSkeletonBone(self):
316                return self.skeletonBone
317        def getChildren(self):
318                return self.children
319        def addChild(self, child):
320                """Appends a child track.
321               
322                   This method gets called from the constructor of SkeletonAnimationTrack.
323               
324                   @param child SkeletonAnimationTrack.
325                """
326                self.children.append(child)
327                return
328        def nKeyframes(self):
329                return len(self.keyframeDict)
330        def getInverseLastKeyframeTotalTransformation(self):
331                """Returns a copy of (pose_matrix * additionalRootBoneTransformation)^(-1)
332                   of last keyframe.
333                   
334                   Called from addKeyframe of child tracks.
335                """
336                return Blender.Mathutils.Matrix(*self.inverseLastKeyframeTotalTransformation)
337        def addKeyframe(self, pose, time, additionalRootBoneTransformation):
338                """Adds current pose as keyframe.
339               
340                   @param additionalRootBoneTransformation transformation from local armature
341                    object space to the mesh object coordinate system.
342                """
343                # Warning: Blender uses left multiplication vector*matrix
344                #
345                # Blender Bone coordinates in armature object space are
346                # (x,y,z,w) * pose_matrix
347                # Hence coordinates in meshObject coordinate system are
348                # (x,y,z,w) * pose_matrix * additionalRootBoneTransformation.
349                #
350                poseTransformation = Blender.Mathutils.Matrix(*pose.bones[self.name].poseMatrix)
351                poseTransformation *= additionalRootBoneTransformation
352               
353                self.inverseLastKeyframeTotalTransformation = Blender.Mathutils.Matrix(*poseTransformation)
354                self.inverseLastKeyframeTotalTransformation.invert()
355               
356                # calculate difference to parent bone
357                if self.parent is not None:
358                        poseTransformation *= self.parent.getInverseLastKeyframeTotalTransformation()
359               
360                self.keyframeDict[time] = Blender.Mathutils.Matrix(*poseTransformation)
361                return
362        def write(self, f, indentation=0):
363                # write optimized keyframes
364                if (len(self.keyframeDict) > 0):
365                        f.write(indent(indentation) + "<track bone=\"%s\">\n" % self.name)
366                        f.write(indent(indentation + 1) + "<keyframes>\n")
367                        # write keyframes
368                        keyframeTimes = self.keyframeDict.keys()
369                        keyframeTimes.sort()
370                        for time in keyframeTimes:
371                                f.write(indent(indentation + 2) + "<keyframe time=\"%f\">\n" % time)
372                                transformation = self.keyframeDict[time]
373                                # get transformation values
374                                # translation relative to parent coordinate system orientation
375                                # and as difference to rest pose translation
376                                translation = transformation.translationPart()
377                                translation -= self.ogreRestPose.translationPart()
378                                translationTuple = tuple(translation)
379                                # rotation relative to local coordiante system
380                                # calculate difference to rest pose
381                                transformation *= self.inverseOgreRestPose
382                                rotationQuaternion = transformation.toQuat()
383                                rotationQuaternion.normalize()
384                                angle = float(rotationQuaternion.angle)/180*math.pi
385                                axisTuple = tuple(rotationQuaternion.axis)
386                                scaleTuple = tuple(transformation.scalePart())
387                                # write transformation values
388                                f.write(indent(indentation + 3) + "<translate x=\"%.6f\" y=\"%.6f\" z=\"%.6f\"/>\n" % translationTuple)
389                                f.write(indent(indentation + 3) + "<rotate angle=\"%.6f\">\n" % angle)
390                                f.write(indent(indentation + 4) + "<axis x=\"%.6f\" y=\"%.6f\" z=\"%.6f\"/>\n" % axisTuple)
391                                f.write(indent(indentation + 3) + "</rotate>\n")
392                                f.write(indent(indentation + 3) + "<scale x=\"%.6f\" y=\"%.6f\" z=\"%.6f\"/>\n" % scaleTuple)
393                                f.write(indent(indentation + 2) + "</keyframe>\n")
394                        f.write(indent(indentation + 1) + "</keyframes>\n")                             
395                        f.write(indent(indentation) + "</track>\n")
396                return
397        def optimizeKeyframes(self):
398                """Reduce number of keyframes.
399               
400                   Note that you can't reduce keyframes locally when using
401                   Catmull-Rom spline interpolation. Doing so would result in
402                   wrong tangents.
403                """
404                return
405
406class ArmatureExporter:
407        """Exports Blender armature and its animations.
408       
409           Only bones with enabled deform button get exported.
410        """
411        def __init__(self, bMeshObject, bArmatureObject):
412                """Constructor.
413                """
414                # Note: getName() and getBoneIndex(boneName) already work prior to export.
415                self.bMeshObject = bMeshObject
416                self.bArmatureObject = bArmatureObject
417                # cache Blender Armature
418                self.bArmature = bArmatureObject.getData()
419                # name, needed as mesh's skeletonlink name
420                # As there may be an additional transformation between bMeshObject and bArmatureObject,
421                # it is generally not possible to share the armature between several meshes.
422                self.name = self.bMeshObject.getName() + "-" + self.bArmatureObject.getData(True)
423                # boneindices, needed for mesh's vertexboneassignments
424                # key = boneName, value = boneIndex
425                boneNameList = [bone.name for bone in self.bArmature.bones.values() if (Blender.Armature.NO_DEFORM not in bone.options)]
426                self.boneIndices = dict(zip(boneNameList, range(len(boneNameList))))
427                # actions
428                self.actionManager = ArmatureActionManager(self)
429                # animations to export
430                self.animationList = []
431                # populated on export
432                self.skeletonBoneList = None
433                self.rootSkeletonBoneList = None
434                self.additionalRootBoneTransformation = None
435                return
436        def addAnimation(self, animation):
437                """Adds animation to export.
438               
439                   @param animation ArmatureAnimation
440                """
441                self.animationList.append(animation)
442                return
443        def export(self, dir, parentTransform, convertXML=False):
444                self._convertBoneHierarchy()
445                self._convertRestpose()
446                self._convertAnimations()
447                self.write(dir, convertXML)
448                return
449        def getName(self):
450                return self.name
451        def getArmatureObject(self):
452                return self.bArmatureObject
453        def getMeshObject(self):
454                return self.bMeshObject
455        def getBoneIndex(self, boneName):
456                """Returns bone index for a given bone name.
457               
458                   @param boneName Name of the bone.
459                   @return Bone index or <code>None</code> if a bone with the given name does not exist.
460                """
461                if self.boneIndices.has_key(boneName):
462                        index = self.boneIndices[boneName]
463                else:
464                        index = None
465                return index
466        def getActions(self):
467                """Returns list of available actions.
468                """
469                return self.actionManager.getActions()
470        def getAdditionalRootBoneTransformation(self):
471                """Retruns transformation from Blender's armature object coordinate system
472                   to Blender's mesh object coordinate system.
473                """
474                return self.additionalRootBoneTransformation
475        def getSkeletonBoneList(self):
476                """Returns list of all OGRE bones.
477                """
478                return self.skeletonBoneList
479        def getRootSkeletonBoneList(self):
480                """Returns list of all OGRE root bones.
481                """
482                return self.rootSkeletonBoneList
483        def write(self, dir, convertXML=False):
484                Log.getSingleton().logInfo("Exporting armature \"%s\"" % self.getName())
485                filename = Blender.sys.join(dir, self.getName() + ".skeleton.xml")
486                f = open(filename, "w")
487                f.write(indent(0) + "<skeleton>\n")
488                self._writeRestpose(f, 1)
489                self._writeBoneHierarchy(f, 1)
490                self._writeAnimations(f, 1)
491                f.write(indent(0) + "</skeleton>\n")
492                f.close()
493                if convertXML:
494                        OgreXMLConverter.getSingleton().convert(filename)
495                return
496        def _generateActionList(self):
497                return
498        def _convertBoneHierarchy(self):
499                self.skeletonBoneList = []
500                ## find root bones
501                self.rootSkeletonBoneList = []
502                for bBone in self.bArmature.bones.values():
503                        # export this bone?
504                        if (Blender.Armature.NO_DEFORM not in bBone.options):
505                                # find possible parent bone, that gets exported.
506                                found = False
507                                current = bBone
508                                while (not(found) and current.hasParent()):
509                                        current = current.parent
510                                        if (Blender.Armature.NO_DEFORM not in current.options):
511                                                found = True
512                                if not(found):
513                                        # bone is root bone
514                                        rootBone = SkeletonBone(self, bBone)
515                                        self.rootSkeletonBoneList.append(rootBone)
516                                        self.skeletonBoneList.append(rootBone)
517                # postcondition: rootSkeletonBoneList contains root bones
518                ## find child bones
519                stack = self.rootSkeletonBoneList[:]
520                # bones on the stack do not have unprocessed parent bones
521                while (len(stack)):
522                        parent = stack.pop(0)
523                        # check for child bones
524                        if parent.getBlenderBone().hasChildren():
525                                children = parent.getBlenderBone().children[:]
526                                while (len(children)):
527                                        bBone = children.pop()
528                                        if Blender.Armature.NO_DEFORM in bBone.options:
529                                                # children of child are possible children
530                                                children.extend(bBone.children)
531                                        else:
532                                                # child found
533                                                child = SkeletonBone(self, bBone, parent)
534                                                stack.append(child)
535                                                self.skeletonBoneList.append(child)
536                # postcondition: all bones processed, self.skeletonBoneList contains all bones to export               
537                return
538        def _convertRestpose(self):
539                """Convert rest pose of Blender skeleton.
540               
541                   Note that not every Blender bone has a corresponding OGRE bone.
542                   Root bones need an additional transformation caused by the
543                   possibliy different object coordinate systems of Blender's
544                   armature object and Blender's mesh object.
545                """
546                # Warning: Blender uses left-multiplication: vector*matrix
547               
548                # additional transformation caused by the objects
549                inverseMeshObjectMatrix = Blender.Mathutils.Matrix(*self.bMeshObject.getMatrix())
550                inverseMeshObjectMatrix.invert()
551                armatureObjectMatrix = Blender.Mathutils.Matrix(*self.bArmatureObject.getMatrix())
552               
553                # additional transformation for root bones:
554                # from armature object space into mesh object space, i.e.,
555                # (x,y,z,w)*AO*MO^(-1)
556                self.additionalRootBoneTransformation = armatureObjectMatrix*inverseMeshObjectMatrix
557               
558                stack = self.rootSkeletonBoneList[:]
559                # precondition: stack contains all root bones, bone hierarchy is fixed
560                while (len(stack) > 0):
561                        bone = stack.pop(0)
562                        bone.setOgreRestMatrix()
563                        stack.extend(bone.getChildren())
564                return
565        def _restPoseMesh(self):
566                """Converts skeleton to mesh.
567                """
568                # TODO
569                return
570        def _writeRestpose(self, f, indentation=0):
571                f.write(indent(indentation) + "<bones>\n")
572                for bone in self.skeletonBoneList:
573                        bone.writeBone(f, indentation + 1)
574                f.write(indent(indentation) + "</bones>\n")
575                return
576        def _writeBoneHierarchy(self, f, indentation=0):
577                f.write(indent(indentation) + "<bonehierarchy>\n")
578                for bone in self.skeletonBoneList:
579                        if bone.getParent() is not None:
580                                f.write(indent(indentation + 1) + "<boneparent bone=\"%s\" parent=\"%s\" />\n" 
581                                        % (bone.getName(), bone.getParent().getName()))
582                f.write(indent(indentation) + "</bonehierarchy>\n")
583                return
584        def _convertAnimations(self):
585                if (len(self.animationList) > 0):
586                        # store current settings
587                        frameAtExportTime = Blender.Get('curframe')
588                        actionAtExportTime = self.bArmatureObject.getAction()
589                       
590                        # frames per second
591                        fps = Blender.Scene.GetCurrent().getRenderingContext().framesPerSec()
592                       
593                        animationNameList = []
594                        for animation in self.animationList:
595                                # warn on morph animation name clash
596                                animationName = animation.getName()
597                                if animationName in animationNameList:
598                                        Log.getSingleton().logWarning("Duplicate animation name \"%s\" for skeleton \"%s\"!" \
599                                                        % (animationName, self.getName()))
600                                animationNameList.append(animationName)
601                                # export
602                                animation.export(self, fps)
603                       
604                        # restore current settings
605                        Blender.Set('curframe', frameAtExportTime)
606                        actionAtExportTime.setActive(self.bArmatureObject)
607                return
608        def _writeAnimations(self, f, indentation=0):
609                if (len(self.animationList) > 0):
610                        f.write(indent(indentation) + "<animations>\n")
611                        for animation in self.animationList:
612                                animation.write(f, indentation + 1)
613                        f.write(indent(indentation) + "</animations>\n")
614                return
Note: See TracBrowser for help on using the repository browser.