Previous Blog Next Blog
Prev/Next Blog
by date

Fun with BVH Files, in Torque.

Fun with BVH Files, in Torque.
Name:Chris Calef
Date Posted:May 14, 2008
Rating:5.0 out of 5
Public:YES
Comments:YES
RSS Feed:GarageGames Blog feedor Subscribe with .
Profile Page:View profile page for Chris Calef

Blog post


WARNING: this blog is rather long and technical, and very heavy on animated gifs! Feel free to cruise through it and just look at the pictures. Or if you just want the code, skip to (HERE). I am including gratuitous explanations in the pages below, in the hopes that it might assist some artists or programmers to have a better understanding of how shapes, skeletons, and sequences work in Torque. Take from it what you will.

This resource is not guaranteed to work, and the user should download it at his own risk. Chris Calef is in no way responsible for anything that might happen as a result of downloading this resource.

----------------------------------------------------------------------------------------

So, anyway, a few weeks ago, an acquaintance dropped a three-letter acronym on me that momentarily changed my life and transformed me into a single-minded, obsessed, fanatically driven introvert. (Okay, so it wasn't that big of a change. My friends didn't really notice.) He said:

"Hey, do you know anything about bvh format?"



I'd had experience with motion capture before and knew there was a lot of data out there -- but I've been busy doing other things for the past couple of years. I have been interested in the engine side of character animation for a long time, though, and wanted to hone my skills a bit in that department anyway.

But as an indie developer, I also just want an easy, cheap or preferably free way to make use of the huge variety of bvh files that are out there on the internet, without buying or even having to learn to use any major art applications.

Call me lazy, I guess, but I'm a coder. I hate using GUIs.

I do also have the impression, though, that even with all the latest tools and techniques, it isn't always easy for artists to convert an animation that was made on one skeleton into a sequence that works with their particular model. The results of failure sometimes aren't pretty.




Well, I'm happy to report, mission accomplished! Some time later, I emerged from my lair with a couple of big shiny new ShapeBase functions that do everything I asked for. Now all I have to do is download a bvh, load up a player, type "$myPlayer.importBVH(coolNewAnimation);" at the console, and voila!



Well, okay, I'm exaggerating just a little. There are a couple of other steps you have to take. But once you get it set up, it's just about that easy.

Just so we're all on the same page, here's what a typical BVH file looks like:



It's a really simple, plain text format that is quite easy to parse. The skeleton is defined at the front of the file, and then the rest of the file is filled with all the frames data. Each line of frames data represents one frame, and all the rotations and translations for all the bodyparts (there can't be any left out) are stuffed into that one line, separated by spaces. Like this:



As you can see, it makes for pretty long lines.

One complication that became immediately evident was the fact that not all the files agreed on whether nodes should have six channels or three. The most efficient and logical method would be to include translation information for the base node, and then only rotations for everything else, since each node has already defined its offset with regard to its parent, and that's all you really need to know.



However, many sources of motion capture data seem to be less than perfectly efficient and logical, and include all six channels for every bodypart. I even found one bvh file that had NINE, adding scale to the end. (It was always "1 1 1", but it was good to know it was there, just in case.)

Also, many files had a lot of float garbage in them (numbers that should have been zero coming out like "-8.88178e-016"). This may not be terminal, depending on how you're scanning the data, but it's just ugly and I wanted it gone before I even started thinking about making these things work with my character.

So, I added another little ShapeBase function: "$myPlayer.cleanupBVH(coolNewAnimation);". What it does is loads up the entire bvh file (in this case "coolNewAnimation.bvh"), changes float garbage to zero, changes channels to three, deletes all the node translation data except for the top node, and then writes it all back out to "coolNewAnimation.new.bvh". It also cuts everything back to a reasonable number of decimal places, like "4.356" instead of "4.3564786755". These files are plain ascii text, and are pretty darn big anyway; I found that just by chopping off that unnecessary resolution, I could drop the file size by a lot.



