diff options
| author | 3gg <3gg@shellblade.net> | 2025-10-30 20:08:53 -0700 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2025-10-30 20:08:53 -0700 |
| commit | 6fde17649f7404e79a17d4d8f96662d03011aca1 (patch) | |
| tree | a93738b26dab567fe551b62e2a4bd1b1556460b1 /src/scene | |
| parent | 440b292c39162284a447b34d3a692143af9fbc87 (diff) | |
Move animation module outside of scene/
Diffstat (limited to 'src/scene')
| -rw-r--r-- | src/scene/animation.c | 518 | ||||
| -rw-r--r-- | src/scene/animation_impl.h | 96 | ||||
| -rw-r--r-- | src/scene/node_impl.h | 2 | ||||
| -rw-r--r-- | src/scene/object_impl.h | 2 | ||||
| -rw-r--r-- | src/scene/scene_impl.h | 2 | ||||
| -rw-r--r-- | src/scene/types.h | 24 |
6 files changed, 3 insertions, 641 deletions
diff --git a/src/scene/animation.c b/src/scene/animation.c deleted file mode 100644 index a498961..0000000 --- a/src/scene/animation.c +++ /dev/null | |||
| @@ -1,518 +0,0 @@ | |||
| 1 | #include "animation_impl.h" | ||
| 2 | |||
| 3 | #include "memory.h" | ||
| 4 | #include "node_impl.h" | ||
| 5 | |||
| 6 | #include <string.h> | ||
| 7 | |||
| 8 | // #include <log/log.h> // Debugging. | ||
| 9 | |||
| 10 | static const R PLAYBACK_UNINITIALIZED = -1; | ||
| 11 | |||
| 12 | static joint_idx get_anima_root_joint_index(Anima* anima) { | ||
| 13 | assert(anima); | ||
| 14 | assert(anima->num_joints > 0); | ||
| 15 | assert(anima->num_joints < GFX_MAX_NUM_JOINTS); | ||
| 16 | return anima->num_joints - 1; | ||
| 17 | } | ||
| 18 | |||
| 19 | static Joint* get_anima_root_joint(Anima* anima) { | ||
| 20 | assert(anima); | ||
| 21 | return &anima->joints[get_anima_root_joint_index(anima)]; | ||
| 22 | } | ||
| 23 | |||
| 24 | static const Joint* get_anima_joint(const Anima* anima, joint_idx index) { | ||
| 25 | assert(anima); | ||
| 26 | assert(index < GFX_MAX_NUM_JOINTS); | ||
| 27 | assert(index != INDEX_NONE); | ||
| 28 | assert(index < anima->num_joints); | ||
| 29 | return &anima->joints[index]; | ||
| 30 | } | ||
| 31 | |||
| 32 | static Joint* get_anima_joint_mut(Anima* anima, joint_idx index) { | ||
| 33 | return (Joint*)get_anima_joint(anima, index); | ||
| 34 | } | ||
| 35 | |||
| 36 | static const Joint* get_skeleton_joint( | ||
| 37 | const Anima* anima, const Skeleton* skeleton, joint_idx index) { | ||
| 38 | assert(anima); | ||
| 39 | assert(skeleton); | ||
| 40 | return get_anima_joint(anima, skeleton->joints[index]); | ||
| 41 | } | ||
| 42 | |||
| 43 | static void set_joint_parent( | ||
| 44 | Anima* anima, joint_idx joint_index, joint_idx parent_index) { | ||
| 45 | assert(anima); | ||
| 46 | assert(joint_index != INDEX_NONE); | ||
| 47 | assert(joint_index != get_anima_root_joint_index(anima)); | ||
| 48 | assert(parent_index != INDEX_NONE); | ||
| 49 | |||
| 50 | Joint* parent = get_anima_joint_mut(anima, parent_index); | ||
| 51 | |||
| 52 | if (parent->child == INDEX_NONE) { | ||
| 53 | parent->child = joint_index; | ||
| 54 | } else { | ||
| 55 | // Find the last child in the chain of children. | ||
| 56 | Joint* child = get_anima_joint_mut(anima, parent->child); | ||
| 57 | while (child->next != INDEX_NONE) { | ||
| 58 | child = get_anima_joint_mut(anima, child->next); | ||
| 59 | } | ||
| 60 | // Wire up this joint as the last child's sibling. | ||
| 61 | child->next = joint_index; | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | static void make_joint(Anima* anima, const JointDesc* desc, Joint* joint) { | ||
| 66 | assert(anima); | ||
| 67 | assert(desc); | ||
| 68 | assert(joint); | ||
| 69 | |||
| 70 | // The joint matrix needs to be initialized so that meshes look right even if | ||
| 71 | // no animation is played. Initializing joint matrices to the identity makes | ||
| 72 | // meshes appear in their bind pose. | ||
| 73 | joint->child = INDEX_NONE; | ||
| 74 | joint->next = INDEX_NONE; | ||
| 75 | joint->transform = mat4_id(); | ||
| 76 | joint->inv_bind_matrix = desc->inv_bind_matrix; | ||
| 77 | joint->joint_matrix = mat4_id(); | ||
| 78 | joint->box = desc->box; | ||
| 79 | } | ||
| 80 | |||
| 81 | static Skeleton* make_skeleton(const SkeletonDesc* desc) { | ||
| 82 | assert(desc); | ||
| 83 | assert(desc->num_joints <= GFX_MAX_NUM_JOINTS); | ||
| 84 | |||
| 85 | Skeleton* skeleton = mem_alloc_skeleton(); | ||
| 86 | skeleton->num_joints = desc->num_joints; | ||
| 87 | memcpy( | ||
| 88 | skeleton->joints, desc->joints, | ||
| 89 | desc->num_joints * sizeof(skeleton->joints[0])); | ||
| 90 | return skeleton; | ||
| 91 | } | ||
| 92 | |||
| 93 | static Animation* make_animation(const AnimationDesc* desc) { | ||
| 94 | assert(desc); | ||
| 95 | assert(desc->num_channels < GFX_MAX_NUM_CHANNELS); | ||
| 96 | |||
| 97 | Animation* animation = mem_alloc_animation(); | ||
| 98 | animation->name = desc->name; | ||
| 99 | animation->duration = 0; | ||
| 100 | animation->num_channels = desc->num_channels; | ||
| 101 | R start_time = 0; | ||
| 102 | R end_time = 0; | ||
| 103 | |||
| 104 | for (size_t c = 0; c < desc->num_channels; ++c) { | ||
| 105 | const ChannelDesc* channel_desc = &desc->channels[c]; | ||
| 106 | Channel* channel = &animation->channels[c]; | ||
| 107 | |||
| 108 | channel->target = channel_desc->target; | ||
| 109 | channel->type = channel_desc->type; | ||
| 110 | channel->interpolation = channel_desc->interpolation; | ||
| 111 | channel->num_keyframes = channel_desc->num_keyframes; | ||
| 112 | assert(channel_desc->num_keyframes < GFX_MAX_NUM_KEYFRAMES); | ||
| 113 | |||
| 114 | for (size_t k = 0; k < channel_desc->num_keyframes; ++k) { | ||
| 115 | const KeyframeDesc* keyframe_desc = &channel_desc->keyframes[k]; | ||
| 116 | Keyframe* keyframe = &channel->keyframes[k]; | ||
| 117 | |||
| 118 | keyframe->time = keyframe_desc->time; | ||
| 119 | keyframe->translation = keyframe_desc->translation; | ||
| 120 | keyframe->rotation = keyframe_desc->rotation; | ||
| 121 | |||
| 122 | start_time = keyframe->time < start_time ? keyframe->time : start_time; | ||
| 123 | end_time = keyframe->time > end_time ? keyframe->time : end_time; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | // LOGD("Animation start/end: %f / %f", start_time, end_time); | ||
| 128 | animation->duration = end_time - start_time; | ||
| 129 | assert(animation->duration >= 0); | ||
| 130 | return animation; | ||
| 131 | } | ||
| 132 | |||
| 133 | Anima* gfx_make_anima(const AnimaDesc* desc) { | ||
| 134 | assert(desc); | ||
| 135 | assert(desc->num_joints > 0); | ||
| 136 | assert(desc->num_joints <= GFX_MAX_NUM_JOINTS); | ||
| 137 | // All joints should have a parent except for the root. | ||
| 138 | for (size_t i = 0; i < desc->num_joints - 1; ++i) { | ||
| 139 | const joint_idx parent = desc->joints[i].parent; | ||
| 140 | assert(parent != INDEX_NONE); | ||
| 141 | assert(parent < desc->num_joints); | ||
| 142 | } | ||
| 143 | // The root should have no parent. | ||
| 144 | assert(desc->joints[desc->num_joints - 1].parent == INDEX_NONE); | ||
| 145 | |||
| 146 | Anima* anima = mem_alloc_anima(); | ||
| 147 | |||
| 148 | // Wire the skeletons in the same order they are given in the descriptor. | ||
| 149 | Skeleton* last_skeleton = 0; | ||
| 150 | for (size_t i = 0; i < desc->num_skeletons; ++i) { | ||
| 151 | Skeleton* skeleton = make_skeleton(&desc->skeletons[i]); | ||
| 152 | const skeleton_idx skeleton_index = mem_get_skeleton_index(skeleton); | ||
| 153 | if (last_skeleton == 0) { | ||
| 154 | anima->skeleton = skeleton_index; | ||
| 155 | } else { | ||
| 156 | last_skeleton->next = skeleton_index; | ||
| 157 | } | ||
| 158 | last_skeleton = skeleton; | ||
| 159 | } | ||
| 160 | |||
| 161 | // Wire the animations in the same order they are given in the descriptor. | ||
| 162 | Animation* last_animation = 0; | ||
| 163 | for (size_t i = 0; i < desc->num_animations; ++i) { | ||
| 164 | Animation* animation = make_animation(&desc->animations[i]); | ||
| 165 | const animation_idx animation_index = mem_get_animation_index(animation); | ||
| 166 | if (last_animation == 0) { | ||
| 167 | anima->animation = animation_index; | ||
| 168 | } else { | ||
| 169 | last_animation->next = animation_index; | ||
| 170 | } | ||
| 171 | last_animation = animation; | ||
| 172 | } | ||
| 173 | |||
| 174 | // Create joints. | ||
| 175 | anima->num_joints = desc->num_joints; | ||
| 176 | // Initialize all joints. | ||
| 177 | // Child and sibling pointers must be initialized before wiring up the | ||
| 178 | // hierarchy. | ||
| 179 | for (size_t i = 0; i < desc->num_joints; ++i) { | ||
| 180 | Joint* joint = get_anima_joint_mut(anima, i); | ||
| 181 | make_joint(anima, &desc->joints[i], joint); | ||
| 182 | } | ||
| 183 | // Wire up joints to their parents. -1 to skip the root. | ||
| 184 | for (size_t i = 0; i < desc->num_joints - 1; ++i) { | ||
| 185 | set_joint_parent(anima, i, desc->joints[i].parent); | ||
| 186 | } | ||
| 187 | |||
| 188 | return anima; | ||
| 189 | } | ||
| 190 | |||
| 191 | void gfx_destroy_anima(Anima** anima) { | ||
| 192 | assert(anima); | ||
| 193 | |||
| 194 | if (*anima) { | ||
| 195 | for (skeleton_idx i = (*anima)->skeleton; i.val != 0;) { | ||
| 196 | Skeleton* skeleton = mem_get_skeleton(i); | ||
| 197 | i = skeleton->next; | ||
| 198 | mem_free_skeleton(&skeleton); | ||
| 199 | } | ||
| 200 | |||
| 201 | for (animation_idx i = (*anima)->animation; i.val != 0;) { | ||
| 202 | Animation* animation = mem_get_animation(i); | ||
| 203 | i = animation->next; | ||
| 204 | mem_free_animation(&animation); | ||
| 205 | } | ||
| 206 | |||
| 207 | mem_free_anima(anima); | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | static Animation* find_animation(animation_idx index, const char* name) { | ||
| 212 | assert(name); | ||
| 213 | |||
| 214 | while (index.val != 0) { | ||
| 215 | Animation* animation = mem_get_animation(index); | ||
| 216 | if (sstring_eq_cstr(animation->name, name)) { | ||
| 217 | // LOGD( | ||
| 218 | // "Found animation at index %u, %s - %s", index.val, | ||
| 219 | // sstring_cstr(&animation->name), name); | ||
| 220 | // LOGD("Animation has duration %f", animation->duration); | ||
| 221 | return animation; | ||
| 222 | } | ||
| 223 | index = animation->next; | ||
| 224 | } | ||
| 225 | |||
| 226 | return 0; | ||
| 227 | } | ||
| 228 | |||
| 229 | bool gfx_play_animation(Anima* anima, const AnimationPlaySettings* settings) { | ||
| 230 | assert(anima); | ||
| 231 | assert(settings); | ||
| 232 | |||
| 233 | // TODO: Should we animate at t=0 here to kickstart the animation? Otherwise | ||
| 234 | // the client is forced to call gfx_update_animation() to do this. | ||
| 235 | Animation* animation = find_animation(anima->animation, settings->name); | ||
| 236 | if (!animation) { | ||
| 237 | return false; | ||
| 238 | } | ||
| 239 | // Playback initialized on first call to update(). | ||
| 240 | AnimationState* state = &anima->state; | ||
| 241 | state->start_time = PLAYBACK_UNINITIALIZED; | ||
| 242 | state->animation = mem_get_animation_index(animation); | ||
| 243 | state->loop = settings->loop; | ||
| 244 | return true; | ||
| 245 | } | ||
| 246 | |||
| 247 | static void gfx_set_joint_position(Joint* joint, vec3 position) { | ||
| 248 | assert(joint); | ||
| 249 | mat4_set_v3(&joint->transform, position); | ||
| 250 | } | ||
| 251 | |||
| 252 | static void gfx_set_joint_rotation(Joint* joint, quat rotation) { | ||
| 253 | assert(joint); | ||
| 254 | mat4_set_3x3(&joint->transform, mat4_from_quat(rotation)); | ||
| 255 | } | ||
| 256 | |||
| 257 | static void find_keyframes(const Channel* channel, R t, int* prev, int* next) { | ||
| 258 | assert(channel); | ||
| 259 | assert(prev); | ||
| 260 | assert(next); | ||
| 261 | |||
| 262 | *prev = -1; | ||
| 263 | *next = 0; | ||
| 264 | while (((*next + 1) < (int)channel->num_keyframes) && | ||
| 265 | (t >= channel->keyframes[*next + 1].time)) { | ||
| 266 | (*prev)++; | ||
| 267 | (*next)++; | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | static R normalize_time(R a, R b, R t) { | ||
| 272 | assert(a <= t); | ||
| 273 | assert(t <= b); | ||
| 274 | return (t - a) / (b - a); | ||
| 275 | } | ||
| 276 | |||
| 277 | static quat interpolate_rotation( | ||
| 278 | const Channel* channel, int prev, int next, R t) { | ||
| 279 | assert(channel); | ||
| 280 | |||
| 281 | if (next == 0) { | ||
| 282 | // Animation has not started at this point in time yet. | ||
| 283 | return channel->keyframes[next].rotation; | ||
| 284 | } else { | ||
| 285 | switch (channel->interpolation) { | ||
| 286 | case StepInterpolation: | ||
| 287 | return channel->keyframes[prev].rotation; | ||
| 288 | case LinearInterpolation: { | ||
| 289 | const R normalized_t = normalize_time( | ||
| 290 | channel->keyframes[prev].time, channel->keyframes[next].time, t); | ||
| 291 | return qnormalize(qslerp( | ||
| 292 | channel->keyframes[prev].rotation, channel->keyframes[next].rotation, | ||
| 293 | normalized_t)); | ||
| 294 | break; | ||
| 295 | } | ||
| 296 | case CubicSplineInterpolation: | ||
| 297 | assert(false); // TODO | ||
| 298 | return qmake(0, 0, 0, 0); | ||
| 299 | default: | ||
| 300 | assert(false); | ||
| 301 | return qmake(0, 0, 0, 0); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | static vec3 interpolate_translation( | ||
| 307 | const Channel* channel, int prev, int next, R t) { | ||
| 308 | assert(channel); | ||
| 309 | |||
| 310 | if (next == 0) { | ||
| 311 | // Animation has not started at this point in time yet. | ||
| 312 | return channel->keyframes[next].translation; | ||
| 313 | } else { | ||
| 314 | switch (channel->interpolation) { | ||
| 315 | case StepInterpolation: | ||
| 316 | return channel->keyframes[prev].translation; | ||
| 317 | case LinearInterpolation: { | ||
| 318 | const R normalized_t = normalize_time( | ||
| 319 | channel->keyframes[prev].time, channel->keyframes[next].time, t); | ||
| 320 | return vec3_lerp( | ||
| 321 | channel->keyframes[prev].translation, | ||
| 322 | channel->keyframes[next].translation, normalized_t); | ||
| 323 | break; | ||
| 324 | } | ||
| 325 | case CubicSplineInterpolation: | ||
| 326 | assert(false); // TODO | ||
| 327 | return vec3_make(0, 0, 0); | ||
| 328 | default: | ||
| 329 | assert(false); | ||
| 330 | return vec3_make(0, 0, 0); | ||
| 331 | } | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | static void animate_channel(Anima* anima, const Channel* channel, R t) { | ||
| 336 | assert(anima); | ||
| 337 | assert(channel); | ||
| 338 | assert(channel->target < anima->num_joints); | ||
| 339 | |||
| 340 | int prev, next; | ||
| 341 | find_keyframes(channel, t, &prev, &next); | ||
| 342 | |||
| 343 | // Note that not all channels extend to the duration of an animation; some | ||
| 344 | // channels may stop animating their targets earlier. Clamp the animation time | ||
| 345 | // to the channel's end keyframe to make the rest of the math (normalize_time) | ||
| 346 | // work. | ||
| 347 | t = t > channel->keyframes[next].time ? channel->keyframes[next].time : t; | ||
| 348 | |||
| 349 | Joint* target = get_anima_joint_mut(anima, channel->target); | ||
| 350 | |||
| 351 | switch (channel->type) { | ||
| 352 | case RotationChannel: { | ||
| 353 | const quat rotation = interpolate_rotation(channel, prev, next, t); | ||
| 354 | gfx_set_joint_rotation(target, rotation); | ||
| 355 | break; | ||
| 356 | } | ||
| 357 | case TranslationChannel: { | ||
| 358 | const vec3 translation = interpolate_translation(channel, prev, next, t); | ||
| 359 | gfx_set_joint_position(target, translation); | ||
| 360 | break; | ||
| 361 | } | ||
| 362 | // Not yet supported. | ||
| 363 | case ScaleChannel: | ||
| 364 | case WeightsChannel: | ||
| 365 | default: | ||
| 366 | // TODO: Add back the assertion or add support for scaling. | ||
| 367 | // assert(false); | ||
| 368 | break; | ||
| 369 | } | ||
| 370 | } | ||
| 371 | |||
| 372 | static void compute_joint_matrices_rec( | ||
| 373 | Anima* anima, Joint* joint, const mat4* parent_global_joint_transform, | ||
| 374 | const mat4* root_inv_global_transform) { | ||
| 375 | assert(anima); | ||
| 376 | assert(joint); | ||
| 377 | assert(parent_global_joint_transform); | ||
| 378 | assert(root_inv_global_transform); | ||
| 379 | |||
| 380 | const mat4 global_joint_transform = | ||
| 381 | mat4_mul(*parent_global_joint_transform, joint->transform); | ||
| 382 | |||
| 383 | // Compute this joint's matrix. | ||
| 384 | joint->joint_matrix = mat4_mul( | ||
| 385 | *root_inv_global_transform, | ||
| 386 | mat4_mul(global_joint_transform, joint->inv_bind_matrix)); | ||
| 387 | |||
| 388 | // Recursively compute the joint matrices for this joint's siblings. | ||
| 389 | if (joint->next != INDEX_NONE) { | ||
| 390 | Joint* sibling = get_anima_joint_mut(anima, joint->next); | ||
| 391 | |||
| 392 | compute_joint_matrices_rec( | ||
| 393 | anima, sibling, parent_global_joint_transform, | ||
| 394 | root_inv_global_transform); | ||
| 395 | } | ||
| 396 | |||
| 397 | // Recursively compute the joint matrices for this joint's children. | ||
| 398 | if (joint->child != INDEX_NONE) { | ||
| 399 | Joint* child = get_anima_joint_mut(anima, joint->child); | ||
| 400 | |||
| 401 | compute_joint_matrices_rec( | ||
| 402 | anima, child, &global_joint_transform, root_inv_global_transform); | ||
| 403 | } | ||
| 404 | } | ||
| 405 | |||
| 406 | void gfx_update_animation(Anima* anima, R t) { | ||
| 407 | assert(anima); | ||
| 408 | |||
| 409 | AnimationState* state = &anima->state; | ||
| 410 | if (state->animation.val == 0) { | ||
| 411 | return; // No active animation. | ||
| 412 | } | ||
| 413 | const Animation* animation = mem_get_animation(state->animation); | ||
| 414 | assert(animation); | ||
| 415 | |||
| 416 | // On a call to play(), the start time is set to -1 to signal that the | ||
| 417 | // animation playback has not yet been initialized. | ||
| 418 | if (state->start_time == PLAYBACK_UNINITIALIZED) { | ||
| 419 | state->start_time = t; | ||
| 420 | } | ||
| 421 | // Locate the current time point inside the animation's timeline. | ||
| 422 | assert(t >= state->start_time); | ||
| 423 | assert(animation->duration >= 0.0); | ||
| 424 | const R local_time = t - state->start_time; | ||
| 425 | const R animation_time = state->loop | ||
| 426 | ? rmod(local_time, animation->duration) | ||
| 427 | : clamp(local_time, 0.0, animation->duration); | ||
| 428 | |||
| 429 | // LOGD( | ||
| 430 | // "animation_time = %f, animation duration: %f", animation_time, | ||
| 431 | // animation->duration); | ||
| 432 | |||
| 433 | // Play through the animation to transform skeleton nodes. | ||
| 434 | for (size_t i = 0; i < animation->num_channels; ++i) { | ||
| 435 | const Channel* channel = &animation->channels[i]; | ||
| 436 | animate_channel(anima, channel, animation_time); | ||
| 437 | } | ||
| 438 | |||
| 439 | // Compute joint matrices after having transformed the skeletons. | ||
| 440 | // | ||
| 441 | // The anima's parent node is the common ancestor of all skeletons, and its | ||
| 442 | // transform maps the skeletons from object space to world space. This is the | ||
| 443 | // transform used as the "global transform" in the joint matrix equations. | ||
| 444 | // | ||
| 445 | // Joint matrix calculation begins by descending from the anima's root joint, | ||
| 446 | // which we have constructed to be the common root of all skeletons. | ||
| 447 | // | ||
| 448 | // This procedure touches every joint exactly once. | ||
| 449 | const mat4 root_global_transform = mat4_id(); | ||
| 450 | const mat4 root_inv_global_transform = mat4_id(); | ||
| 451 | |||
| 452 | Joint* root_joint = get_anima_root_joint(anima); | ||
| 453 | compute_joint_matrices_rec( | ||
| 454 | anima, root_joint, &root_global_transform, &root_inv_global_transform); | ||
| 455 | } | ||
| 456 | |||
| 457 | const Skeleton* gfx_get_anima_skeleton(const Anima* anima, size_t i) { | ||
| 458 | assert(anima); | ||
| 459 | |||
| 460 | skeleton_idx skeleton_index = anima->skeleton; | ||
| 461 | const Skeleton* skeleton = mem_get_skeleton(skeleton_index); | ||
| 462 | |||
| 463 | for (size_t j = 1; j < i; ++j) { | ||
| 464 | skeleton_index = skeleton->next; | ||
| 465 | mem_get_skeleton(skeleton_index); | ||
| 466 | } | ||
| 467 | |||
| 468 | return skeleton; | ||
| 469 | } | ||
| 470 | |||
| 471 | size_t gfx_get_skeleton_num_joints(const Skeleton* skeleton) { | ||
| 472 | assert(skeleton); | ||
| 473 | return skeleton->num_joints; | ||
| 474 | } | ||
| 475 | |||
| 476 | bool gfx_joint_has_box( | ||
| 477 | const Anima* anima, const Skeleton* skeleton, size_t joint_index) { | ||
| 478 | assert(anima); | ||
| 479 | assert(skeleton); | ||
| 480 | assert(joint_index < skeleton->num_joints); | ||
| 481 | |||
| 482 | const Joint* joint = get_skeleton_joint(anima, skeleton, joint_index); | ||
| 483 | return !aabb3_is_empty(joint->box); | ||
| 484 | } | ||
| 485 | |||
| 486 | Box gfx_get_joint_box( | ||
| 487 | const Anima* anima, const Skeleton* skeleton, size_t joint_index) { | ||
| 488 | assert(anima); | ||
| 489 | assert(skeleton); | ||
| 490 | |||
| 491 | const Joint* joint = get_skeleton_joint(anima, skeleton, joint_index); | ||
| 492 | |||
| 493 | // Transform the box to anima space. | ||
| 494 | // Note that joint matrices do not usually have a translation since joints | ||
| 495 | // mostly just rotate with respect to their parent. | ||
| 496 | const vec3 pmin = joint->box.min; | ||
| 497 | const vec3 pmax = joint->box.max; | ||
| 498 | return (Box){ | ||
| 499 | .vertices = { | ||
| 500 | mat4_mul_vec3( | ||
| 501 | joint->joint_matrix, vec3_make(pmin.x, pmin.y, pmax.z), 1), | ||
| 502 | mat4_mul_vec3( | ||
| 503 | joint->joint_matrix, vec3_make(pmax.x, pmin.y, pmax.z), 1), | ||
| 504 | mat4_mul_vec3( | ||
| 505 | joint->joint_matrix, vec3_make(pmax.x, pmax.y, pmax.z), 1), | ||
| 506 | mat4_mul_vec3( | ||
| 507 | joint->joint_matrix, vec3_make(pmin.x, pmax.y, pmax.z), 1), | ||
| 508 | mat4_mul_vec3( | ||
| 509 | joint->joint_matrix, vec3_make(pmin.x, pmin.y, pmin.z), 1), | ||
| 510 | mat4_mul_vec3( | ||
| 511 | joint->joint_matrix, vec3_make(pmax.x, pmin.y, pmin.z), 1), | ||
| 512 | mat4_mul_vec3( | ||
| 513 | joint->joint_matrix, vec3_make(pmax.x, pmax.y, pmin.z), 1), | ||
| 514 | mat4_mul_vec3( | ||
| 515 | joint->joint_matrix, vec3_make(pmin.x, pmax.y, pmin.z), 1), | ||
| 516 | } | ||
| 517 | }; | ||
| 518 | } | ||
diff --git a/src/scene/animation_impl.h b/src/scene/animation_impl.h deleted file mode 100644 index ef1492c..0000000 --- a/src/scene/animation_impl.h +++ /dev/null | |||
| @@ -1,96 +0,0 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <gfx/scene/animation.h> | ||
| 4 | #include <gfx/sizes.h> | ||
| 5 | |||
| 6 | #include "types.h" | ||
| 7 | |||
| 8 | #include <cstring.h> | ||
| 9 | #include <math/defs.h> | ||
| 10 | #include <math/mat4.h> | ||
| 11 | #include <math/quat.h> | ||
| 12 | #include <math/vec3.h> | ||
| 13 | |||
| 14 | #include <stddef.h> | ||
| 15 | |||
| 16 | typedef struct Buffer Buffer; | ||
| 17 | |||
| 18 | // Currently ignoring scale in skinning and animation. | ||
| 19 | // | ||
| 20 | // TODO: Simultaneous animation of disjoint animations. | ||
| 21 | |||
| 22 | /// Skeleton joint. | ||
| 23 | /// Joints are mutable and store the transform and joint matrices that result | ||
| 24 | /// from animation, aside from the inverse bind matrix. | ||
| 25 | typedef struct Joint { | ||
| 26 | joint_idx child; // First child Joint; index into Anima's joints. | ||
| 27 | joint_idx next; // Next sibling Joint; index into Anima's joints. | ||
| 28 | mat4 transform; // Local transform relative to parent. | ||
| 29 | mat4 inv_bind_matrix; // Transforms the mesh into the joint's local space. | ||
| 30 | mat4 joint_matrix; // inv(global) * global joint transform * inv(bind). | ||
| 31 | aabb3 box; // Bounding box of vertices affected by joint. | ||
| 32 | } Joint; | ||
| 33 | |||
| 34 | /// Animation skeleton. | ||
| 35 | typedef struct Skeleton { | ||
| 36 | skeleton_idx next; | ||
| 37 | size_t num_joints; | ||
| 38 | joint_idx joints[GFX_MAX_NUM_JOINTS]; // Indices into Anima's joints array. | ||
| 39 | } Skeleton; | ||
| 40 | |||
| 41 | /// A keyframe of animation. | ||
| 42 | typedef struct Keyframe { | ||
| 43 | R time; // Start time in [0, end animation time] | ||
| 44 | union { | ||
| 45 | vec3 translation; | ||
| 46 | quat rotation; | ||
| 47 | }; | ||
| 48 | } Keyframe; | ||
| 49 | |||
| 50 | /// Animation channel. | ||
| 51 | typedef struct Channel { | ||
| 52 | joint_idx target; // Index into Anima's joints array. | ||
| 53 | ChannelType type; | ||
| 54 | AnimationInterpolation interpolation; | ||
| 55 | size_t num_keyframes; | ||
| 56 | Keyframe keyframes[GFX_MAX_NUM_KEYFRAMES]; | ||
| 57 | } Channel; | ||
| 58 | |||
| 59 | /// A skeletal animation. | ||
| 60 | typedef struct Animation { | ||
| 61 | animation_idx next; | ||
| 62 | sstring name; | ||
| 63 | R duration; | ||
| 64 | size_t num_channels; | ||
| 65 | Channel channels[GFX_MAX_NUM_CHANNELS]; | ||
| 66 | } Animation; | ||
| 67 | |||
| 68 | /// Animation state. | ||
| 69 | /// | ||
| 70 | /// This represents the current state of an animation. | ||
| 71 | typedef struct AnimationState { | ||
| 72 | R start_time; // Time when the current animation started playing. -1 means the | ||
| 73 | // animation playback has not yet been initialized. | ||
| 74 | animation_idx animation; // Current animation. 0 = no animation. | ||
| 75 | bool loop; | ||
| 76 | } AnimationState; | ||
| 77 | |||
| 78 | /// Animation object. | ||
| 79 | /// | ||
| 80 | /// This is the top-level animation object that encapsulates everything | ||
| 81 | /// necessary for animation. | ||
| 82 | /// | ||
| 83 | /// For lack of a better name, this is called an Anima. It is short and the | ||
| 84 | /// Latin root of animation. | ||
| 85 | /// | ||
| 86 | /// The last joint of the joints array at index 'num_joints - 1' is the root of | ||
| 87 | /// all skeletons; specifically, the root of all joints that otherwise would | ||
| 88 | /// have no parent (a skeleton need not have its own root and can be a set of | ||
| 89 | /// disjoint node hierarchies). | ||
| 90 | typedef struct Anima { | ||
| 91 | skeleton_idx skeleton; // Index of first skeleton. | ||
| 92 | animation_idx animation; // Index of first animation. | ||
| 93 | AnimationState state; // Current animation state. | ||
| 94 | size_t num_joints; // Number of actual joints in the array. | ||
| 95 | Joint joints[GFX_MAX_NUM_JOINTS]; // Shared by all skeletons. | ||
| 96 | } Anima; | ||
diff --git a/src/scene/node_impl.h b/src/scene/node_impl.h index 15ee232..aba8c41 100644 --- a/src/scene/node_impl.h +++ b/src/scene/node_impl.h | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | #include <gfx/scene/node.h> | 3 | #include <gfx/scene/node.h> |
| 4 | 4 | ||
| 5 | #include "types.h" | 5 | #include "../types.h" |
| 6 | 6 | ||
| 7 | #include <math/camera.h> | 7 | #include <math/camera.h> |
| 8 | #include <math/mat4.h> | 8 | #include <math/mat4.h> |
diff --git a/src/scene/object_impl.h b/src/scene/object_impl.h index b7a0752..1b14cb5 100644 --- a/src/scene/object_impl.h +++ b/src/scene/object_impl.h | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | #include <gfx/scene/object.h> | 3 | #include <gfx/scene/object.h> |
| 4 | 4 | ||
| 5 | #include "types.h" | 5 | #include "../types.h" |
| 6 | 6 | ||
| 7 | typedef struct MeshLink { | 7 | typedef struct MeshLink { |
| 8 | mesh_idx mesh; | 8 | mesh_idx mesh; |
diff --git a/src/scene/scene_impl.h b/src/scene/scene_impl.h index 992f620..06a69f0 100644 --- a/src/scene/scene_impl.h +++ b/src/scene/scene_impl.h | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | #include <gfx/scene/scene.h> | 3 | #include <gfx/scene/scene.h> |
| 4 | 4 | ||
| 5 | #include "types.h" | 5 | #include "../types.h" |
| 6 | 6 | ||
| 7 | typedef struct SceneNode SceneNode; | 7 | typedef struct SceneNode SceneNode; |
| 8 | 8 | ||
diff --git a/src/scene/types.h b/src/scene/types.h deleted file mode 100644 index d0ffc41..0000000 --- a/src/scene/types.h +++ /dev/null | |||
| @@ -1,24 +0,0 @@ | |||
| 1 | /// Strongly-typed indices for every kind of scene node resource. | ||
| 2 | #pragma once | ||
| 3 | |||
| 4 | #include <stdint.h> | ||
| 5 | |||
| 6 | typedef uint16_t gfx_idx; | ||
| 7 | |||
| 8 | #define DEF_STRONG_INDEX(TYPE_NAME, IDX_TYPE) \ | ||
| 9 | typedef struct TYPE_NAME##_idx { \ | ||
| 10 | IDX_TYPE val; \ | ||
| 11 | } TYPE_NAME##_idx; | ||
| 12 | |||
| 13 | DEF_STRONG_INDEX(anima, gfx_idx) | ||
| 14 | DEF_STRONG_INDEX(animation, gfx_idx) | ||
| 15 | DEF_STRONG_INDEX(camera, gfx_idx) | ||
| 16 | DEF_STRONG_INDEX(light, gfx_idx) | ||
| 17 | DEF_STRONG_INDEX(material, gfx_idx) | ||
| 18 | DEF_STRONG_INDEX(mesh, gfx_idx) | ||
| 19 | DEF_STRONG_INDEX(mesh_link, gfx_idx) | ||
| 20 | DEF_STRONG_INDEX(model, gfx_idx) | ||
| 21 | DEF_STRONG_INDEX(node, gfx_idx) | ||
| 22 | DEF_STRONG_INDEX(object, gfx_idx) | ||
| 23 | DEF_STRONG_INDEX(scene, gfx_idx) | ||
| 24 | DEF_STRONG_INDEX(skeleton, gfx_idx) | ||
