forked from bartvdbraak/blender
e86a489327
& small pep8 changes.
551 lines
21 KiB
Python
551 lines
21 KiB
Python
# ##### 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
|
|
|
|
import math
|
|
from math import radians
|
|
|
|
import bpy
|
|
import mathutils
|
|
from mathutils import Vector, Euler, Matrix
|
|
|
|
|
|
class bvh_node_class(object):
|
|
__slots__ = (
|
|
'name', # bvh joint name
|
|
'parent', # bvh_node_class type or None for no parent
|
|
'children', # a list of children of this type.
|
|
'rest_head_world', # worldspace rest location for the head of this node
|
|
'rest_head_local', # localspace rest location for the head of this node
|
|
'rest_tail_world', # worldspace rest location for the tail of this node
|
|
'rest_tail_local', # worldspace rest location for the tail of this node
|
|
'channels', # list of 6 ints, -1 for an unused channel, otherwise an index for the BVH motion data lines, lock triple then rot triple
|
|
'rot_order', # a triple of indicies as to the order rotation is applied. [0,1,2] is x/y/z - [None, None, None] if no rotation.
|
|
'rot_order_str', # same as above but a string 'XYZ' format.
|
|
'anim_data', # a list one tuple's one for each frame. (locx, locy, locz, rotx, roty, rotz), euler rotation ALWAYS stored xyz order, even when native used.
|
|
'has_loc', # Conveinience function, bool, same as (channels[0]!=-1 or channels[1]!=-1 channels[2]!=-1)
|
|
'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 = {\
|
|
(0, 1, 2): 'XYZ',
|
|
(0, 2, 1): 'XZY',
|
|
(1, 0, 2): 'YXZ',
|
|
(1, 2, 0): 'YZX',
|
|
(2, 0, 1): 'ZXY',
|
|
(2, 1, 0): 'ZYX'}
|
|
|
|
def __init__(self, name, rest_head_world, rest_head_local, parent, channels, rot_order):
|
|
self.name = name
|
|
self.rest_head_world = rest_head_world
|
|
self.rest_head_local = rest_head_local
|
|
self.rest_tail_world = None
|
|
self.rest_tail_local = None
|
|
self.parent = parent
|
|
self.channels = channels
|
|
self.rot_order = tuple(rot_order)
|
|
self.rot_order_str = __class__._eul_order_lookup[self.rot_order]
|
|
|
|
# convenience functions
|
|
self.has_loc = channels[0] != -1 or channels[1] != -1 or channels[2] != -1
|
|
self.has_rot = channels[3] != -1 or channels[4] != -1 or channels[5] != -1
|
|
|
|
self.children = []
|
|
|
|
# list of 6 length tuples: (lx,ly,lz, rx,ry,rz)
|
|
# even if the channels arnt used they will just be zero
|
|
#
|
|
self.anim_data = [(0, 0, 0, 0, 0, 0)]
|
|
|
|
def __repr__(self):
|
|
return 'BVH name:"%s", rest_loc:(%.3f,%.3f,%.3f), rest_tail:(%.3f,%.3f,%.3f)' %\
|
|
(self.name,\
|
|
self.rest_head_world.x, self.rest_head_world.y, self.rest_head_world.z,\
|
|
self.rest_head_world.x, self.rest_head_world.y, self.rest_head_world.z)
|
|
|
|
|
|
def read_bvh(context, file_path, rotate_mode='XYZ', global_scale=1.0):
|
|
# File loading stuff
|
|
# Open the file for importing
|
|
file = open(file_path, 'rU')
|
|
|
|
# Seperate into a list of lists, each line a list of words.
|
|
file_lines = file.readlines()
|
|
# Non standard carrage returns?
|
|
if len(file_lines) == 1:
|
|
file_lines = file_lines[0].split('\r')
|
|
|
|
# Split by whitespace.
|
|
file_lines = [ll for ll in [l.split() for l in file_lines] if ll]
|
|
|
|
# Create Hirachy as empties
|
|
if file_lines[0][0].lower() == 'hierarchy':
|
|
#print 'Importing the BVH Hierarchy for:', file_path
|
|
pass
|
|
else:
|
|
raise 'ERROR: This is not a BVH file'
|
|
|
|
bvh_nodes = {None: None}
|
|
bvh_nodes_serial = [None]
|
|
|
|
channelIndex = -1
|
|
|
|
lineIdx = 0 # An index for the file.
|
|
while lineIdx < len(file_lines) - 1:
|
|
#...
|
|
if file_lines[lineIdx][0].lower() == 'root' or file_lines[lineIdx][0].lower() == 'joint':
|
|
|
|
# Join spaces into 1 word with underscores joining it.
|
|
if len(file_lines[lineIdx]) > 2:
|
|
file_lines[lineIdx][1] = '_'.join(file_lines[lineIdx][1:])
|
|
file_lines[lineIdx] = file_lines[lineIdx][:2]
|
|
|
|
# MAY NEED TO SUPPORT MULTIPLE ROOT's HERE!!!, Still unsure weather multiple roots are possible.??
|
|
|
|
# Make sure the names are unique- Object names will match joint names exactly and both will be unique.
|
|
name = file_lines[lineIdx][1]
|
|
|
|
#print '%snode: %s, parent: %s' % (len(bvh_nodes_serial) * ' ', name, bvh_nodes_serial[-1])
|
|
|
|
lineIdx += 2 # Increment to the next line (Offset)
|
|
rest_head_local = Vector((float(file_lines[lineIdx][1]), float(file_lines[lineIdx][2]), float(file_lines[lineIdx][3]))) * global_scale
|
|
lineIdx += 1 # Increment to the next line (Channels)
|
|
|
|
# newChannel[Xposition, Yposition, Zposition, Xrotation, Yrotation, Zrotation]
|
|
# newChannel references indecies to the motiondata,
|
|
# if not assigned then -1 refers to the last value that will be added on loading at a value of zero, this is appended
|
|
# We'll add a zero value onto the end of the MotionDATA so this is always refers to a value.
|
|
my_channel = [-1, -1, -1, -1, -1, -1]
|
|
my_rot_order = [None, None, None]
|
|
rot_count = 0
|
|
for channel in file_lines[lineIdx][2:]:
|
|
channel = channel.lower()
|
|
channelIndex += 1 # So the index points to the right channel
|
|
if channel == 'xposition':
|
|
my_channel[0] = channelIndex
|
|
elif channel == 'yposition':
|
|
my_channel[1] = channelIndex
|
|
elif channel == 'zposition':
|
|
my_channel[2] = channelIndex
|
|
|
|
elif channel == 'xrotation':
|
|
my_channel[3] = channelIndex
|
|
my_rot_order[rot_count] = 0
|
|
rot_count += 1
|
|
elif channel == 'yrotation':
|
|
my_channel[4] = channelIndex
|
|
my_rot_order[rot_count] = 1
|
|
rot_count += 1
|
|
elif channel == 'zrotation':
|
|
my_channel[5] = channelIndex
|
|
my_rot_order[rot_count] = 2
|
|
rot_count += 1
|
|
|
|
channels = file_lines[lineIdx][2:]
|
|
|
|
my_parent = bvh_nodes_serial[-1] # account for none
|
|
|
|
# Apply the parents offset accumulatively
|
|
if my_parent is None:
|
|
rest_head_world = Vector(rest_head_local)
|
|
else:
|
|
rest_head_world = my_parent.rest_head_world + rest_head_local
|
|
|
|
bvh_node = bvh_nodes[name] = bvh_node_class(name, rest_head_world, rest_head_local, my_parent, my_channel, my_rot_order)
|
|
|
|
# If we have another child then we can call ourselves a parent, else
|
|
bvh_nodes_serial.append(bvh_node)
|
|
|
|
# Account for an end node
|
|
if file_lines[lineIdx][0].lower() == 'end' and file_lines[lineIdx][1].lower() == 'site': # There is sometimes a name after 'End Site' but we will ignore it.
|
|
lineIdx += 2 # Increment to the next line (Offset)
|
|
rest_tail = Vector((float(file_lines[lineIdx][1]), float(file_lines[lineIdx][2]), float(file_lines[lineIdx][3]))) * global_scale
|
|
|
|
bvh_nodes_serial[-1].rest_tail_world = bvh_nodes_serial[-1].rest_head_world + rest_tail
|
|
bvh_nodes_serial[-1].rest_tail_local = bvh_nodes_serial[-1].rest_head_local + rest_tail
|
|
|
|
# Just so we can remove the Parents in a uniform way- End has kids
|
|
# so this is a placeholder
|
|
bvh_nodes_serial.append(None)
|
|
|
|
if len(file_lines[lineIdx]) == 1 and file_lines[lineIdx][0] == '}': # == ['}']
|
|
bvh_nodes_serial.pop() # Remove the last item
|
|
|
|
if len(file_lines[lineIdx]) == 1 and file_lines[lineIdx][0].lower() == 'motion':
|
|
#print '\nImporting motion data'
|
|
lineIdx += 3 # Set the cursor to the first frame
|
|
break
|
|
|
|
lineIdx += 1
|
|
|
|
# Remove the None value used for easy parent reference
|
|
del bvh_nodes[None]
|
|
# Dont use anymore
|
|
del bvh_nodes_serial
|
|
|
|
bvh_nodes_list = bvh_nodes.values()
|
|
|
|
while lineIdx < len(file_lines):
|
|
line = file_lines[lineIdx]
|
|
for bvh_node in bvh_nodes_list:
|
|
#for bvh_node in bvh_nodes_serial:
|
|
lx = ly = lz = rx = ry = rz = 0.0
|
|
channels = bvh_node.channels
|
|
anim_data = bvh_node.anim_data
|
|
if channels[0] != -1:
|
|
lx = global_scale * float(line[channels[0]])
|
|
|
|
if channels[1] != -1:
|
|
ly = global_scale * float(line[channels[1]])
|
|
|
|
if channels[2] != -1:
|
|
lz = global_scale * float(line[channels[2]])
|
|
|
|
if channels[3] != -1 or channels[4] != -1 or channels[5] != -1:
|
|
|
|
rx = radians(float(line[channels[3]]))
|
|
ry = radians(float(line[channels[4]]))
|
|
rz = radians(float(line[channels[5]]))
|
|
|
|
# Done importing motion data #
|
|
anim_data.append((lx, ly, lz, rx, ry, rz))
|
|
lineIdx += 1
|
|
|
|
# Assign children
|
|
for bvh_node in bvh_nodes.values():
|
|
bvh_node_parent = bvh_node.parent
|
|
if bvh_node_parent:
|
|
bvh_node_parent.children.append(bvh_node)
|
|
|
|
# Now set the tip of each bvh_node
|
|
for bvh_node in bvh_nodes.values():
|
|
|
|
if not bvh_node.rest_tail_world:
|
|
if len(bvh_node.children) == 0:
|
|
# could just fail here, but rare BVH files have childless nodes
|
|
bvh_node.rest_tail_world = Vector(bvh_node.rest_head_world)
|
|
bvh_node.rest_tail_local = Vector(bvh_node.rest_head_local)
|
|
elif len(bvh_node.children) == 1:
|
|
bvh_node.rest_tail_world = Vector(bvh_node.children[0].rest_head_world)
|
|
bvh_node.rest_tail_local = bvh_node.rest_head_local + bvh_node.children[0].rest_head_local
|
|
else:
|
|
# allow this, see above
|
|
#if not bvh_node.children:
|
|
# raise 'error, bvh node has no end and no children. bad file'
|
|
|
|
# Removed temp for now
|
|
rest_tail_world = Vector((0.0, 0.0, 0.0))
|
|
rest_tail_local = Vector((0.0, 0.0, 0.0))
|
|
for bvh_node_child in bvh_node.children:
|
|
rest_tail_world += bvh_node_child.rest_head_world
|
|
rest_tail_local += bvh_node_child.rest_head_local
|
|
|
|
bvh_node.rest_tail_world = rest_tail_world * (1.0 / len(bvh_node.children))
|
|
bvh_node.rest_tail_local = rest_tail_local * (1.0 / len(bvh_node.children))
|
|
|
|
# Make sure tail isnt the same location as the head.
|
|
if (bvh_node.rest_tail_local - bvh_node.rest_head_local).length <= 0.001 * global_scale:
|
|
bvh_node.rest_tail_local.y = bvh_node.rest_tail_local.y + global_scale / 10
|
|
bvh_node.rest_tail_world.y = bvh_node.rest_tail_world.y + global_scale / 10
|
|
|
|
return bvh_nodes
|
|
|
|
|
|
def bvh_node_dict2objects(context, bvh_name, bvh_nodes, rotate_mode='NATIVE', frame_start=1, IMPORT_LOOP=False):
|
|
|
|
if frame_start < 1:
|
|
frame_start = 1
|
|
|
|
scene = context.scene
|
|
for obj in scene.objects:
|
|
obj.select = False
|
|
|
|
objects = []
|
|
|
|
def add_ob(name):
|
|
obj = bpy.data.objects.new(name, None)
|
|
scene.objects.link(obj)
|
|
objects.append(obj)
|
|
obj.select = True
|
|
|
|
# nicer drawing.
|
|
obj.empty_draw_type = 'CUBE'
|
|
obj.empty_draw_size = 0.1
|
|
|
|
return obj
|
|
|
|
# Add objects
|
|
for name, bvh_node in bvh_nodes.items():
|
|
bvh_node.temp = add_ob(name)
|
|
bvh_node.temp.rotation_mode = bvh_node.rot_order_str[::-1]
|
|
|
|
# Parent the objects
|
|
for bvh_node in bvh_nodes.values():
|
|
for bvh_node_child in bvh_node.children:
|
|
bvh_node_child.temp.parent = bvh_node.temp
|
|
|
|
# Offset
|
|
for bvh_node in bvh_nodes.values():
|
|
# Make relative to parents offset
|
|
bvh_node.temp.location = bvh_node.rest_head_local
|
|
|
|
# Add tail objects
|
|
for name, bvh_node in bvh_nodes.items():
|
|
if not bvh_node.children:
|
|
ob_end = add_ob(name + '_end')
|
|
ob_end.parent = bvh_node.temp
|
|
ob_end.location = bvh_node.rest_tail_world - bvh_node.rest_head_world
|
|
|
|
for name, bvh_node in bvh_nodes.items():
|
|
obj = bvh_node.temp
|
|
|
|
for frame_current in range(len(bvh_node.anim_data)):
|
|
|
|
lx, ly, lz, rx, ry, rz = bvh_node.anim_data[frame_current]
|
|
|
|
if bvh_node.has_loc:
|
|
obj.delta_location = Vector((lx, ly, lz)) - bvh_node.rest_head_world
|
|
obj.keyframe_insert("delta_location", index=-1, frame=frame_start + frame_current)
|
|
|
|
if bvh_node.has_rot:
|
|
obj.delta_rotation_euler = rx, ry, rz
|
|
obj.keyframe_insert("delta_rotation_euler", index=-1, frame=frame_start + frame_current)
|
|
|
|
return objects
|
|
|
|
|
|
def bvh_node_dict2armature(context, bvh_name, bvh_nodes, rotate_mode='XYZ', frame_start=1, IMPORT_LOOP=False):
|
|
|
|
if frame_start < 1:
|
|
frame_start = 1
|
|
|
|
# Add the new armature,
|
|
scene = context.scene
|
|
for obj in scene.objects:
|
|
obj.select = False
|
|
|
|
arm_data = bpy.data.armatures.new(bvh_name)
|
|
arm_ob = bpy.data.objects.new(bvh_name, arm_data)
|
|
|
|
scene.objects.link(arm_ob)
|
|
|
|
arm_ob.select = True
|
|
scene.objects.active = arm_ob
|
|
|
|
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
|
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
|
|
|
# Get the average bone length for zero length bones, we may not use this.
|
|
average_bone_length = 0.0
|
|
nonzero_count = 0
|
|
for bvh_node in bvh_nodes.values():
|
|
l = (bvh_node.rest_head_local - bvh_node.rest_tail_local).length
|
|
if l:
|
|
average_bone_length += l
|
|
nonzero_count += 1
|
|
|
|
# Very rare cases all bones couldbe zero length???
|
|
if not average_bone_length:
|
|
average_bone_length = 0.1
|
|
else:
|
|
# Normal operation
|
|
average_bone_length = average_bone_length / nonzero_count
|
|
|
|
# XXX, annoying, remove bone.
|
|
while arm_data.edit_bones:
|
|
arm_ob.edit_bones.remove(arm_data.edit_bones[-1])
|
|
|
|
ZERO_AREA_BONES = []
|
|
for name, bvh_node in bvh_nodes.items():
|
|
# New editbone
|
|
bone = bvh_node.temp = arm_data.edit_bones.new(name)
|
|
|
|
bone.head = bvh_node.rest_head_world
|
|
bone.tail = bvh_node.rest_tail_world
|
|
|
|
# ZERO AREA BONES.
|
|
if (bone.head - bone.tail).length < 0.001:
|
|
if bvh_node.parent:
|
|
ofs = bvh_node.parent.rest_head_local - bvh_node.parent.rest_tail_local
|
|
if ofs.length: # is our parent zero length also?? unlikely
|
|
bone.tail = bone.tail + ofs
|
|
else:
|
|
bone.tail.y = bone.tail.y + average_bone_length
|
|
else:
|
|
bone.tail.y = bone.tail.y + average_bone_length
|
|
|
|
ZERO_AREA_BONES.append(bone.name)
|
|
|
|
for bvh_node in bvh_nodes.values():
|
|
if bvh_node.parent:
|
|
# bvh_node.temp is the Editbone
|
|
|
|
# Set the bone parent
|
|
bvh_node.temp.parent = bvh_node.parent.temp
|
|
|
|
# Set the connection state
|
|
if not bvh_node.has_loc and\
|
|
bvh_node.parent and\
|
|
bvh_node.parent.temp.name not in ZERO_AREA_BONES and\
|
|
bvh_node.parent.rest_tail_local == bvh_node.rest_head_local:
|
|
bvh_node.temp.use_connect = True
|
|
|
|
# Replace the editbone with the editbone name,
|
|
# to avoid memory errors accessing the editbone outside editmode
|
|
for bvh_node in bvh_nodes.values():
|
|
bvh_node.temp = bvh_node.temp.name
|
|
|
|
# Now Apply the animation to the armature
|
|
|
|
# Get armature animation data
|
|
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
|
|
|
pose = arm_ob.pose
|
|
pose_bones = pose.bones
|
|
|
|
if rotate_mode == 'NATIVE':
|
|
for bvh_node in bvh_nodes.values():
|
|
bone_name = bvh_node.temp # may not be the same name as the bvh_node, could have been shortened.
|
|
pose_bone = pose_bones[bone_name]
|
|
pose_bone.rotation_mode = bvh_node.rot_order_str
|
|
|
|
elif rotate_mode != 'QUATERNION':
|
|
for pose_bone in pose_bones:
|
|
pose_bone.rotation_mode = rotate_mode
|
|
else:
|
|
# Quats default
|
|
pass
|
|
|
|
context.scene.update()
|
|
|
|
arm_ob.animation_data_create()
|
|
action = bpy.data.actions.new(name=bvh_name)
|
|
arm_ob.animation_data.action = action
|
|
|
|
# Replace the bvh_node.temp (currently an editbone)
|
|
# With a tuple (pose_bone, armature_bone, bone_rest_matrix, bone_rest_matrix_inv)
|
|
for bvh_node in bvh_nodes.values():
|
|
bone_name = bvh_node.temp # may not be the same name as the bvh_node, could have been shortened.
|
|
pose_bone = pose_bones[bone_name]
|
|
rest_bone = arm_data.bones[bone_name]
|
|
bone_rest_matrix = rest_bone.matrix_local.rotation_part()
|
|
|
|
bone_rest_matrix_inv = Matrix(bone_rest_matrix)
|
|
bone_rest_matrix_inv.invert()
|
|
|
|
bone_rest_matrix_inv.resize4x4()
|
|
bone_rest_matrix.resize4x4()
|
|
bvh_node.temp = (pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv)
|
|
|
|
# Make a dict for fast access without rebuilding a list all the time.
|
|
|
|
# KEYFRAME METHOD, SLOW, USE IPOS DIRECT
|
|
# TODO: use f-point samples instead (Aligorith)
|
|
if rotate_mode != 'QUATERNION':
|
|
prev_euler = [Euler() for i in range(len(bvh_nodes))]
|
|
|
|
# Animate the data, the last used bvh_node will do since they all have the same number of frames
|
|
for frame_current in range(len(bvh_node.anim_data) - 1): # skip the first frame (rest frame)
|
|
# print frame_current
|
|
|
|
# if frame_current==40: # debugging
|
|
# break
|
|
|
|
scene.frame_set(frame_start + frame_current)
|
|
|
|
# Dont neet to set the current frame
|
|
for i, bvh_node in enumerate(bvh_nodes.values()):
|
|
pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv = bvh_node.temp
|
|
lx, ly, lz, rx, ry, rz = bvh_node.anim_data[frame_current + 1]
|
|
|
|
if bvh_node.has_rot:
|
|
# apply rotation order and convert to XYZ
|
|
# note that the rot_order_str is reversed.
|
|
bone_rotation_matrix = Euler((rx, ry, rz), bvh_node.rot_order_str[::-1]).to_matrix().resize4x4()
|
|
bone_rotation_matrix = bone_rest_matrix_inv * bone_rotation_matrix * bone_rest_matrix
|
|
|
|
if rotate_mode == 'QUATERNION':
|
|
pose_bone.rotation_quaternion = bone_rotation_matrix.to_quat()
|
|
else:
|
|
euler = bone_rotation_matrix.to_euler(bvh_node.rot_order_str, prev_euler[i])
|
|
pose_bone.rotation_euler = euler
|
|
prev_euler[i] = euler
|
|
|
|
if bvh_node.has_loc:
|
|
pose_bone.location = (bone_rest_matrix_inv * Matrix.Translation(Vector((lx, ly, lz)) - bvh_node.rest_head_local)).translation_part()
|
|
|
|
if bvh_node.has_loc:
|
|
pose_bone.keyframe_insert("location")
|
|
if bvh_node.has_rot:
|
|
if rotate_mode == 'QUATERNION':
|
|
pose_bone.keyframe_insert("rotation_quaternion")
|
|
else:
|
|
pose_bone.keyframe_insert("rotation_euler")
|
|
|
|
for cu in action.fcurves:
|
|
if IMPORT_LOOP:
|
|
pass # 2.5 doenst have cyclic now?
|
|
|
|
for bez in cu.keyframe_points:
|
|
bez.interpolation = 'LINEAR'
|
|
|
|
return arm_ob
|
|
|
|
|
|
def load(operator, context, filepath="", target='ARMATURE', rotate_mode='NATIVE', global_scale=1.0, use_cyclic=False, frame_start=1):
|
|
import time
|
|
t1 = time.time()
|
|
print('\tparsing bvh %r...' % filepath, end="")
|
|
|
|
bvh_nodes = read_bvh(context, filepath,
|
|
rotate_mode=rotate_mode,
|
|
global_scale=global_scale)
|
|
|
|
print('%.4f' % (time.time() - t1))
|
|
|
|
frame_orig = context.scene.frame_current
|
|
|
|
t1 = time.time()
|
|
print('\timporting to blender...', end="")
|
|
|
|
bvh_name = bpy.path.display_name_from_filepath(filepath)
|
|
|
|
if target == 'ARMATURE':
|
|
bvh_node_dict2armature(context, bvh_name, bvh_nodes,
|
|
rotate_mode=rotate_mode,
|
|
frame_start=frame_start,
|
|
IMPORT_LOOP=use_cyclic)
|
|
|
|
elif target == 'OBJECT':
|
|
bvh_node_dict2objects(context, bvh_name, bvh_nodes,
|
|
rotate_mode=rotate_mode,
|
|
frame_start=frame_start,
|
|
IMPORT_LOOP=use_cyclic)
|
|
|
|
else:
|
|
raise Exception("invalid type")
|
|
|
|
print('Done in %.4f\n' % (time.time() - t1))
|
|
|
|
context.scene.frame_set(frame_orig)
|
|
|
|
return {'FINISHED'}
|