tsoding / tula

Turing Language
MIT License
164 stars 2 forks source link

suggestion for a different animation paradigm #2

Closed ratchetfreak closed 3 months ago

ratchetfreak commented 4 months ago

I've been following the development of the panim development (which unfortunately doesn't have a repo yet or I would've created a pull request there) and I believe that there is another viable and perhaps simpler way to describe animations in code.

This is based on Sebastian Lague's animation system. It uses a more immediate mode style api where every update you go over the entire animation code and decide what actually happens based on when in the animation you are.

The state required for this is:

typedef struct {
    float currentTime; // time since start of the animation preserved between frames
    float clipStartTime; // where the code is in the animation, updated by each "wait" operation, reset every update.
    float globEnd; // when the animation ends, reset every update.
} ImAnim;

Then you can get a float t from 0 to 1 based on where in the animation you are:

float clipTime(ImAnim* a, float duration) {
    long timeSinceStart = a->currentTime - a->clipStartTime;
    float animT = timeSinceStart / duration;
    if(animT < 0) //before the clip requested
        animT = 0f;
    if(animT > 1) //after the clip requested
        animT = 1f;

    if(a->globEnd < (a->clipStartTime + duration))
        a->globEnd = a->clipStartTime + duration;

    a->lastDur = duration;
    return animT;
}
void wait_for_end(ImAnim *a){
    a->clipStartTime = a->globEnd;
}

If it returns 0 then you are before the clip and should leave the animated object untouched, if it returns 1 then the clip is finished and the animated object should be as if the clip just finished.

A lerp would do the natural thing with the result.

Your shuffle squares example would look something like: (bugs might be present)

void imanim_move_Vector2(ImAnim *a, Vector2 *from, Vector2 to, float duration){
    float t = ease_in_out(clipTime(a, duration));
    from* = lerp(from*, to, t);
}

void shuffle_squares(ImAnim* a, Square *s1, Square *s2, Square *s3){
    imanim_move_Vector2(a, &s1->position, grid(1, 1), 0.25);
    imanim_move_Vector2(a, &s2->position, grid(0, 0), 0.25);
    imanim_move_Vector2(a, &s3->position, grid(0, 1), 0.25);
    wait_for_end(a);

    imanim_move_Vector4(a, &s1->color, RED, 0.25);
    imanim_move_Vector4(a, &s2->color, GREEN, 0.25);
    imanim_move_Vector4(a, &s3->color, BLUE, 0.25);
    wait_for_end(a);

    imanim_move_Vector2(a, &s3->position, grid(1, 0), 0.25);
    wait_for_end(a);

    imanim_move_Vector4(a, &s1->color, FOREGROUND_COLOR, 0.25);
    imanim_move_Vector4(a, &s2->color, FOREGROUND_COLOR, 0.25);
    imanim_move_Vector4(a, &s3->color, FOREGROUND_COLOR, 0.25);
    wait_for_end(a);

}

void reset_ImAnim(void* v){
    ImAnim *a = (ImAnim*)v;

    a-> currentTime  =0;
}

bool update_ImAnim_squares(Env* env, void* v){
    ImAnim *a = (ImAnim*)v;

    if(a->currentTime >= a->globEnd) return true;

    a->currentTime += env.delta_time;
    a-> clipStartTime=0;
    a-> globEnd      =0;

    // reset squares to initial position
       reset_squares();

    Square *s1 = &p->squares[0];
    Square *s2 = &p->squares[1];
    Square *s3 = &p->squares[2];

    shuffle_squares(a, s1, s2, s3);
    shuffle_squares(a, s2, s3, s1);
    shuffle_squares(a, s3, s1, s2);
    wait_for(a, 1.0f);

    return a->currentTime >= a->globEnd;
}

no allocation or function pointers required.

In addition you can seek to any point in the animation by setting the currentTime field to which frame you want to display.

rexim commented 3 months ago

Unrelated to the project

ratchetfreak commented 3 months ago

Fair enough. But like I said if your Panim repo was public I'd have posted this there. (and be able to properly test my alternative)