add_camera_rigs: refactor and cleanup
[blender-addons.git] / io_anim_bvh / __init__.py
blob6112ee421f5561b2f021adb2ea8e38cff319797a
1 # ##### BEGIN GPL LICENSE BLOCK #####
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.
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.
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.
17 # ##### END GPL LICENSE BLOCK #####
19 # <pep8-80 compliant>
21 bl_info = {
22 "name": "BioVision Motion Capture (BVH) format",
23 "author": "Campbell Barton",
24 "version": (1, 0, 0),
25 "blender": (2, 81, 6),
26 "location": "File > Import-Export",
27 "description": "Import-Export BVH from armature objects",
28 "warning": "",
29 "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
30 "import_export/io_anim_bvh.html",
31 "support": 'OFFICIAL',
32 "category": "Import-Export",
35 if "bpy" in locals():
36 import importlib
37 if "import_bvh" in locals():
38 importlib.reload(import_bvh)
39 if "export_bvh" in locals():
40 importlib.reload(export_bvh)
42 import bpy
43 from bpy.props import (
44 StringProperty,
45 FloatProperty,
46 IntProperty,
47 BoolProperty,
48 EnumProperty,
50 from bpy_extras.io_utils import (
51 ImportHelper,
52 ExportHelper,
53 orientation_helper,
54 axis_conversion,
58 @orientation_helper(axis_forward='-Z', axis_up='Y')
59 class ImportBVH(bpy.types.Operator, ImportHelper):
60 """Load a BVH motion capture file"""
61 bl_idname = "import_anim.bvh"
62 bl_label = "Import BVH"
63 bl_options = {'REGISTER', 'UNDO'}
65 filename_ext = ".bvh"
66 filter_glob: StringProperty(default="*.bvh", options={'HIDDEN'})
68 target: EnumProperty(
69 items=(
70 ('ARMATURE', "Armature", ""),
71 ('OBJECT', "Object", ""),
73 name="Target",
74 description="Import target type",
75 default='ARMATURE',
77 global_scale: FloatProperty(
78 name="Scale",
79 description="Scale the BVH by this value",
80 min=0.0001, max=1000000.0,
81 soft_min=0.001, soft_max=100.0,
82 default=1.0,
84 frame_start: IntProperty(
85 name="Start Frame",
86 description="Starting frame for the animation",
87 default=1,
89 use_fps_scale: BoolProperty(
90 name="Scale FPS",
91 description=(
92 "Scale the framerate from the BVH to the current scenes, "
93 "otherwise each BVH frame maps directly to a Blender frame"
95 default=False,
97 update_scene_fps: BoolProperty(
98 name="Update Scene FPS",
99 description=(
100 "Set the scene framerate to that of the BVH file (note that this "
101 "nullifies the 'Scale FPS' option, as the scale will be 1:1)"
103 default=False,
105 update_scene_duration: BoolProperty(
106 name="Update Scene Duration",
107 description="Extend the scene's duration to the BVH duration (never shortens the scene)",
108 default=False,
110 use_cyclic: BoolProperty(
111 name="Loop",
112 description="Loop the animation playback",
113 default=False,
115 rotate_mode: EnumProperty(
116 name="Rotation",
117 description="Rotation conversion",
118 items=(
119 ('QUATERNION', "Quaternion",
120 "Convert rotations to quaternions"),
121 ('NATIVE', "Euler (Native)",
122 "Use the rotation order defined in the BVH file"),
123 ('XYZ', "Euler (XYZ)", "Convert rotations to euler XYZ"),
124 ('XZY', "Euler (XZY)", "Convert rotations to euler XZY"),
125 ('YXZ', "Euler (YXZ)", "Convert rotations to euler YXZ"),
126 ('YZX', "Euler (YZX)", "Convert rotations to euler YZX"),
127 ('ZXY', "Euler (ZXY)", "Convert rotations to euler ZXY"),
128 ('ZYX', "Euler (ZYX)", "Convert rotations to euler ZYX"),
130 default='NATIVE',
133 def execute(self, context):
134 keywords = self.as_keywords(
135 ignore=(
136 "axis_forward",
137 "axis_up",
138 "filter_glob",
141 global_matrix = axis_conversion(
142 from_forward=self.axis_forward,
143 from_up=self.axis_up,
144 ).to_4x4()
146 keywords["global_matrix"] = global_matrix
148 from . import import_bvh
149 return import_bvh.load(context, report=self.report, **keywords)
151 def draw(self, context):
152 pass
155 class BVH_PT_import_main(bpy.types.Panel):
156 bl_space_type = 'FILE_BROWSER'
157 bl_region_type = 'TOOL_PROPS'
158 bl_label = ""
159 bl_parent_id = "FILE_PT_operator"
160 bl_options = {'HIDE_HEADER'}
163 @classmethod
164 def poll(cls, context):
165 sfile = context.space_data
166 operator = sfile.active_operator
168 return operator.bl_idname == "IMPORT_ANIM_OT_bvh"
170 def draw(self, context):
171 layout = self.layout
172 layout.use_property_split = True
173 layout.use_property_decorate = False # No animation.
175 sfile = context.space_data
176 operator = sfile.active_operator
178 layout.prop(operator, "target")
181 class BVH_PT_import_transform(bpy.types.Panel):
182 bl_space_type = 'FILE_BROWSER'
183 bl_region_type = 'TOOL_PROPS'
184 bl_label = "Transform"
185 bl_parent_id = "FILE_PT_operator"
187 @classmethod
188 def poll(cls, context):
189 sfile = context.space_data
190 operator = sfile.active_operator
192 return operator.bl_idname == "IMPORT_ANIM_OT_bvh"
194 def draw(self, context):
195 layout = self.layout
196 layout.use_property_split = True
197 layout.use_property_decorate = False # No animation.
199 sfile = context.space_data
200 operator = sfile.active_operator
202 layout.prop(operator, "global_scale")
203 layout.prop(operator, "rotate_mode")
204 layout.prop(operator, "axis_forward")
205 layout.prop(operator, "axis_up")
208 class BVH_PT_import_animation(bpy.types.Panel):
209 bl_space_type = 'FILE_BROWSER'
210 bl_region_type = 'TOOL_PROPS'
211 bl_label = "Animation"
212 bl_parent_id = "FILE_PT_operator"
214 @classmethod
215 def poll(cls, context):
216 sfile = context.space_data
217 operator = sfile.active_operator
219 return operator.bl_idname == "IMPORT_ANIM_OT_bvh"
221 def draw(self, context):
222 layout = self.layout
223 layout.use_property_split = True
224 layout.use_property_decorate = False # No animation.
226 sfile = context.space_data
227 operator = sfile.active_operator
229 layout.prop(operator, "frame_start")
230 layout.prop(operator, "use_fps_scale")
231 layout.prop(operator, "use_cyclic")
233 layout.prop(operator, "update_scene_fps")
234 layout.prop(operator, "update_scene_duration")
237 class ExportBVH(bpy.types.Operator, ExportHelper):
238 """Save a BVH motion capture file from an armature"""
239 bl_idname = "export_anim.bvh"
240 bl_label = "Export BVH"
242 filename_ext = ".bvh"
243 filter_glob: StringProperty(
244 default="*.bvh",
245 options={'HIDDEN'},
248 global_scale: FloatProperty(
249 name="Scale",
250 description="Scale the BVH by this value",
251 min=0.0001, max=1000000.0,
252 soft_min=0.001, soft_max=100.0,
253 default=1.0,
255 frame_start: IntProperty(
256 name="Start Frame",
257 description="Starting frame to export",
258 default=0,
260 frame_end: IntProperty(
261 name="End Frame",
262 description="End frame to export",
263 default=0,
265 rotate_mode: EnumProperty(
266 name="Rotation",
267 description="Rotation conversion",
268 items=(
269 ('NATIVE', "Euler (Native)",
270 "Use the rotation order defined in the BVH file"),
271 ('XYZ', "Euler (XYZ)", "Convert rotations to euler XYZ"),
272 ('XZY', "Euler (XZY)", "Convert rotations to euler XZY"),
273 ('YXZ', "Euler (YXZ)", "Convert rotations to euler YXZ"),
274 ('YZX', "Euler (YZX)", "Convert rotations to euler YZX"),
275 ('ZXY', "Euler (ZXY)", "Convert rotations to euler ZXY"),
276 ('ZYX', "Euler (ZYX)", "Convert rotations to euler ZYX"),
278 default='NATIVE',
280 root_transform_only: BoolProperty(
281 name="Root Translation Only",
282 description="Only write out translation channels for the root bone",
283 default=False,
286 @classmethod
287 def poll(cls, context):
288 obj = context.object
289 return obj and obj.type == 'ARMATURE'
291 def invoke(self, context, event):
292 self.frame_start = context.scene.frame_start
293 self.frame_end = context.scene.frame_end
295 return super().invoke(context, event)
297 def execute(self, context):
298 if self.frame_start == 0 and self.frame_end == 0:
299 self.frame_start = context.scene.frame_start
300 self.frame_end = context.scene.frame_end
302 keywords = self.as_keywords(
303 ignore=(
304 "axis_forward",
305 "axis_up",
306 "check_existing",
307 "filter_glob",
311 from . import export_bvh
312 return export_bvh.save(context, **keywords)
314 def draw(self, context):
315 pass
318 class BVH_PT_export_transform(bpy.types.Panel):
319 bl_space_type = 'FILE_BROWSER'
320 bl_region_type = 'TOOL_PROPS'
321 bl_label = "Transform"
322 bl_parent_id = "FILE_PT_operator"
324 @classmethod
325 def poll(cls, context):
326 sfile = context.space_data
327 operator = sfile.active_operator
329 return operator.bl_idname == "EXPORT_ANIM_OT_bvh"
331 def draw(self, context):
332 layout = self.layout
333 layout.use_property_split = True
334 layout.use_property_decorate = False # No animation.
336 sfile = context.space_data
337 operator = sfile.active_operator
339 layout.prop(operator, "global_scale")
340 layout.prop(operator, "rotate_mode")
341 layout.prop(operator, "root_transform_only")
344 class BVH_PT_export_animation(bpy.types.Panel):
345 bl_space_type = 'FILE_BROWSER'
346 bl_region_type = 'TOOL_PROPS'
347 bl_label = "Animation"
348 bl_parent_id = "FILE_PT_operator"
350 @classmethod
351 def poll(cls, context):
352 sfile = context.space_data
353 operator = sfile.active_operator
355 return operator.bl_idname == "EXPORT_ANIM_OT_bvh"
357 def draw(self, context):
358 layout = self.layout
359 layout.use_property_split = True
360 layout.use_property_decorate = False # No animation.
362 sfile = context.space_data
363 operator = sfile.active_operator
365 col = layout.column(align=True)
366 col.prop(operator, "frame_start", text="Frame Start")
367 col.prop(operator, "frame_end", text="End")
370 def menu_func_import(self, context):
371 self.layout.operator(ImportBVH.bl_idname, text="Motion Capture (.bvh)")
374 def menu_func_export(self, context):
375 self.layout.operator(ExportBVH.bl_idname, text="Motion Capture (.bvh)")
378 classes = (
379 ImportBVH,
380 BVH_PT_import_main,
381 BVH_PT_import_transform,
382 BVH_PT_import_animation,
383 ExportBVH,
384 BVH_PT_export_transform,
385 BVH_PT_export_animation,
388 def register():
389 for cls in classes:
390 bpy.utils.register_class(cls)
392 bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
393 bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
396 def unregister():
397 for cls in classes:
398 bpy.utils.unregister_class(cls)
400 bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
401 bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
403 if __name__ == "__main__":
404 register()