We take pride in your success. We let our positivity drive us, day in and out. Talk to us at Mindfire to know us more.

BVH, Biovision Hierarchy

Background of this article:

We are exporting animation data into .bvh file in one of the application we are developing currently. There is no SDK for writing bvh files. I have written the code in C++ for exporting the head animation data into bvh file.

Lets start the article with brief introduction of bvh files and its format and the legends used in bvh files and how to write the code.

 

BVH Introduction:

BVH, Biovision Hierarchy, is an animation file format which is available in several 3D applications that do not support FBX. It stores the animation data in terms of ROOT, JOINTS and OFFSET.

 

BVH Format:

bvh format

BVH sample file:

Given below is a sample bvh file content.

 


HIERARCHY

ROOT Neck

{

OFFSET 0.000000 0.000000 0.000000

CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation

JOINT Head

{

OFFSET 0.200000 0.400000 0.500000

CHANNELS 3 Zrotation Xrotation Yrotation

End Site

{

OFFSET 0.600000 8.000000 2.000000

}

}

}

MOTION

Frames: 1

Frame Time: 0.040000

0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000


 

Detailed description:

There are two sections in a bvh file.

1. Hierarchy and

2. Motion

Hierarchy:

- BVH file starts with HIERARCHY keyword. This section contains Root and Joints. And also its channels.

- We observed some bvh files. All files are having ROOT and JOINTs. And that too ROOT has both channels Position and Rotation. where as JOINT has only Rotation channels.

Motion:

- This section contains number of frames, frame rate and (more importantly) motion  data. Each line of the motion data represents a single and complete frame.

-The values in a single line of a motion data = Root channels + Joint channels.

So, from the above sample bvh content, we can map the motion data as follows ...

 

  Neck        Neck     Neck        Neck      Neck      Neck       Head       Head     Head

X pos        Y pos     Z pos       Z rot      X rot      Y rot       Z rot      X rot      Y rot

0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000

 

Legends used in bvh file:

- HIERARCHY: The state of the header section begins with HIERARCHY

- ROOT: Root segment

- JOINT: Child segment

- CHANNELS: Number of channels (position and rotation) of a JOINT

or a ROOT

- OFFSET: Length and direction used for drawing the parent

segment

- End Site: End of a JOINT

- MOTION: The motion section begins with MOTION

- FRAMES: Number of frames

  •  
    • Frame Time:Sampling rate of the data

If we map the bvh according to our requirement, then

ROOT = Neck

Head = Head or face

JOINT = 3D markers

 

Source Code:

BVH.h file

 

handling

//-----------------------------

#define NEXT_LINE fwrite("\n", 1, strlen("\n"), m_filePtr)

#define TAB_SPACE fwrite("\t", 1, strlen("\t"), m_filePtr)

#define ONE_SPACE fwrite(" ", 1, strlen(" "), m_filePtr)

#define BEGIN_BRACKET fwrite("{", 1, strlen("{"), m_filePtr)

#define CLOSE_BRACKET fwrite("}", 1, strlen("}"), m_filePtr)

#define OFFSET fwrite("OFFSET", 1, strlen("OFFSET"), m_filePtr)

#define JOINT fwrite("JOINT", 1, strlen("JOINT"), m_filePtr)

#define CHANNELS fwrite("CHANNELS", 1, strlen("CHANNELS"),

m_filePtr)

#define END_SITE fwrite("End Site", 1, strlen("END Site"),

m_filePtr)

//-----------------------------

// Hierarchy

//-----------------------------

enum Hierarchy

{

root = 0,

joint,

childJoint

};

//-----------------------------

// BVH class

//-----------------------------

class BVH

{

public:

BVH();

~BVH();

int CreateBVHFile(char* rootName, bool hasRotChannel, bool

hasPosChannel, float* offsetValues);

int CloseBVHFile();

void SetFileName(CString& fileName);

void ExportToBvh();

protected:

//-- HIERARCHY

int AddHierarchy();

int CloseHierarchy();

int AddJoint(const char* jointName, bool hasRotChannel, bool

hasPosChannel, float* offsetValues);

int AddChildJoint(const char* childName, bool hasRotChannel,

bool hasPosChannel, float* offsetValues);

int AddJointEndSite(float* offsetValues);

int AddChildJointEndSite(float* offsetValues);

int CloseJoint();

int CloseChildJoint();

//-- MOTION

int AddMotionHeader(float frameTime);

int AddMotionFrameData(int currFrame, char* floatValStr);

int GetNumFrames();

void SetNumFrames(int numFrames);

private:

int AddBody(const char* name, bool hasRotChannel, bool

hasPosChannel, float* offsetValues, Hierarchy hierarchy);

int AddEndSite(float* offsetValues, Hierarchy hierarchy);

FILE* m_filePtr;

CString m_rootName;

CString m_fileName;

bool m_hasRotChannel;

bool m_hasPosChannel;

float* m_offsetValues;

int m_numFrames;

};