So, cleaned up, shrunken down and reasonably efficient motion files in hand, the next step to make this work was to get it into a Torque character. For this, I started with a handy character from BrokeAss Games, who was rigged with globally aligned local bodypart orientations and hence was a convenient testing ground. (If you have no idea what that last sentence meant... stick with me, all will soon be made clear.)



This is where the rubber really hits the road, because the main problem people have with using other people's animations (and this is true whether we're talking about dsq files or bvh files or anything else) is the fact that all models do not share the same skeleton. Even though all normal humans may have two arms, two legs and a head, there is no protocol in place to define whether the right upper leg is called RThigh or RUpLeg or right_femur or what. It's entirely up to the artist, and this is the first of our problems. Often this problem alone keeps people from being able to share animations.

It gets much worse than lack of naming conventions, however. Even if we had two skeletons with exactly the same bodyparts, with the same names, defined in exactly the same order, the real stickler is going to come when we look at how the geometry itself is aligned with the world. When an artist is making a character, they might decide that the right upper leg could be represented by a cylinder, which they will then modify and attach to the rest of the body. This cylinder will be created by their modeling application on some initial axis (like, horizontally on the X axis), and they will then rotate it around to the position they want that leg to be in, and attach it to the body.




This is all well and good, but this means that when we want to rotate this bodypart, we have to be aware that it has local axes that differ from the world. For the right upper leg, the direction that the world calls "down", or negative Z in Torque, might actually be positive X.

This problem right here is why you can't just past rotations from one model into another without them looking really stupid. Working around it can be kind of a brain twister. I had to solve this problem once before for the ragdoll pack, though, so I wasn't intimidated this time.




The solution I came up with was to address all of the above problems in one little plain text config file. Often one config file for a model will work with a whole set of related bvh files, but I set it up so you can make one config file per model per animation, to cover all possibilities. You simply name your cfg file after a particular motion (if you have a "frontkick.bvh" to import, then name the config file "frontkick.cfg") and keep it in the same directory as your bvh files for this model. If the system doesn't find a cfg file named after this motion, it then looks for one named "default.cfg" in the same directory.

Default.cfg for Kork came out looking like this:




The first column of numbers is the index of the bones in the bvh skeleton, mapped to the second column which are the corresponding nodes in the dts model. All the rest of the numbers are Euler vectors that represent rotations.

Along the way here I also added a few handy console methods, just to get a better look at my models and sequences. One of them, "$myPlayer.showNodes();" prints out the list of all the nodes in this shape, with their IDs. It makes the mapping part a little easier.

In this case, I had the following skeletons to match up:

KORK


BVH


The reason I started with the BrokeAss model instead of Kork is because when you look at him in ShowTool, you find that in his default position, standing straight up with his arms held straight out, all of his bodyparts have local axes that exactly match the global axes. With Kork, as with most other character models, this is most definitely not the case.

This is only our first problem, however. When it comes to node rotations, there are really two separate issues to deal with. Besides our local node transforms, we also have to take into account the fact that our models may not be starting in the same position, either! My BVH character is defined with his arms down at his sides, but even my friend the BrokeAss Guy starts his life with his arms straight out. This means that even though a Y axis rotation might mean the same thing to both characters, a ninety degree rotation for the BVH guy lifts his arm from down to straight out, while the same rotation on the game character raises his arm from straight out to straight up over his head!

When we get to Kork, he's even worse -- he defaults to "standing holding a weapon" where none of his limbs are aligned with any axis at all.




So, let's take a look at that cfg file for Kork again:


0;0;(-0.0674,-86.4207,-0.0969);(0.0,90.0,0.0);(0.0,-90.0,0.0);(0.0,0.0,0.0);
1;18;(11.7373,-168.9883,-27.6818);(0.0,180.0,0.0);(0.0,90.0,0.0);(0.0,0.0,0.0);
2;19;(0.0,0.0,-44.9645);(0.0,0.0,0.0);(0.0,90.0,0.0);(0.0,0.0,0.0);


After the node mapping, there are four sets of Euler vectors (rotations about X,Y,Z). The first two are for putting the body physically into the position of the bvh character (arms down), and the second two are for dealing with the local rotations of the nodes once we get into this position.




