Skip to content
Snippets Groups Projects
export_x.py 51 KiB
Newer Older
  • Learn to ignore specific revisions
  •                 TemporaryGenerator = ArmatureAnimationGenerator(self.Config,
                        None, Object)
                    self.Animations += TemporaryGenerator.Animations
                else:
                    TemporaryGenerator = GenericAnimationGenerator(self.Config,
                        None, Object)
                    self.Animations += TemporaryGenerator.Animations
    
    
    # Creates an Animation object for the ArmatureExportObject it gets passed and
    # an Animation object for each bone in the armature (if options allow)
    class ArmatureAnimationGenerator(GenericAnimationGenerator):
        def __init__(self, Config, SafeName, ArmatureExportObject):
            GenericAnimationGenerator.__init__(self, Config, SafeName,
                ArmatureExportObject)
            
            if self.Config.ExportArmatureBones:
                self._GenerateBoneKeys()
            
        # "Protected" Interface
        
        def _GenerateBoneKeys(self):
            from itertools import zip_longest as zip
            
            Scene = bpy.context.scene # Convenience alias
            BlenderCurrentFrame = Scene.frame_current
            
            ArmatureObject = self.ExportObject.BlenderObject
            ArmatureSafeName = self.ExportObject.SafeName
            
            # Create Animation objects for each bone
            BoneAnimations = [Animation(ArmatureSafeName + "_" + \
                Util.SafeName(Bone.name)) for Bone in ArmatureObject.pose.bones]
            
            for Frame in range(Scene.frame_start, Scene.frame_end + 1):
                Scene.frame_set(Frame)
                
                for Bone, BoneAnimation in \
                    zip(ArmatureObject.pose.bones, BoneAnimations):
                    
                    Rotation = ArmatureObject.data.bones[Bone.name] \
                        .matrix.to_quaternion() * \
                        Bone.rotation_quaternion
                    
                    PoseMatrix = Matrix()
                    if Bone.parent:
                        PoseMatrix = Bone.parent.matrix.inverted()
                    PoseMatrix *= Bone.matrix
                    
                    Scale = PoseMatrix.to_scale()
                    Position = PoseMatrix.to_translation()
                    
                    BoneAnimation.RotationKeys.append(Rotation)
                    BoneAnimation.ScaleKeys.append(Scale)
                    BoneAnimation.PositionKeys.append(Position)
            
            self.Animations += BoneAnimations
            Scene.frame_set(BlenderCurrentFrame)
    
    
    # Container for all AnimationGenerators that belong in a single AnimationSet
    class AnimationSet:
        def __init__(self, SafeName, AnimationGenerators):
            self.SafeName = SafeName
            self.AnimationGenerators = AnimationGenerators
    
    
    # Writes all animation data to file.  Implementations will control the
    # separation of AnimationGenerators into distinct AnimationSets.
    class AnimationWriter:
        def __init__(self, Config, Exporter, AnimationGenerators):
            self.Config = Config
            self.Exporter = Exporter
            self.AnimationGenerators = AnimationGenerators
            
            self.AnimationSets = []
            
        # "Public" Interface
        
        # Writes all AnimationSets.  Implementations probably won't have to override
        # this method.
        def WriteAnimationSets(self):
            if self.Config.IncludeFrameRate:
                self.Exporter.Log("Writing frame rate...")
                self.__WriteFrameRate()
                self.Exporter.Log("Done")
                
            for Set in self.AnimationSets:
                self.Exporter.Log("Writing animation set {}".format(Set.SafeName))
                self.Exporter.File.Write("AnimationSet {} {{\n".format(
                    Set.SafeName))
                self.Exporter.File.Indent()
                
                # Write each animation of each generator
                for Generator in Set.AnimationGenerators:
                    for CurrentAnimation in Generator.Animations:
                        self.Exporter.Log("Writing animation of {}".format(
                            CurrentAnimation.SafeName))
                        self.Exporter.File.Write("Animation {\n")
                        self.Exporter.File.Indent()
                        self.Exporter.File.Write("{{{}}}\n".format(
                            CurrentAnimation.SafeName))
                        
                        KeyCount = CurrentAnimation.GetKeyCount()
                        
                        # Write rotation keys
                        self.Exporter.File.Write("AnimationKey { // Rotation\n");
                        self.Exporter.File.Indent()
                        self.Exporter.File.Write("0;\n")
                        self.Exporter.File.Write("{};\n".format(KeyCount))
                        
                        for Frame, Key in enumerate(CurrentAnimation.RotationKeys):
                            self.Exporter.File.Write(
                                "{};4;{:9f},{:9f},{:9f},{:9f};;".format(
                                Frame, -Key[0], Key[1], Key[2], Key[3]))
                            
                            if Frame == KeyCount - 1:
                                self.Exporter.File.Write(";\n", Indent=False)
                            else:
                                self.Exporter.File.Write(",\n", Indent=False)
                        
                        self.Exporter.File.Unindent()
                        self.Exporter.File.Write("}\n")
                        
                        # Write scale keys
                        self.Exporter.File.Write("AnimationKey { // Scale\n");
                        self.Exporter.File.Indent()
                        self.Exporter.File.Write("1;\n")
                        self.Exporter.File.Write("{};\n".format(KeyCount))
                        
                        for Frame, Key in enumerate(CurrentAnimation.ScaleKeys):
                            self.Exporter.File.Write(
                                "{};3;{:9f},{:9f},{:9f};;".format(
                                Frame, Key[0], Key[1], Key[2]))
                            
                            if Frame == KeyCount - 1:
                                self.Exporter.File.Write(";\n", Indent=False)
                            else:
                                self.Exporter.File.Write(",\n", Indent=False)
                        
                        self.Exporter.File.Unindent()
                        self.Exporter.File.Write("}\n")
                        
                        # Write position keys
                        self.Exporter.File.Write("AnimationKey { // Position\n");
                        self.Exporter.File.Indent()
                        self.Exporter.File.Write("2;\n")
                        self.Exporter.File.Write("{};\n".format(KeyCount))
                        
                        for Frame, Key in enumerate(CurrentAnimation.PositionKeys):
                            self.Exporter.File.Write(
                                "{};3;{:9f},{:9f},{:9f};;".format(
                                Frame, Key[0], Key[1], Key[2]))
                            
                            if Frame == KeyCount - 1:
                                self.Exporter.File.Write(";\n", Indent=False)
                            else:
                                self.Exporter.File.Write(",\n", Indent=False)
                        
                        self.Exporter.File.Unindent()
                        self.Exporter.File.Write("}\n")
                        
                        self.Exporter.File.Unindent()
                        self.Exporter.File.Write("}\n")
                        self.Exporter.Log("Done")
                        
                self.Exporter.File.Unindent()
                self.Exporter.File.Write("}} // End of AnimationSet {}\n".format(
                    Set.SafeName))
                self.Exporter.Log("Done writing animation set {}".format(
                    Set.SafeName))
        
        # "Private" Methods
        
        def __WriteFrameRate(self):
            Scene = bpy.context.scene # Convenience alias
            
            # Calculate the integer frame rate
            FrameRate = int(Scene.render.fps / Scene.render.fps_base)
            
            self.Exporter.File.Write("AnimTicksPerSecond {\n");
            self.Exporter.File.Indent()
            self.Exporter.File.Write("{};\n".format(FrameRate))
            self.Exporter.File.Unindent()
            self.Exporter.File.Write("}\n")
    
    # Implementation of AnimationWriter that sticks all generators into a
    # single AnimationSet
    class JoinedSetAnimationWriter(AnimationWriter):
        def __init__(self, Config, Exporter, AnimationGenerators):
            AnimationWriter.__init__(self, Config, Exporter, AnimationGenerators)
            
            self.AnimationSets = [AnimationSet("Global", self.AnimationGenerators)]
    
    # Implementation of AnimationWriter that puts each generator into its
    # own AnimationSet
    class SplitSetAnimationWriter(AnimationWriter):
        def __init__(self, Config, Exporter, AnimationGenerators):
            AnimationWriter.__init__(self, Config, Exporter, AnimationGenerators)
            
            self.AnimationSets = [AnimationSet(Generator.SafeName, [Generator])
                for Generator in AnimationGenerators]
    
    
    # Interface to the file.  Supports automatic whitespace indenting.
    class File:
        def __init__(self, FilePath):
            self.FilePath = FilePath
            self.File = None
            self.__Whitespace = 0
    
        def Open(self):
            if not self.File:
                self.File = open(self.FilePath, 'w')
    
        def Close(self):
            self.File.close()
            self.File = None
    
        def Write(self, String, Indent=True):
            if Indent:
                # Escape any formatting braces
                String = String.replace("{", "{{")
                String = String.replace("}", "}}")
                self.File.write(("{}" + String).format("  " * self.__Whitespace))
            else:
                self.File.write(String)
    
        def Indent(self, Levels=1):
            self.__Whitespace += Levels
    
        def Unindent(self, Levels=1):
            self.__Whitespace -= Levels
            if self.__Whitespace < 0:
                self.__Whitespace = 0
    
    
    # Static utilities
    class Util:
        @staticmethod
        def SafeName(Name):
            # Replaces each character in OldSet with NewChar
            def ReplaceSet(String, OldSet, NewChar):
                for OldChar in OldSet:
                    String = String.replace(OldChar, NewChar)
                return String
    
            import string
    
            NewName = ReplaceSet(Name, string.punctuation + " ", "_")
            if NewName[0].isdigit() or NewName in ["ARRAY", "DWORD", "UCHAR",
                "FLOAT", "ULONGLONG", "BINARY_RESOURCE", "SDWORD", "UNICODE",
                "CHAR", "STRING", "WORD", "CSTRING", "SWORD", "DOUBLE", "TEMPLATE"]:
                NewName = "_" + NewName
            return NewName
    
        @staticmethod
        def WriteMatrix(File, Matrix):
            File.Write("{:9f},{:9f},{:9f},{:9f},\n".format(Matrix[0][0],
                Matrix[1][0], Matrix[2][0], Matrix[3][0]))
            File.Write("{:9f},{:9f},{:9f},{:9f},\n".format(Matrix[0][1],
                Matrix[1][1], Matrix[2][1], Matrix[3][1]))
            File.Write("{:9f},{:9f},{:9f},{:9f},\n".format(Matrix[0][2],
                Matrix[1][2], Matrix[2][2], Matrix[3][2]))
            File.Write("{:9f},{:9f},{:9f},{:9f};;\n".format(Matrix[0][3],
                Matrix[1][3], Matrix[2][3], Matrix[3][3]))
        
        # Used on lists of blender objects and lists of ExportObjects, both of
        # which have a name field
        @staticmethod
        def SortByNameField(List):
            def SortKey(x):
                return x.name
            
            return sorted(List, key=SortKey)