Implement linear interpolation between animation keyframes
This commit is contained in:
1 parent
381890892b
commit
f4096b5a07
6 files changed
+102
-28
No files matched your search
+66
-25
@@ -1,9 +1,44 @@
|
|||||||
#include "animation.h"
|
#include "animation.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.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)
|
static mat4_t bone_transform(bone_t bone)
|
||||||
{
|
{
|
||||||
return mat4_mul(
|
return mat4_mul(
|
||||||
@@ -27,6 +62,27 @@ static void compute_bone_matrices(
|
|||||||
matrices[i] = mat4_mul(matrices[i], inverse_bind_matrices[i]);
|
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)
|
animation_state_t animation_init(const model_t *model)
|
||||||
{
|
{
|
||||||
mat4_t *bone_matrices = malloc(model->bone_count * sizeof(mat4_t));
|
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->elapsed_us = 0;
|
||||||
|
state->current_keyframe = start_keyframe;
|
||||||
|
load_keyframe(state, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
void animation_update(
|
void animation_update(
|
||||||
@@ -60,27 +112,16 @@ void animation_update(
|
|||||||
state->elapsed_us += dt_us;
|
state->elapsed_us += dt_us;
|
||||||
if (state->elapsed_us >= state->duration_us) { // Move to next keyframe
|
if (state->elapsed_us >= state->duration_us) { // Move to next keyframe
|
||||||
state->elapsed_us -= state->duration_us;
|
state->elapsed_us -= state->duration_us;
|
||||||
|
|
||||||
state->current_keyframe = state->next_keyframe;
|
state->current_keyframe = state->next_keyframe;
|
||||||
const uint32_t keyframe_count = model->animation.keyframe_count;
|
load_keyframe(state, model);
|
||||||
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;
|
bone_t bones[MAX_BONE_COUNT];
|
||||||
const uint32_t offset = state->next_keyframe * model->bone_count;
|
interpolate(
|
||||||
state->next = model->animation.keyframes + offset;
|
bones, state->current, state->next, model->bone_count,
|
||||||
}
|
(double)state->elapsed_us / state->duration_us);
|
||||||
|
|
||||||
compute_bone_matrices(
|
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);
|
model->inverse_bind_matrices, state->bone_matrices);
|
||||||
}
|
}
|
||||||
+2
-1
@@ -13,7 +13,8 @@ typedef struct {
|
|||||||
animation_state_t animation_init(const model_t *model);
|
animation_state_t animation_init(const model_t *model);
|
||||||
void animation_cleanup(animation_state_t *state);
|
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);
|
||||||
void animation_update(
|
void animation_update(
|
||||||
animation_state_t *state, const model_t *model, uint32_t dt_us);
|
animation_state_t *state, const model_t *model, uint32_t dt_us);
|
||||||
|
|
||||||
|
|||||||
+26
-1
@@ -2,11 +2,21 @@
|
|||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
vec3_t vec3_add(vec3_t v, vec3_t u)
|
||||||
|
{
|
||||||
|
return (vec3_t) { v.x + u.x, v.y + u.y, v.z + u.z };
|
||||||
|
}
|
||||||
|
|
||||||
vec3_t vec3_sub(vec3_t v, vec3_t u)
|
vec3_t vec3_sub(vec3_t v, vec3_t u)
|
||||||
{
|
{
|
||||||
return (vec3_t) { v.x - u.x, v.y - u.y, v.z - u.z };
|
return (vec3_t) { v.x - u.x, v.y - u.y, v.z - u.z };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec3_t vec3_scale(float s, vec3_t v)
|
||||||
|
{
|
||||||
|
return (vec3_t) { s * v.x, s * v.y, s * v.z };
|
||||||
|
}
|
||||||
|
|
||||||
float vec3_len(vec3_t v)
|
float vec3_len(vec3_t v)
|
||||||
{
|
{
|
||||||
return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);
|
return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);
|
||||||
@@ -15,7 +25,7 @@ float vec3_len(vec3_t v)
|
|||||||
vec3_t vec3_norm(vec3_t v)
|
vec3_t vec3_norm(vec3_t v)
|
||||||
{
|
{
|
||||||
const float l = vec3_len(v);
|
const float l = vec3_len(v);
|
||||||
return (vec3_t) { v.x / l, v.y / l, v.z / l };
|
return vec3_scale(1 / l, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
float vec3_dot(vec3_t v, vec3_t u)
|
float vec3_dot(vec3_t v, vec3_t u)
|
||||||
@@ -32,6 +42,21 @@ vec3_t vec3_cross(vec3_t v, vec3_t u)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec4_t vec4_add(vec4_t v, vec4_t u)
|
||||||
|
{
|
||||||
|
return (vec4_t) { v.x + u.x, v.y + u.y, v.z + u.z, v.w + u.w };
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4_t vec4_scale(float s, vec4_t v)
|
||||||
|
{
|
||||||
|
return (vec4_t) { s * v.x, s * v.y, s * v.z, s * v.w };
|
||||||
|
}
|
||||||
|
|
||||||
|
float vec4_dot(vec4_t v, vec4_t u)
|
||||||
|
{
|
||||||
|
return v.x * u.x + v.y * u.y + v.z * u.z + v.w * u.w;
|
||||||
|
}
|
||||||
|
|
||||||
mat4_t mat4_mul(mat4_t m, mat4_t n)
|
mat4_t mat4_mul(mat4_t m, mat4_t n)
|
||||||
{
|
{
|
||||||
#define ELEM(i, j) m.x.i *n.j.x + m.y.i *n.j.y + m.z.i *n.j.z + m.w.i *n.j.w
|
#define ELEM(i, j) m.x.i *n.j.x + m.y.i *n.j.y + m.z.i *n.j.z + m.w.i *n.j.w
|
||||||
|
|||||||
@@ -71,12 +71,18 @@ typedef struct {
|
|||||||
}
|
}
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
vec3_t vec3_add(vec3_t v, vec3_t u);
|
||||||
vec3_t vec3_sub(vec3_t v, vec3_t u);
|
vec3_t vec3_sub(vec3_t v, vec3_t u);
|
||||||
|
vec3_t vec3_scale(float s, vec3_t v);
|
||||||
float vec3_len(vec3_t v);
|
float vec3_len(vec3_t v);
|
||||||
vec3_t vec3_norm(vec3_t v);
|
vec3_t vec3_norm(vec3_t v);
|
||||||
float vec3_dot(vec3_t v, vec3_t u);
|
float vec3_dot(vec3_t v, vec3_t u);
|
||||||
vec3_t vec3_cross(vec3_t v, vec3_t u);
|
vec3_t vec3_cross(vec3_t v, vec3_t u);
|
||||||
|
|
||||||
|
vec4_t vec4_add(vec4_t v, vec4_t u);
|
||||||
|
vec4_t vec4_scale(float s, vec4_t v);
|
||||||
|
float vec4_dot(vec4_t v, vec4_t u);
|
||||||
|
|
||||||
mat4_t mat4_mul(mat4_t m, mat4_t n);
|
mat4_t mat4_mul(mat4_t m, mat4_t n);
|
||||||
mat4_t mat4_lookat(vec3_t camera, vec3_t target, vec3_t up);
|
mat4_t mat4_lookat(vec3_t camera, vec3_t target, vec3_t up);
|
||||||
mat4_t mat4_perspective(float aspect, float fov, float near, float far);
|
mat4_t mat4_perspective(float aspect, float fov, float near, float far);
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define MAX_BONE_COUNT 64
|
||||||
|
|
||||||
typedef uint16_t index_t;
|
typedef uint16_t index_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ static_assert(
|
|||||||
#define VERSION 2
|
#define VERSION 2
|
||||||
|
|
||||||
#define MAX_STRING_SIZE 128
|
#define MAX_STRING_SIZE 128
|
||||||
#define MAX_BONE_COUNT 32
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
CHUNK_TYPE_JSON = 0x4e4f534a, // "JSON"
|
CHUNK_TYPE_JSON = 0x4e4f534a, // "JSON"
|
||||||
|
|||||||
Reference in new issue
Block a user