libgdx / fbx-conv

Command line utility using the FBX SDK to convert FBX/Collada/Obj files to a custom text/binary format for static, keyframed and skinned meshes.
Apache License 2.0
447 stars 117 forks source link

Issue in animation optimization leading to problems with transformations #43

Closed titovmaxim closed 10 years ago

titovmaxim commented 10 years ago

Introduction. The described below is connected to combination of logic of BaseAnimationController and fbx-converter. So one have to decide which part would be fixed. Currently I've made a quick fix of fbx-conv part to solve the issue.

In complex skinning animation transition could lead to a very strange result. For example there are two animations where the hero stands just close (but different) poses. In one animation it move hands, in another - head. During transition (as it was in my case) the leg start to move to the side during the transition time and then immediately returns to the correct position exactly when the transitions was finished.

BaseAnimationController has the method:

/** Apply two animations, blending the second onto to first using weight. */
protected void applyAnimations(final Animation anim1, final float time1, final Animation anim2, final float time2, final float weight) {
    if (anim2 == null || weight == 0.f)
        applyAnimation(anim1, time1);
    else if (anim1 == null || weight == 1.f)
        applyAnimation(anim2, time2);
    else if (applying)
        throw new GdxRuntimeException("Call end() first");
    else {
        begin();
        apply(anim1, time1, 1.f);
        apply(anim2, time2, weight);
        end();
    }
}

where in the last lines we apply the previous animation with weight 1, and then directly apply new (current) animation with specific weight. Under the hood the method make linear interpolation of everything like prev_(1-weight) + curr_weight.

Keep in mind, that we first apply previous animation at the full weight, and only then reduce it weight to (1-weight) when applying the current animation. So, if some bones will not be present in the current animation, but will present in prev animation we will have very strange results (like described in the beginning).

What's the deal? We could just add the correct initial values of all bones to the first animation key in Blender (or whatever) to all animations. Then all animations will have exactly the same amount of bones in use.

This it true, but fbx-conv bring another surprise. In FbxConverter.h, line 633 we have

// Only add keyframes really needed addKeyframes(nodeAnim, frames); if (nodeAnim->rotate || nodeAnim->scale || nodeAnim->translate) animation->nodeAnimations.push_back(nodeAnim); else delete nodeAnim;

So, if the animation does not use the bone, and if the initial bone state is zero (no rotation, transition, etc.) it will not be included in the g3db file. So, disregarding we have added all initial bone states in Blender, we will have very funny transitions.

It take some time for me to understand the problem. I have fixed it by removing this check in fbx-conv and leaving only one line:

animation->nodeAnimations.push_back(nodeAnim);

disregarding if the bone have some transformation.

Another, probably more proper solution, will be initially apply prev and curr animation with (1-weight) and (weight) coefficients. Or apply some "zero" bone to the transformations, which are present in prev, but absent in curr animations. Just to scale them down to (1-weight).

xoppa commented 10 years ago

This isn't a fbx-conv issue.