Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'io_anim_bvh/export_bvh.py')
-rw-r--r--io_anim_bvh/export_bvh.py276
1 files changed, 276 insertions, 0 deletions
diff --git a/io_anim_bvh/export_bvh.py b/io_anim_bvh/export_bvh.py
new file mode 100644
index 00000000..c6173b6a
--- /dev/null
+++ b/io_anim_bvh/export_bvh.py
@@ -0,0 +1,276 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+# Script copyright (C) Campbell Barton
+# fixes from Andrea Rugliancich
+
+import bpy
+
+
+def write_armature(context,
+ filepath,
+ frame_start,
+ frame_end,
+ global_scale=1.0,
+ rotate_mode='NATIVE',
+ ):
+
+ 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, Euler
+ from math import degrees
+
+ file = open(filepath, "w", encoding="utf8", newline="\n")
+
+ obj = context.object
+ arm = obj.data
+
+ # Build a dictionary of children.
+ # None for parentless
+ children = {None: []}
+
+ # initialize with blank lists
+ for bone in arm.bones:
+ children[bone.name] = []
+
+ for bone in arm.bones:
+ children[getattr(bone.parent, "name", None)].append(bone.name)
+
+ # sort the children
+ for children_list in children.values():
+ children_list.sort()
+
+ # bone name list in the order that the bones are written
+ serialized_names = []
+
+ node_locations = {}
+
+ file.write("HIERARCHY\n")
+
+ def write_recursive_nodes(bone_name, indent):
+ my_children = children[bone_name]
+
+ 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]
+
+ if indent:
+ file.write("%sJOINT %s\n" % (indent_str, bone_name))
+ else:
+ file.write("%sROOT %s\n" % (indent_str, bone_name))
+
+ 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 %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]))
+
+ if my_children:
+ # store the location for the children
+ # to het their relative offset
+
+ # Write children
+ for child_bone in my_children:
+ serialized_names.append(child_bone)
+ write_recursive_nodes(child_bone, indent + 1)
+
+ else:
+ # Write the bone end.
+ file.write("%s\tEnd Site\n" % indent_str)
+ file.write("%s\t{\n" % indent_str)
+ loc = bone.tail_local - node_locations[bone_name]
+ file.write("%s\t\tOFFSET %.6f %.6f %.6f\n" % (indent_str, loc.x * global_scale, loc.y * global_scale, loc.z * global_scale))
+ file.write("%s\t}\n" % indent_str)
+
+ file.write("%s}\n" % indent_str)
+
+ if len(children[None]) == 1:
+ key = children[None][0]
+ serialized_names.append(key)
+ indent = 0
+
+ write_recursive_nodes(key, indent)
+
+ else:
+ # Write a dummy parent node
+ file.write("ROOT %s\n" % key)
+ file.write("{\n")
+ file.write("\tOFFSET 0.0 0.0 0.0\n")
+ file.write("\tCHANNELS 0\n") # Xposition Yposition Zposition Xrotation Yrotation Zrotation
+ key = None
+ indent = 1
+
+ write_recursive_nodes(key, indent)
+
+ file.write("}\n")
+
+ # redefine bones as sorted by serialized_names
+ # so we can write motion
+
+ class DecoratedBone(object):
+ __slots__ = (
+ "name", # bone name, used as key in many places
+ "parent", # decorated bone parent, set in a later loop
+ "rest_bone", # blender armature bone
+ "pose_bone", # blender pose bone
+ "pose_mat", # blender pose matrix
+ "rest_arm_mat", # blender rest matrix (armature space)
+ "rest_local_mat", # blender rest batrix (local space)
+ "pose_imat", # pose_mat inverted
+ "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?
+ "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 = DecoratedBone._eul_order_lookup[self.rot_order_str]
+
+ self.pose_mat = self.pose_bone.matrix
+
+ # mat = self.rest_bone.matrix # UNUSED
+ self.rest_arm_mat = self.rest_bone.matrix_local
+ self.rest_local_mat = self.rest_bone.matrix
+
+ # inverted mats
+ self.pose_imat = self.pose_mat.inverted()
+ self.rest_arm_imat = self.rest_arm_mat.inverted()
+ self.rest_local_imat = self.rest_local_mat.inverted()
+
+ 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)
+
+ def update_posedata(self):
+ self.pose_mat = self.pose_bone.matrix
+ self.pose_imat = self.pose_mat.inverted()
+
+ def __repr__(self):
+ if self.parent:
+ return "[\"%s\" child on \"%s\"]\n" % (self.name, self.parent.name)
+ else:
+ return "[\"%s\" root bone]\n" % (self.name)
+
+ bones_decorated = [DecoratedBone(bone_name) for bone_name in serialized_names]
+
+ # Assign parents
+ bones_decorated_dict = {}
+ for dbone in bones_decorated:
+ bones_decorated_dict[dbone.name] = dbone
+
+ for dbone in bones_decorated:
+ parent = dbone.rest_bone.parent
+ if parent:
+ dbone.parent = bones_decorated_dict[parent.name]
+ del bones_decorated_dict
+ # finish assigning parents
+
+ scene = bpy.context.scene
+
+ file.write("MOTION\n")
+ file.write("Frames: %d\n" % (frame_end - frame_start + 1))
+ file.write("Frame Time: %.6f\n" % (1.0 / (scene.render.fps / scene.render.fps_base)))
+
+ for frame in range(frame_start, frame_end + 1):
+ scene.frame_set(frame)
+
+ for dbone in bones_decorated:
+ dbone.update_posedata()
+
+ for dbone in bones_decorated:
+ trans = Matrix.Translation(dbone.rest_bone.head_local)
+ itrans = Matrix.Translation(-dbone.rest_bone.head_local)
+
+ if dbone.parent:
+ mat_final = dbone.parent.rest_arm_mat * dbone.parent.pose_imat * dbone.pose_mat * dbone.rest_arm_imat
+ mat_final = itrans * mat_final * trans
+ loc = mat_final.to_translation() + (dbone.rest_bone.head_local - dbone.parent.rest_bone.head_local)
+ else:
+ mat_final = dbone.pose_mat * dbone.rest_arm_imat
+ mat_final = itrans * mat_final * trans
+ loc = mat_final.to_translation() + dbone.rest_bone.head
+
+ # 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:
+ 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]])))
+
+ dbone.prev_euler = rot
+
+ file.write("\n")
+
+ file.close()
+
+ print("BVH Exported: %s frames:%d\n" % (filepath, frame_end - frame_start + 1))
+
+
+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'}