[Bf-extensions-cvs] SVN commit: /data/svn/bf-extensions [4389] trunk/py/scripts/addons/ io_anim_bvh: patch [#34683] BVH Importer, Faster import and option to use BVH animation timing
Campbell Barton
ideasman42 at gmail.com
Tue Mar 19 00:37:31 CET 2013
Revision: 4389
http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-extensions&revision=4389
Author: campbellbarton
Date: 2013-03-18 23:37:31 +0000 (Mon, 18 Mar 2013)
Log Message:
-----------
patch [#34683] BVH Importer, Faster import and option to use BVH animation timing
from Luke Tokheim (luketokheim)
Modified Paths:
--------------
trunk/py/scripts/addons/io_anim_bvh/__init__.py
trunk/py/scripts/addons/io_anim_bvh/import_bvh.py
Modified: trunk/py/scripts/addons/io_anim_bvh/__init__.py
===================================================================
--- trunk/py/scripts/addons/io_anim_bvh/__init__.py 2013-03-18 19:16:18 UTC (rev 4388)
+++ trunk/py/scripts/addons/io_anim_bvh/__init__.py 2013-03-18 23:37:31 UTC (rev 4389)
@@ -80,6 +80,13 @@
description="Starting frame for the animation",
default=1,
)
+ use_fps_scale = BoolProperty(
+ name="Scale FPS",
+ description=("Scale the framerate from the BVH to "
+ " the current scenes, Otherwise each "
+ "BVH frame maps directly to a Blender frame"),
+ default=False,
+ )
use_cyclic = BoolProperty(
name="Loop",
description="Loop the animation playback",
Modified: trunk/py/scripts/addons/io_anim_bvh/import_bvh.py
===================================================================
--- trunk/py/scripts/addons/io_anim_bvh/import_bvh.py 2013-03-18 19:16:18 UTC (rev 4388)
+++ trunk/py/scripts/addons/io_anim_bvh/import_bvh.py 2013-03-18 23:37:31 UTC (rev 4389)
@@ -113,6 +113,7 @@
bvh_nodes = {None: None}
bvh_nodes_serial = [None]
+ bvh_frame_time = None
channelIndex = -1
@@ -197,9 +198,22 @@
if len(file_lines[lineIdx]) == 1 and file_lines[lineIdx][0] == '}': # == ['}']
bvh_nodes_serial.pop() # Remove the last item
+ # End of the hierarchy. Begin the animation section of the file with
+ # the following header.
+ # MOTION
+ # Frames: n
+ # Frame Time: dt
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
+ lineIdx += 2 # Read frame rate.
+
+ if (len(file_lines[lineIdx]) == 3 and
+ file_lines[lineIdx][0].lower() == 'frame' and
+ file_lines[lineIdx][1].lower() == 'time:'):
+
+ bvh_frame_time = float(file_lines[lineIdx][2])
+
+ lineIdx += 1 # Set the cursor to the first frame
+
break
lineIdx += 1
@@ -277,7 +291,7 @@
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
+ return bvh_nodes, bvh_frame_time
def bvh_node_dict2objects(context, bvh_name, bvh_nodes, rotate_mode='NATIVE', frame_start=1, IMPORT_LOOP=False):
@@ -346,10 +360,12 @@
def bvh_node_dict2armature(context,
bvh_name,
bvh_nodes,
+ bvh_frame_time,
rotate_mode='XYZ',
frame_start=1,
IMPORT_LOOP=False,
global_matrix=None,
+ use_fps_scale=False,
):
if frame_start < 1:
@@ -464,6 +480,7 @@
# Replace the bvh_node.temp (currently an editbone)
# With a tuple (pose_bone, armature_bone, bone_rest_matrix, bone_rest_matrix_inv)
+ num_frame = 0
for bvh_node in bvh_nodes_list:
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]
@@ -477,50 +494,97 @@
bone_rest_matrix.resize_4x4()
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.
+ if 0 == num_frame:
+ num_frame = len(bvh_node.anim_data)
- # 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))]
+ # Choose to skip some frames at the beginning. Frame 0 is the rest pose
+ # used internally by this importer. Frame 1, by convention, is also often
+ # the rest pose of the skeleton exported by the motion capture system.
+ skip_frame = 1
+ if num_frame > skip_frame:
+ num_frame = num_frame - skip_frame
- # 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
+ # Create a shared time axis for all animation curves.
+ time = [float(frame_start)] * num_frame
+ if use_fps_scale:
+ dt = scene.render.fps * bvh_frame_time
+ for frame_i in range(1, num_frame):
+ time[frame_i] += float(frame_i) * dt
+ else:
+ for frame_i in range(1, num_frame):
+ time[frame_i] += float(frame_i)
- # if frame_current==40: # debugging
- # break
+ #print("bvh_frame_time = %f, dt = %f, num_frame = %d"
+ # % (bvh_frame_time, dt, num_frame]))
- scene.frame_set(frame_start + frame_current)
+ for i, bvh_node in enumerate(bvh_nodes_list):
+ pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv = bvh_node.temp
- # Dont neet to set the current frame
- for i, bvh_node in enumerate(bvh_nodes_list):
- 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_loc:
+ # Not sure if there is a way to query this or access it in the
+ # PoseBone structure.
+ data_path = 'pose.bones["%s"].location' % pose_bone.name
- if bvh_node.has_rot:
+ location = [(0.0, 0.0, 0.0)] * num_frame
+ for frame_i in range(num_frame):
+ bvh_loc = bvh_node.anim_data[frame_i + skip_frame][:3]
+
+ bone_translate_matrix = Matrix.Translation(
+ Vector(bvh_loc) - bvh_node.rest_head_local)
+ location[frame_i] = (bone_rest_matrix_inv *
+ bone_translate_matrix).to_translation()
+
+ # For each location x, y, z.
+ for axis_i in range(3):
+ curve = action.fcurves.new(data_path=data_path, index=axis_i)
+ keyframe_points = curve.keyframe_points
+ keyframe_points.add(num_frame)
+
+ for frame_i in range(num_frame):
+ keyframe_points[frame_i].co = \
+ (time[frame_i], location[frame_i][axis_i])
+
+ if bvh_node.has_rot:
+ data_path = None
+ rotate = None
+
+ if 'QUATERNION' == rotate_mode:
+ rotate = [(1.0, 0.0, 0.0, 0.0)] * num_frame
+ data_path = ('pose.bones["%s"].rotation_quaternion'
+ % pose_bone.name)
+ else:
+ rotate = [(0.0, 0.0, 0.0)] * num_frame
+ data_path = ('pose.bones["%s"].rotation_euler' %
+ pose_bone.name)
+
+ prev_euler = Euler((0.0, 0.0, 0.0))
+ for frame_i in range(num_frame):
+ bvh_rot = bvh_node.anim_data[frame_i + skip_frame][3:]
+
# 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().to_4x4()
- bone_rotation_matrix = bone_rest_matrix_inv * bone_rotation_matrix * bone_rest_matrix
+ euler = Euler(bvh_rot, bvh_node.rot_order_str[::-1])
+ bone_rotation_matrix = euler.to_matrix().to_4x4()
+ 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_quaternion()
+ if 4 == len(rotate[frame_i]):
+ rotate[frame_i] = bone_rotation_matrix.to_quaternion()
else:
- euler = bone_rotation_matrix.to_euler(pose_bone.rotation_mode, prev_euler[i])
- pose_bone.rotation_euler = euler
- prev_euler[i] = euler
+ rotate[frame_i] = bone_rotation_matrix.to_euler(
+ pose_bone.rotation_mode, prev_euler)
+ prev_euler = rotate[frame_i]
- if bvh_node.has_loc:
- pose_bone.location = (bone_rest_matrix_inv * Matrix.Translation(Vector((lx, ly, lz)) - bvh_node.rest_head_local)).to_translation()
+ # For each Euler angle x, y, z (or Quaternion w, x, y, z).
+ for axis_i in range(len(rotate[0])):
+ curve = action.fcurves.new(data_path=data_path, index=axis_i)
+ keyframe_points = curve.keyframe_points
+ curve.keyframe_points.add(num_frame)
- 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 frame_i in range(0, num_frame):
+ keyframe_points[frame_i].co = \
+ (time[frame_i], rotate[frame_i][axis_i])
for cu in action.fcurves:
if IMPORT_LOOP:
@@ -545,19 +609,24 @@
use_cyclic=False,
frame_start=1,
global_matrix=None,
+ use_fps_scale=False,
):
import time
t1 = time.time()
print('\tparsing bvh %r...' % filepath, end="")
- bvh_nodes = read_bvh(context, filepath,
+ bvh_nodes, bvh_frame_time = read_bvh(context, filepath,
rotate_mode=rotate_mode,
global_scale=global_scale)
print('%.4f' % (time.time() - t1))
- frame_orig = context.scene.frame_current
+ scene = context.scene
+ frame_orig = scene.frame_current
+ fps = scene.render.fps
+ if bvh_frame_time is None:
+ bvh_frame_time = 1.0 / scene.render.fps
t1 = time.time()
print('\timporting to blender...', end="")
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-extensions-cvs
mailing list