That file's pretty ugly, though, so let's go back to the easier one. Recall that our BrokeAss human has his arms out, and all I need to do is to move his arms down in order to match my bvh position:


10;30;(0.0,90.0,0.0);(0.0,0.0,0.0);(0.0,-90.0,0.0);(0.0,0.0,0.0);
11;31;(0.0,0.0,0.0);(0.0,0.0,0.0);(0.0,-90.0,0.0);(0.0,0.0,0.0);
12;32;(0.0,0.0,0.0);(0.0,0.0,0.0);(0.0,-90.0,0.0);(0.0,0.0,0.0);


How about that? What we're looking at right there is the left shoulder, elbow, and wrist. Since the local axes are all global, and our character has Z up and Y forward, then all I have to do is rotate his shoulder 90 degrees on the Y and it will make his arm point down to the ground instead of out to his left.

This is all good, but what happens to our local axes when we do that? Suddenly our left shoulder, forearm and hand, which used to be pointing toward toward negative X, are now pointing toward negative Z! Oh no! What do we do?

That's where the last two numbers come in. No matter where we end up, it is always going to be possible to rotate our frame of reference back to the global, in two if not in one set of X,Y,Z rotations. (It's possible in one, but for humans it's sometimes much easier to use two, so I included that option. It turned out to be a very good idea, as we'll see shortly when we get back to Kork.) For our BrokeAss human's arms, then, we simply take the reverse of the ninety degree Y rotation we did at the shoulder, and lo and behold, everything works!

For those who are interested, the actual transform process looked like this:


m.identity();              //Start with an identity matrix.
m.mul(matBvhPose); //move this bodypart from its default pose to match the bvh pose.
m.mul(matAxesFix); //then temporarily move it back to get the local axes matching global.
m.mul(matY); //then do the bvh rotations
m.mul(matX); // ...
m.mul(matZ); // ...
m.mul(matAxesUnfix); //and then rotate by the _inverse_ of that temporary transform,
//to take it back out. Voila!


This wasn't too hard with the BrokeAss guy, since all I had to do was rotate his arms, but what about Kork? What are we supposed to do with a guy who not only has a huge mess of local transforms, but then he's standing there holding a crossbow instead of politely keeping his arms out?

Well, this could be nasty, but it turned out it wasn't all that horribly difficult, either. All you need to know is that Torque has a lot of useful quaternion and matrix functions, and my current favorite is called MatrixF::toEuler(). What it means is if you have a matrix representing your current position, and you take its inverse (which brings you back to the global frame of reference), and then you call toEuler() on that inverse, what you end up with is three magic numbers that you can plug right into my config file. Weird starting position, gone!

In code, it gets slightly complicated by the fact that we have to go from Quat16 to QuatF to MatrixF in order to make it work, but that's not so hard:


q16 = kShape->defaultRotations[2];
q16.getQuatF(&myQuat);
myQuat.inverse();
myQuat.setMatrix(&myMatrix);
myEuler = myMatrix.toEuler();


Unfortunately, if you have no starting position at all, what you get is this:



And that's why I'm glad I gave myself two Eulers per operation instead of only one. Now that I'm back to null, it's pretty easy to rotate Kork's bodyparts around with 90s and 180s until he's standing up with his arms down:


0;0;(-0.0674,-86.4207,-0.0969);(0.0,90.0,0.0);(0.0,-90.0,0.0);(0.0,0.0,0.0);
1;18;(11.7373,-168.9883,-27.6818);(0.0,180.0,0.0);(0.0,90.0,0.0);(0.0,0.0,0.0);
2;19;(0.0,0.0,-44.9645);(0.0,0.0,0.0);(0.0,90.0,0.0);(0.0,0.0,0.0);


When trying to visualize all these rotations, it can help to draw yourself a picture (see below, sorry for the sloppy artwork.) This is Kork with his back to us (so we're all facing the same direction), if he was standing with his arms out. Globally the X axis (red) goes to our right, Z (blue) is up, and Y (green) should go into the page.




And that's really the process, in a nutshell. Once all the frames are loaded and suitably processed, my importBVH function simply adds a sequence to our TSShape, sets it up with whatever flags it needs, and adds all of our new rotations and node[0] translations to the shape's nodeTranslations and nodeRotations lists. (If you have a Torque license and want to see the actual code, go (HERE)).

And with that, I believe I will draw this lengthy and perhaps overly detailed blog to a well-deserved conclusion. In the tradition of free motion capture files on the internet and many years of great advice and free stuff for Torque, I'm just going to throw this little project out there gratis as a "thank you" to everybody who's ever helped me figure something out. Have fun, and make awesome games!!


<shameless plug>
I am available for contract work, however, so feel free to get in touch with me if you need help!
chris (dot) calef (at) gmail (dot) com
<end plug>




Recent Blog Posts
List:05/14/08 - Fun with BVH Files, in Torque.
03/12/08 - Hello, Goodbye, Israel, and LSystems
04/30/07 - The Grand Canyon is really, really, really big.
12/07/05 - Torque Build Environment (TBE) Changes Course
09/13/05 - Ragdoll Pack Gets Multiple Character Support
04/08/05 - Ragdoll Pack Is SHIPPED!!
02/24/05 - Ragdoll Pack On The Way
01/29/05 - Ragdoll Pack Preview

Submit ResourceSubmit your own resources!

Ross \"Boaz\" Leland   (May 14, 2008 at 07:35 GMT)
The link to view the game/source is dead.

Chris Calef   (May 14, 2008 at 07:41 GMT)
You have to be a licensed Torque owner to access the code, did it die on the way to the forum post or once you were in there?

Phil Carlisle   (May 14, 2008 at 07:42 GMT)
The "here" links dont work Chris, excellent discussion though, I really enjoyed reading it. Although the dancing orc in the last gif is quite worrying :)

Chris Calef   (May 14, 2008 at 07:43 GMT)
Ha ha. Yes, I did mean to disturb you with that. Kork does ballet, who knew?

Can anyone but me get the links to work yet?
Edited on May 14, 2008 07:45 GMT

Joseph Greenawalt   (May 14, 2008 at 08:28 GMT)   Resource Rating: 5
Great work Chris :-)

The links are working fine for me here.

Phil Carlisle   (May 14, 2008 at 08:45 GMT)
Ah, probably wasnt logged in on my other machine. Links seem to work fine now.

J.C. Smith   (May 14, 2008 at 09:21 GMT)   Resource Rating: 5
Good stuff Chris. Very useful.

Michael Perry   (May 14, 2008 at 12:51 GMT)
Awesome, awesome work man. So awesome, in fact:



Aun Arinyasak   (May 14, 2008 at 13:01 GMT)
Thanks, for your great work.

Rex   (May 14, 2008 at 13:38 GMT)
Heh-Heh-Heh...nice, ;), Chris. I like building rigs with text files, too....lol, GUI's, m'eh; just can't get them as accurate as I'd like[give me an entry field anyday!]! I have known of the value of the local zero coordinates for a bit and the ease of how I can construct differing shapes and jam the motions into them; albeit with a rexySized hammer...LOL. Elegant refinement!

Concise, well documented! ...being an artist primarily, I can work the programs; just can't essplane the math, lol! Glad there is someone like you, who can, and make it legible...

Anthony Rosenbaum   (May 14, 2008 at 13:48 GMT)
Nice Chris!

Dunsany   (May 14, 2008 at 14:59 GMT)   Resource Rating: 5
Very cool.

Cliff   (May 14, 2008 at 15:10 GMT)   Resource Rating: 5
Truly awesome stuff.

David Montgomery-Blake   (May 14, 2008 at 15:19 GMT)
This definitely falls under the wicked sweet category!

Chris Calef   (May 14, 2008 at 15:36 GMT)
Hey, thanks, glad you all like it! A couple things I forgot to mention last night:

1) There's a great little free program called bvhacker (http://davedub.co.uk/bvhacker/) which was primarily geared toward getting animations into Second Life, but it's very useful for pre-processing your files and picking out the part of the animation you want to use. It lets you crop a file and save out the part you want, and it also solves some of the problems I mentioned above. It does break if exposed to a 6 Channels bvh file, though, which is why I had to write that myself.

2) People probably already have sources, but I got most of what I was working with from mocapdata.com, as well as bvhfiles.com.

3) The only known problem that I didn't have time to fix yet in the above project is with bvh files that have extra nodes that aren't in the dts model. It didn't come up for me until the very end of the project, and I just didn't have time to address it, but it would be an easy fix. Particularly with asf/acm files that have been converted to bvh, there are a number of dummy nodes that would get in the way right now.

4) On that note, however, there is also a sweet little program out there called amc2bvh (http://vipbase.net/amc2bvh/) which does what you might expect. This is especially nice to know because there is a GIANT library of human motion in asf/acm format, available all for free, courtesy of Carnegie Mellon University: http://mocap.cs.cmu.edu/.

If anyone takes the time to fix that little glitch so we can easily skip nodes, please email me and I'll include your fix in the resource.
Edited on May 14, 2008 16:36 GMT

Guy Allard   (May 14, 2008 at 15:53 GMT)
That is some awesome work.

How tricky would it be to output a .dsq for the animation sequence once it's been imported, so that the import doesn't need to be performed every time a .bvh is loaded?

Michael Perry   (May 14, 2008 at 15:56 GMT)
@Guy -

$MyPlayer.saveOut("myNewAnim");


It currently saves out all the sequences to a single DSQ, but looking at the code right now it would probably not take long to implement a single sequence export...like Chris said.

Chris Calef   (May 14, 2008 at 16:17 GMT)
Yeah, that is another thing that is definitely on my list, and for which I'd be happy to accept submissions. It's not extremely difficult, just a little time consuming. Basically you would have to go through all the rotation, translation, trigger etc. arrays in the shape, and grab all that are related to this sequence. I've been wanting to do some sequence management tools like move and delete, too, but these also require changing all the baseRotation, baseTranslation, etc. values for all the sequences that follow the one in question.

So many things to do, so little time, but I hope this project gets some people started.
Edited on May 14, 2008 16:17 GMT

Deborah Marshall   (May 14, 2008 at 17:23 GMT)   Resource Rating: 5
I feel like dancing after reading all this.

Michael Perry   (May 14, 2008 at 17:35 GMT)
@Deborah - You can dance if you want to...You can leave your friends behind...

<song>'Cause your friends don't dance and if they don't dance, well they're no friends of mine</song>

Sorry, couldn't resists =)

Chris Calef   (May 14, 2008 at 17:45 GMT)
You can dance all you want, just make sure you put your suit on and send me the capture files when you're done!

asmaloney (Andy)   (May 14, 2008 at 18:04 GMT)
Nice post Chris - looks like an awesome tool for the toolbox!

Jondo   (May 14, 2008 at 20:03 GMT)
Can you make me a GUI for this? :)

Chris, two things:

1. Awesome Work!
2. UR killin' me...

Naw, seriously this goes to show something we have been saying at the BAG table for years now:

BVH OWNZ U!!!1

Oh, and I hosted those videos you sent of the above epicness... animated GIF's? You fans deserve better!


kork_bvh_ballet
male_bvh_walksneak
male_bvh_zombie

Michael Perry   (May 14, 2008 at 20:09 GMT)
@Jondo - Your videos are borked my friend =(

Chris Calef   (May 14, 2008 at 20:21 GMT)
Ha ha, you see why I post only gifs... avis always break for somebody.

@Michael: those vids are in XVid codec, try installing that if you don't have it.

http://www.xvidmovies.com/codec/

Michael Perry   (May 14, 2008 at 20:23 GMT)
Bah...codecs...you know what I think?

ONE FORMAT TO RULE THEM ALL!!! =)

At any rate, I'm uploading my own video to Youtube...gonna take a while, but it's worth it =)

Jondo   (May 14, 2008 at 20:46 GMT)
Workin' fine over here on the west coast... I am a bit codec whorish, though. I suggest everyone reboot for good measure.