BVH.cpp file:

#include "stdafx.h"

#include "BVH.h"

//---------------------------------------------------------------------

//BVH constructor

//---------------------------------------------------------------------

BVH::BVH()

{

m_rootName = "";

m_fileName = "";

m_hasRotChannel = false;

m_hasPosChannel = false;

m_offsetValues = NULL;

m_filePtr = NULL;

m_numFrames = 0;

}

//----------------------------------------

//OnBvhBnClickedOk:

//----------------------------------------

void BVH::ExportToBvh()

{

//-- create bvh file

float rootOffsetValues[3] = {1.0f, 1.0f, 1.0f};

CreateBVHFile("Neck", true, true, rootOffsetValues);

//-- Add Hierarchy

AddHierarchy();

{

AddJoint("Head", true, false, rootOffsetValues);

AddJointEndSite(rootOffsetValues);

{

for (int i = 0; i < SOME_NUMBER_HERE; ++i)

{

AddChildJoint(SOME_NAME_HERE, false, true,

rootOffsetValues);

AddChildJointEndSite(rootOffsetValues);

CloseChildJoint();

}

}

CloseJoint();

}

CloseHierarchy();

//-- Add Motion data or frame data

SetNumFrames(theApp.m_lastTrackedFrame);

AddMotionHeader(0.040000f);

char motionData[2096] = {0};

for(int f = 1; f <= theApp.m_lastTrackedFrame; ++f)

{

motionData = one complete frame's motoin data here.

AddMotionFrameData(f,motionData);

}

//-- close bvh file

CloseBVHFile();

}

//---------------------------------------------------------------------

//Create: creates the bvh file

//---------------------------------------------------------------------

int BVH::CreateBVHFile(char* rootName, bool hasRotChannel, bool

hasPosChannel, float* offsetValues)

{

m_rootName.SetString(rootName);

m_hasRotChannel = hasRotChannel;

m_hasPosChannel = hasPosChannel;

m_offsetValues = offsetValues;

m_filePtr = fopen(m_fileName.GetString(), FILE_FLAGS);

if (m_filePtr)

return 1;

return 0;

}

//---------------------------------------------------------------------

//Create: creates the bvh file

//---------------------------------------------------------------------

int BVH::CloseBVHFile()

{

if (m_filePtr)

fclose(m_filePtr);

m_filePtr = NULL;

return 1;

}

//---------------------------------------------------------------------

//BVH estructor

//---------------------------------------------------------------------

BVH::~BVH

{

if (m_filePtr)

fclose(m_filePtr);

m_filePtr = NULL;

}

//---------------------------------------------------------------------

//AddHierarchy: Adds the default hierarchy data into .bvh file

//---------------------------------------------------------------------

int BVH::AddHierarchy()

{

if (!m_filePtr)

return 0;

//Header data

fwrite("HIERARCHY", 1, strlen("HIERARCHY"), m_filePtr);

NEXT_LINE;

fwrite("ROOT", 1, strlen("ROOT"), m_filePtr);

//body

return AddBody(m_rootName.GetBuffer(), m_hasRotChannel,

m_hasPosChannel, m_offsetValues, root);

}

//---------------------------------------------------------------------

//CloseHierarchy: closes hierarchy

//---------------------------------------------------------------------

int BVH::CloseHierarchy()

{

if (!m_filePtr)

return 0;

NEXT_LINE;

CLOSE_BRACKET;

NEXT_LINE;

return 1;

}

//---------------------------------------------------------------------

//AddJoint: Adds the joint into the .bvh file

//---------------------------------------------------------------------

int BVH::AddJoint(const char* jointName, bool hasRotChannel, bool

hasPosChannel, float* offsetValues)

