From b61c103559c83d6fe7309f2ca4489f09e701c4cf Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Mon, 13 Aug 2012 13:18:43 +0300 Subject: [PATCH] Support for exporting armatures and vertex groups --- blender/io_mspgl/__init__.py | 42 +++++++++++++++++++++-------- blender/io_mspgl/armature.py | 39 +++++++++++++++++++++++++++ blender/io_mspgl/export_armature.py | 21 +++++++++++++++ blender/io_mspgl/export_mesh.py | 26 ++++++++++++++++++ blender/io_mspgl/mesh.py | 8 ++++++ 5 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 blender/io_mspgl/armature.py create mode 100644 blender/io_mspgl/export_armature.py diff --git a/blender/io_mspgl/__init__.py b/blender/io_mspgl/__init__.py index 2ac3bd19..f43d3c01 100644 --- a/blender/io_mspgl/__init__.py +++ b/blender/io_mspgl/__init__.py @@ -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 index 00000000..ccf5a553 --- /dev/null +++ b/blender/io_mspgl/armature.py @@ -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 index 00000000..1d70b591 --- /dev/null +++ b/blender/io_mspgl/export_armature.py @@ -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() diff --git a/blender/io_mspgl/export_mesh.py b/blender/io_mspgl/export_mesh.py index 3f0771fe..0d2e66ab 100644 --- a/blender/io_mspgl/export_mesh.py +++ b/blender/io_mspgl/export_mesh.py @@ -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)