Cleanup: trailing space
[blender-addons.git] / io_export_pc2.py
bloba56e4c33135f9e740e9e802809577e5ac64ab094
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 bl_info = {
4 "name": "Export Pointcache Format(.pc2)",
5 "author": "Florian Meyer (tstscr)",
6 "version": (1, 1, 2),
7 "blender": (2, 80, 0),
8 "location": "File > Export > Pointcache (.pc2)",
9 "description": "Export mesh Pointcache data (.pc2)",
10 "warning": "",
11 "doc_url": "{BLENDER_MANUAL_URL}/addons/import_export/pc2.html",
12 "category": "Import-Export",
15 """
16 Related links:
17 https://developer.blender.org/T34456
18 https://developer.blender.org/T25408
20 Usage Notes:
22 in Maya Mel:
23 cacheFile -pc2 1 -pcf "<insert filepath of source>" -f "<insert target filename w/o extension>" -dir "<insert directory path for target>" -format "OneFile";
25 """
27 import bpy
28 from bpy.props import BoolProperty, IntProperty, EnumProperty
29 import mathutils
30 from bpy_extras.io_utils import ExportHelper
32 from os import remove
33 import time
34 import math
35 import struct
38 def get_sampled_frames(start, end, sampling):
39 return [math.modf(start + x * sampling) for x in range(int((end - start) / sampling) + 1)]
42 def do_export(context, props, filepath):
43 mat_x90 = mathutils.Matrix.Rotation(-math.pi/2, 4, 'X')
44 ob = context.active_object
45 sc = context.scene
46 start = props.range_start
47 end = props.range_end
48 sampling = float(props.sampling)
49 apply_modifiers = props.apply_modifiers
50 depsgraph = None
51 if apply_modifiers:
52 depsgraph = context.evaluated_depsgraph_get()
53 me = ob.evaluated_get(depsgraph).to_mesh()
54 else:
55 me = ob.to_mesh()
56 vertCount = len(me.vertices)
57 sampletimes = get_sampled_frames(start, end, sampling)
58 sampleCount = len(sampletimes)
60 # Create the header
61 headerFormat = '<12siiffi'
62 headerStr = struct.pack(headerFormat, b'POINTCACHE2\0',
63 1, vertCount, start, sampling, sampleCount)
65 file = open(filepath, "wb")
66 file.write(headerStr)
68 for frame in sampletimes:
69 # stupid modf() gives decimal part first!
70 sc.frame_set(int(frame[1]), subframe=frame[0])
71 if apply_modifiers:
72 me = ob.evaluated_get(depsgraph).to_mesh()
73 else:
74 me = ob.to_mesh()
76 if len(me.vertices) != vertCount:
77 bpy.data.meshes.remove(me, do_unlink=True)
78 file.close()
79 try:
80 remove(filepath)
81 except:
82 empty = open(filepath, 'w')
83 empty.write('DUMMIFILE - export failed\n')
84 empty.close()
85 print('Export failed. Vertexcount of Object is not constant')
86 return False
88 if props.world_space:
89 me.transform(ob.matrix_world)
90 if props.rot_x90:
91 me.transform(mat_x90)
93 for v in me.vertices:
94 thisVertex = struct.pack('<fff', float(v.co[0]),
95 float(v.co[1]),
96 float(v.co[2]))
97 file.write(thisVertex)
99 if apply_modifiers:
100 ob.evaluated_get(depsgraph).to_mesh_clear()
101 else:
102 me = ob.to_mesh_clear()
104 file.flush()
105 file.close()
106 return True
109 # EXPORT OPERATOR
110 class Export_pc2(bpy.types.Operator, ExportHelper):
111 """Export the active Object as a .pc2 Pointcache file"""
112 bl_idname = "export_shape.pc2"
113 bl_label = "Export Pointcache (.pc2)"
115 filename_ext = ".pc2"
117 rot_x90: BoolProperty(
118 name="Convert to Y-up",
119 description="Rotate 90 degrees around X to convert to y-up",
120 default=True,)
121 world_space: BoolProperty(
122 name="Export into Worldspace",
123 description="Transform the Vertexcoordinates into Worldspace",
124 default=False,)
125 apply_modifiers: BoolProperty(
126 name="Apply Modifiers",
127 description="Applies the Modifiers",
128 default=True,)
129 range_start: IntProperty(
130 name='Start Frame',
131 description='First frame to use for Export',
132 default=1,)
133 range_end: IntProperty(
134 name='End Frame',
135 description='Last frame to use for Export',
136 default=250,)
137 sampling: EnumProperty(
138 name='Sampling',
139 description='Sampling --> frames per sample (0.1 yields 10 samples per frame)',
140 items=(('0.01', '0.01', ''),
141 ('0.05', '0.05', ''),
142 ('0.1', '0.1', ''),
143 ('0.2', '0.2', ''),
144 ('0.25', '0.25', ''),
145 ('0.5', '0.5', ''),
146 ('1', '1', ''),
147 ('2', '2', ''),
148 ('3', '3', ''),
149 ('4', '4', ''),
150 ('5', '5', ''),
151 ('10', '10', ''),
153 default='1',
156 @classmethod
157 def poll(cls, context):
158 obj = context.active_object
159 return (
160 obj is not None
161 and obj.type in {'MESH', 'CURVE', 'SURFACE', 'FONT'}
164 def execute(self, context):
165 start_time = time.time()
166 print('\n_____START_____')
167 props = self.properties
168 filepath = self.filepath
169 filepath = bpy.path.ensure_ext(filepath, self.filename_ext)
171 exported = do_export(context, props, filepath)
173 if exported:
174 print('finished export in %s seconds' %
175 ((time.time() - start_time)))
176 print(filepath)
178 return {'FINISHED'}
180 def invoke(self, context, event):
181 wm = context.window_manager
183 if True:
184 # File selector
185 wm.fileselect_add(self) # will run self.execute()
186 return {'RUNNING_MODAL'}
187 elif True:
188 # search the enum
189 wm.invoke_search_popup(self)
190 return {'RUNNING_MODAL'}
191 elif False:
192 # Redo popup
193 return wm.invoke_props_popup(self, event)
194 elif False:
195 return self.execute(context)
198 def menu_func_export_button(self, context):
199 self.layout.operator(Export_pc2.bl_idname, text="Pointcache (.pc2)")
202 classes = [
203 Export_pc2,
207 def register():
208 for cls in classes:
209 bpy.utils.register_class(cls)
211 bpy.types.TOPBAR_MT_file_export.append(menu_func_export_button)
212 #bpy.types.VIEW3D_PT_tools_objectmode.prepend(menu_func_export_button)
215 def unregister():
216 bpy.types.TOPBAR_MT_file_export.remove(menu_func_export_button)
217 #bpy.types.VIEW3D_PT_tools_objectmode.remove(menu_func_export_button)
218 for cls in classes:
219 bpy.utils.unregister_class(cls)
222 if __name__ == "__main__":
223 register()