@Chris: Why not just rock some good 'ol wmv's or avi's next time?

Chris Calef   (May 14, 2008 at 20:50 GMT)
@Jondo: not a video expert here, by "good 'ol avi" you mean... uncompressed? (i.e. HUGE?) Otherwise from where I'm sittin, it looks like I gotta pick one. XVid seems smallest and cleanest for what I'm doing, as far as I could tell.

EDIT: but yeah, I should just youtube it.
Edited on May 14, 2008 20:51 GMT

BrokeAss Games   (May 14, 2008 at 20:57 GMT)   Resource Rating: 5
I just spilled my coffee... :)

Jondo   (May 14, 2008 at 21:08 GMT)
@Chris

True, default avi is uncompressed (and at a pretty high res), but you can spit it out in different sizes and framerates. Just works on more default media players. And .wmv is really versatile and tends to work on most windows boxes.

but yeah...

You may as well just youtube it.
Edited on May 14, 2008 21:09 GMT

Michael Perry   (May 14, 2008 at 21:55 GMT)
Hopefully before the world ends and our planet decays over millions of years, my video of this tech will upload to Youtube...

Chris Calef   (May 14, 2008 at 22:58 GMT)
Ha ha, good luck with that. Lemme know when you see it up there!

For anyone else who downloaded the code resource, you might want to go back and get the latest version, Michael provided some useful changes and I fixed a couple of other things as well.

Rex   (May 14, 2008 at 23:52 GMT)
I found BVHacker to be a good nip/tuck tool...

the "Mr.Bone" series of BVH seem to have an extra node[neckDummy] above the neck in the chain...

I had to use bvhacker to remove it, then compensate the offset with the provided sliders to 'zero' nodes.

Also, I'm wondering about the code portion of the bloq, I'll probably shoot over there to pose the observation I've gathered towards the index listing....

Daz   (May 15, 2008 at 05:44 GMT)
Sweet!!! Thanks a million Chris, because I was looking at getting some .bvh animations and this sounds like it is exactly what I need to get them working with minimal stress. If love was currency, you could buy a Ferrari with what I am feeling. Many, many thanks!

Daz   (May 15, 2008 at 05:50 GMT)
I almost forgot, a big thanks to Michael Perry too. Mike, your work in the forum thread with this was extremely helpful. Nicely done!

Michael Perry   (May 15, 2008 at 13:28 GMT)
Even though I didn't do much, you're welcome Daz! =)
Edited on May 15, 2008 13:28 GMT

Chris Calef   (May 15, 2008 at 16:22 GMT)
You're welcome, Daz!

I should mention at this point a couple of things that this software doesn't do yet. The main thing is it doesn't do anything with ground frames, and does not touch the issue of player bounding box. Ideally there still needs to be a way to transform node[0] translation (overall movement of the whole character) from the animation sequence into actual game movement with bounding box attached, so we can't walk through walls or escape being hit just because we're doing an animation. I think this is the reason that the player's shadow does weird things sometimes on animations that involve moving some distance from the starting point.

But this should definitely get you started! Let me know how it goes.

kingkong   (May 16, 2008 at 02:50 GMT)   Resource Rating: 5
cooooool!!! i have to try this. this is a really good resource!!
thanks

Michael Perry   (May 20, 2008 at 19:40 GMT)
An unexpected result:

Video of Kork on Ice (YouTube)

Orion Elenzil   (May 20, 2008 at 20:22 GMT)
haha, that's beauoo-teeful!

Michael Perry   (May 20, 2008 at 20:23 GMT)
I thought it was funny =)

Jondo   (May 20, 2008 at 22:43 GMT)
Heh, I run into this from time to time when blending animations over other actions, such as running. Key word being 'blending'. When an animation is going to be allowed to play while the player transforms, I make it a blend, even if it has some leg data. Looks pretty cool usually.

Jondo

JoZ   (Jun 03, 2008 at 13:38 GMT)
Chris, really tnx for sharing this! ;-)

You must be a member and be logged in to either append comments or rate this resource.