BVHTools icon indicating copy to clipboard operation
BVHTools copied to clipboard

Problems when using different models to load animation

Open bsgg opened this issue 5 years ago • 6 comments

Great tool! It works nice and it is saving me a lot of time.

I think I found a mistake on your code but I'm not so sure. I am testing you tool with models from mixamo website. So far so good, I was able to export some animations, check them on blender, and reproduced in unity at runtime. But I have some problems, I am tryingo to use different models with a Humanoid configuration. My idea is to record with 1 model the motion capture and use a different (or several) model to play the animation. Eveytime I record I use the option enforceHumanoidBones, to record with humanoid unity bones. If I use the same model to play the record, it works, but with different models is not always the case. I don't know if this has a solution.

Now, one of the things I saw on your code, is what I think a small bug. the function buildSkeleton, on BVHRecorder script, when you are creating the skel tree with the possible bones, you are not taking into account if someone whants to use enforceHumanoidBones. In some cases there are extra bones on the bvh file that are not suppose to be there. Reading your code I know you are adding those bones withouth checking if they are humanoidbones. I know this happens because I tested with one of the models from mixamo and in the exported file I had the bone Neck1 which is not part of Unity HumanBodyBones enumerated

I hope this helps to improve the tool.

bsgg avatar May 02 '19 19:05 bsgg

Thank you for reporting this issue!

Currently, this is by design, as completely skipping bones can be a bit tricky, especially on the playback side, so even with enforceHumanoidBones it will still include other bones inbetween. I guess the name does not quite reflect this, so it should probably be renamed until this functionality is fully implemented.

emilianavt avatar May 04 '19 15:05 emilianavt

Thanks for answer so quickly

