BVH workflow.

Just wondering if anyone has any workflow tips for BVH in Blender. Something I have just started looking into. I understand and can do the basic importing, but if you have any tutorials or suggestions for re-targeting and clean up I’d appreciate it. Something I am going to research more but I want to know what others have been doing.

Currently I am looking at using the NLE and Action editor to create a lib of actions and so on that can be mixed and reused. I have some ideas about also cleaning up the BVH to keyframe poses so that they can be more easily incorporated. Since there is a keyframe on every frame the Action Editor and NLE editor slow to a crawl on large clips.

One idea I have played with a little is copying poses from the BVH and pasting them.

And on a basic note, the way I see it, I’t seems to work best if I import a rig and then use that rig on a character. But there is of course the option to import it as empties and re-target the animation to an existing rig. Any tips in this area?

Anyway anyone interested in re-targeting and some of what has inspired this you can take a look at this video on Motion Builder:

http://vimeo.com/13189187

Any ideas, tutorials or WF tips would be great. Thanks.

BVHacker is the first tool you´ll need :smiley:

Personally I tend to cleanup, orientate and make loops with BVHacker, import the BVH to a non-render layer in Blender and leave it be there as it is.
Usually the royalty free files around are rather messy regarding bone roll. I completely ignore that. I add empties, Orient the axis manually, which is done rather quick, and then use a copy location(with offset) and a copy rotation constraint.
Basically I use the BVH rig as a driverrig for the animation rig. Worked quite nice so far for me, although its a dirty method =)

I found mixing BVH files to be rather complicated to get nice results, haven´t tried though in 2.5 yet. All BVH stuff i did was in 2.4.
But I also used the action editor.

Cool! Thank you for the WF tip and thanks for the tip on BVhacker. I ran across that but with your suggestion I’ll DL and have a look!

Thanks.

I concur on using bvhacker… very useful tool. On the version i have it gives the option to use the animazoo naming convention and also adjust the frame rate.

It also pays to do a “clean” on the curves after importing… this will clear out a lot of the action on every frame.

I dicked around for a while between using the bvh generated rig or using IK driven by the bones/empties and went for the “dirty” method of using the generated rig, resized to fit my character then all i have to do is use those actions and whallah off he goes… pretty well out of the box after auto bone weight paint.

Currently the importer in 2.5 is glitchy but there is a pearler in the code snippets.

Anyway here is a go at the workflow i use.

Firstly i use the carnegie melon bvh library… these have already been “normalised” re bone naming and have a T pose for a rest position…

