# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### bl_info = { "name": "Export MX Simulator Model (.jm)", "description": "Export MX Simulator Model (.jm)", "author": "Josh Vanderhoof", "version": (0, 1), "blender": (2, 5, 7), "api": 36079, "location": "File > Export > MX Simulator Model (.jm)", "warning": "", "category": "Import-Export" } import bpy from bpy.props import * import mathutils, math, struct from os import remove from bpy_extras.io_utils import ExportHelper def reduce_verts(v, t): s = [] # sorted with original indices for i in range(0, len(v)): s.append((v[i], i)) s.sort() r = [] # new vertex array tr = [] # new triangle array m = [0] * len(s) # maps original vertex indices to new indices r.append(s[0][0]) for i in range(0, len(s)): if r[len(r) - 1] != s[i][0]: r.append(s[i][0]) m[s[i][1]] = len(r) - 1 for i in range(0, len(t)): tr.append(m[t[i]]) return (r, tr) def bone_index(bonelist, bonename): for i in range(len(bonelist)): if bonename == bonelist[i]: return i return 0 def vert_to_string(v, bones): s = "%f %f %f %f %f %f %f %f" % (v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]) for i in range(len(bones)): w = " 0" for b in v[8]: if bones[i] == b[0]: w = " %f" % (b[1]) s += w s += "\n" return s def get_vertex_influences(obj, mesh, vidx): r = [] for g in mesh.vertices[vidx].groups: name = obj.vertex_groups[g.group].name r.append((name, g.weight)) return r def add_face(face, m, key, mesh, obj): try: uv = mesh.uv_textures.active.data[face.index].uv except: uv = ((0.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0)) if not key in m: m[key] = ([],[]) verts, tris = m[key] vbase = len(verts) for i in range(len(face.vertices)): vidx = face.vertices[i] v = mesh.vertices[vidx] tc = (uv[i][0], uv[i][1]) verts.append((v.co[0], v.co[1], v.co[2], v.normal[0], v.normal[1], v.normal[2]) + tc + (get_vertex_influences(obj, mesh, vidx),)) for i in range(3): tris.append(vbase + i) if len(face.vertices) == 4: # output other half of quad for i in (2, 3, 0): tris.append(vbase + i) def add_object_by_material(obj, m, props): mesh = obj.to_mesh(bpy.context.scene, props.apply_modifiers, "RENDER") if props.rot_x90: mesh.transform(mathutils.Matrix.Rotation(-math.pi * 0.5, 4, 'X')) for face in mesh.faces: try: mat = mesh.materials[face.material_index] mirror_alpha = mat.raytrace_mirror.reflect_factor mirror_rgb = mat.mirror_color spec_alpha = mat.specular_alpha spec_rgb = mat.specular_color spec_exp = mat.specular_hardness name = mat.name except: mirror_alpha = 0.0 mirror_rgb = [0.0, 0.0, 0.0] spec_alpha = 0.0 spec_rgb = [0.0, 0.0, 0.0] spec_exp = 0 name = "0" if mirror_alpha == 0.0: key = (name,) + (spec_exp,) + (spec_alpha,) + tuple(spec_rgb) add_face(face, m, key, mesh, obj) elif mirror_alpha == 1.0: key = ("\x7f",) + (-1.0,) + (mirror_alpha,) + tuple(mirror_rgb) add_face(face, m, key, mesh, obj) else: key = (name,) + (spec_exp,) + (spec_alpha,) + tuple(spec_rgb) add_face(face, m, key, mesh, obj) key = ("\x7f",) + (-1.0,) + (mirror_alpha,) + tuple(mirror_rgb) add_face(face, m, key, mesh, obj) bpy.data.meshes.remove(mesh) def find_armature(obj): mseq = obj.modifiers for i in range(mseq.__len__()): m = mseq.__getitem__(i) if m.type == Modifier.Types.ARMATURE: return m.__getitem__(Modifier.Settings.OBJECT) return None def get_bones(obj): arm = obj.find_armature() if arm == None: return [] return arm.data.bones.keys() def do_export(context, props, filepath): file = open(filepath, "w", encoding="ascii", newline="") mesh_by_material = {} bones = [] for obj in bpy.context.selected_objects: add_object_by_material(obj, mesh_by_material, props) bones = bones + get_bones(obj) keys = list(mesh_by_material.keys()) keys.sort() bones.sort() if len(bones) > 0: file.write("JM2\n") file.write("%d\n" % (len(bones))) file.write(",".join(bones)) file.write("\n") else: file.write("JM1\n") for key in keys: verts, tris = mesh_by_material[key] verts, tris = reduce_verts(verts, tris) pad = "" for val in key[1:]: file.write("%s%f" % (pad, val)) pad = " " file.write("\n%d %d\n" % (len(verts), len(tris) / 3)) for v in verts: file.write(vert_to_string(v, bones)) for t in tris: file.write("%d\n" % t) file.flush() file.close() return True class Export_jm(bpy.types.Operator, ExportHelper): '''Exports the active Object as a .jm file.''' bl_idname = "export_shape.jm" bl_label = "Export MX Simulator (.jm)" filename_ext = ".jm" rot_x90 = BoolProperty(name="Convert to Y-up", description="Rotate 90 degrees around X to convert to y-up", default=True) apply_modifiers = BoolProperty(name="Apply Modifiers", description="Applies the Modifiers", default=True) # "poll" reports if this operator makes sense in the current conditions. # (I think.) @classmethod def poll(cls, context): return context.active_object.type == 'MESH' # "execute" is called non interactively. def execute(self, context): props = self.properties filepath = self.filepath filepath = bpy.path.ensure_ext(filepath, self.filename_ext) exported = do_export(context, props, filepath) return {'FINISHED'} # "invoke" is called because of user interaction. def invoke(self, context, event): wm = context.window_manager if True: # File selector wm.fileselect_add(self) # will run self.execute() return {'RUNNING_MODAL'} elif True: # search the enum wm.invoke_search_popup(self) return {'RUNNING_MODAL'} elif False: # Redo popup return wm.invoke_props_popup(self, event) elif False: return self.execute(context) def menu_func(self, context): self.layout.operator(Export_jm.bl_idname, text="MX Simulator Model (.jm)") def register(): bpy.utils.register_module(__name__) bpy.types.INFO_MT_file_export.append(menu_func) def unregister(): bpy.utils.unregister_module(__name__) bpy.types.INFO_MT_file_export.remove(menu_func) if __name__ == "__main__": register()