]> git.tdb.fi Git - libs/gl.git/commitdiff
Support for exporting armatures and vertex groups
authorMikko Rasa <tdb@tdb.fi>
Mon, 13 Aug 2012 10:18:43 +0000 (13:18 +0300)
committerMikko Rasa <tdb@tdb.fi>
Mon, 13 Aug 2012 10:18:43 +0000 (13:18 +0300)
blender/io_mspgl/__init__.py
blender/io_mspgl/armature.py [new file with mode: 0644]
blender/io_mspgl/export_armature.py [new file with mode: 0644]
blender/io_mspgl/export_mesh.py
blender/io_mspgl/mesh.py

index 2ac3bd19f48d6f58731dbccb958218d267a66eca..f43d3c01ea63f7cae545d65025f58e8256fc8a7f 100644 (file)
@@ -7,14 +7,28 @@ bl_info = {
 
 if "bpy" in locals():
        import imp
-       for sub in "export_mesh", "mesh", "outfile", "util":
+       for sub in "armature", "export_armature", "export_mesh", "mesh", "outfile", "util":
                if sub in locals():
                        imp.reload(locals()[sub])
 
 import bpy
 from bpy_extras.io_utils import ExportHelper
 
-class ExportMspGLMeshBase(ExportHelper):
+class ExportMspGLBase(ExportHelper):
+       def execute(self, context):
+               exporter = self.create_exporter()
+               self.prepare_exporter(exporter)
+               exporter.export(context, self.filepath)
+               return {"FINISHED"}
+
+       def create_exporter(self):
+               raise Exception("create_exporter must be overridden")
+
+       def prepare_exporter(self, exporter):
+               for k, v in self.as_keywords().items():
+                       setattr(exporter, k, v)
+
+class ExportMspGLMeshBase(ExportMspGLBase):
        use_strips = bpy.props.BoolProperty(name="Use strips", description="Combine faces into triangle strips", default=True)
        use_degen_tris = bpy.props.BoolProperty(name="Use degen tris", description="Concatenate triangle strips with degenerate triangles", default=False)
        max_strip_len = bpy.props.IntProperty(name="Max strip length", description="Maximum length for a triangle strip", default=1024, min=4, max=16384)
@@ -32,23 +46,18 @@ class ExportMspGLMeshBase(ExportHelper):
                items=(("NONE", "None", "No smoothing"),
                        ("BLENDER", "Blender", "Use Blender's vertex normals"),
                        ("MSPGL", "MspGL", "Compute vertex normals internally")))
+       export_groups = bpy.props.BoolProperty(name="Vertex groups", description="Export vertex groups and weights", default=False)
 
-       def execute(self, context):
+       def create_exporter(self):
                from .export_mesh import MeshExporter
-               exporter = MeshExporter()
-               self.prepare_exporter(exporter)
-               exporter.export(context, self.filepath)
-               return {"FINISHED"}
-
-       def prepare_exporter(self, exporter):
-               for k, v in self.as_keywords().items():
-                       setattr(exporter, k, v)
+               return MeshExporter()
 
        def draw(self, context):
                col = self.layout.column()
                col.prop(self, "export_lines")
                col.prop(self, "compound")
                col.prop(self, "smoothing")
+               col.prop(self, "export_groups")
                self.layout.separator()
                col = self.layout.column()
                col.label("Triangle strips")
@@ -96,9 +105,20 @@ class ExportMspGLObject(bpy.types.Operator, ExportMspGLMeshBase):
                col.prop(self, "textures")
                col.prop(self, "material_tex")
 
+class ExportMspGLArmature(bpy.types.Operator, ExportMspGLBase):
+       bl_idname = "export.mspgl_armature"
+       bl_label = "Export Msp GL armature"
+
+       filename_ext = ".armature"
+
+       def create_exporter(self):
+               from .export_armature import ArmatureExporter
+               return ArmatureExporter()
+
 def menu_func_export(self, context):
        self.layout.operator(ExportMspGLMesh.bl_idname, text="Msp GL mesh")
        self.layout.operator(ExportMspGLObject.bl_idname, text="Msp GL object")
+       self.layout.operator(ExportMspGLArmature.bl_idname, text="Msp GL armature")
 
 def register():
        bpy.utils.register_module(__name__)
