From 8c5adbcd98b20e61f77263a2fb9095d331785a10 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 14 May 2011 16:22:21 +0000 Subject: support to export different rotation orders as well as pose bone native order. --- io_anim_bvh/__init__.py | 16 +++++++++++++++- io_anim_bvh/export_bvh.py | 41 +++++++++++++++++++++++++++++++++++------ io_anim_bvh/import_bvh.py | 2 +- 3 files changed, 51 insertions(+), 8 deletions(-) (limited to 'io_anim_bvh') diff --git a/io_anim_bvh/__init__.py b/io_anim_bvh/__init__.py index e8b51a77..5cdc79f2 100644 --- a/io_anim_bvh/__init__.py +++ b/io_anim_bvh/__init__.py @@ -37,7 +37,8 @@ if "bpy" in locals(): import imp if "import_bvh" in locals(): imp.reload(import_bvh) - + if "export_bvh" in locals(): + imp.reload(export_bvh) import bpy from bpy.props import StringProperty, FloatProperty, IntProperty, BoolProperty, EnumProperty @@ -95,6 +96,19 @@ class ExportBVH(bpy.types.Operator, ExportHelper): 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') + @classmethod def poll(cls, context): obj = context.object diff --git a/io_anim_bvh/export_bvh.py b/io_anim_bvh/export_bvh.py index 2a49f083..6d36e587 100644 --- a/io_anim_bvh/export_bvh.py +++ b/io_anim_bvh/export_bvh.py @@ -23,8 +23,12 @@ import bpy +def write_armature(context, filepath, frame_start, frame_end, global_scale=1.0, rotate_mode="NATIVE"): -def write_armature(context, filepath, frame_start, frame_end, global_scale=1.0): + def ensure_rot_order(rot_order_str): + if set(rot_order_str) != {'X', 'Y', 'Z'}: + rot_order_str = "XYZ" + return rot_order_str from mathutils import Matrix, Vector, Euler from math import degrees @@ -62,9 +66,15 @@ def write_armature(context, filepath, frame_start, frame_end, global_scale=1.0): indent_str = "\t" * indent bone = arm.bones[bone_name] + pose_bone = obj.pose.bones[bone_name] loc = bone.head_local node_locations[bone_name] = loc + if rotate_mode == "NATIVE": + rot_order_str = ensure_rot_order(pose_bone.rotation_mode) + else: + rot_order_str = rotate_mode + # make relative if we can if bone.parent: loc = loc - node_locations[bone.parent.name] @@ -77,9 +87,9 @@ def write_armature(context, filepath, frame_start, frame_end, global_scale=1.0): 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: - file.write("%s\tCHANNELS 3 Xrotation Yrotation Zrotation\n" % indent_str) + 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 Xrotation Yrotation Zrotation\n" % indent_str) + 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])) if my_children: # store the location for the children @@ -137,13 +147,30 @@ def write_armature(context, filepath, frame_start, frame_end, global_scale=1.0): "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? + "rot_order", + "rot_order_str", ) + _eul_order_lookup = { + 'XYZ': (0, 1, 2), + 'XZY': (0, 2, 1), + 'YXZ': (1, 0, 2), + 'YZX': (1, 2, 0), + 'ZXY': (2, 0, 1), + 'ZYX': (2, 1, 0)} + def __init__(self, bone_name): self.name = bone_name self.rest_bone = arm.bones[bone_name] self.pose_bone = obj.pose.bones[bone_name] + if rotate_mode == "NATIVE": + self.rot_order_str = ensure_rot_order(self.pose_bone.rotation_mode) + else: + self.rot_order_str = rotate_mode + + self.rot_order = __class__._eul_order_lookup[self.rot_order_str] + self.pose_mat = self.pose_bone.matrix mat = self.rest_bone.matrix @@ -156,7 +183,7 @@ def write_armature(context, filepath, frame_start, frame_end, global_scale=1.0): self.rest_local_imat = self.rest_local_mat.inverted() self.parent = None - self.prev_euler = Euler((0.0, 0.0, 0.0)) + 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) def update_posedata(self): @@ -209,12 +236,12 @@ def write_armature(context, filepath, frame_start, frame_end, global_scale=1.0): loc = mat_final.to_translation() + dbone.rest_bone.head # keep eulers compatible, no jumping on interpolation. - rot = mat_final.to_3x3().inverted().to_euler('XYZ', dbone.prev_euler) + rot = mat_final.to_3x3().inverted().to_euler(dbone.rot_order_str, dbone.prev_euler) if not dbone.connected: file.write("%.6f %.6f %.6f " % (loc * global_scale)[:]) - file.write("%.6f %.6f %.6f " % (-degrees(rot[0]), -degrees(rot[1]), -degrees(rot[2]))) + file.write("%.6f %.6f %.6f " % (-degrees(rot[dbone.rot_order[0]]), -degrees(rot[dbone.rot_order[1]]), -degrees(rot[dbone.rot_order[2]]))) dbone.prev_euler = rot @@ -229,12 +256,14 @@ def save(operator, context, filepath="", frame_start=-1, frame_end=-1, global_scale=1.0, + rotate_mode="NATIVE", ): write_armature(context, filepath, frame_start=frame_start, frame_end=frame_end, global_scale=global_scale, + rotate_mode=rotate_mode, ) return {'FINISHED'} diff --git a/io_anim_bvh/import_bvh.py b/io_anim_bvh/import_bvh.py index d0f3fde8..c4d358d1 100644 --- a/io_anim_bvh/import_bvh.py +++ b/io_anim_bvh/import_bvh.py @@ -45,7 +45,7 @@ class bvh_node_class(object): 'has_rot', # Conveinience function, bool, same as (channels[3]!=-1 or channels[4]!=-1 channels[5]!=-1) 'temp') # use this for whatever you want - _eul_order_lookup = {\ + _eul_order_lookup = { (0, 1, 2): 'XYZ', (0, 2, 1): 'XZY', (1, 0, 2): 'YXZ', -- cgit v1.2.3