46b38c3c9e6c2a567541decf43725ed50d7b115c
[blender.git] / release / scripts / io / export_mdd.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 __author__ = "Bill L.Nieuwendorp"
22 __bpydoc__ = """\
23 This script Exports Lightwaves MotionDesigner format.
24
25 The .mdd format has become quite a popular Pipeline format<br>
26 for moving animations from package to package.
27
28 Be sure not to use modifiers that change the number or order of verts in the mesh
29 """
30 #Please send any fixes,updates,bugs to Slow67_at_Gmail.com or cbarton_at_metavr.com
31 #Bill Niewuendorp
32 # ***** BEGIN GPL LICENSE BLOCK *****
33 #
34 # This program is free software; you can redistribute it and/or
35 # modify it under the terms of the GNU General Public License
36 # as published by the Free Software Foundation; either version 2
37 # of the License, or (at your option) any later version.
38 #
39 # This program is distributed in the hope that it will be useful,
40 # but WITHOUT ANY WARRANTY; without even the implied warranty of
41 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
42 # GNU General Public License for more details.
43 #
44 # You should have received a copy of the GNU General Public License
45 # along with this program; if not, write to the Free Software Foundation,
46 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
47 #
48 # ***** END GPL LICENCE BLOCK *****
49
50 import bpy
51 import mathutils
52 from struct import pack
53
54
55 def zero_file(filepath):
56     '''
57     If a file fails, this replaces it with 1 char, better not remove it?
58     '''
59     file = open(filepath, 'w')
60     file.write('\n') # apparently macosx needs some data in a blank file?
61     file.close()
62
63
64 def check_vertcount(mesh, vertcount):
65     '''
66     check and make sure the vertcount is consistent throughout the frame range
67     '''
68     if len(mesh.verts) != vertcount:
69         raise Exception('Error, number of verts has changed during animation, cannot export')
70         f.close()
71         zero_file(filepath)
72         return
73
74
75 def write(filename, sce, ob, PREF_STARTFRAME, PREF_ENDFRAME, PREF_FPS):
76     """
77     Blender.Window.WaitCursor(1)
78
79     mesh_orig = Mesh.New()
80     mesh_orig.getFromObject(ob.name)
81     """
82
83     bpy.ops.object.mode_set(mode='OBJECT')
84
85     orig_frame = sce.frame_current
86     sce.set_frame(PREF_STARTFRAME)
87     me = ob.create_mesh(sce, True, 'PREVIEW')
88
89     #Flip y and z
90     mat_flip = mathutils.Matrix(\
91     [1.0, 0.0, 0.0, 0.0],\
92     [0.0, 0.0, 1.0, 0.0],\
93     [0.0, 1.0, 0.0, 0.0],\
94     [0.0, 0.0, 0.0, 1.0],\
95     )
96
97     numverts = len(me.verts)
98
99     numframes = PREF_ENDFRAME - PREF_STARTFRAME + 1
100     PREF_FPS = float(PREF_FPS)
101     f = open(filename, 'wb') #no Errors yet:Safe to create file
102
103     # Write the header
104     f.write(pack(">2i", numframes, numverts))
105
106     # Write the frame times (should we use the time IPO??)
107     f.write(pack(">%df" % (numframes), *[frame / PREF_FPS for frame in range(numframes)])) # seconds
108
109     #rest frame needed to keep frames in sync
110     """
111     Blender.Set('curframe', PREF_STARTFRAME)
112     me_tmp.getFromObject(ob.name)
113     """
114
115     check_vertcount(me, numverts)
116     me.transform(mat_flip * ob.matrix_world)
117     f.write(pack(">%df" % (numverts * 3), *[axis for v in me.verts for axis in v.co]))
118
119     for frame in range(PREF_STARTFRAME, PREF_ENDFRAME + 1):#in order to start at desired frame
120         """
121         Blender.Set('curframe', frame)
122         me_tmp.getFromObject(ob.name)
123         """
124
125         sce.set_frame(frame)
126         me = ob.create_mesh(sce, True, 'PREVIEW')
127         check_vertcount(me, numverts)
128         me.transform(mat_flip * ob.matrix_world)
129
130         # Write the vertex data
131         f.write(pack(">%df" % (numverts * 3), *[axis for v in me.verts for axis in v.co]))
132
133     """
134     me_tmp.verts= None
135     """
136     f.close()
137
138     print('MDD Exported: %s frames:%d\n' % (filename, numframes - 1))
139     """
140     Blender.Window.WaitCursor(0)
141     Blender.Set('curframe', orig_frame)
142     """
143     sce.set_frame(orig_frame)
144
145 from bpy.props import *
146
147
148 class ExportMDD(bpy.types.Operator):
149     '''Animated mesh to MDD vertex keyframe file'''
150     bl_idname = "export.mdd"
151     bl_label = "Export MDD"
152
153     # get first scene to get min and max properties for frames, fps
154
155     minframe = 1
156     maxframe = 300000
157     minfps = 1
158     maxfps = 120
159
160     # List of operator properties, the attributes will be assigned
161     # to the class instance from the operator settings before calling.
162     filepath = StringProperty(name="File Path", description="Filepath used for exporting the MDD file", maxlen=1024)
163     check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=True, options={'HIDDEN'})
164     fps = IntProperty(name="Frames Per Second", description="Number of frames/second", min=minfps, max=maxfps, default=25)
165     frame_start = IntProperty(name="Start Frame", description="Start frame for baking", min=minframe, max=maxframe, default=1)
166     frame_end = IntProperty(name="End Frame", description="End frame for baking", min=minframe, max=maxframe, default=250)
167
168     @staticmethod
169     def poll(context):
170         ob = context.active_object
171         return (ob and ob.type == 'MESH')
172
173     def execute(self, context):
174         filepath = self.properties.filepath
175         filepath = bpy.path.ensure_ext(filepath, ".mdd")
176         
177         write(filepath,
178               context.scene,
179               context.active_object,
180               self.properties.frame_start,
181               self.properties.frame_end,
182               self.properties.fps,
183               )
184
185         return {'FINISHED'}
186
187     def invoke(self, context, event):
188         wm = context.manager
189         wm.add_fileselect(self)
190         return {'RUNNING_MODAL'}
191
192
193 def menu_func(self, context):
194     import os
195     default_path = os.path.splitext(bpy.data.filepath)[0] + ".mdd"
196     self.layout.operator(ExportMDD.bl_idname, text="Lightwave Point Cache (.mdd)").filepath = default_path
197
198
199 def register():
200     bpy.types.INFO_MT_file_export.append(menu_func)
201
202
203 def unregister():
204     bpy.types.INFO_MT_file_export.remove(menu_func)
205
206 if __name__ == "__main__":
207     register()