diff options
Diffstat (limited to 'io_shape_mdd')
-rw-r--r-- | io_shape_mdd/__init__.py | 172 | ||||
-rw-r--r-- | io_shape_mdd/export_mdd.py | 127 | ||||
-rw-r--r-- | io_shape_mdd/import_mdd.py | 99 |
3 files changed, 398 insertions, 0 deletions
diff --git a/io_shape_mdd/__init__.py b/io_shape_mdd/__init__.py new file mode 100644 index 00000000..ff8d7308 --- /dev/null +++ b/io_shape_mdd/__init__.py @@ -0,0 +1,172 @@ +# ##### 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 ##### + +# <pep8 compliant> + +bl_info = { + "name": "NewTek MDD format", + "author": "Bill L.Nieuwendorp", + "blender": (2, 5, 7), + "api": 35622, + "location": "File > Import-Export", + "description": "Import-Export MDD as mesh shape keys", + "warning": "", + "wiki_url": ("http://wiki.blender.org/index.php/Extensions:2.5/Py/" + "Scripts/Import-Export/NewTek_OBJ"), + "tracker_url": "", + "support": 'OFFICIAL', + "category": "Import-Export"} + +if "bpy" in locals(): + import imp + if "import_mdd" in locals(): + imp.reload(import_mdd) + if "export_mdd" in locals(): + imp.reload(export_mdd) + + +import bpy +from bpy.props import StringProperty, IntProperty +from bpy_extras.io_utils import ExportHelper, ImportHelper + + +class ImportMDD(bpy.types.Operator, ImportHelper): + '''Import MDD vertex keyframe file to shape keys''' + bl_idname = "import_shape.mdd" + bl_label = "Import MDD" + + filename_ext = ".mdd" + + filter_glob = StringProperty( + default="*.mdd", + options={'HIDDEN'}, + ) + frame_start = IntProperty( + name="Start Frame", + description="Start frame for inserting animation", + min=-300000, max=300000, + default=0, + ) + frame_step = IntProperty( + name="Step", + min=1, max=1000, + default=1, + ) + + @classmethod + def poll(cls, context): + ob = context.active_object + return (ob and ob.type == 'MESH') + + def execute(self, context): + + # initialize from scene if unset + scene = context.scene + if not self.frame_start: + self.frame_start = scene.frame_current + + keywords = self.as_keywords(ignore=("filter_glob",)) + + from . import import_mdd + return import_mdd.load(self, context, **keywords) + + +class ExportMDD(bpy.types.Operator, ExportHelper): + '''Animated mesh to MDD vertex keyframe file''' + bl_idname = "export_shape.mdd" + bl_label = "Export MDD" + + filename_ext = ".mdd" + filter_glob = StringProperty(default="*.mdd", options={'HIDDEN'}) + + # get first scene to get min and max properties for frames, fps + + minframe = 1 + maxframe = 300000 + minfps = 1 + maxfps = 120 + + # List of operator properties, the attributes will be assigned + # to the class instance from the operator settings before calling. + fps = IntProperty( + name="Frames Per Second", + description="Number of frames/second", + min=minfps, max=maxfps, + default=25, + ) + frame_start = IntProperty( + name="Start Frame", + description="Start frame for baking", + min=minframe, max=maxframe, + default=1, + ) + frame_end = IntProperty( + name="End Frame", + description="End frame for baking", + min=minframe, max=maxframe, + default=250, + ) + + @classmethod + def poll(cls, context): + obj = context.active_object + return (obj and obj.type == 'MESH') + + def execute(self, context): + # initialize from scene if unset + scene = context.scene + if not self.frame_start: + self.frame_start = scene.frame_start + if not self.frame_end: + self.frame_end = scene.frame_end + if not self.fps: + self.fps = scene.render.fps + + keywords = self.as_keywords(ignore=("check_existing", "filter_glob")) + + from . import export_mdd + return export_mdd.save(self, context, **keywords) + + +def menu_func_import(self, context): + self.layout.operator(ImportMDD.bl_idname, + text="Lightwave Point Cache (.mdd)", + ) + + +def menu_func_export(self, context): + self.layout.operator(ExportMDD.bl_idname, + text="Lightwave Point Cache (.mdd)", + ) + + +def register(): + bpy.utils.register_module(__name__) + + bpy.types.INFO_MT_file_import.append(menu_func_import) + bpy.types.INFO_MT_file_export.append(menu_func_export) + + +def unregister(): + bpy.utils.unregister_module(__name__) + + bpy.types.INFO_MT_file_import.remove(menu_func_import) + bpy.types.INFO_MT_file_export.remove(menu_func_export) + +if __name__ == "__main__": + register() diff --git a/io_shape_mdd/export_mdd.py b/io_shape_mdd/export_mdd.py new file mode 100644 index 00000000..9b0ad8b6 --- /dev/null +++ b/io_shape_mdd/export_mdd.py @@ -0,0 +1,127 @@ +# ##### 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 ##### + +# <pep8 compliant> + +# Contributors: Bill L.Nieuwendorp + +""" +This script Exports Lightwaves MotionDesigner format. + +The .mdd format has become quite a popular Pipeline format<br> +for moving animations from package to package. + +Be sure not to use modifiers that change the number or order of verts in the mesh +""" + +import bpy +import mathutils +from struct import pack + + +def zero_file(filepath): + ''' + If a file fails, this replaces it with 1 char, better not remove it? + ''' + file = open(filepath, 'w') + file.write('\n') # apparently macosx needs some data in a blank file? + file.close() + + +def check_vertcount(mesh, vertcount): + ''' + check and make sure the vertcount is consistent throughout the frame range + ''' + if len(mesh.vertices) != vertcount: + raise Exception('Error, number of verts has changed during animation, cannot export') + + +def save(operator, context, filepath="", frame_start=1, frame_end=300, fps=25): + """ + Blender.Window.WaitCursor(1) + + mesh_orig = Mesh.New() + mesh_orig.getFromObject(obj.name) + """ + + scene = context.scene + obj = context.object + + if bpy.ops.object.mode_set.poll(): + bpy.ops.object.mode_set(mode='OBJECT') + + orig_frame = scene.frame_current + scene.frame_set(frame_start) + me = obj.to_mesh(scene, True, 'PREVIEW') + + #Flip y and z + mat_flip = mathutils.Matrix(((1.0, 0.0, 0.0, 0.0), + (0.0, 0.0, 1.0, 0.0), + (0.0, 1.0, 0.0, 0.0), + (0.0, 0.0, 0.0, 1.0), + )) + + numverts = len(me.vertices) + + numframes = frame_end - frame_start + 1 + fps = float(fps) + f = open(filepath, 'wb') # no Errors yet:Safe to create file + + # Write the header + f.write(pack(">2i", numframes, numverts)) + + # Write the frame times (should we use the time IPO??) + f.write(pack(">%df" % (numframes), *[frame / fps for frame in range(numframes)])) # seconds + + #rest frame needed to keep frames in sync + """ + Blender.Set('curframe', frame_start) + me_tmp.getFromObject(obj.name) + """ + + check_vertcount(me, numverts) + me.transform(mat_flip * obj.matrix_world) + f.write(pack(">%df" % (numverts * 3), *[axis for v in me.vertices for axis in v.co])) + + for frame in range(frame_start, frame_end + 1): # in order to start at desired frame + """ + Blender.Set('curframe', frame) + me_tmp.getFromObject(obj.name) + """ + + scene.frame_set(frame) + me = obj.to_mesh(scene, True, 'PREVIEW') + check_vertcount(me, numverts) + me.transform(mat_flip * obj.matrix_world) + + # Write the vertex data + f.write(pack(">%df" % (numverts * 3), *[axis for v in me.vertices for axis in v.co])) + + """ + me_tmp.vertices= None + """ + f.close() + + print('MDD Exported: %r frames:%d\n' % (filepath, numframes - 1)) + """ + Blender.Window.WaitCursor(0) + Blender.Set('curframe', orig_frame) + """ + scene.frame_set(orig_frame) + + return {'FINISHED'} diff --git a/io_shape_mdd/import_mdd.py b/io_shape_mdd/import_mdd.py new file mode 100644 index 00000000..332b7590 --- /dev/null +++ b/io_shape_mdd/import_mdd.py @@ -0,0 +1,99 @@ +# ##### 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 ##### + +# <pep8 compliant> + +# mdd importer by Bill L.Nieuwendorp +# conversion to blender 2.5: Ivo Grigull (loolarge) +# +# Warning if the vertex order or vertex count differs from the +# origonal model the mdd was Baked out from their will be Strange +# behavior +# +# vertex animation to ShapeKeys with ipo and gives the frame a value of 1.0 +# A modifier to read mdd files would be Ideal but thats for another day :) +# +# Please send any fixes,updates,bugs to Slow67_at_Gmail.com +# Bill Niewuendorp + +import bpy +from struct import unpack + + +def load(operator, context, filepath, frame_start=0, frame_step=1): + + scene = context.scene + obj = context.object + + print('\n\nimporting mdd %r' % filepath) + + if bpy.ops.object.mode_set.poll(): + bpy.ops.object.mode_set(mode='OBJECT') + + file = open(filepath, 'rb') + frames, points = unpack(">2i", file.read(8)) + time = unpack((">%df" % frames), file.read(frames * 4)) + + print('\tpoints:%d frames:%d' % (points, frames)) + + # If target object doesn't have Basis shape key, create it. + if not obj.data.shape_keys: + basis = obj.shape_key_add() + basis.name = "Basis" + obj.data.update() + + scene.frame_current = frame_start + + def UpdateMesh(ob, fr): + + # Insert new shape key + new_shapekey = obj.shape_key_add() + new_shapekey.name = ("frame_%.4d" % fr) + + obj.active_shape_key_index = len(obj.data.shape_keys.keys) - 1 + index = len(obj.data.shape_keys.keys) - 1 + obj.show_only_shape_key = True + + verts = obj.data.shape_keys.keys[len(obj.data.shape_keys.keys) - 1].data + + for v in verts: # 12 is the size of 3 floats + v.co[:] = unpack('>3f', file.read(12)) + # me.update() + obj.show_only_shape_key = False + + # insert keyframes + shape_keys = obj.data.shape_keys + + scene.frame_current -= 1 + obj.data.shape_keys.keys[index].value = 0.0 + shape_keys.keys[len(obj.data.shape_keys.keys) - 1].keyframe_insert("value") + + scene.frame_current += 1 + obj.data.shape_keys.keys[index].value = 1.0 + shape_keys.keys[len(obj.data.shape_keys.keys) - 1].keyframe_insert("value") + + scene.frame_current += 1 + obj.data.shape_keys.keys[index].value = 0.0 + shape_keys.keys[len(obj.data.shape_keys.keys) - 1].keyframe_insert("value") + + obj.data.update() + + for i in range(frames): + UpdateMesh(obj, i) + + return {'FINISHED'} |