Implement linear interpolation between animation keyframes

This commit is contained in:
Camden Dixie O'Brien committed 2025-10-02 17:28:03 +01:00
1 parent 381890892b
commit f4096b5a07
6 files changed
+103 -29

No files matched your search

+67 -26
View File
@@ -1,9 +1,44 @@
#include "animation.h"
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
static vec3_t lerp(vec3_t v, vec3_t u, double t)
{
return vec3_add(vec3_scale(1 - t, v), vec3_scale(t, u));
}
static vec4_t slerp(vec4_t v, vec4_t u, double t)
{
float dot = vec4_dot(v, u);
if (dot < 0) {
dot = -dot;
u = vec4_scale(-1, u);
}
if (dot < 0.95) {
const float a = acosf(dot);
return vec4_scale(
1 / sin(a),
vec4_add(
vec4_scale(sinf(a * (1 - t)), v),
vec4_scale(sinf(a * t), u)));
} else {
return vec4_add(vec4_scale(1 - t, v), vec4_scale(t, u));
}
}
static void interpolate(
bone_t *dst, const bone_t *as, const bone_t *bs, size_t n, double t)
{
for (unsigned i = 0; i < n; ++i) {
dst[i].translation = lerp(as[i].translation, bs[i].translation, t);
dst[i].rotation = slerp(as[i].rotation, bs[i].rotation, t);
}
}
static mat4_t bone_transform(bone_t bone)
{
return mat4_mul(
@@ -27,6 +62,27 @@ static void compute_bone_matrices(
matrices[i] = mat4_mul(matrices[i], inverse_bind_matrices[i]);
}
static void load_keyframe(animation_state_t *state, const model_t *model)
{
const uint32_t keyframe_count = model->animation.keyframe_count;
if (state->current_keyframe + 1 < keyframe_count) {
state->next_keyframe = state->current_keyframe + 1;
state->duration_us = model->animation.times_us[state->next_keyframe]
- model->animation.times_us[state->current_keyframe];
} else {
// Handle loop-around
state->next_keyframe = 0;
state->duration_us = model->animation.length_us
- model->animation.times_us[state->current_keyframe]
+ model->animation.times_us[state->next_keyframe];
}
state->current = model->animation.keyframes
+ state->current_keyframe * model->bone_count;
state->next = model->animation.keyframes
+ state->next_keyframe * model->bone_count;
}
animation_state_t animation_init(const model_t *model)
{
mat4_t *bone_matrices = malloc(model->bone_count * sizeof(mat4_t));
@@ -42,16 +98,12 @@ void animation_cleanup(animation_state_t *state)
}
}
void animation_start(animation_state_t *state, const model_t *model)
void animation_start(
animation_state_t *state, const model_t *model, uint32_t start_keyframe)
{
state->current_keyframe = model->animation.keyframe_count - 1;
state->next_keyframe = 0;
state->current = model->bones;
state->next = model->animation.keyframes;
state->duration_us = model->animation.times_us[0];
state->elapsed_us = 0;
state->current_keyframe = start_keyframe;
load_keyframe(state, model);
}
void animation_update(
@@ -60,27 +112,16 @@ void animation_update(
state->elapsed_us += dt_us;
if (state->elapsed_us >= state->duration_us) { // Move to next keyframe
state->elapsed_us -= state->duration_us;
state->current_keyframe = state->next_keyframe;
const uint32_t keyframe_count = model->animation.keyframe_count;
if (state->current_keyframe + 1 < keyframe_count) {
state->next_keyframe = state->current_keyframe + 1;
state->duration_us
= model->animation.times_us[state->next_keyframe]
- model->animation.times_us[state->current_keyframe];
} else {
// Handle loop-around
state->next_keyframe = 0;
state->duration_us
= model->animation.times_us[state->next_keyframe];
}
state->current = state->next;
const uint32_t offset = state->next_keyframe * model->bone_count;
state->next = model->animation.keyframes + offset;
load_keyframe(state, model);
}
bone_t bones[MAX_BONE_COUNT];
interpolate(
bones, state->current, state->next, model->bone_count,
(double)state->elapsed_us / state->duration_us);
compute_bone_matrices(
state->current, model->bone_count, model->bone_parents,
bones, model->bone_count, model->bone_parents,
model->inverse_bind_matrices, state->bone_matrices);
}