How to use root motion?

I searched for root motion here and in the wiki but could not find anything useful other than code that eliminates translation on the root joint.

I have animation clips with root motion, i.e. a translation on the root bone but somewhere on its way into jme it got lost. The imported model is playing its animation clip on the spot and a debug shows there is no transform in x and z. I assume that it was nulled but cannot find if and where that might happen.
Is there a simple way already to get root motion working and I’m digging where it’s not necessary?

1 Like

Is this the same model as in your previous post? From mixamo?

Yes, it’s a makehuman model, uploaded to mixamo and downloaded with animation clips. The clips play now thanks to the AnimComposer found down the hierarchy but I checked the root bone while playing and the x and z translations are always 0. Maybe the ImportMixamo did that although I could not find where this might happen.
But I’d really like to know whether there is already some support for root motion before I try to roll my own.

ImportMixamo is able to do this, though I’m not sure if it’s done by default. Mixamo also has a checkbox to make animations in place on download, so make sure that wasn’t enabled, too.

Root animation is fully supported by jme. It’s just like animation for any other bone.

1 Like

OK, but that means I’d need to counteract that if I’d like to apply the root motion to a parent for example? I mean there is no automatic extraction I guess.

I’m not sure what you mean by this.

ImportMixamo or Mixamo itself deleted all offsets of the root bone in order to keep the character “in place.” To fix your problem, you need to ensure the “in place” features for both ImportMixamo and Mixamo are disabled.

Also, are you sure your animations shouldn’t be in place anyway? In place animations are nearly always better for characters in games.

What I imagine (and used to from other engines) is that the root animation is extracted and can be applied to let’s say a character controller or a rigidbody in order to have full control over the model’s movement.
And yes, I’d like to use root motion because I have really bad experience getting rid of sliding models because the animation does not match the character’s speed if you disregard root motion. It’s just so much more tweaking to make it look good IMO.

1 Like

Tip: If you are ever in a situation where you don’t already have the root offsets then an “old school” trick is to place an animated floor under the character while creating the walk/run/etc. animations. That way the feet are already moving at a consistent speed and no tweaking is necessary.

I learned this tip back in the 1990s or so and I’m surprised it’s not used more widely. So many “in place” walk/run models that I download have inconsistent foot motion that makes dialing in a specific speed essentially impossible.

In case someone is looking for that in the future: It’s in a function called postProcess and in ImportMixamo.java #98:
final private static boolean convertToInPlace = true;

That sounds like a good way to create animations although I plan to use animations that others did (or more precisely are most probably done using motion tracking software).
I’m going to use several animation clips so trying to fix the characters speed to each of them is more work than getting root translation working. I once added code to make sure the feet aren’t sliding and adapted the speed for that and although it worked it’s more work than necessary IMO.

Ah, I see what you want. Here’s a quick control I whipped up that should do that:

public class RootMotionControl extends AbstractControl {
    
    private final String controlJointName;
    private SkinningControl skin;
    private Joint controlJoint;
    private Vector3f rootPosition = new Vector3f();
    private Vector3f delta = new Vector3f();
    
    public RootMotionControl(String controlJoint) {
        this(controlJoint, null);
    }
    public RootMotionControl(String controlJoint, SkinningControl skin) {
        this.controlJointName = controlJoint;
        this.skin = skin;
    }
    
    @Override
    protected void controlUpdate(float tpf) {
        Vector3f current = getControlJointPosition(null);
        current.subtract(rootPosition, delta);
        rootPosition.set(current);
    }
    @Override
    protected void controlRender(RenderManager rm, ViewPort vp) {}
    @Override
    public void setSpatial(Spatial spat) {
        super.setSpatial(spat);
        if (spatial != null) {
            if (skin == null) {
                skin = spatial.getControl(SkinningControl.class);
            }
            controlJoint = skin.getArmature().getJoint(controlJointName);
            getControlJointPosition(rootPosition);
        }
    }
    
    public Vector3f getMotionDelta() {
        return delta;
    }
    