diff --git a/blender/io_mspgl/armature.py b/blender/io_mspgl/armature.py
new file mode 100644 (file)
index 0000000..ccf5a55
--- /dev/null
@@ -0,0 +1,39 @@
+class Link:
+       def __init__(self, bone):
+               self._bone = bone
+               self.name = self._bone.name
+               self.index = None
+               self.parent_name = None
+               if self._bone.parent:
+                       self.parent_name = self._bone.parent.name
+               self.parent = None
+               self.base = self._bone.head_local
+
+class Armature:
+       def __init__(self, arm):
+               self._armature = arm
+
+               self.links = [Link(b) for b in self._armature.bones]
+               for i, l in enumerate(self.links):
+                       l.index = i
+
+               links_by_name = dict((l.name, l) for l in self.links)
+               for l in self.links:
+                       if l.parent_name:
+                               l.parent = links_by_name[l.parent_name]
+
+       def sort_links(self):
+               sorted_links = []
+               for l in self.links:
+                       if l in sorted_links:
+                               continue
+                       if not l.parent:
+                               sorted_links.append(l)
+                       else:
+                               chain = [l]
+                               p = l.parent
+                               while p and p not in sorted_links:
+                                       chain.append(p)
+                                       p = p.parent
+                               sorted_links += reversed(chain)
+               self.links = sorted_links
diff --git a/blender/io_mspgl/export_armature.py b/blender/io_mspgl/export_armature.py
new file mode 100644 (file)
index 0000000..1d70b59
--- /dev/null
@@ -0,0 +1,21 @@
+from .outfile import OutFile
+
+class ArmatureExporter:
+       def export(self, context, fn):
+               obj = context.active_object
+               if obj.type!="ARMATURE":
+                       raise Exception("Can only export Armature data")
+
+               from .armature import Armature
+
+               armature = Armature(obj.data)
+               armature.sort_links()
+
+               out_file = OutFile(fn)
+               for l in armature.links:
+                       out_file.begin("link", '"{}"'.format(l.name))
+                       out_file.write("index", l.index)
+                       if l.parent:
+                               out_file.write("parent", '"{}"'.format(l.parent.name))
+                       out_file.write("base", *tuple(l.base))
+                       out_file.end()
index 3f0771fe426b60ad1a59d4fbeb47f85016c5a686..0d2e66ab442206b9f2454baa0ffe2e37f7557cb3 100644 (file)
@@ -1,3 +1,4 @@
+import itertools
 import bpy
 from .outfile import OutFile
 
@@ -48,6 +49,8 @@ class MeshExporter:
                self.material_tex = False
                self.textures = "REF"
                self.smoothing = "MSPGL"
+               self.export_groups = False
+               self.max_groups = 2
 
        def stripify(self, mesh, progress = None):
                for f in mesh.faces:
@@ -227,6 +230,18 @@ class MeshExporter:
                if self.smoothing!="BLENDER":
                        mesh.compute_normals()
 
+               if self.export_groups:
+                       mesh.sort_vertex_groups(self.max_groups)
+
+                       # Create a mapping from vertex group indices to bone indices
+                       group_index_map = dict((i, i) for i in range(len(objs[0].vertex_groups)))
+                       if objs[0].parent and objs[0].parent.type=="ARMATURE":
+                               armature = objs[0].parent.data
+                               bone_indices = dict((armature.bones[i].name, i) for i in range(len(armature.bones)))
+                               for g in objs[0].vertex_groups:
+                                       if g.name in bone_indices:
+                                               group_index_map[g.index] = bone_indices[g.name]
+
                if self.material_tex and mesh.materials:
                        mesh.generate_material_uv()
 
@@ -280,12 +295,15 @@ class MeshExporter:
                                        fmt += "_TEXCOORD2%d"%u.unit
                        if self.tbn_vecs:
                                fmt += "_ATTRIB33_ATTRIB34"
+               if self.export_groups:
+                       fmt += "_ATTRIB%d5"%(self.max_groups*2)
                fmt += "_VERTEX3"
                out_file.begin("vertices", fmt)
                normal = None
                uvs = [None]*len(texunits)
                tan = None
                bino = None
+               group = None
                for v in mesh.vertices:
                        if v.normal!=normal:
                                out_file.write("normal3", *v.normal)
@@ -304,6 +322,14 @@ class MeshExporter:
                                if v.bino!=bino:
                                        out_file.write("attrib3", 4, *v.bino)
                                        bino = v.bino
+                       if self.export_groups:
+                               group_attr = [(group_index_map[g.group], g.weight*v.group_weight_scale) for g in v.groups[:self.max_groups]]
+                               while len(group_attr)<self.max_groups:
+                                       group_attr.append((0, 0.0))
+                               group_attr = list(itertools.chain(*group_attr))
+                               if group_attr!=group:
+                                       out_file.write("attrib%d"%len(group_attr), 5, *group_attr)
+                                       group = group_attr
                        out_file.write("vertex3", *v.co)
                out_file.end()
                for s in strips:
index aa8df8d254665d4240b50a34934753515a56e96c..fc31a1252a630fa0ef03fff894b7b735d31e2a78 100644 (file)
@@ -43,11 +43,13 @@ class Vertex:
                        self.uvs = mv.uvs[:]
                        self.tan = mv.tan
                        self.bino = mv.bino
+                       self.group_weight_scale = mv.group_weight_scale
                else:
                        self._mvert = mv
                        self.uvs = []
                        self.tan = None
                        self.bino = None
+                       self.group_weight_scale = 1
                self.flag = False
                self.faces = []
 
@@ -361,6 +363,12 @@ class Mesh:
                        if v.bino.length:
                                v.bino.normalize()
 
+       def sort_vertex_groups(self, max_groups):
+               for v in self.vertices:
+                       if v.groups:
+                               v.groups = sorted(v.groups, key=(lambda g: g.weight), reverse=True)
+                               v.group_weight_scale = 1.0/sum(g.weight for g in v.groups[:max_groups])
+
        def create_strip(self, face, max_len):
                # Find an edge with another unused face next to it
                edge = None