Animation current frame

Hi there,

I’m trying to get control over my animations I’ve imported from blender.
Just playing animations is already working here, but we 'd like to take it a bit further and give users control over various animations.
At the moment I’ve managed to control the animation sequence based on my mouse position.

Now I’m looking to further get control over the animations using scroll.
Is there a way to get the current frame in the animation (number)?

scene.animationGroups[1].goToFrame(scrllPos);

scrllPos is incremented on each scroll.

And it does work, however on a windows desktop pc the scroll isn’t the most fluently.
I’d like to work on a scroll, when for ex you scroll three times fast after each other you initiate launching the animation which dims out over time. Unless you scroll again within a certain timeframe to spin up the animation again.
Think of it as a car (the camera) on a predetermined track which you can sling forward, and which dims out over time.

Any help here is welcome!

Thanks
Pieter

1 Like

Going to bring in @carolhmj for this one

You should be able to get it querying the runtimeAnimation of the animation (Babylon.js docs) and accessing its currentFrame property ( Babylon.js docs)

I think we can achieve this “dimming out” by using Easing Functions: Advanced Animation Methods | Babylon.js Documentation, possibly the cubic ease out: Easing Functions Cheat Sheet
I prepared a simple example of this behavior, when you click the sphere we start an animation with the cubic out easing: https://playground.babylonjs.com/#5JQPI1#1 you can see the animation is “stronger” at the beginning then it gets weaker at the end :slight_smile:

1 Like

Hi @carolhmj

Thanks for the references and PG!
I’m having difficulties getting the runtimeAnimation. This is a test-blender-file with 2 animations(?), however I just have 2 animationGroups which came in from the glb file (exported from blender).

image

So would it be possible to get the runtimeAnimation of these animation groups?

I also found this PG with the same approach, and with an interface and slider which displays the current animation frame: https://playground.babylonjs.com/#68AHME#5

For the dimming out using the easing curves, is that also going to work with the animated objects?
So would it be possible to animate an animation(group)? Could something like this work?

// just illustrative 
var ease = new CubicEase();
ease.setEasingMode(EasingFunction.EASINGMODE_EASEOUT);

var animationTorus = new Animation('animationAnimation', 'position', 30, Animation.ANIMATIONTYPE_VECTOR3, Animation.ANIMATIONLOOPMODE_CYCLE);
var anim = Animation.CreateAndStartAnimation('move', scene.animationGroups[0].goToFrame, 'goToFrame', 30, 30, 0, 150, Animation.ANIMATIONLOOPMODE_CONSTANT, ease);

So that you animate the animation to go from frame x, to frame y with an easing curve.
That would be great, as I also would like to implement some kind of “snap” when nearing a certain frame number. So this could take over the scroll input and ease straight away to the predefined frame number with an animation.

Thanks!

It is, using the same idea as the PG you linked, you access the targetedAnimations[i].animation.runtimeAnimations.

For the dimming out using the easing curves, is that also going to work with the animated objects?
So would it be possible to animate an animation(group)? Could something like this work?

I don’t quite understand this part here, you want the animation object to be also animated? If I understood that right, the AnimationGroup object is not animatable: https://doc.babylonjs.com/typedoc/classes/babylon.animationgroup, so that wouldn’t be possible. But you can set an easing function for each animation from the group, and start the group.

That would be great, as I also would like to implement some kind of “snap” when nearing a certain frame number.

If the Cubic Ease doesn’t work for that, you could also try other, more customizable easing modes, like the Bezier Ease or even a custom ease: Advanced Animation Methods | Babylon.js Documentation

Thanks!

With the following code I can get the current frame number which is playing.

scene.animationGroups[0].targetedAnimations[0].animation.runtimeAnimations[0].currentFrame

Indeed the approach about animating the animation is a bit confusing.
So let’s rephrase, what I’m after is the following:

I have a predesigned camera following a path in Blender/3dsMax exported in a glb file.
This camera (animated) comes along a few points of interest on the animated path (can be virtual, no stops, just a frame number).
I managed to import and make us of this animated file in BJS.
Depending on the scroll delta of your mouse I would like to initiate an animation of the camera.
Let’s say we have an animation of 400 frames in blender, and we’ve landed in BJS on frame 120.
When I scroll I’d like to initiate the camera animation forward 180 frames with an easing curve.

Here is a sketch about it:

Is that something that could be done?
Thanks for the help in advance.
Pieter

i think you could add a global variable that holds an ideal forward position or frame state that gets incremented when the user scrolls. also add an scene.registerBeforeRender callback that moves the camera or follow target some extra steps if the variable is greater than 0, and also decrement the variable by however much you moved. something like (pseudo code):

scrollBoost = 0
window.onscroll = () => scrollBoost + 100;

scene.registerBeforeRender(function () {
    if(scrollBoost > 0) {
         implmentationLogic()
        scrollBoost = scrollBoost - 20 (adjust speed and tweening here)
    }
  });
