From 01ef1c9b2c80e87cec0644aade7c1e4deb949901 Mon Sep 17 00:00:00 2001 From: Campbell Barton <ideasman42@gmail.com> Date: Mon, 15 Aug 2011 11:56:36 +0000 Subject: [PATCH] bvh export - add option to export root transform only (may help with secondlife compatibility) - operators are now pep8 compliant. - frame is set back to the original when export is done. --- io_anim_bvh/__init__.py | 130 ++++++++++++++++++++++++++------------ io_anim_bvh/export_bvh.py | 17 +++-- 2 files changed, 101 insertions(+), 46 deletions(-) diff --git a/io_anim_bvh/__init__.py b/io_anim_bvh/__init__.py index 94d048f67..b06eb58a6 100644 --- a/io_anim_bvh/__init__.py +++ b/io_anim_bvh/__init__.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8 compliant> +# <pep8-80 compliant> bl_info = { "name": "BioVision Motion Capture (BVH) format", @@ -26,13 +26,12 @@ bl_info = { "location": "File > Import-Export", "description": "Import-Export BVH from armature objects", "warning": "", - "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"\ - "Scripts/Import-Export/MotionCapture_BVH", + "wiki_url": ("http://wiki.blender.org/index.php/Extensions:2.5/Py/" + "Scripts/Import-Export/MotionCapture_BVH"), "tracker_url": "", "support": 'OFFICIAL', "category": "Import-Export"} -# To support reload properly, try to access a package var, if it's there, reload everything if "bpy" in locals(): import imp if "import_bvh" in locals(): @@ -41,7 +40,12 @@ if "bpy" in locals(): imp.reload(export_bvh) import bpy -from bpy.props import StringProperty, FloatProperty, IntProperty, BoolProperty, EnumProperty +from bpy.props import (StringProperty, + FloatProperty, + IntProperty, + BoolProperty, + EnumProperty, + ) from bpy_extras.io_utils import ImportHelper, ExportHelper @@ -62,26 +66,45 @@ class ImportBVH(bpy.types.Operator, ImportHelper): description="Import target type.", default='ARMATURE') - global_scale = FloatProperty(name="Scale", description="Scale the BVH by this value", min=0.0001, max=1000000.0, soft_min=0.001, soft_max=100.0, default=1.0) - frame_start = IntProperty(name="Start Frame", description="Starting frame for the animation", default=1) - use_cyclic = BoolProperty(name="Loop", description="Loop the animation playback", default=False) - rotate_mode = EnumProperty(items=( - ('QUATERNION', "Quaternion", "Convert rotations to quaternions"), - ('NATIVE', "Euler (Native)", "Use the rotation order defined in the BVH file"), - ('XYZ', "Euler (XYZ)", "Convert rotations to euler XYZ"), - ('XZY', "Euler (XZY)", "Convert rotations to euler XZY"), - ('YXZ', "Euler (YXZ)", "Convert rotations to euler YXZ"), - ('YZX', "Euler (YZX)", "Convert rotations to euler YZX"), - ('ZXY', "Euler (ZXY)", "Convert rotations to euler ZXY"), - ('ZYX', "Euler (ZYX)", "Convert rotations to euler ZYX"), - ), - name="Rotation", - description="Rotation conversion.", - default='NATIVE') + global_scale = FloatProperty( + name="Scale", + description="Scale the BVH by this value", + min=0.0001, max=1000000.0, + soft_min=0.001, soft_max=100.0, + default=1.0, + ) + frame_start = IntProperty( + name="Start Frame", + description="Starting frame for the animation", + default=1, + ) + use_cyclic = BoolProperty( + name="Loop", + description="Loop the animation playback", + default=False, + ) + rotate_mode = EnumProperty( + name="Rotation", + description="Rotation conversion.", + items=(('QUATERNION', "Quaternion", + "Convert rotations to quaternions"), + ('NATIVE', "Euler (Native)", ("Use the rotation order " + "defined in the BVH file")), + ('XYZ', "Euler (XYZ)", "Convert rotations to euler XYZ"), + ('XZY', "Euler (XZY)", "Convert rotations to euler XZY"), + ('YXZ', "Euler (YXZ)", "Convert rotations to euler YXZ"), + ('YZX', "Euler (YZX)", "Convert rotations to euler YZX"), + ('ZXY', "Euler (ZXY)", "Convert rotations to euler ZXY"), + ('ZYX', "Euler (ZYX)", "Convert rotations to euler ZYX"), + ), + default='NATIVE', + ) def execute(self, context): + keywords = self.as_keywords(ignore=("filter_glob",)) + from . import import_bvh - return import_bvh.load(self, context, **self.as_keywords(ignore=("filter_glob",))) + return import_bvh.load(self, context, **keywords) class ExportBVH(bpy.types.Operator, ExportHelper): @@ -90,24 +113,47 @@ class ExportBVH(bpy.types.Operator, ExportHelper): bl_label = "Export BVH" filename_ext = ".bvh" - filter_glob = StringProperty(default="*.bvh", options={'HIDDEN'}) - - global_scale = FloatProperty(name="Scale", description="Scale the BVH by this value", min=0.0001, max=1000000.0, soft_min=0.001, soft_max=100.0, default=1.0) - frame_start = IntProperty(name="Start Frame", description="Starting frame to export", default=0) - frame_end = IntProperty(name="End Frame", description="End frame to export", default=0) - - rotate_mode = EnumProperty(items=( - ('NATIVE', "Euler (Native)", "Use the rotation order defined in the BVH file"), - ('XYZ', "Euler (XYZ)", "Convert rotations to euler XYZ"), - ('XZY', "Euler (XZY)", "Convert rotations to euler XZY"), - ('YXZ', "Euler (YXZ)", "Convert rotations to euler YXZ"), - ('YZX', "Euler (YZX)", "Convert rotations to euler YZX"), - ('ZXY', "Euler (ZXY)", "Convert rotations to euler ZXY"), - ('ZYX', "Euler (ZYX)", "Convert rotations to euler ZYX"), - ), - name="Rotation", - description="Rotation conversion.", - default='NATIVE') + filter_glob = StringProperty( + default="*.bvh", + options={'HIDDEN'}, + ) + + global_scale = FloatProperty( + name="Scale", + description="Scale the BVH by this value", + min=0.0001, max=1000000.0, + soft_min=0.001, soft_max=100.0, + default=1.0, + ) + frame_start = IntProperty( + name="Start Frame", + description="Starting frame to export", + default=0, + ) + frame_end = IntProperty( + name="End Frame", + description="End frame to export", + default=0, + ) + rotate_mode = EnumProperty( + name="Rotation", + description="Rotation conversion.", + items=(('NATIVE', "Euler (Native)", + "Use the rotation order defined in the BVH file"), + ('XYZ', "Euler (XYZ)", "Convert rotations to euler XYZ"), + ('XZY', "Euler (XZY)", "Convert rotations to euler XZY"), + ('YXZ', "Euler (YXZ)", "Convert rotations to euler YXZ"), + ('YZX', "Euler (YZX)", "Convert rotations to euler YZX"), + ('ZXY', "Euler (ZXY)", "Convert rotations to euler ZXY"), + ('ZYX', "Euler (ZYX)", "Convert rotations to euler ZYX"), + ), + default='NATIVE', + ) + root_transform_only = BoolProperty( + name="Root Transform Only", + description="Only write out transform channels for the root bone", + default=False, + ) @classmethod def poll(cls, context): @@ -125,8 +171,10 @@ class ExportBVH(bpy.types.Operator, ExportHelper): self.frame_start = context.scene.frame_start self.frame_end = context.scene.frame_end + keywords = self.as_keywords(ignore=("check_existing", "filter_glob")) + from . import export_bvh - return export_bvh.save(self, context, **self.as_keywords(ignore=("check_existing", "filter_glob"))) + return export_bvh.save(self, context, **keywords) def menu_func_import(self, context): diff --git a/io_anim_bvh/export_bvh.py b/io_anim_bvh/export_bvh.py index c6173b6a0..977c36f53 100644 --- a/io_anim_bvh/export_bvh.py +++ b/io_anim_bvh/export_bvh.py @@ -30,6 +30,7 @@ def write_armature(context, frame_end, global_scale=1.0, rotate_mode='NATIVE', + root_transform_only=False, ): def ensure_rot_order(rot_order_str): @@ -93,7 +94,7 @@ def write_armature(context, file.write("%s{\n" % indent_str) file.write("%s\tOFFSET %.6f %.6f %.6f\n" % (indent_str, loc.x * global_scale, loc.y * global_scale, loc.z * global_scale)) - if bone.use_connect and bone.parent: + if (bone.use_connect or root_transform_only) and bone.parent: file.write("%s\tCHANNELS 3 %srotation %srotation %srotation\n" % (indent_str, rot_order_str[0], rot_order_str[1], rot_order_str[2])) else: file.write("%s\tCHANNELS 6 Xposition Yposition Zposition %srotation %srotation %srotation\n" % (indent_str, rot_order_str[0], rot_order_str[1], rot_order_str[2])) @@ -153,7 +154,7 @@ def write_armature(context, "rest_arm_imat", # rest_arm_mat inverted "rest_local_imat", # rest_local_mat inverted "prev_euler", # last used euler to preserve euler compability in between keyframes - "connected", # is the bone connected to the parent bone? + "skip_position", # is the bone disconnected to the parent bone? "rot_order", "rot_order_str", ) @@ -164,7 +165,8 @@ def write_armature(context, 'YXZ': (1, 0, 2), 'YZX': (1, 2, 0), 'ZXY': (2, 0, 1), - 'ZYX': (2, 1, 0)} + 'ZYX': (2, 1, 0), + } def __init__(self, bone_name): self.name = bone_name @@ -191,7 +193,7 @@ def write_armature(context, self.parent = None self.prev_euler = Euler((0.0, 0.0, 0.0), self.rot_order_str) - self.connected = (self.rest_bone.use_connect and self.rest_bone.parent) + self.skip_position = ((self.rest_bone.use_connect or root_transform_only) and self.rest_bone.parent) def update_posedata(self): self.pose_mat = self.pose_bone.matrix @@ -218,6 +220,7 @@ def write_armature(context, # finish assigning parents scene = bpy.context.scene + frame_current = scene.frame_current file.write("MOTION\n") file.write("Frames: %d\n" % (frame_end - frame_start + 1)) @@ -245,7 +248,7 @@ def write_armature(context, # keep eulers compatible, no jumping on interpolation. rot = mat_final.to_3x3().inverted().to_euler(dbone.rot_order_str, dbone.prev_euler) - if not dbone.connected: + if not dbone.skip_position: file.write("%.6f %.6f %.6f " % (loc * global_scale)[:]) file.write("%.6f %.6f %.6f " % (-degrees(rot[dbone.rot_order[0]]), -degrees(rot[dbone.rot_order[1]]), -degrees(rot[dbone.rot_order[2]]))) @@ -256,6 +259,8 @@ def write_armature(context, file.close() + scene.frame_set(frame_current) + print("BVH Exported: %s frames:%d\n" % (filepath, frame_end - frame_start + 1)) @@ -264,6 +269,7 @@ def save(operator, context, filepath="", frame_end=-1, global_scale=1.0, rotate_mode="NATIVE", + root_transform_only=False, ): write_armature(context, filepath, @@ -271,6 +277,7 @@ def save(operator, context, filepath="", frame_end=frame_end, global_scale=global_scale, rotate_mode=rotate_mode, + root_transform_only=root_transform_only, ) return {'FINISHED'} -- GitLab