Just for myself (for now I only intended to use the tool with humanoid bones) I implemented a quick fix (It's not a proper solution just somethin quick for now). In your class BVHAnimationLoader, I added an extra dictionary. As a key for the dictionary I use the enum HumanBodyBones as as string, and as a value I have a struct where with 2 members. the HumanBodyBones enum and the transform.

public struct StandardHumanoidBone { // Id bone public HumanBodyBones BoneIndex; public Transform TransformBone; }

private Dictionary<string, StandardHumanoidBone> retargetBonesMap;

I added the a public function to initialize this function, and all I do is populate the dictionary with Transform bone = targetAvatar.GetBoneTransform(boneIndex);

(nothing out of ordinary)

In the method getCurves, I commented the line

Transform nodeTransform = getBoneByName(node.name, bone, first);

and use my dictionary to get the nodeTransform

if (retargetBonesMap.ContainsKey(node.name)) { nodeTransform = retargetBonesMap[node.name].TransformBone; }

After this I need to check if this nodeTransform is null to avoid null references later in your code.

For now I am only interested in humanoid bones, so this is not adecuate for your tool since you support "both options", but here is a quick idea to "fix" this.

I also implemented another fix on the BvhRecorder, I added a recursive function to skip bones that are not standard unity humanoid

**private void buildHumanoidSkeleton(SkelTree currentParentJoint, Transform currentBone, bool isBoneRoot = false) { SkelTree nextParentJoint;

    if (!isBoneRoot)
    {
        HumanBodyBones tBone = HumanBodyBones.LastBone;
        if (IsStandardHumanoidBone(currentBone, out tBone))
        {
            SkelTree childBone = new SkelTree(currentBone, boneMap);
            currentParentJoint.children.Add(childBone);

            nextParentJoint = childBone;
        }
        else
        {
            nextParentJoint = currentParentJoint;
        }
    }else
    {
        nextParentJoint = currentParentJoint;
    }

    // Get childs
    if (currentBone.childCount > 0)
    {
        for (int i = 0; i < currentBone.childCount; i++)
        {
            buildHumanoidSkeleton(nextParentJoint, currentBone.GetChild(i));
        }
    }
}**

I added this function to check if the bone is standard **private bool IsStandardHumanoidBone(Transform bone, out HumanBodyBones boneType) { boneType = HumanBodyBones.LastBone;

    for (int i=0; i<(int)HumanBodyBones.LastBone; i++ )
    {
        HumanBodyBones indexBone = (HumanBodyBones)i;
        Transform humanoidBone = targetAvatar.GetBoneTransform(indexBone);
        if (humanoidBone == bone)
        {
            boneType = indexBone;
            return true;
        }
        
    }
    return false;
}**

I use this function in buildSkeleton when enforceHumanoidBones is true

if (enforceHumanoidBones) { buildHumanoidSkeleton(skel, rootBone, true); }

I hope this helps

bsgg avatar May 04 '19 16:05 bsgg

Thank you for the details! I will look into adding something like this when I have time.

emilianavt avatar May 05 '19 10:05 emilianavt

Thanks for answer so quickly

Just for myself (for now I only intended to use the tool with humanoid bones) I implemented a quick fix (It's not a proper solution just somethin quick for now). In your class BVHAnimationLoader, I added an extra dictionary. As a key for the dictionary I use the enum HumanBodyBones as as string, and as a value I have a struct where with 2 members. the HumanBodyBones enum and the transform.

public struct StandardHumanoidBone { // Id bone public HumanBodyBones BoneIndex; public Transform TransformBone; }

private Dictionary<string, StandardHumanoidBone> retargetBonesMap;

I added the a public function to initialize this function, and all I do is populate the dictionary with Transform bone = targetAvatar.GetBoneTransform(boneIndex);

(nothing out of ordinary)

In the method getCurves, I commented the line

Transform nodeTransform = getBoneByName(node.name, bone, first);

and use my dictionary to get the nodeTransform

if (retargetBonesMap.ContainsKey(node.name)) { nodeTransform = retargetBonesMap[node.name].TransformBone; }

After this I need to check if this nodeTransform is null to avoid null references later in your code.

For now I am only interested in humanoid bones, so this is not adecuate for your tool since you support "both options", but here is a quick idea to "fix" this.

I also implemented another fix on the BvhRecorder, I added a recursive function to skip bones that are not standard unity humanoid

**private void buildHumanoidSkeleton(SkelTree currentParentJoint, Transform currentBone, bool isBoneRoot = false) { SkelTree nextParentJoint;

    if (!isBoneRoot)
    {
        HumanBodyBones tBone = HumanBodyBones.LastBone;
        if (IsStandardHumanoidBone(currentBone, out tBone))
        {
            SkelTree childBone = new SkelTree(currentBone, boneMap);
            currentParentJoint.children.Add(childBone);

            nextParentJoint = childBone;
        }
        else
        {
            nextParentJoint = currentParentJoint;
        }
    }else
    {
        nextParentJoint = currentParentJoint;
    }

    // Get childs
    if (currentBone.childCount > 0)
    {
        for (int i = 0; i < currentBone.childCount; i++)
        {
            buildHumanoidSkeleton(nextParentJoint, currentBone.GetChild(i));
        }
    }
}**

I added this function to check if the bone is standard **private bool IsStandardHumanoidBone(Transform bone, out HumanBodyBones boneType) { boneType = HumanBodyBones.LastBone;

    for (int i=0; i<(int)HumanBodyBones.LastBone; i++ )
    {
        HumanBodyBones indexBone = (HumanBodyBones)i;
        Transform humanoidBone = targetAvatar.GetBoneTransform(indexBone);
        if (humanoidBone == bone)
        {
            boneType = indexBone;
            return true;
        }
        
    }
    return false;
}**

I use this function in buildSkeleton when enforceHumanoidBones is true

if (enforceHumanoidBones) { buildHumanoidSkeleton(skel, rootBone, true); }

I hope this helps

Hello,I've made this work, I record muscles and root motion,but current I have no idea to build a animationClip but play it at runtime.Any idea?

superowner avatar May 27 '19 05:05 superowner

Any update on this?

radrad avatar Nov 25 '19 14:11 radrad

@bsgg hi, how do you "record with 1 model the motion capture and use a different (or several) model to play the animation. "?

I try to implement this function by following code according readme file, but it fails(there are two errors in ellipse):

无标题

Any idea will be appreciated !

cssddnnc9527 avatar Sep 09 '22 07:09 cssddnnc9527