1 Like

Also I noticed that the “frame rate” from BJS is as follows:
400 frames in blender at 24fps, correspond with 16.6666 (frames?) in BJS.
So this scene.animationGroups[0].targetedAnimations[0].animation.runtimeAnimations[0].currentFrame is more like the number of seconds that have passed by?

The GLB format uses seconds instead of frames as its animation unit, so as to not assume anything about the original frame rate, when a GLB Animation is imported its converted to a 1fps animation. So frames ends up being the same as seconds. :slight_smile:

Ok, I think I follow along here, but if this would have become a 1fps animation wouldn’t it have taken 400seconds? I’ve exported a 400 frame animation @24fps from blender to glb.

In BJS the animation plays back for 16.6666 sec (at least that’s the duration (to) for the animation)

Another question about the animation. Could something like this work:

const ag = scene.animationGroups[0];
Animation.CreateAndStartAnimation('camAniMove', ag, 'goToFrame', 30, 120, 0, 8);

To animate the (animated) camera in order to tween it from frame 0 to 8. In this way I could play a subsection from the animation at a certain speed depending on some scroll parameters or so.
At the moment this isn’t working for me yet…

However the following did work for me, but I couldn’t set the from goToFrame

gsap.gsap.fromTo(
    scene.animationGroups[0],
    2,
    { goToFrame: scrllPosStart },
    {
        goToFrame: scrllPos,
        // onComplete: () => {
        //     console.log('done scrllPos', scrllPosStart, scrllPos);
        //     console.log(scene.animationGroups[0].targetedAnimations[0].animation.runtimeAnimations[0].currentFrame);
        // },
        // ease: 'power3.out',
    },
);

Using gsap would limit our commercial possibilities in the future so I’m looking for another way to get this done. Like with CreateAndStartAnimation or tweenjs or so.

@jeremy-coleman thanks for the suggestion, however I couldn’t get your sample code to work for me here in this case. I’ll try again later and let you know if I got it to work.

That wont work because goToFrame is a function not a property, but you can wrap it in a property called currentFrame and animate that property with CreateAndStartAnimation for example, like below where I animate the frame of an imported model’s AnimationGroup. :slight_smile:

400 frames @ 24 fps =~ 16s in the GLB, when BJS loads this 16s animation it transforms it into a ~16 frames @ 1 fps animation so the duration is the same :smiley:

Thank you very much @Blake
Looks like a very good solution to this problem.

I was testing with tweenjs for the moment, which I got working quite nicely by now, but looks way more complex…

let camPosTime = { pos: 0 };
let nextInc = 4;
let reset = false;
let toFrame = nextInc;
let scrollMovement = false;


if (scrollMovement) {
    return;
}
let camMax = scene.animationGroups[0].to;
if (toFrame > camMax) {
    console.log('reset');
    toFrame = camMax;
    reset = true;
}
console.log(reset);
scrollMovement = true;
const camTween = new TWEEN.Tween(camPosTime)
    .to({ pos: toFrame })
    .duration(1600)
    .easing(TWEEN.Easing.Sinusoidal.InOut)
    .delay(0)
    .onUpdate(() => {
        // console.log(scene.animationGroups[0].targetedAnimations[0].animation.runtimeAnimations[0].currentFrame.toFixed(2));
        console.log(camPosTime.pos.toFixed(2));
        scene.animationGroups[0].goToFrame(camPosTime.pos);
    })
    .onComplete(() => {
        console.log('done');
        if (reset === true) {
            camPosTime.pos = 0;
            toFrame = nextInc;
            reset = false;
        } else {
            toFrame += nextInc;
        }
        scrollMovement = false;
    })
    .start();

It does what it should, but I’ll definitely try your solution tomorrow.
Thanks

1 Like

ok :slight_smile: when you look at it like that it does make sense :stuck_out_tongue:
thanks

1 Like

LOL animationGroup.currentFrame is so much easier to remember and type out than animationGroup.targetedAnimations[0]?.animation.runtimeAnimations[0]?.currentFrame. Would make a good PR I guess to add a “currentFrame” property to AnimationGroup? :slight_smile:

EDIT: or are the group’s animations not necessarily always normalized to have the same number of frames and always be at the same frame? I thought they were but now I’m not sure. :thinking:

Yep they are not necessarily :frowning:

Aah well, I suppose a generalized solution would be more complicated then, like returning the max currentFrame of all the runtimeAnimations or something… :slightly_frowning_face: :thinking:

Good point, but if there is a possibility that there are different currentFrame values within, it’s better to write a custom function to shorten this.

What was mainly a bit frustrating is having to look for this which made it quite a task :stuck_out_tongue:

Yep, that’s what I meant by using the max currentFrame instead… And def agree it’s frustrating to have to deduce or find/lookup a long, complicated way to get the current frame… :slight_smile: