Skip to content

Future Plan

endersaka edited this page Dec 2, 2025 · 9 revisions

Starting with the experimental script goofy_importer.py, and its first "fully" working version, introduced with this pull request, I start to have some base code.

Using Antlr4, I generated the classes BVHLexer, BVHParser, BVHListener, and BVHVisitor. The latest two can be leveraged, by subclassing, to further elaborate the BVH data as I did in the BPYBVHVisitor.

BPYBVHVisitor outputs the BVH data sections as a Python dictionary that, if dumped to a file (with json.dumps()) presents a structure like the tree.json file.

MOTION frames are stored in motion_data property, as a List of Lists (or Array of Arrays, in JSON/JavaScript jargon). Each element of motion_data is a List containing one Dict for each bone. The fundamental difference with the RAW BVH format is that they are named!

"motion_data": [
    [
        {
            "name": "root",
            "Xposition": 2e-06,
            "Yposition": 1.674489,
            "Zposition": -8.356668,
            "Xrotation": -53.132072,
            "Yrotation": 3e-06,
            "Zrotation": -9e-06
        },
        {
            "name": "spine05",
            "Xposition": -0.0,
            "Yposition": -0.944275,
            "Zposition": 0.116946,
            "Xrotation": -7e-06,
            "Yrotation": -0.0,
            "Zrotation": -0.0
        },
        // Other elements...
    ]
    // Other frames...
]

Not only, also transform components are named using exactly the same convention as CHANNELS section. I designed it that way to facilitate the access to each frame-bone information.

What's next?

At this point blender/script/goofy_importer.py showcases a series of utility functions that need to be adjusted, carefully tailored, and promoted to an API.

pose_bones() is the most relevant function for consuming by the MPFB Extension. pose_bones() must be capable to apply the first frame transformations stored in the BVH file (the only one frame at all, stored in the BVH files found in the MakeHuman Asset Packs) to a target Blender armature (for example the "Default" MPFB armature) as a its new pose, which is, essentially a Blender action with one keyframe.

pose_bones() does not implement yet the following ones:

  1. Apply the pose to a target armature (it creates a new one, based on the BVH HIERARCHY section, which in turn is the "Default" MPFB armature).
  2. Store the bones positions as a Blender action with one keyframe.

I believe that the target armature (for accessing rest pose data) can be accessed in this way (which should be available in any mode).

# 'Human.rig' is the commonly name of MPFB default rig.
armature_data = bpy.data.armatures['Human.rig']

# or
armature_data = bpy.data.armatures.get('Human.rig')

The second snippet (even if unusual in Blender community) is exception free: like the Dict equivalent method, bpy.types.bpy_prop_collection.get() returns None if no element with that name is found.

Once we got the armature data (an instance of Armature class) we can access its properties bones or edit_bones to retrive rest pose data.

Naturally, line 305,

bone_rest_pose_data = next((b for b in REST_POSE if b.get('name') == segment_name), None)

will change to something like:

bone_rest_pose_data = armature_data.bones.get(segment_name)

And the following lines (307 and 308) will have to change accordingly.

Read also this annotation.

This is the only dependency of pose_bones(), apart from REST_POSE. And I think it can be perfectioned as I mentioned above, by getting the armature first and then all the rest, with checks in the middle.

Useful Information

While it is possible to find (given time and patience) all the required information inside the official BPY API documentation, such information is scattered among different documents. A central guide on how to work with bones is missing (as far as I know). Therefore, I decided to write it down here, for my convenience and the convenience of eventual contributors in the future.

Accessing Bones

Bone information is held withing the data structure of three different types, the classes bpy.types.Bone, bpy.types.EditBone, and bpy.types.PoseBone.

If an armature is selected or active, it is possible to access it quickly, using bpy.context.object or bpy.context.active_object.

armature_object = bpy.context.object

#or
armature_object = bpy.context.active_object

When we access bpy.context.object.data, the actual type of the property depends on the type of object selected in the scene. So, since, in our case the armature is the selected object, the class of bpy.context.object.data is bpy.types.Aramature.

# Instance of `bpy.types.Armature`
armature_data = armature_object.data

bpy.types.Aramature exposes two properties that give access to different types of bones data: bpy.types.Armature.bones and bpy.types.Armature.edit_bones.

To access them we have to be in the correct object interaction mode among: OBJECT, EDIT, or POSE.

In fact, as explained in Gotchas - Edit Bones, bpy.context.object.data.edit_bones returns a collection of type ArmatureEditBones, and to access it "you must set the armature mode to Edit-Mode first (edit bones do not exist in Object or Pose-Mode)".

Similarly, bpy.context.object.data.bones "live in Object-Mode".

# Access edit bones for reading and writing
armature_object.mode_set('EDIT')

edit_bones = armature_data.edit_bones

And I presume that bpy.context.object.pose.bones can be accessed "in Object or Pose-Mode", since they say so. Though, it's not clear if they are programmatically "posable" in Object-Mode, but I assume that they are not.

To access pose bones, the call path it's slightly different: bpy.context.object.pose, which returns an instance of class bpy.types.Pose, which in turn exposes the property bpy.types.Pose.bones (note that this collection does not have its own specialized class, it is just an instance of bpy.types.bpy_prop_collection).

# Access pose bones for reading and writing
armature_object.mode_set('POSE')

armature_pose = armature_object.pose
pose_bones = armature_pose.bones

There is a particular aspect of Blender BPY documentation and examples that makes me think. Why don't they use bpy.context.armature in place of bpy.context.object? There is also bpy.context.active_object, but this, instead of helping me, is raising a lot of questions.

How to Store the Pose

At the moment I am applying the pose transforms directly to the pose bones, but soon I will have to change it, so that it follows one of the methods described here Animation, possibly the second one, since it allows to eventually store more than one frame quickly.

Clone this wiki locally