finally got this puppy working … It’s still a work in progress … so excuse the sloppiness…
I’ve been dabbling with bvh files. Initially I had been using bvh from trailerspark which had been converted to what i believe was character studio format that had a standard T-pose as a rest position… Alas someone stopped paying for that site and I hadn’t bothered to download them all… ho hum.
Currently I have to manually apply the Tpose to rest pose after running script… thought I’d post b4 I get my head around that. bpy.ops.pose.apply_armature() works ok but if it’s not in frame 0 it applies the pose from active frame… this context thing for ops is still a great confuser…
Oh you just need to change the Id of the action you want to convert… eg ActionId=“youraction” the other stuff shouldn’t matter it’s only leftover from a bit of copy paste.
import bpy
import mathutils
# Just hardcoding names at the moment
rigId = "BvhRig" # Id of the armature object
ActionId = "BvhRig.003Action.001" # Id of the action to convert
rootBoneId = "Hips" # the rootbone to take travel out from..
ob = bpy.data.objects["BvhRig.003"]
action = bpy.data.actions[ActionId]
print(action.name)
#action.select = True
#bpy.ops.action.clean(threshold=0.01)
frames = action.frame_range.magnitude
# for each key in the action for the hip posebone subtract out the movement.
transformVector = mathutils.Vector((0,0,0))
IdentityQuatro = [1,0,0,0]
quatro = [0,0,0,0]
for fcurve in action.fcurves:
points = fcurve.keyframe_points
i = 1
while i < len(points):
#if it's a rotation key we'll smack it hard.
j = 0
point = points[i]
frame = 0
if fcurve.data_path.find("rotation_quaternion") > 0:
# co gives us frame in 0, value in 1.
frame = point.co[0]
value = point.co[1]
quatro[fcurve.array_index] = value + IdentityQuatro[fcurve.array_index] - points[0].co[1]
#Ok take out the difference from pose mode.
point.co[1] = quatro[fcurve.array_index]
if fcurve.data_path == "pose.bones[\"%s\"].rotation_quaternion"%(rootBoneId):
print("frame:{0} {1}".format(frame,quatro))
i += 1
#To do ... change the rest position to the pose in frame 0
# with bpy.ops.pose.armature_apply() but not sure how to make sure it's in frame 0 otherwise it puts pose in active frame which turns it to crap. Running the script in pose mode at frame 0 and it works a treat with this.
# Either make all quatros in frame 0 to Identity or remove frame 0 to set it back to Tpose.
# Move the Tpose in frame 0 to the origin. Well the Hips i suppose... not a real biggy.
#bpy.ops.pose.armature_apply()
Once again excuse all the crap left in… I’m wanting to put together a more complete bvh addon and will clean it up for that… But that may be a while… thought it may be handy for some.
Oh I forgot to mention. When I was working out that import stuff a bit ago, I got a commission to write a tutorial for a magazine on a Blender topic. And I wound up deciding to write it on importing and re-targeting BVH since it all was fresh on my mind and I thought it would be interesting. Just thought I’d say thanks for the help with that also. It was a combination of my own experiments and your help as well as others that made that possible.
The problem with changing the rest pose is that you are stuck with one set of bvh files. If you want to mix bvh files from different sources, you must modify the f-curves instead. You must also change the scale of the animation, because the hip bone is translated and not just rotated.
MakeHuman’s mocap tool can retarget animations from a number of sources to the MHX rig, with decent although not perfect result:
Motionbuilder: Star pose
3dsmax: star pose mocapdata.com: arms down
Accad female: T-pose
Accad male: just weird, cannot handle
I made my first humanoid model with makehuman 1.9 hehe… did it go commercial for a while after that?..
Cheers for the pointer to the DAZ friendly CMU files… sheesh be good if there was a set at reasonable framerate 120fps is a bit excessive for my needs… yeah i can resample … I live in rural Oz… so my broadband ain’t so broad… or cheap… 8^(.
I just grabbed one bvh 09_09.bvh and it seems a little hip wobbly to me… can you suggest a good sample file… are there any that use the hands or eyes? I’m assuming they would prob have high chapter numbers… otherwise I’m happy to strip them out and go with a hand rig parented to the hand bone… and a head rig parented to the head bone.
I’ve been using the bvh import script you had in your code snippets… Was looking at the makehuman bvh import and thought hmmmm that looks familiar. You had code in there that read the frametime, so i tweaked it with
to match the fps of my file. Prob should incorporate framerate base for completeness.
On my change rest pose script… it changes the fcurves to match the pose in frame 0 as if it was the rest position… be that a Tpose already there or one I pose myself, by taking out the rotational difference from the pose mode. ie the rest pose in pose mode will have quatrot(1,0,0,0) on all joints… so i just change the curves accordingly. It would then be relatively simple to re-target from there for different rigs… (especially since you have done the ground work of defining them all) . but with 2500 mocaps to choose from … I’m happy to stick to one set.
I remember some of the first bvh’s i worked with had a wierd toe touching bent in half twisted restpose… I’ll try and track one down and see how this puppy goes at changing the rest pose on that one.
On translation. I have another script that takes the travel out of bvh motions and retargets the motion to an axis. I use the armature object as a kind of puppet rig that has a linear motion… it’s at this thread http://blenderartists.org/forum/showthread.php?t=204377
Scale? are you talking about bone length in different rigs?
No, the overall size of the character, which depends on the choice of unit. 1 unit = 1 dm in MH, 1 inch at mocapdata.com, 1 inch/0.45 at CMU (nobody seems to know why) and 1 cm (or perhaps 1 m) in Daz. Whereas angles are universally measured in degrees (at least in bvh files), the choice of units affect the translation of the hip bone.
Over at Messiah’s forum SetupTab they have a script to retarget the rotation from a BVH file to an existing messiah rig. Reading through the author’s notes was interesting, and I was wondering if a similar approach might work here…
Here is what Chris says:
In a BVH file, usually the root bone has translation and rotation applied, while the other bones only have rotation applied. The very first expression [of my script] ‘ExpMoveHip’ scales down the translation of the source root bone and applies it to the destination root bone. As a scale ratio, I have taken the relative distances between hip and feet for both rigs (assuming that a character with legs half the original length will move only half the original distances).
The following expressions [in the script] ‘ExpRetarget…’ transfer the rotation of the source bones to the destination bones. The parameters of the ‘EP_Retarget’ function are:
destination bone; the bone the rotation should be applied from
the reference frame for the destination bone; in this example it is frame 0 (more about this later)
source bone; the bone with the original BVH rotation
the reference frame for the source bone; frame 0 in this example (more about this later)
a master control for the destination rig; the rotation of this master control is taken into account when transferring the rotation. So by e.g. rotating the master control around 180 degrees on heading you can make a character walk from left to right rather than walking from right to left.
The reference pose in this example is frame 0 for both the destination and source rig. On this frame, both rigs should be in a comparable pose, taking into account the differences in the rig. As you can see, e.g. the destination rig has the shoulder bones pointing down in their default pose, while the shoulder bones of the source rig are completely straight. By setting the referenc poses, the ‘EP_Retarget’ function now knows that these shoulder rotations should be considered as identical.
Other differences between the rigs are e.g. the position of the legs in the default pose and the usage of pivot nulls in the destination rig.
Using the expressions as given above should let you transfer the basic rotations between rigs
In the end subtracting out euler rotations seems to do the trick.
But first I had to modify the bvh import script … by adding this method
def arange(x):
# make the angle stay within 0 to 2Pi try recursion
if x > radians(360) :
return arange(x-radians(360))
if x < 0 :
return arange(x + radians(360))
return x
And changing these lines of code. At line 222 in the original 2.56\scripts\addons\io_anim_bvh\import_bvh.py
if channels[3] != -1 or channels[4] != -1 or channels[5] != -1:
rx = arange(radians(float(line[channels[3]])))
ry = arange(radians(float(line[channels[4]])))
rz = arange(radians(float(line[channels[5]])))
To restrict the rotations to 0 to 360 degrees… I noticed that some of these mocaps rotations were getting to 5000 degrees. The rotation_quaternion is still in there but it doesn’t always work… produces 180 degree mistakes on some bones… I’ve been investigating a few ways to convert to euler and do the same thing as with eulers with pose bones to no success.
import bpy
import mathutils
#before running
# import bvh .. i suggest using import_euler.
#back up
#select rig
#have the pose you want as rest pose in first frame
#after running
#select the pose in first frame and apply as rest pose.
ob = bpy.context.active_object
action = ob.animation_data.action
print(action.name)
#action.select = True
#bpy.ops.action.clean(threshold=0.01)
frames = action.frame_range.magnitude
# for each key in the action for the hip posebone subtract out the movement.
transformVector = mathutils.Vector((0,0,0))
IdentityQuatro = [1,0,0,0]
quatro = [0,0,0,0]
for fcurve in action.fcurves:
points = fcurve.keyframe_points
i = 1
while i < len(points):
#if it's a rotation key we'll smack it hard.
j = 0
point = points[i]
frame = 0
if fcurve.data_path.find("rotation_quaternion") > 0:
# co gives us frame in 0, value in 1.
#This is buggy with quat rotation..
frame = point.co[0]
value = point.co[1]
quatro[fcurve.array_index] = value +IdentityQuatro[fcurve.array_index]- points[0].co[1]
#Ok take out the difference from pose mode.
point.co[1] = quatro[fcurve.array_index]
if fcurve.data_path.find("rotation_euler") > 0:
# co gives us frame in 0, value in 1.
frame = point.co[0]
value = point.co[1]
transformVector[fcurve.array_index] = value - points[0].co[1]
#Ok take out the difference from pose mode.
point.co[1] = transformVector[fcurve.array_index]
print("processing frame {0}".format(i))
i += 1