BVH Tools for Unity let you record and export motion data from avatars or skeletons to BVH files so they can be edited with Blender or other programs. The included animation loading component makes it possible import BVH files into Unity at runtime.
The BVHRecorder component should usually run last, so that it can capture any
modifications to bone rotations made by other scripts. To achieve this, after
importing the scripts, add the BVHRecorder component at the end of the script
execution order list. You can find this list under Edit
, Project Settings
,
Script Execution Order
. Press the +
button and select the BVHRecorder
.
It should appear at the end of the list. Finally click Apply
.
The most simple way to get started is to attach the "BVH Recorder" component to an avatar. Set the "Target Avatar" field to refer to the avatar, and set a filename and path for the BVH file. Then play the scene and check the "Capturing" box. You can uncheck and check it as you like. Captured motion data will be added at the end. Once you are happy with your motion data, press the save button in the inspector panel.
All fields have tooltips, so if you want to delve deeper, please take a look at them. The component also provides a simple API. Looking at the corresponding Editor script should give something of an overview.
If the bones of the model being recorded do not have rotations of zero in the rest pose, the rest pose in the resulting BVH file can look very odd. As a workaround, exporting the model to VRM format using UniVRM and importing it back into unity will produce a model with zero rotation bones. A short guide about the process can be found here.
If you want to edit your file in Blender, please enable the "Blender" checkbox for both recording and loading and use the following settings during import into Blender:
Forward: Y Forward
Up: Z Up
When importing files into Blender that were recorded with Blender mode disabled, please use the following settings instead:
Forward: -Z Forward
Up: Y Up
When loading files that were exported from Blender, the Blender checkbox always has to be enabled.
To load the file back into Unity, attach the "BVH Animation Loader" component to your avatar, set it as the "Target Avatar" and enter the filename. Also check the "Auto Start" box and then play the scene. Your animation should play.
You can watch a quick introduction video on the usage of these Unity components here.
This software is distributed under the terms of the MIT license.
BVH Tools for Unity was made by Emiliana for Virtual YouTuber purposes, but it can also be used for games or other applications.
If you want to control the import and export functionality from your own scripts, you will find all the necessary information in this section.
This component can be used to record BVH data and save it to a file.
This is the only required field. Set the target avatar here.
This is usually the hips or pelvis bone. It is the root bone of the skeleton. If not set, it will be automatically detected. However, setting it can be useful to only capture motion for a part of a skeleton.
This list contains the bones for which motion data will be recorded. It can be
filled automatically using the getBones()
function.
This the how often bone rotations will be recorded every second, when the
capturing
flag is set to true. The frame duration written to the BVH file is
also derived from this.
This is the directory into which BVH files are written. If left empty, it will be initialized to the standard Unity persistant data path, unless the filename field contains a slash or a backslash, in which case this field will be ignored completely instead.
This field is used by the saveBVH()
function and specifies the filename of
the BVH file it will create. If no filename is given, a new one will be
generated based on a timestamp. If the file already exists, a number will be
appended.
When this flag is set to true, files will be overwritten. No numbers will be appended to filenames.
Setting this flag will prevent the automatic initialization of this component at startup.
When this flag is set, motion will be captured at the interval given by
frameRate
. It is possible to pause and resume capturing by setting this
flag at any time.
This flag changes the coordinate system to match that of Blender instead of the default BVH coordinate system. It is recommended to set this when files are going to be edited in Blender and later loaded back into Unity. It is also enabled by default.
This makes getBones()
only include humanoid bones in the bone list. It is
mainly useful to exclude things like hair and skirt bones from the recording,
which can make the resulting file a lot bigger.
Enabling this option will rename humanoid bones to their standard Unity names in the generated file.
Setting this to true will allow capturing with the capturing
flag to speed
up after if the frame rate drops, to keep the duration of the captured
animation correct. Disabling this flag will ensure that at least as many
milliseconds pass after every captured frame, as one frame should require,
according to the given frameRate
.
This field shows how many frames are currently captured. Clearing the capture will reset this to 0.
This field will be set to the filename written to by the saveBVH()
function.
Unless the list of bones is set manually, this function has to be called to populate the list of bones that will be recorded.
This function removes empty entries from the bones list. It is also called by other functions, so calling it manually is usually not necessary.
This function builds a spanning tree of game objects covering all selected bones. It always has to be called before capturing and after setting the bones.
This function also has to be called before motion capture. It further prepares hierarchy information about the skeleton built by the previous function.
Call this function to record the target avatar's pose at the current frame.
Setting the capturing
flag to true will automatically call this every frame.
Using this function, all previously capture motion data can be discarded.
Calling this function will return a string containing a BVH file for the captured motion data.
This function calls genBVH() and writes the string to the file specified by
the filename
field.
This function can be used to detect the root bone of an avatar.
This function can be used to detect the root bone of an avatar, given a set of bones.
This function can be used on humanoid avatars to generate a mapping from bones to standard Unity humanoid bone names.
Here is a short example of how you could start the capturing process:
BVHRecorder recorder = gameObject.AddComponent<BVHRecorder>();
recorder.targetAvatar = GetComponent<Animator>();
recorder.scripted = true;
recorder.getBones();
recorder.buildSkeleton();
recorder.genHierarchy();
recorder.capturing = true;
Then, after you are done capturing your animation, you save it to a file:
recorder.capturing = false;
recorder.filename = "motion.bvh";
recorder.saveBVH();
If you want to capture another animation, just clear out the collected data. You can also skip this, if you want to add to your current animation.
recorder.clearCapture();
recorder.capturing = true;
And save it again:
recorder.capturing = false;
recorder.filename = "motion2.bvh";
recorder.saveBVH();
This component allows the creation of (legacy) animation clips from BVH files. The skeleton defined in the BVH file should match that of the avatar for which it is being loaded.
This field specifies the avatar to which the animation should be applied.
This field specifies the filename from which parseFile()
will read the BVH
data.
This flag has to be enabled to correctly load BVH files exported from Blender
or written by the BVHRecorder
component with the blender
flag set. When
Blender is part of the animation workflow, it is usually best to enable this
option in both BVHRecorder
and BVHAnimationLoader
. It is also enabled by
default.
When the flag below is set, this frame rate will override that derived from the frame time given in the BVH file.
When this flag is set, the frame duration will not be overridden by the value
set in frameRate
.
This name will be given to the newly created AnimationClip
when loading. If
this field is left empty, a name will be generated automatically.
When this option is enabled, standard Unity humanoid bone names will be mapped to the corresponding bones of the skeleton.
When this option is disabled, bone names have to match exactly.
If the bone names of the avatar do not match those in the file and the file doesn't use Unity's standard bone names, but the structure matches otherwise, you can define a mapping of names in this array to rename the bones to match.
public struct FakeDictionary {
public string bvhName;
public string targetName;
}
If this flag is enabled, animations will start playing as soon as they are loaded.
If this flag is enabled, an animation is loaded as soon as the script starts
running. This also enables the autoPlay
flag.
Once an animation has been loaded, the Animation
component to which it has
been added can be accessed through this field.
This field contains the latest loaded animation clip.
First, the BVH data has to be parsed. These functions do not call any Unity API functions and can safely be called from another thread if some of the animation loading process should be done in the background.
In the case of parseFile()
, the BVH data will be loaded from the file
specified through the filename
field.
This function turns the parsed BVH data into a (legacy) animation clip. If
the autoPlay
flag is set, it will also start playing the animation right
away.
The loaded animation will be added to an Animation
component on the target
avatar. Calling this function multiple times (e.g. with different parsed
files) will add multiple animations to the Animation
component, which
can all played by accessing it. The name will be assigned from the clipName
field or assigned automatically if it is empty.
This function plays the animation that was loaded last.
This function stops animation playing through the Animation
component.
This function generates a string containing the path from one game object to
another. If the skipFirst
flag is set, the first element of the path is
discarded. If the skipLast
flag is set, the last element of the path is
discarded.
It is possible to reuse this component to load multiple animations by calling one of the parsing functions and this function in sequence multiple times.
For example:
BVHAnimationLoader loader = gameObject.AddComponent<BVHAnimationLoader>();
loader.targetAvatar = GetComponent<Animator>();
loader.clipName = "anim1";
loader.filename = "anim1.bvh";
loader.parseFile();
loader.loadAnimation();
loader.clipName = "anim2";
loader.filename = "anim2.bvh";
loader.parseFile();
loader.loadAnimation();
loader.clipName = "anim3";
loader.filename = "anim3.bvh";
loader.parseFile();
loader.loadAnimation();
The loaded animations can then be played through the Animation
component:
loader.anim.Play("anim2");