    private Vector3f getControlJointPosition(Vector3f store) {
        if (store == null) {
            store = new Vector3f();
        }
        return store.set(controlJoint.getLocalTranslation());
    }
    
}

The armature must be properly prepared for it to work:

  1. Open the model in blender.
  2. Duplicate the root joint. Keep the new joint in the same location and name it, something like “root-motion-control.”
  3. Transfer all animations from the root joint to the new joint.
  4. Delete all animations from the root joint (except maybe the vertical animations).

Add a RootMotionControl to the model containing a SkinningControl:

RootMotionControl rmc = new RootMotionControl("root-motion-control");
model.addControl(rmc);
anim.setCurrentAction("myRunningAction");

And read the motion delta when you update the character’s movement speed:

float speed = rmc.getMotionDelta().length();

Unfortunately, the motion delta jumps significantly at the end of each animation when the motion control joint resets. I can think of several ways to deal with it, but none are very reliable.

Thank you, I’m going to try that as soon as I get the translation on the model. Unfortunately it’s still lost even after disabling the postProcess() function.

Have you disabled “in place” in Mixamo?

Yes.
I find it surprisingly difficult to get a mixamo animation from a makehuman model into jme with root translation. I tried dae and fbx and different ways of importing. From the most obvious using the SDK to third-party tools like the FBX2glTF. The best I got so far is a j3o done by MonkeyWrench in terms of scaling but the root translation is gone.
Given the age of jme and the model/animation sources I very much doubt I’m the first one trying that. How complicated could that be? If someone knows of a functional workflow I’d appreciate any hints.

1 Like

@capdevon taught me the following asset pipeline:

  1. Go to mixamo.com and sign in using an Adobe account. Go to the Characters tab at the top and select a character.
  2. Download the character with T-pose animation and the default settings.
  3. Search for and select the animations you want, and download them. Check the “in place” box if there is one, and in the download options select “without skin”.
  4. To convert 3D models from FBX to glTF use the latest version of FacebookIncubator/FBX2glTF . For example:
FBX2glTF-windows-x64.exe --embed --verbose ./Mixamo/Erika/Erika.fbx -o ./Mixamo/Erika_out/Erika.gltf
  1. Delete the “.fbm” directory.
  2. To merge the animations into the T-pose character, use his copyAnimation() utility method:
    jme-animation-fsm/src/main/java/com/capdevon/anim/AnimUtils.java at b217f668492406bccf7b542812d0990e33c4bd48 · capdevon/jme-animation-fsm · GitHub

Last year I developed a simpler approach using Collada .dae format and MonkeyWrench, but it doesn’t always yield good results.

2 Likes

I tried that workflow and it works to some extend. In my case FBX2glTF created an invalid glb file (according to the KhronosGroup validator). I had some success with a model without animations though.

That’s the key point. Is there no simple way to get the root translation in jme? I tried blender as well but then I get errors saying jme supports linear interpolation only. To me it looks like every frame is a keyframe so I don’t really care about what the interpolation is set to.
The one created with FBX2glTF issues warnings about the scale and the forward axis. The result is a weird bunch of triangles in jme SDK that hardly resembles any recognizable model.

Is there no simple way to get the root translation in jme?

I’ve used root motion in JME (both the old and new animation systems) and I’m convinced it works. So I assume the issues you’re having are with importing 3-D models, not with root motion itself.

At this point, JME’s Blender importer and FBX importer are of little use. When importing 3-D models one should use GLB or glTF format. The GLB/glTF importer in jme3-plugins is good, so the usual challenge is getting the model into GLB or glTF format.

surprisingly difficult to get a mixamo animation from a makehuman model into jme with root translation.

I just noticed you’re trying to combine Mixamo animations with MakeHuman. That’s extra challenging because Mixamo and MakeHuman use different bone names and axes, so animation retargeting is required. Are you using Wes for that?

1 Like

I’m uploading a makehuman model to mixamo and get an animated skinned model back. I used that in other engines and it works just fine. I guess the challenge here is that I need to convert the downloaded mixamo file so that jme can read it without losing vital information. That’s why I asked about a workflow people have tested. Maybe nobody is doing that although I doubt that.

1 Like