Carnegie motion capture files (Use this like an index… get the file from the trailerpark… (I think you can download the lot somewhere)

Trailer park.

Now open the file in bvhacker. Change the framerate… they are generally much higher than required. In the version of bvhacker i have it has a button “SLify” This aligns the arms to the x direction and changes them to animazoo naming format. Lastly change the start and/or end locations of your hip bone to match your scene.

Now import this rig into blender. In edit mode for the rig carefully resize it fit your mesh, it even pays to have an original rig just to see the proportions and pelvis position etc… I have found that some changes give your animations a bit more character. To move the knees for instance select the the knee node of both knees and scale x to move both knees at same time etc. A bit of fiddling here will pay off as you only have to do this once. For any more mocap simply import a newie and use action editor or NLA to see how it goes.

As i mentioned before … selecting all bones in pose mode then weight painting “automatically assigned to bones” gives a pretty good result out of the box… as usual the better your model eg poles and loops the better it works. Oh and remember to apply mirror modifier on mesh if you have one before doing this or you’ll get a blurk result.

Here is a screen shot of my current mocap rig in action. I’m still chasing triangles around… need to get a better bum loop …

Great info! Thanks.

I already have all of the CMU files but I got them from a guy who made them available for MB.

There is also a data base he has with descriptions of all the poses. That is included in each file you DL.

So far I like those better. Because the arms are in a T after I set the T in Hacker. In the link you gave me, those files have a different T position and are you saying that button puts the arms in a T? I can not find it in my version. There is only a Set T button and it reverts to another pose with the arms down.

Another question I have. Are you saying you can import a BVH into the armature you have already sized?

A bit of fiddling here will pay off as you only have to do this once. For any more mocap simply import a newie and use action editor or NLA to see how it goes.
Thanks for the great tips.

Oh also, I can not seem to get the offset right. I move the hip down to the center of the floor in Hacker and even still it is a little off. When I go between edit mode and object mode in Blender after importing if I don’t set the hip to the floor in Hacker first, it jumps down. So when I fix it that handles it but I can never seem to get it spot on and there is always a little irritating offset from Edit Mode to Object Mode.

Any ideas?

Edit: OK sorry, fixed that. There is XZY button in hacker that sets everything to center. So I am able to get it on the money.

And additionally I found what might be a feature I am not aware of, but something I don’t want. When I hit the XYZ button it turns the mocap into a walk-in-place animation. But I also found that I can enter a numeric value into the slider to get it at 0. So I solved it that way. Wonder what that other thing is about? I suppose if you wanted that it’d be cool.

So far I like those better. Because the arms are in a T after I set the T in Hacker. In the link you gave me, those files have a different T position and are you saying that button puts the arms in a T? I can not find it in my version. There is only a Set T button and it reverts to another pose with the arms down.

I was talking about the rest position T.

And additionally I found what might be a feature I am not aware of, but something I don’t want. When I hit the XYZ button it turns the mocap into a walk-in-place animation. But I also found that I can enter a numeric value into the slider to get it at 0. So I solved it that way. Wonder what that other thing is about? I suppose if you wanted that it’d be cool.

Been thinking about that one too…Wondering whether use XYZ to put them in place … then use just the hip of the original to make a motion path. Would make it easier to “meld” with walkcycles and runcycles… and also adding the path to the scene

Another question I have. Are you saying you can import a BVH into the armature you have already sized?

Exactly… just swap the actions around. Another project I’m considering is pose matching. Find poses in different mocaps to be able to jump from one to the other at designated entry/exit points…

Lastly I’m looking at adding a toe roll … another thing that makes being able to position important.

Thanks for the clarifications. I’ll look into swapping actions. Great tip!

Those last two points you made are exactly where I am going with this.

One other problem:

In BVHacker I can change the frame rate, but when I bring it into Blender it is still very slow and seems to maintain the original sample rate. Basically amounts to way too many keyframes between poses. Like a walk cycle is something on the order of 35 frames from contact to contact and it should be about half that I think.

Any ideas?

EDIT: I mean other than scaling the strip. I’d like to have less keyframes, if at all possible out of Hacker.

What is your blender framerate? what does 35frames come to in seconds?

I am at 24.

But I have this socker kick also. This is ultra slow. Come sout to 800 frames and I slowed it down in the NLA to about 300 and at 24 it is still in slow mo practically. (saved out a screen animation and played it back real time in my NLE) The file is 10_01 I think. Basically just waaaaay to many keys than is even needed by a long shot. Wish there was a way to reduce the sample rate.

Oh Ok. You’re wanting less keyframes… not one for each prop at each frame.
This is where clean frames comes in. Not sure how to do it in 2.5, but in 2.49 when you clean pose ipos, there is a threshold variable… setting it higher will remove frames. . Playing around with that will remove a lot of keyframes and you end up with some really simple curves.

Oh I just had a look and realised… my version of bvhacker has a resample to 20fps. It does exactly what you are after… well at least for 20fps

Yeah, right. You got it. I am aware of the clean keys option in the IPO. I am looking to reduce the sample rate. So in effect I have less frames per pose.

I am using V. 1.7 http://davedub.co.uk/bvhacker/ what version are you using?

Thanks for the tip on resample. That helped me to look further. Funny, in my version there is an up and down arrow option to resample. When you do that, it does nothing. Even though it displays the changed rate, the animation is still paying back at the original rate. That was what I was trying before. But after you responded it made look again and I found a “Resample to 1/2 frames” option in the Edit menu which works and done 2 times brings it to 30 and actually changes the playback sample. Both options show the changed rate in the display but only the Edit option actually works. And that seems to work in Blender as well.

So thanks again!

I had a few versions of bvhacker and ended up reverting to one that had the functionality i needed.

The version with the resample to 20fps is 1.3.1.3. I “upgraded” to 1.5 and there was a resample button there but it shot up the good 'ol “work in progress” alert… so i reverted.

Just had a look at 1.7… there is a resample by half buttton…

Yeah. That is what I started using.

Also getting weird results from layering animation in the NLA over the BVH. I am wondering if that has to do with the rotations of the bones or the Bone Roll. Have you run into this? It works on bones I create in Blender but the imports kind of go wonky.

Richard,

here is a fix to the bvh import script… notably adding the timing based on render.fps multiplied by the frametime of the bvh to give you the correct timing on bvh’s actions.

Enjoy.

#----------------------------------------------------------
# File simple_bvh_import.py
# Simple bvh exporter
#----------------------------------------------------------
import bpy, os, math, mathutils, time
from mathutils import Vector, Matrix

#
#    class CNode:
#

class CNode:
    def __init__(self, words, parent):
        name = words[1]
        for word in words[2:]:
            name += ' '+word
        
        self.name = name
        self.parent = parent
        self.children = []
        self.head = Vector((0,0,0))
        self.offset = Vector((0,0,0))
        if parent:
            parent.children.append(self)
        self.channels = []
        self.matrix = None
        self.inverse = None
        return

    def __repr__(self):
        return "CNode %s" % (self.name)

    def display(self, pad):
        vec = self.offset
        if vec.length < Epsilon:
            c = '*'
        else:
            c = ' '
        print("%s%s%10s (%8.3f %8.3f %8.3f)" % 
            (c, pad, self.name, vec[0], vec[1], vec[2]))
        for child in self.children:
            child.display(pad+"  ")
        return

    def build(self, amt, orig, parent):
        self.head = orig + self.offset
        if not self.children:
            return self.head
        
        zero = (self.offset.length < Epsilon)
        eb = amt.edit_bones.new(self.name)        
        if parent:
            eb.parent = parent
        eb.head = self.head
        tails = Vector((0,0,0))
        for child in self.children:
            tails += child.build(amt, self.head, eb)
        n = len(self.children)
        eb.tail = tails/n
        self.matrix = eb.matrix.rotation_part()
        self.inverse = self.matrix.copy().invert()
        if zero:
            return eb.tail
        else:        
            return eb.head

#
#    readBvhFile(context, filepath, rot90, scale):
#

Location = 1
Rotation = 2
Hierarchy = 1
Motion = 2
Frames = 3

Deg2Rad = math.pi/180
Epsilon = 1e-5

def readBvhFile(context, filepath, rot90, scale):
    fileName = os.path.realpath(os.path.expanduser(filepath))
    (shortName, ext) = os.path.splitext(fileName)
    if ext.lower() != ".bvh":
        raise NameError("Not a bvh file: " + fileName)
    print( "Loading BVH file "+ fileName )

    time1 = time.clock()
    level = 0
    nErrors = 0
    scn = context.scene
            
    fp = open(fileName, "rU")
    print( "Reading skeleton" )
    lineNo = 0
    for line in fp: 
        words= line.split()
        lineNo += 1
        if len(words) == 0:
            continue
        key = words[0].upper()
        if key == 'HIERARCHY':
            status = Hierarchy
        elif key == 'MOTION':
            if level != 0:
                raise NameError("Tokenizer out of kilter %d" % level)    
            amt = bpy.data.armatures.new("BvhAmt")
            rig = bpy.data.objects.new("BvhRig", amt)
            scn.objects.link(rig)
            scn.objects.active = rig
            bpy.ops.object.mode_set(mode='EDIT')
            root.build(amt, Vector((0,0,0)), None)
            #root.display('')
            bpy.ops.object.mode_set(mode='OBJECT')
            status = Motion
        elif status == Hierarchy:
            if key == 'ROOT':    
                node = CNode(words, None)
                root = node
                nodes = [root]
            elif key == 'JOINT':
                node = CNode(words, node)
                nodes.append(node)
            elif key == 'OFFSET':
                (x,y,z) = (float(words[1]), float(words[2]), float(words[3]))
                if rot90:                    
                    node.offset = scale*Vector((x,-z,y))
                else:
                    node.offset = scale*Vector((x,y,z))
            elif key == 'END':
                node = CNode(words, node)
            elif key == 'CHANNELS':
                oldmode = None
                for word in words[2:]:
                    if rot90:
                        (index, mode, sign) = channelZup(word)
                    else:
                        (index, mode, sign) = channelYup(word)
                    if mode != oldmode:
                        indices = []
                        node.channels.append((mode, indices))
                        oldmode = mode
                    indices.append((index, sign))
            elif key == '{':
                level += 1
            elif key == '}':
                level -= 1
                node = node.parent
            else:
                raise NameError("Did not expect %s" % words[0])
        elif status == Motion:
            if key == 'FRAMES:':
                nFrames = int(words[1])
            elif key == 'FRAME' and words[1].upper() == 'TIME:':
                frameTime = bpy.context.scene.render.fps*float(words[2])
                print(frameTime)
                #frameTime = 1
                status = Frames
                frame = 0
                t = 0
                bpy.ops.object.mode_set(mode='POSE')
                pbones = rig.pose.bones
                for pb in pbones:
                    pb.rotation_mode = 'QUATERNION'
        elif status == Frames:
            addFrame(words, frame, nodes, pbones, scale)
            t += frameTime
            frame += frameTime
           
    fp.close()
    time2 = time.clock()
    print("Bvh file loaded in %.3f s" % (time2-time1))
    return rig

#
#    channelYup(word):
#    channelZup(word):
#

def channelYup(word):
    if word == 'Xrotation':
        return ('X', Rotation, +1)
    elif word == 'Yrotation':
        return ('Y', Rotation, +1)
    elif word == 'Zrotation':
        return ('Z', Rotation, +1)
    elif word == 'Xposition':
        return (0, Location, +1)
    elif word == 'Yposition':
        return (1, Location, +1)
    elif word == 'Zposition':
        return (2, Location, +1)

def channelZup(word):
    if word == 'Xrotation':
        return ('X', Rotation, +1)
    elif word == 'Yrotation':
        return ('Z', Rotation, +1)
    elif word == 'Zrotation':
        return ('Y', Rotation, -1)
    elif word == 'Xposition':
        return (0, Location, +1)
    elif word == 'Yposition':
        return (2, Location, +1)
    elif word == 'Zposition':
        return (1, Location, -1)

#
#    addFrame(words, frame, nodes, pbones, scale):
#

def addFrame(words, frame, nodes, pbones, scale):
    m = 0
    for node in nodes:
        name = node.name
        try:
            pb = pbones[name]
        except:
            pb = None
        if pb:
            for (mode, indices) in node.channels:
                if mode == Location:
                    vec = Vector((0,0,0))
                    for (index, sign) in indices:
                        vec[index] = sign*float(words[m])
                        m += 1
                    pb.location = node.inverse * (scale * vec - node.head)                
                    for n in range(3):
                        pb.keyframe_insert('location', index=n, frame=frame, group=name)
                elif mode == Rotation:
                    mats = []
                    for (axis, sign) in indices:
                        angle = sign*float(words[m])*Deg2Rad
                        mats.append(Matrix.Rotation(angle, 3, axis))
                        m += 1
                    mat = node.inverse * mats[0] * mats[1] * mats[2] * node.matrix
                    pb.rotation_quaternion = mat.to_quat()
                    for n in range(4):
                        pb.keyframe_insert('rotation_quaternion',
                                           index=n, frame=frame, group=name)
    return

#
#    initSceneProperties(scn):
#

def initSceneProperties(scn):
    bpy.types.Scene.MyBvhRot90 = bpy.props.BoolProperty(
        name="Rotate 90 degrees", 
        description="Rotate the armature to make Z point up")
    scn['MyBvhRot90'] = True

    bpy.types.Scene.MyBvhScale = bpy.props.FloatProperty(
        name="Scale", 
        default = 1.0,
        min = 0.01,
        max = 100)
    scn['MyBvhScale'] = 1.0

initSceneProperties(bpy.context.scene)

#
#    class BvhImportPanel(bpy.types.Panel):
#

class BvhImportPanel(bpy.types.Panel):
    bl_label = "BVH import"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"

    def draw(self, context):
        self.layout.prop(context.scene, "MyBvhRot90")
        self.layout.prop(context.scene, "MyBvhScale")
        self.layout.operator("object.LoadBvhButton")

#
#    class OBJECT_OT_LoadBvhButton(bpy.types.Operator):
#

class OBJECT_OT_LoadBvhButton(bpy.types.Operator):
    bl_idname = "OBJECT_OT_LoadBvhButton"
    bl_label = "Load BVH file (.bvh)"

    filepath = bpy.props.StringProperty(name="File Path", 
        maxlen=1024, default="")

    def execute(self, context):
        import bpy, os
        readBvhFile(context, self.properties.filepath, 
            context.scene.MyBvhRot90, context.scene.MyBvhScale)
        return{'FINISHED'}    

    def invoke(self, context, event):
        context.window_manager.add_fileselect(self)
        return {'RUNNING_MODAL'}

Hey, thanks for that!

What I found myself doing was simply finding the ideal frame rate in BVHacker, 15 frms seems to work fine. Then I import into Blender and scale it up. That way there is a key on about every other frame. 7.5 works reasonably well also giving roughly a key on every 3 frames. Either one is considerably more stable and requires little if any clean up.

Yep that is what I did in the script.

The original version just put each frame from the bvh into successive frames in blender. The bvh file has its frametime variable… say 0.1 for 10 frames per second. I just multiplied this by the scenes fps, for example 24… and then put keys down every 2.4 frames.

Nice!

Thanks.

By the way, thought I’d bump this. I wound up writing a tutorial on this for a magazine. While I was researching this I wound up getting the commission to write a tutorial for Blender on a topic of my choice and since I was just working though this I figured it would be a great subject.

So thanks for all who contributed and helped me get this sorted. :slight_smile:

When and where can we see this article, Richard?

I believe this one will be in the Jan 15, 2011 issue of 3D Artist Magazine. I am going to try and see if I can get a news announcement on it. They have a series called Q&A. I am one of the “Experts”, a very pretentious title given my experience, but that is the gig. It is a question followed by a 3 part answer. It will be a one page article. Also I have another coming up and that will be a 2 pager on layered textures. I think that will go into the Feb issue.