{

if (!m_filePtr)

return 0;

//only one tab space

TAB_SPACE;

fwrite("JOINT", 1, strlen("JOINT"), m_filePtr);

return AddBody(jointName, hasRotChannel, hasPosChannel,

offsetValues, joint);

}

//---------------------------------------------------------------------

//CloseJoint: closes the joint

//---------------------------------------------------------------------

int BVH::CloseJoint()

{

if (!m_filePtr)

return 0;

TAB_SPACE;

CLOSE_BRACKET;

return 1;

}

//---------------------------------------------------------------------

//AddJoint: Adds the child joint into the .bvh file

//---------------------------------------------------------------------

int BVH::AddChildJoint(const char* childName, bool hasRotChannel, bool

hasPosChannel, float* offsetValues)

{

if (!m_filePtr)

return 0;

//two tabs

TAB_SPACE;

TAB_SPACE;

fwrite("JOINT", 1, strlen("JOINT"), m_filePtr);

return AddBody(childName, hasRotChannel, hasPosChannel,

offsetValues, childJoint);

}

//----------------------------------------------------------------------

//CloseChildJoint: closes the child joint

//---------------------------------------------------------------------

int BVH::CloseChildJoint()

{

if (!m_filePtr)

return 0;

TAB_SPACE;

TAB_SPACE;

CLOSE_BRACKET;

NEXT_LINE;

return 1;

}

//---------------------------------------------------------------------

//AddJointEndSite: Adds the "End Site" of the joint into .bvh file

//---------------------------------------------------------------------

int BVH::AddJointEndSite(float* offsetValues)

{

TAB_SPACE;

TAB_SPACE;

return AddEndSite(offsetValues, joint);

}

//---------------------------------------------------------------------

//AddChildJointEndSite: Adds the "End Site" of the child joint into

.bvh file

//---------------------------------------------------------------------

int BVH::AddChildJointEndSite(float* offsetValues)

{

TAB_SPACE;

TAB_SPACE;

TAB_SPACE;

return AddEndSite(offsetValues, childJoint);

}

//---------------------------------------------------------------------

//AddEndSite: Adds the "End Site" of the joint or child joint

//---------------------------------------------------------------------

int BVH::AddEndSite(float* offsetValues, Hierarchy hierarchy)

{

char floatVal[32] = {0};

END_SITE;

NEXT_LINE;

if (hierarchy == joint)

{

TAB_SPACE;

TAB_SPACE;

}

else if (hierarchy == childJoint)

{

TAB_SPACE;

TAB_SPACE;

TAB_SPACE;

}

BEGIN_BRACKET;

NEXT_LINE;

if (hierarchy == joint)

{

TAB_SPACE;

TAB_SPACE;

TAB_SPACE;

}

else if (hierarchy == childJoint)

{

TAB_SPACE;

TAB_SPACE;

TAB_SPACE;

TAB_SPACE;

}

OFFSET;

TAB_SPACE;

sprintf(floatVal,"%f",offsetValues[0]);

fwrite(floatVal, 1, strlen(floatVal), m_filePtr);

ONE_SPACE;

sprintf(floatVal,"%f",offsetValues[0]);

fwrite(floatVal, 1, strlen(floatVal), m_filePtr);

ONE_SPACE;

sprintf(floatVal,"%f",offsetValues[0]);

fwrite(floatVal, 1, strlen(floatVal), m_filePtr);

NEXT_LINE;

if (hierarchy == joint)

{

TAB_SPACE;

TAB_SPACE;

}

else if (hierarchy == childJoint)

{

TAB_SPACE;

TAB_SPACE;

TAB_SPACE;

}

CLOSE_BRACKET;

NEXT_LINE;

return 1;

}

//---------------------------------------------------------------------

//AddBody: Adds the body of the Root, Joint and child joint

//---------------------------------------------------------------------

int BVH::AddBody(const char* name, bool hasRotChannel, bool

hasPosChannel, float* offsetValues, Hierarchy hierarchy)

