Facebook
From Lafllamme, 2 Weeks ago, written in C.
This paste is a reply to animation.c from Gabriel Dechichi - go back
Embed
Viewing differences between animation.c and Re: animation.c
#include "animation.h"
#include "lib/array.h"
#include "lib/assert.h"
#include "lib/math.h"
#include "lib/typedefs.h"
#include "renderer.h"
#include "vendor/cglm/types.h"

void apply_joint_transform_recursive(Joint_Array joints,
                                     mat4_Array joint_matrices, u32 joint_idx,
                                     mat4 parent_transform) {
  mat4 *joint_transform = arr_get_ptr(joint_matrices, joint_idx);
  mat4_mul(parent_transform, *joint_transform, *joint_transform);
  Joint *joint = arr_get_ptr(joints, joint_idx);
  arr_foreach(joint->children, u32, child_idx) {
    apply_joint_transform_recursive(joints, joint_matrices, child_idx,
                                    *joint_transform);
  }

  mat4_mul(*joint_transform, joint->inverse_bind_matrix, *joint_transform);
}

b32 find_start_end_keyframe(AnimationState *animation,
                            _out_ i32 *start_keyframe_idx,
                            _out_ i32 *end_keyframe_idx, _out_ f32 *t) {
  debug_assert(animation->animation->keyframes.len > 0);
  if (animation->animation->keyframes.len <= 0) {
    return false;
  }

  u32 len = animation->animation->keyframes.len;
  f32 time = animation->time;

  // handle wrap case
  if (animation->animation->keyframes.items[0].timestamp >= time) {
    *start_keyframe_idx = len - 1;
    *end_keyframe_idx = 0;
    *t = lerp_inverse(
        animation->animation->keyframes.items[*start_keyframe_idx].timestamp,
        animation->animation->keyframes.items[*end_keyframe_idx].timestamp,
        animation->time);
    return true;
  }

  // binary search for the keyframe
  u32 left = 0;
  u32 right = len - 1;

  while (left < right) {
    u32 mid = left + (right - left) / 2;
    if (animation->animation->keyframes.items[mid].timestamp <= time) {
      left = mid + 1;
    } else {
      right = mid;
    }
  }

  // left is now the first keyframe with timestamp > time
  *start_keyframe_idx = left - 1;
  *end_keyframe_idx = left;
  *t = lerp_inverse(
      animation->animation->keyframes.items[*start_keyframe_idx].timestamp,
      animation->animation->keyframes.items[*end_keyframe_idx].timestamp,
      animation->time);

  return true;
}

void animation_update(AnimationState *animation, f32 dt) {
  f32 length = animation->animation->length;

  animation->time += dt * animation->speed;

  if (length > 0.0f) {
    // Use fmod to handle multiple wraps and negative values
    animation->time = fmodf(animation->time, length);

    // Ensure positive time for negative wrap-around
    if (animation->time < 0.0f) {
      animation->time += length;
    }
  }
}

void animation_evaluate(AnimationState *animation,
                        _out_ mat4_Array joint_matrices) {
  assert(animation && animation->animation);
  if (!animation || !animation->animation) {
    return;
  }

  i32 start_keyframe_idx, end_keyframe_idx;
  f32 percent;
  if (!find_start_end_keyframe(animation, &start;_keyframe_idx,
                               &end;_keyframe_idx, &percent;)) {
    return;
  }

  Keyframe *start_keyframe =
      &animation;->animation->keyframes.items[start_keyframe_idx];
  Keyframe *end_keyframe =
      &animation;->animation->keyframes.items[end_keyframe_idx];

  // evaluate all joints for keyframe and store local transforms
  for (u32 i = 0; i < start_keyframe->joint_transforms.len; i++) {
    KeyframeJointTransform *start_joint_transform =
        &start;_keyframe->joint_transforms.items[i];
    KeyframeJointTransform *end_joint_transform =
        &end;_keyframe->joint_transforms.items[i];

    // again, every keyframe defines every joint in the same order
    assert(start_joint_transform->index == end_joint_transform->index);

    vec3 translation;
    quaternion rotation;
    vec3_lerp(start_joint_transform->translation,
              end_joint_transform->translation, percent, translation);
    quat_slerp(start_joint_transform->rotation, end_joint_transform->rotation,
               percent, rotation);

    mat4 *joint_mat = arr_get_ptr(joint_matrices, start_joint_transform->index);
    debug_assert(joint_mat);
    if (!joint_mat) {
      continue;
    }

    mat_tr(translation, rotation, *joint_mat);
  }

  // apply hierarchy
  Model3DData *model = animation->animation->model;
  Joint_Array model_joints =
      arr_from_c_array(Joint, model->joints, model->len_joints);
  apply_joint_transform_recursive(model_joints, joint_matrices, 0,
                                  MAT4_IDENTITY);
}
looks good!