Wasserwecken / bvhio

Read, write, edit and create .bvh files with hierarchical 3D transforms
MIT License
47 stars 11 forks source link

Converting non- root 6 channels to 3 #5

Closed dj-kefir-siorbacz closed 1 year ago

dj-kefir-siorbacz commented 1 year ago

Hi @Wasserwecken ! Great repo! Loving it!

I was wandering whether it is possible to change a .bvh having 6 channels for all joints to an equivalent one having only 3 channels (only rotations) for all non-root joints.

Tbh I couldn't find any documentation on how parsers parse such .bvh and I would love to know! Are extra positions modifying poses each keyframe?

I know MotionBuilder can do it, but i don't want to rely on it :)

dj-kefir-siorbacz commented 1 year ago

Blender, I assume, will struggle as its .bvh import/export is weak

Wasserwecken commented 1 year ago

The best documentation of the format, that I could find, has been this one: https://research.cs.wisc.edu/graphics/Courses/cs-838-1999/Jeff/BVH.html

This libary supports rotations and positions given in any order. Partially given rotations and positions are supported too (e.g. Yrotation only).

If you look into their example .bvh, you can see exactly your expected definition, where the root holds rotation and position and child joints only rotation: https://research.cs.wisc.edu/graphics/Courses/cs-838-1999/Jeff/Example1.bvh

If a position is given for a child joint, this will move the joint relative to its parent. This is used in the rest pose usually for upper leg and upper arm joints because they are not connected "directly" to the spine. If you set the positions in the keyframe data, this counts as offset to the rest pose positon.

Blender, I assume, will struggle as its .bvh import/export is weak

Blenders import is fine and may more robust than this libary. I always tested against blenders import while developing this here ;)

dj-kefir-siorbacz commented 1 year ago

Hi @Wasserwecken !

Yeah, I know this documentation. The problem is that it says: I have never encountered a BVH file that didn't have 6 channels for the root object and 3 channels for every other object in the hierarchy., so it is of no help to my query :D

Maybe I wasn't specific enough in my question. I have a .bvh like this. It contains positional info in HIERARCHY for each joint (so 6 channels for every joint). The problem is that some .bvh parsers (maybe you're familiar with deep learning 3D animation research? then you might have stumbled upon custom parsers people've written :D) can't read this kind of 6 channels info. Therefore I was thinking if bvhio could convert it to the standard format of 6 channels for root and 3 channels for child joints. This conversion would be lossless in the case of my .bvh as the extra positional channels are redundant.

If a position is given for a child joint, this will move the joint relative to its parent. This is used in the rest pose usually for upper leg and upper arm joints because they are not connected "directly" to the spine. If you set the positions in the keyframe data, this counts as offset to the rest pose positon. So the ?position channels in non-root keyframes are used for something like bendy bones? i.e. when you want to change the length of bones during animation?

Wasserwecken commented 1 year ago

Ah okay, you want to clean or modify the animation data. Sorry for the misunderstanding! This exactly what the libary is intended for. I think this is the snippet youre looking for:

import bvhio

bvh = bvhio.readAsBvh('combat_idle_turn_270_R_001__A373.bvh')

bvh.Root = bvh.Root.Children[0]  # this removes the root bone if you want

for joint, index, depth in bvh.Root.layout():
    if "Hips" not in joint.Name:
        joint.Channels = [channel for channel in joint.Channels if 'position' not in channel]  # remove the channels you want to discard

bvhio.writeBvh('new.bvh', bvh, percision=6)

This code will !discard! any positional information. But if you have animated positions, then you have to do more in order to preserve the original motion. You can look into these snippets if you want to modify the rest pose or the animation data:

So the ?position channels in non-root keyframes are used for something like bendy bones? i.e. when you want to change the length of bones during animation?

If you look closer to the snippet you will notice that any positional channel are excluded except for the hips ('if "Hips" not in joint.Name:'). This is because the hips will actually translate to space for the aimation. The rest pose will set the default position for the hip, and the animation data will offset the hip bone. Combined together (restpose + animation) they will result in the correct position.

This behaviour is the same for any other positional information too. I dont know any use case for this, but you could set positional animation data for the upper legs joint to slim or widen the hips. "Bendy"-bones might not be possible with this, but you could "scale" the bones.

dj-kefir-siorbacz commented 1 year ago

I see, thanks!

So positional info. is discarded, but original motions is not preserved : (

Wasserwecken commented 1 year ago

So positional info. is discarded, but original motions is not preserved : (

If there is no positional animation, then the original motion is preserved! This is the common case in 99%. So youre good to go :)