{

char floatVal[32] = {0};

if (!m_filePtr)

return 0;

ONE_SPACE;

fwrite(name, 1, strlen(name), m_filePtr);

NEXT_LINE;

if (hierarchy == joint)

{

TAB_SPACE;

}

else if (hierarchy == childJoint)

{

TAB_SPACE;

TAB_SPACE;

}

BEGIN_BRACKET;

NEXT_LINE;

if (hierarchy == root)

{

TAB_SPACE;

}

else if (hierarchy == joint)

{

TAB_SPACE;

TAB_SPACE;

}

else if (hierarchy == childJoint)

{

TAB_SPACE;

TAB_SPACE;

TAB_SPACE;

}

OFFSET;

TAB_SPACE;

sprintf(floatVal,"%f",offsetValues[0]);

fwrite(floatVal, 1, strlen(floatVal), m_filePtr);

sprintf(floatVal," %f",offsetValues[1]);

fwrite(floatVal, 1, strlen(floatVal), m_filePtr);

sprintf(floatVal," %f",offsetValues[2]);

fwrite(floatVal, 1, strlen(floatVal), m_filePtr);

NEXT_LINE;

if (hierarchy == root)

{

TAB_SPACE;

}

else if (hierarchy == joint)

{

TAB_SPACE;

TAB_SPACE;

}

else if (hierarchy == childJoint)

{

TAB_SPACE;

TAB_SPACE;

TAB_SPACE;

}

//channel data

CHANNELS;

ONE_SPACE;

if (hasRotChannel & hasPosChannel)

{

fwrite("6 Xposition Yposition Zposition Zrotation

Xrotation Yrotation",

1, strlen("6 Xposition Yposition Zposition

Zrotation Xrotation Yrotation"), m_filePtr);

}

else if(hasRotChannel)

{

fwrite("3 Zrotation Xrotation Yrotation",

1, strlen("3 Zrotation Xrotation Yrotation"),

m_filePtr);

}

else if(hasPosChannel)

{

fwrite("3 Xposition Yposition Zposition",

1, strlen("3 Xposition Yposition Zposition"),

m_filePtr);

}

NEXT_LINE;

return 1;

}

//---------------------------------------------------------------------

//SetNumFrames: Sets the number frames into the .bvh file

//---------------------------------------------------------------------

void BVH::SetFileName(CString& fileName)

{

m_fileName = fileName;

}

//---------------------------------------------------------------------

//SetNumFrames: Sets the number frames into the .bvh file

//---------------------------------------------------------------------

void BVH::SetNumFrames(int numFrames)

{

m_numFrames = numFrames;

}

//---------------------------------------------------------------------

//GetNumFrames: returns the number of frames in a .bvh file

//---------------------------------------------------------------------

int BVH::GetNumFrames()

{

return m_numFrames;

}

//---------------------------------------------------------------------

//AddMotionHeader: Adds the minimum default motion data into .bvh

file

//---------------------------------------------------------------------

int BVH::AddMotionHeader(float frameTime)

{

char frameStr[32] = {0};

fwrite("MOTION", 1, strlen("MOTION"), m_filePtr);

NEXT_LINE;

fwrite("Frames:", 1, strlen("Frames:"), m_filePtr);

TAB_SPACE;

sprintf(frameStr, "%d", m_numFrames);

fwrite(frameStr, 1, strlen(frameStr), m_filePtr);

NEXT_LINE;

fwrite("Frame Time:", 1, strlen("Frame Time:"), m_filePtr);

ONE_SPACE;

sprintf(frameStr, "%f", frameTime);

fwrite(frameStr, 1, strlen(frameStr), m_filePtr);

NEXT_LINE;

return 1;

}

//---------------------------------------------------------------------

//AddMotionFrameData: Adds the motion data or frame data into .bvh

file

//---------------------------------------------------------------------

int BVH::AddMotionFrameData(int currFrame, char* floatValStr)

{

if (currFrame > m_numFrames)

return 0;

fwrite(floatValStr, 1, strlen(floatValStr), m_filePtr);

NEXT_LINE;

return 1;

}

 

Testcase:

 

#include "stdafx.h"

#include "BVH.h"

int main(int argc, _TCHAR* argv[])

{

if (argc < 2)

return 0;

BVH bvh;

bvh.SetFileName(argv[1]);

 

 

Reference:

 http://www.cs.cityu.edu.hk/~howard/Teaching/CS4185-5185-2007-SemA/Group12/BVH.html

http://www.cs.wisc.edu/graphics/Courses/cs-838-1999/Jeff/BVH.html

 

By,

Manjunath

top

News & Updates

  • February 12

    Mindfire Solutions successfully conducted DDM IT Quiz'2012

  • February 12

    Mindfire releases a CRM interface

Let us Connect!

privacy

copyright (c) Mindfire Solutions 2007-2012. Login