io_mesh_uv_layout: speed up png export with OIIO (x7)
[blender-addons.git] / io_anim_bvh / __init__.py
blob5541063c96c2925044de835548d36c1fd608073c
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 bl_info = {
4 "name": "BioVision Motion Capture (BVH) format",
5 "author": "Campbell Barton",
6 "version": (1, 0, 1),
7 "blender": (2, 81, 6),
8 "location": "File > Import-Export",
9 "description": "Import-Export BVH from armature objects",
10 "warning": "",
11 "doc_url": "{BLENDER_MANUAL_URL}/addons/import_export/anim_bvh.html",
12 "support": 'OFFICIAL',
13 "category": "Import-Export",
16 if "bpy" in locals():
17 import importlib
18 if "import_bvh" in locals():
19 importlib.reload(import_bvh)
20 if "export_bvh" in locals():
21 importlib.reload(export_bvh)
23 import bpy
24 from bpy.props import (
25 StringProperty,
26 FloatProperty,
27 IntProperty,
28 BoolProperty,
29 EnumProperty,
31 from bpy_extras.io_utils import (
32 ImportHelper,
33 ExportHelper,
34 orientation_helper,
35 axis_conversion,
39 @orientation_helper(axis_forward='-Z', axis_up='Y')
40 class ImportBVH(bpy.types.Operator, ImportHelper):
41 """Load a BVH motion capture file"""
42 bl_idname = "import_anim.bvh"
43 bl_label = "Import BVH"
44 bl_options = {'REGISTER', 'UNDO'}
46 filename_ext = ".bvh"
47 filter_glob: StringProperty(default="*.bvh", options={'HIDDEN'})
49 target: EnumProperty(
50 items=(
51 ('ARMATURE', "Armature", ""),
52 ('OBJECT', "Object", ""),
54 name="Target",
55 description="Import target type",
56 default='ARMATURE',
58 global_scale: FloatProperty(
59 name="Scale",
60 description="Scale the BVH by this value",
61 min=0.0001, max=1000000.0,
62 soft_min=0.001, soft_max=100.0,
63 default=1.0,
65 frame_start: IntProperty(
66 name="Start Frame",
67 description="Starting frame for the animation",
68 default=1,
70 use_fps_scale: BoolProperty(
71 name="Scale FPS",
72 description=(
73 "Scale the framerate from the BVH to the current scenes, "
74 "otherwise each BVH frame maps directly to a Blender frame"
76 default=False,
78 update_scene_fps: BoolProperty(
79 name="Update Scene FPS",
80 description=(
81 "Set the scene framerate to that of the BVH file (note that this "
82 "nullifies the 'Scale FPS' option, as the scale will be 1:1)"
84 default=False,
86 update_scene_duration: BoolProperty(
87 name="Update Scene Duration",
88 description="Extend the scene's duration to the BVH duration (never shortens the scene)",
89 default=False,
91 use_cyclic: BoolProperty(
92 name="Loop",
93 description="Loop the animation playback",
94 default=False,
96 rotate_mode: EnumProperty(
97 name="Rotation",
98 description="Rotation conversion",
99 items=(
100 ('QUATERNION', "Quaternion",
101 "Convert rotations to quaternions"),
102 ('NATIVE', "Euler (Native)",
103 "Use the rotation order defined in the BVH file"),
104 ('XYZ', "Euler (XYZ)", "Convert rotations to euler XYZ"),
105 ('XZY', "Euler (XZY)", "Convert rotations to euler XZY"),
106 ('YXZ', "Euler (YXZ)", "Convert rotations to euler YXZ"),
107 ('YZX', "Euler (YZX)", "Convert rotations to euler YZX"),
108 ('ZXY', "Euler (ZXY)", "Convert rotations to euler ZXY"),
109 ('ZYX', "Euler (ZYX)", "Convert rotations to euler ZYX"),
111 default='NATIVE',
114 def execute(self, context):
115 keywords = self.as_keywords(
116 ignore=(
117 "axis_forward",
118 "axis_up",
119 "filter_glob",
122 global_matrix = axis_conversion(
123 from_forward=self.axis_forward,
124 from_up=self.axis_up,
125 ).to_4x4()
127 keywords["global_matrix"] = global_matrix
129 from . import import_bvh
130 return import_bvh.load(context, report=self.report, **keywords)
132 def draw(self, context):
133 pass
136 class BVH_PT_import_main(bpy.types.Panel):
137 bl_space_type = 'FILE_BROWSER'
138 bl_region_type = 'TOOL_PROPS'
139 bl_label = ""
140 bl_parent_id = "FILE_PT_operator"
141 bl_options = {'HIDE_HEADER'}
144 @classmethod
145 def poll(cls, context):
146 sfile = context.space_data
147 operator = sfile.active_operator
149 return operator.bl_idname == "IMPORT_ANIM_OT_bvh"
151 def draw(self, context):
152 layout = self.layout
153 layout.use_property_split = True
154 layout.use_property_decorate = False # No animation.
156 sfile = context.space_data
157 operator = sfile.active_operator
159 layout.prop(operator, "target")
162 class BVH_PT_import_transform(bpy.types.Panel):
163 bl_space_type = 'FILE_BROWSER'
164 bl_region_type = 'TOOL_PROPS'
165 bl_label = "Transform"
166 bl_parent_id = "FILE_PT_operator"
168 @classmethod
169 def poll(cls, context):
170 sfile = context.space_data
171 operator = sfile.active_operator
173 return operator.bl_idname == "IMPORT_ANIM_OT_bvh"
175 def draw(self, context):
176 layout = self.layout
177 layout.use_property_split = True
178 layout.use_property_decorate = False # No animation.
180 sfile = context.space_data
181 operator = sfile.active_operator
183 layout.prop(operator, "global_scale")
184 layout.prop(operator, "rotate_mode")
185 layout.prop(operator, "axis_forward")
186 layout.prop(operator, "axis_up")
189 class BVH_PT_import_animation(bpy.types.Panel):
190 bl_space_type = 'FILE_BROWSER'
191 bl_region_type = 'TOOL_PROPS'
192 bl_label = "Animation"
193 bl_parent_id = "FILE_PT_operator"
195 @classmethod
196 def poll(cls, context):
197 sfile = context.space_data
198 operator = sfile.active_operator
200 return operator.bl_idname == "IMPORT_ANIM_OT_bvh"
202 def draw(self, context):
203 layout = self.layout
204 layout.use_property_split = True
205 layout.use_property_decorate = False # No animation.
207 sfile = context.space_data
208 operator = sfile.active_operator
210 layout.prop(operator, "frame_start")
211 layout.prop(operator, "use_fps_scale")
212 layout.prop(operator, "use_cyclic")
214 layout.prop(operator, "update_scene_fps")
215 layout.prop(operator, "update_scene_duration")
218 class ExportBVH(bpy.types.Operator, ExportHelper):
219 """Save a BVH motion capture file from an armature"""
220 bl_idname = "export_anim.bvh"
221 bl_label = "Export BVH"
223 filename_ext = ".bvh"
224 filter_glob: StringProperty(
225 default="*.bvh",
226 options={'HIDDEN'},
229 global_scale: FloatProperty(
230 name="Scale",
231 description="Scale the BVH by this value",
232 min=0.0001, max=1000000.0,
233 soft_min=0.001, soft_max=100.0,
234 default=1.0,
236 frame_start: IntProperty(
237 name="Start Frame",
238 description="Starting frame to export",
239 default=0,
241 frame_end: IntProperty(
242 name="End Frame",
243 description="End frame to export",
244 default=0,
246 rotate_mode: EnumProperty(
247 name="Rotation",
248 description="Rotation conversion",
249 items=(
250 ('NATIVE', "Euler (Native)",
251 "Use the rotation order defined in the BVH file"),
252 ('XYZ', "Euler (XYZ)", "Convert rotations to euler XYZ"),
253 ('XZY', "Euler (XZY)", "Convert rotations to euler XZY"),
254 ('YXZ', "Euler (YXZ)", "Convert rotations to euler YXZ"),
255 ('YZX', "Euler (YZX)", "Convert rotations to euler YZX"),
256 ('ZXY', "Euler (ZXY)", "Convert rotations to euler ZXY"),
257 ('ZYX', "Euler (ZYX)", "Convert rotations to euler ZYX"),
259 default='NATIVE',
261 root_transform_only: BoolProperty(
262 name="Root Translation Only",
263 description="Only write out translation channels for the root bone",
264 default=False,
267 @classmethod
268 def poll(cls, context):
269 obj = context.object
270 return obj and obj.type == 'ARMATURE'
272 def invoke(self, context, event):
273 self.frame_start = context.scene.frame_start
274 self.frame_end = context.scene.frame_end
276 return super().invoke(context, event)
278 def execute(self, context):
279 if self.frame_start == 0 and self.frame_end == 0:
280 self.frame_start = context.scene.frame_start
281 self.frame_end = context.scene.frame_end
283 keywords = self.as_keywords(
284 ignore=(
285 "axis_forward",
286 "axis_up",
287 "check_existing",
288 "filter_glob",
292 from . import export_bvh
293 return export_bvh.save(context, **keywords)
295 def draw(self, context):
296 pass
299 class BVH_PT_export_transform(bpy.types.Panel):
300 bl_space_type = 'FILE_BROWSER'
301 bl_region_type = 'TOOL_PROPS'
302 bl_label = "Transform"
303 bl_parent_id = "FILE_PT_operator"
305 @classmethod
306 def poll(cls, context):
307 sfile = context.space_data
308 operator = sfile.active_operator
310 return operator.bl_idname == "EXPORT_ANIM_OT_bvh"
312 def draw(self, context):
313 layout = self.layout
314 layout.use_property_split = True
315 layout.use_property_decorate = False # No animation.
317 sfile = context.space_data
318 operator = sfile.active_operator
320 layout.prop(operator, "global_scale")
321 layout.prop(operator, "rotate_mode")
322 layout.prop(operator, "root_transform_only")
325 class BVH_PT_export_animation(bpy.types.Panel):
326 bl_space_type = 'FILE_BROWSER'
327 bl_region_type = 'TOOL_PROPS'
328 bl_label = "Animation"
329 bl_parent_id = "FILE_PT_operator"
331 @classmethod
332 def poll(cls, context):
333 sfile = context.space_data
334 operator = sfile.active_operator
336 return operator.bl_idname == "EXPORT_ANIM_OT_bvh"
338 def draw(self, context):
339 layout = self.layout
340 layout.use_property_split = True
341 layout.use_property_decorate = False # No animation.
343 sfile = context.space_data
344 operator = sfile.active_operator
346 col = layout.column(align=True)
347 col.prop(operator, "frame_start", text="Frame Start")
348 col.prop(operator, "frame_end", text="End")
351 def menu_func_import(self, context):
352 self.layout.operator(ImportBVH.bl_idname, text="Motion Capture (.bvh)")
355 def menu_func_export(self, context):
356 self.layout.operator(ExportBVH.bl_idname, text="Motion Capture (.bvh)")
359 classes = (
360 ImportBVH,
361 BVH_PT_import_main,
362 BVH_PT_import_transform,
363 BVH_PT_import_animation,
364 ExportBVH,
365 BVH_PT_export_transform,
366 BVH_PT_export_animation,
369 def register():
370 for cls in classes:
371 bpy.utils.register_class(cls)
373 bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
374 bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
377 def unregister():
378 for cls in classes:
379 bpy.utils.unregister_class(cls)
381 bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
382 bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
384 if __name__ == "__main__":
385 register()