Questions about AnimComposer

I was surprised by this but then maybe I’m using AnimComposer “wrong”. So I decided to see how the community is doing these things.

I have a case where I will setup blendable actions any put them in a layer that masks off part of the rig. Think, an action to blend between looking up and looking down while still walking. The “Look” blend action will mask off the relevant bones in its layer and then the app can set the blend to look up or down.

So far this works ok even if setup might be a bit awkward sometimes.

But if you save the j3o, none of this is saved. Only AnimClips are saved.

This makes it seem like AnimComposer only cares about AnimClips and anything else is “runtime temporary data”. This is why I thought that maybe I’m using AnimComposer incorrectly. (Also may be why we cannot ask for the action names on an AnimComposer.)

However, given that setting up things like blended actions, action sequences, etc. can be quite a bit of code that is then otherwise accessible just by name, it’s a shame that this setup is not saved.

Am I missing a perspective?

1 Like

Hmm, do we get back to the “how to save tween” question

In my case, I define them in code by implementing Lemur Effect.

For example here is my humanoid walk-run blend action. It is supposed to be used on all of my humanoid characters.

/**
 * Mobility effect for humanoid characters.
 *
 * @author Ali-RS
 */
public class HumanoidMovement extends EntityEffect {
    //final Filterd lowPass = new SimpleMovingMean(10); // 1/6th second of data
    final BlendSpace space = new LinearBlendSpace(0, 1);
    final double baseSpeed = 2.8;

    Tween tween;

    public HumanoidMovement() {
        // specify anim channel name
        super("BaseMobility");
    }

    @Override
    public void initialize(Application app, EntityId id, Spatial model, EntityData ed) {
        AnimComposer ac = AnimUtils.findAnimComposer(model);
        if (model.getControl(SpeedFilter.class) == null) {
            model.addControl(new SpeedFilter());
        }

        AnimComposer locomotion = AnimPacks.getInstance().locomotionPack();
        ac.addAnimClip(AnimUtils.retargetClip(locomotion.getAnimClip("idle_2"), model));
        ac.addAnimClip(AnimUtils.retargetClip(locomotion.getAnimClip("walking"), model));
        ac.addAnimClip(AnimUtils.retargetClip(locomotion.getAnimClip("running"), model));

        // Make a blend tween animation for idle/walk/run mobility state
        tween = stretch(0.8, blend(space, 0,
                ac.getAnimClip("idle_2"), ac.getAnimClip("walking"), ac.getAnimClip("running")));
    }

    @Override
    public Animation create(Spatial target, EffectInfo existing) {
        SpeedFilter filter = target.getControl(SpeedFilter.class);

        return new TweenAnimation(true, tween) {
            @Override
            public boolean animate(double tpf) {
                boolean animate = super.animate(tpf);

                //double forward = Math.abs(vc.forward);

                double blendValue = filter.getFilteredValue() / baseSpeed;
                // Pass the value through a low-pass filter using
                // a moving average
                //lowPass.addValue(blendValue);
                //blendValue = lowPass.getFilteredValue();

                blendValue = Math.round(blendValue * 1000) / 1000.0;
                // Clamp it to [0,1]
                blendValue = Math.min(blendValue, 1);

                space.setValue((float) blendValue);

                return animate;
            }
        };
    }
}

I do not play them with AnimComposer, instead, I am using Lemur EffectControl which internally uses Lemur AnimationState to play animations.

I first check EffectControl and if there was no animation registered then I ask EffectFactoryState and it loads the effect from a java class or a groovy script.

I would bet my dollar on the fact that there is no editor so no one stumbled upon the issue of not beeing able to save it

Well, there was a discussion on this but it concluded that tweens are not the things we want to save but we do need to have a set of objects (i.e TweenBuilder things) that are savable and are used to build the tweens at runtime, or else we should create them in code for example in groovy or pure java.

Edit:

I tried to follow TweenBuilder things in my editor but it turned out to be complicated and I found creating them in code is much easier.

I just needed to enable the code Hot Reload feature in IDE so there was no need for an editor even;)

Yeah… savable tweens. In the general case turns out to be quite complicated even if in the “anim composer” case it would be within reason.

I thought about hacking action and tween “savability” into my local JME fork but it does turn out to be quite a deep hole. And as it turns out, because of other reasons (and JME limitations) I already wrapped the JME stuff in a separate Rig class that I serialize. To that I can add whatever other definitions that I want and create the real actions on load.

It’s just a shame because there is quite a bit of very skeleton-specific code involved in wiring these up. This is a bit different than adding sparks + sound to a blade swing.

Also another case where we’d have more options of JME had just used the perfectly good serialization already built into Java instead of rolling its own.