Where are these animations from?
Most of the BVH files I'm using in this demo are from the open source Bandai-Namco-Research-Motiondataset repository on github. Additionally you can read the related paper "Motion Capture Dataset for Practical Use of AI-based Motion Editing and Stylization" Kobayashi et al. (2023).
A couple of the BVH files (Fight & Sports, Push & Fall, and Jumps) are from the Ubisoft La Forge Animation dataset which consists of data and code corresponding to the following paper: "Robust Motion In-betweening" Harvey et al. (2020)
So how do you parse BVH files?
Overall the BVH format is plain ASCII text and consists of two sections, a definition of joint points and a set of animation frames.
Joints
The joint or HIEARCHY section consists of nested joint
definitions which define various properties:
-
The type of joint (
ROOTorJOINT) and its name which follows the type. The next joint properties and children joints are defined within a pair of braces. There can also be multipleROOTjoints in a single bvh file allowing for multiple skeletons, but I ignored this in my parser. -
The positions of joints in the
OFFSETfield which is in X, Y, Z order -
The properties animation frames will later change,
CHANNELS. Note that these channels can be in any order and this is the order the transformations should be applied. I believe it is convention for position channels to be first and then rotation, but this doesn't seem to be required. Additionally rotation channels are conventionally in YXZ order but do not even have to be consistent within a model, joints may all have different rotation orders... This was quite a headache as I did not realize the channels would deviate from YXZ so things were quite wonky looking at first. -
End Sitenodes label the end of a joint branch and only contain anOFFSET
Below is a small snippet from the walk.bvh file. Notice
how we create the shape of the skeleton's lower body and begin the
spine with multiple children joints branching off of the
Hips joint.
HIERARCHY
ROOT joint_Root
{
OFFSET 0 0 0
CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
JOINT Hips
{
OFFSET 0 93.9241 -3
CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
JOINT Spine
{
OFFSET 15.7357 0 0
CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
JOINT Chest
{
...
End Site
{
OFFSET 0 0 0
}
...
}
}
JOINT UpperLeg_L
{
OFFSET -2.19248 -6.46944 0
CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
JOINT LowerLeg_L
{
...
}
}
JOINT UpperLeg_R
{
...
}
}
}
Once we parse this information we can create a simple skeleton by
making "bones" between the joints defined as seen below. The blue
skeleton on may look malformed, but this is actually the starting
position of the model in the HIERARCHY before any motion
capture data is collected. On the other hand the green skeleton is in
a more realistic pose, and to achieve this I've simply allowed it
about one frame of animation (from the MOTION data we we
look at next) to get it into place. Some BVH files will have a
skeleton start in a realistic pose but this is not always the case as
seen here.
Animation
After the HIERARCHY section there is the
MOTION section which details the animation of the joints.
First we have some metadata, the count of frames, and the duration of
a frame in seconds. Next we have the actual animation information in
the form of many lines of numbers. These numbers correspond directly
to the CHANNELS we previously encountered on the joint
definitions. The numbers are matched to a channel by order of
occurrence if you were to traverse the joints depth first. The
transformations themselves are meant to be done in the local space of
the object, not the world space, so our shiny new hierarchical model
setup is very suitable for running the animation data.
Below is a small snippet from the walk.bvh file which
includes 3 frames of animation.
MOTION
Frames: 268
Frame Time: 0.0333333
0 0 0 0 0 -0 0.959454 90.2936 -449.181 87.094 -4.94869 1.62257 15.7357 5.1072e-06 -5.51764e-07 129.124 86.8712 -128.344 8.98455 -0.406207 -4.3199e-07 -2.94051 3.87408 1.66051 16.3773 -1.3905e-06 -6.99897e-07 -1.8675 1.01855 -1.01557 6.45672 -1.25837e-06 -1.02172e-07 12.7673 -2.02074 -1.38811 12.9942 -0.242716 4.98039 -89.8756 -1.84588 -101.145 5.34809 -7.22636e-06 -6.2814e-07 -19.4591 24.5857 -41.3202 24.3286 1.87007e-05 4.90208e-06 -75.8071 -9.53684 -87.6014 22.2916 -9.88446e-07 4.63217e-06 -14.2898 4.84507 10.4561 12.9945 -0.242716 -4.98039 89.9809 -2.07821 -105.526 -5.34813 -1.25947e-06 -3.79883e-06 -23.9883 35.7952 -42.43 -24.3289 0.000322201 -8.65921e-06 -63.3656 -20.2656 -80.15 -22.2915 -0.000108084 -4.47941e-06 -12.362 4.94249 8.51928 -2.19248 -6.46944 -1.22533e-07 -177.457 2.25992 -23.5738 39.0811 -6.73843e-06 -1.23785e-05 -0.609883 0.176582 73.1989 40.9854 -6.48686e-06 -1.26872e-05 8.71774 -12.1342 -37.4451 11.7543 -2.30145e-07 3.05095e-06 0.490902 -0.601515 -22.2359 -2.19251 6.46944 -1.45247e-06 -167.417 8.83501 169.46 -39.0811 -0.00228153 -0.0401928 0.119082 -0.181246 26.5367 -40.9854 7.72618e-06 5.99936e-06 8.83326 -7.86449 -65.8611 -11.7543 -5.6619e-06 -5.58687e-06 -0.00165169 0.0012362 -16.5738
0 0 0 0 0 -0 1.24666 91.2451 -445.96 89.7333 -5.53666 1.08827 15.7357 5.1072e-06 -5.51765e-07 135.442 86.6449 -135.773 8.98455 -0.406207 -4.3199e-07 -3.04882 4.8105 -0.535979 16.3773 -1.3905e-06 -6.99897e-07 -2.09566 1.02984 -0.811003 6.45672 -1.25837e-06 -1.02172e-07 12.5819 -2.41917 -1.05434 12.9942 -0.242716 4.98039 -89.8262 -2.36338 -100.793 5.34809 -7.22636e-06 -6.2814e-07 -20.3247 22.8234 -43.7733 24.3286 1.87007e-05 4.90208e-06 -77.2797 -12.8712 -87.1218 22.2916 -9.88446e-07 4.63217e-06 -14.2029 4.86707 10.0289 12.9945 -0.242716 -4.98039 89.9523 -2.43589 -106.121 -5.34813 -1.25947e-06 -3.79883e-06 -25.5602 33.7592 -43.1227 -24.3289 0.000322201 -8.65921e-06 -64.5739 -22.8613 -79.5359 -22.2915 -0.000108084 -4.47941e-06 -12.5155 4.95282 8.30693 -2.19248 -6.46944 -1.22533e-07 -179.87 4.05562 -28.3149 39.0811 -6.73843e-06 -1.23785e-05 -0.562461 0.223095 67.6643 40.9854 -6.48686e-06 -1.26872e-05 11.2424 -16.7812 -39.784 11.7543 -2.30145e-07 3.05095e-06 0.433005 -0.523957 -21.5312 -2.19251 6.46944 -1.45247e-06 -170.912 9.70571 173.423 -39.0811 -0.00228153 -0.0401928 0.0684819 -0.116161 22.0186 -40.9854 7.72618e-06 5.99936e-06 8.27842 -6.26925 -66.0645 -11.7543 -5.6619e-06 -5.58687e-06 -0.0036831 0.00266675 -16.5739
0 0 0 0 0 -0 1.39856 91.9036 -442.87 91.4525 -6.0235 0.33171 15.7357 5.1072e-06 -5.51765e-07 139.76 86.5316 -140.868 8.98455 -0.406207 -4.3199e-07 -3.23842 5.39908 -2.0646 16.3773 -1.3905e-06 -6.99897e-07 -2.58245 1.09776 -0.662911 6.45672 -1.25837e-06 -1.02172e-07 12.2873 -2.68414 -0.699305 12.9942 -0.242716 4.98039 -89.7924 -2.67515 -100.561 5.34809 -7.22636e-06 -6.2814e-07 -21.15 20.9502 -45.6618 24.3286 1.87007e-05 4.90208e-06 -77.2927 -15.6094 -86.5278 22.2916 -9.88446e-07 4.63217e-06 -14.2336 4.87257 9.92146 12.9945 -0.242716 -4.98039 89.9205 -2.73302 -106.666 -5.34813 -1.25947e-06 -3.79883e-06 -26.1836 31.8358 -44.6418 -24.3289 0.000322201 -8.65921e-06 -65.8445 -25.7351 -78.9822 -22.2915 -0.000108084 -4.47941e-06 -12.9265 4.96166 8.12521 -2.19248 -6.46944 -1.22534e-07 179.112 5.54949 -31.3896 39.0811 -6.73843e-06 -1.23785e-05 -0.42697 0.256017 58.1821 40.9854 -6.48686e-06 -1.26872e-05 12.0188 -18.5941 -40.746 11.7543 -2.30145e-07 3.05095e-06 0.462022 -0.562541 -21.8827 -2.19251 6.46944 -1.45247e-06 -173.238 10.9493 177.438 -39.0811 -0.00228153 -0.0401928 0.0321904 -0.0612898 16.6742 -40.9854 7.72618e-06 5.99936e-06 8.48321 -4.93461 -65.6941 -11.7543 -5.6619e-06 -5.58687e-06 0.0119357 -0.012366 -16.6695
Conclusion / Notes
Now properly parse these two parts and you can load most BVH files. Overall, this format is relatively simple to parse since it is plain text like .obj files. Additionally braces are the only kind of grouping present and joint definitions are recursive so the parser itself ends up fairly simple. Some adjustments to a standard hierarchal model setup are needed to allow any order of rotations, but the changes aren't too large.
I don't believe I'll be using this in any future work since mapping actual geometry to the models would be complex but it was a interesting exercise regardless.
Why?
It was interesting...