2011-01-01 08:35:38 +00:00
|
|
|
# ##### 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 #####
|
2011-01-01 07:26:02 +00:00
|
|
|
|
2011-01-01 08:35:38 +00:00
|
|
|
# <pep8 compliant>
|
2011-01-01 07:26:02 +00:00
|
|
|
|
2011-01-01 08:35:38 +00:00
|
|
|
# Script copyright (C) Campbell Barton
|
|
|
|
# fixes from Andrea Rugliancich
|
2011-01-01 07:26:02 +00:00
|
|
|
|
2011-01-01 08:35:38 +00:00
|
|
|
import bpy
|
2011-01-01 07:26:02 +00:00
|
|
|
|
2011-01-03 16:22:30 +00:00
|
|
|
|
2011-01-06 13:49:09 +00:00
|
|
|
def write_armature(context, filepath, frame_start, frame_end, global_scale=1.0):
|
2011-01-01 07:26:02 +00:00
|
|
|
|
2011-01-01 10:38:28 +00:00
|
|
|
from mathutils import Matrix, Vector, Euler
|
2011-01-01 09:44:13 +00:00
|
|
|
from math import degrees
|
2011-01-01 07:26:02 +00:00
|
|
|
|
2011-01-01 08:44:17 +00:00
|
|
|
file = open(filepath, "w")
|
|
|
|
|
2011-01-01 09:44:13 +00:00
|
|
|
obj = context.object
|
|
|
|
arm = obj.data
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-06 13:49:09 +00:00
|
|
|
# Build a dictionary of children.
|
|
|
|
# None for parentless
|
|
|
|
children = {None: []}
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
# initialize with blank lists
|
2011-01-01 09:44:13 +00:00
|
|
|
for bone in arm.bones:
|
2011-01-06 13:49:09 +00:00
|
|
|
children[bone.name] = []
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 09:44:13 +00:00
|
|
|
for bone in arm.bones:
|
2011-01-06 13:49:09 +00:00
|
|
|
children[getattr(bone.parent, "name", None)].append(bone.name)
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
# sort the children
|
2011-01-06 13:49:09 +00:00
|
|
|
for children_list in children.values():
|
2011-01-01 07:26:02 +00:00
|
|
|
children_list.sort()
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
# bone name list in the order that the bones are written
|
2011-01-06 13:49:09 +00:00
|
|
|
serialized_names = []
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-06 13:49:09 +00:00
|
|
|
node_locations = {}
|
2011-01-01 08:44:17 +00:00
|
|
|
|
|
|
|
file.write("HIERARCHY\n")
|
|
|
|
|
2011-01-06 13:49:09 +00:00
|
|
|
def write_recursive_nodes(bone_name, indent):
|
|
|
|
my_children = children[bone_name]
|
2011-01-01 08:44:17 +00:00
|
|
|
|
|
|
|
indent_str = "\t" * indent
|
|
|
|
|
2011-01-01 09:44:13 +00:00
|
|
|
bone = arm.bones[bone_name]
|
2011-01-01 08:35:38 +00:00
|
|
|
loc = bone.head_local
|
2011-01-06 13:49:09 +00:00
|
|
|
node_locations[bone_name] = loc
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
# make relative if we can
|
|
|
|
if bone.parent:
|
2011-01-06 13:49:09 +00:00
|
|
|
loc = loc - node_locations[bone.parent.name]
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
if indent:
|
2011-01-01 08:44:17 +00:00
|
|
|
file.write("%sJOINT %s\n" % (indent_str, bone_name))
|
2011-01-01 07:26:02 +00:00
|
|
|
else:
|
2011-01-01 08:44:17 +00:00
|
|
|
file.write("%sROOT %s\n" % (indent_str, bone_name))
|
|
|
|
|
|
|
|
file.write("%s{\n" % indent_str)
|
2011-01-01 09:44:13 +00:00
|
|
|
file.write("%s\tOFFSET %.6f %.6f %.6f\n" % (indent_str, loc.x * global_scale, loc.y * global_scale, loc.z * global_scale))
|
2011-01-09 16:46:01 +00:00
|
|
|
if bone.use_connect and bone.parent:
|
|
|
|
file.write("%s\tCHANNELS 3 Xrotation Yrotation Zrotation\n" % indent_str)
|
|
|
|
else:
|
|
|
|
file.write("%s\tCHANNELS 6 Xposition Yposition Zposition Xrotation Yrotation Zrotation\n" % indent_str)
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-06 13:49:09 +00:00
|
|
|
if my_children:
|
2011-01-01 07:26:02 +00:00
|
|
|
# store the location for the children
|
|
|
|
# to het their relative offset
|
2011-01-01 08:44:17 +00:00
|
|
|
|
|
|
|
# Write children
|
2011-01-06 13:49:09 +00:00
|
|
|
for child_bone in my_children:
|
|
|
|
serialized_names.append(child_bone)
|
|
|
|
write_recursive_nodes(child_bone, indent + 1)
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
else:
|
|
|
|
# Write the bone end.
|
2011-01-01 08:44:17 +00:00
|
|
|
file.write("%s\tEnd Site\n" % indent_str)
|
|
|
|
file.write("%s\t{\n" % indent_str)
|
2011-01-06 13:49:09 +00:00
|
|
|
loc = bone.tail_local - node_locations[bone_name]
|
2011-01-01 09:44:13 +00:00
|
|
|
file.write("%s\t\tOFFSET %.6f %.6f %.6f\n" % (indent_str, loc.x * global_scale, loc.y * global_scale, loc.z * global_scale))
|
2011-01-01 08:44:17 +00:00
|
|
|
file.write("%s\t}\n" % indent_str)
|
|
|
|
|
|
|
|
file.write("%s}\n" % indent_str)
|
|
|
|
|
2011-01-06 13:49:09 +00:00
|
|
|
if len(children[None]) == 1:
|
|
|
|
key = children[None][0]
|
|
|
|
serialized_names.append(key)
|
2011-01-01 07:26:02 +00:00
|
|
|
indent = 0
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-06 13:49:09 +00:00
|
|
|
write_recursive_nodes(key, indent)
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
else:
|
|
|
|
# Write a dummy parent node
|
2011-01-01 08:44:17 +00:00
|
|
|
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
|
2011-01-01 07:26:02 +00:00
|
|
|
key = None
|
|
|
|
indent = 1
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-06 13:49:09 +00:00
|
|
|
write_recursive_nodes(key, indent)
|
2011-01-01 08:44:17 +00:00
|
|
|
|
|
|
|
file.write("}\n")
|
|
|
|
|
2011-01-06 13:49:09 +00:00
|
|
|
# redefine bones as sorted by serialized_names
|
2011-01-01 09:44:13 +00:00
|
|
|
# so we can write motion
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
class decorated_bone(object):
|
|
|
|
__slots__ = (\
|
2011-01-01 08:44:17 +00:00
|
|
|
"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
|
2011-01-01 10:38:28 +00:00
|
|
|
"rest_local_imat", # rest_local_mat inverted
|
2011-01-02 11:06:50 +00:00
|
|
|
"prev_euler", # last used euler to preserve euler compability in between keyframes
|
2011-01-09 16:46:01 +00:00
|
|
|
"connected", # is the bone connected to the parent bone?
|
2011-01-01 10:38:28 +00:00
|
|
|
)
|
2011-01-03 16:22:30 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
def __init__(self, bone_name):
|
|
|
|
self.name = bone_name
|
2011-01-01 09:44:13 +00:00
|
|
|
self.rest_bone = arm.bones[bone_name]
|
|
|
|
self.pose_bone = obj.pose.bones[bone_name]
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 08:35:38 +00:00
|
|
|
self.pose_mat = self.pose_bone.matrix
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
mat = self.rest_bone.matrix
|
2011-01-01 08:35:38 +00:00
|
|
|
self.rest_arm_mat = self.rest_bone.matrix_local
|
|
|
|
self.rest_local_mat = self.rest_bone.matrix
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
# inverted mats
|
|
|
|
self.pose_imat = self.pose_mat.copy().invert()
|
|
|
|
self.rest_arm_imat = self.rest_arm_mat.copy().invert()
|
|
|
|
self.rest_local_imat = self.rest_local_mat.copy().invert()
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
self.parent = None
|
2011-01-01 10:38:28 +00:00
|
|
|
self.prev_euler = Euler((0.0, 0.0, 0.0))
|
2011-01-09 16:46:01 +00:00
|
|
|
self.connected = (self.rest_bone.use_connect and self.rest_bone.parent)
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
def update_posedata(self):
|
2011-01-01 08:35:38 +00:00
|
|
|
self.pose_mat = self.pose_bone.matrix
|
2011-01-01 07:26:02 +00:00
|
|
|
self.pose_imat = self.pose_mat.copy().invert()
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
def __repr__(self):
|
|
|
|
if self.parent:
|
2011-01-01 08:44:17 +00:00
|
|
|
return "[\"%s\" child on \"%s\"]\n" % (self.name, self.parent.name)
|
2011-01-01 07:26:02 +00:00
|
|
|
else:
|
2011-01-01 08:44:17 +00:00
|
|
|
return "[\"%s\" root bone]\n" % (self.name)
|
|
|
|
|
2011-01-06 13:49:09 +00:00
|
|
|
bones_decorated = [decorated_bone(bone_name) for bone_name in serialized_names]
|
2011-01-01 07:26:02 +00:00
|
|
|
|
|
|
|
# Assign parents
|
|
|
|
bones_decorated_dict = {}
|
|
|
|
for dbone in bones_decorated:
|
|
|
|
bones_decorated_dict[dbone.name] = dbone
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
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
|
|
|
|
|
2011-01-01 10:38:28 +00:00
|
|
|
scene = bpy.context.scene
|
|
|
|
|
2011-01-01 08:44:17 +00:00
|
|
|
file.write("MOTION\n")
|
2011-01-01 09:44:13 +00:00
|
|
|
file.write("Frames: %d\n" % (frame_end - frame_start + 1))
|
2011-01-01 10:38:28 +00:00
|
|
|
file.write("Frame Time: %.6f\n" % (1.0 / (scene.render.fps / scene.render.fps_base)))
|
2011-01-01 08:35:38 +00:00
|
|
|
|
2011-01-01 09:44:13 +00:00
|
|
|
for frame in range(frame_start, frame_end + 1):
|
2011-01-01 08:35:38 +00:00
|
|
|
scene.frame_set(frame)
|
2011-01-01 10:38:28 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
for dbone in bones_decorated:
|
|
|
|
dbone.update_posedata()
|
2011-01-01 10:38:28 +00:00
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
for dbone in bones_decorated:
|
2011-01-01 10:38:28 +00:00
|
|
|
trans = Matrix.Translation(dbone.rest_bone.head_local)
|
|
|
|
itrans = Matrix.Translation(-dbone.rest_bone.head_local)
|
|
|
|
|
2011-01-01 07:26:02 +00:00
|
|
|
if dbone.parent:
|
2011-01-01 10:38:28 +00:00
|
|
|
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.translation_part() + (dbone.rest_bone.head_local - dbone.parent.rest_bone.head_local)
|
2011-01-01 07:26:02 +00:00
|
|
|
else:
|
2011-01-01 10:38:28 +00:00
|
|
|
mat_final = dbone.pose_mat * dbone.rest_arm_imat
|
|
|
|
mat_final = itrans * mat_final * trans
|
|
|
|
loc = mat_final.translation_part() + dbone.rest_bone.head
|
|
|
|
|
|
|
|
# keep eulers compatible, no jumping on interpolation.
|
|
|
|
rot = mat_final.rotation_part().invert().to_euler('XYZ', dbone.prev_euler)
|
2011-01-01 08:57:09 +00:00
|
|
|
|
2011-01-09 16:46:01 +00:00
|
|
|
if not dbone.connected:
|
|
|
|
file.write("%.6f %.6f %.6f " % (loc * global_scale)[:])
|
|
|
|
|
2011-01-01 10:38:28 +00:00
|
|
|
file.write("%.6f %.6f %.6f " % (-degrees(rot[0]), -degrees(rot[1]), -degrees(rot[2])))
|
2011-01-01 07:26:02 +00:00
|
|
|
|
2011-01-01 10:38:28 +00:00
|
|
|
dbone.prev_euler = rot
|
2011-01-01 08:44:17 +00:00
|
|
|
|
|
|
|
file.write("\n")
|
2011-01-01 07:26:02 +00:00
|
|
|
|
|
|
|
file.close()
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 09:44:13 +00:00
|
|
|
print("BVH Exported: %s frames:%d\n" % (filepath, frame_end - frame_start + 1))
|
2011-01-01 08:44:17 +00:00
|
|
|
|
|
|
|
|
2011-01-01 09:44:13 +00:00
|
|
|
def save(operator, context, filepath="",
|
|
|
|
frame_start=-1,
|
|
|
|
frame_end=-1,
|
|
|
|
global_scale=1.0,
|
|
|
|
):
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-06 13:49:09 +00:00
|
|
|
write_armature(context, filepath,
|
2011-01-01 09:44:13 +00:00
|
|
|
frame_start=frame_start,
|
|
|
|
frame_end=frame_end,
|
|
|
|
global_scale=global_scale,
|
|
|
|
)
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 09:44:13 +00:00
|
|
|
return {'FINISHED'}
|
2011-01-01 07:26:02 +00:00
|
|
|
|
2011-01-01 08:44:17 +00:00
|
|
|
|
2011-01-01 09:44:13 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
scene = bpy.context.scene
|
|
|
|
_read(bpy.data.filepath.rstrip(".blend") + ".bvh", bpy.context.object, scene.frame_start, scene.frame_end, 1.0)
|