Created
June 19, 2025 21:13
-
-
Save gabrieldechichi/c1043c77867a64d188a37a949fcee1f1 to your computer and use it in GitHub Desktop.
C Engine - animation.c - 250519
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment