summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2024-03-02 07:47:29 -0800
committer3gg <3gg@shellblade.net>2024-03-02 07:47:29 -0800
commit4bc4ca2796bd434880b77d3c4bcbb56107456777 (patch)
tree6fc4280d14740108a6ace0963d9a4b0ef99f38ef
parentdaaa3ef68705da389d39ef625840bf5278b25f22 (diff)
Compute joint bounding boxes.
-rw-r--r--game/src/plugins/viewer.c66
-rw-r--r--gfx/include/gfx/renderer.h13
-rw-r--r--gfx/include/gfx/scene/animation.h33
-rw-r--r--gfx/include/gfx/scene/object.h4
-rw-r--r--gfx/src/asset/asset_cache.c11
-rw-r--r--gfx/src/asset/model.c493
-rw-r--r--gfx/src/renderer/imm_renderer.c45
-rw-r--r--gfx/src/renderer/renderer.c6
-rw-r--r--gfx/src/scene/animation.c104
-rw-r--r--gfx/src/scene/animation_impl.h15
-rw-r--r--gfx/src/scene/object.c5
-rw-r--r--gfx/src/scene/object_impl.h4
-rw-r--r--gfx/src/scene/types.h1
13 files changed, 520 insertions, 280 deletions
diff --git a/game/src/plugins/viewer.c b/game/src/plugins/viewer.c
index 4f4ef03..5e8d7d3 100644
--- a/game/src/plugins/viewer.c
+++ b/game/src/plugins/viewer.c
@@ -7,6 +7,8 @@
7#include <math/camera.h> 7#include <math/camera.h>
8#include <math/spatial3.h> 8#include <math/spatial3.h>
9 9
10#include <log/log.h>
11
10#include <stdlib.h> 12#include <stdlib.h>
11 13
12// Paths to various scene files. 14// Paths to various scene files.
@@ -20,6 +22,8 @@ static const char* DAMAGED_HELMET =
20 "/assets/glTF-Sample-Models/2.0/DamagedHelmet/glTF/DamagedHelmet.gltf"; 22 "/assets/glTF-Sample-Models/2.0/DamagedHelmet/glTF/DamagedHelmet.gltf";
21static const char* GIRL = 23static const char* GIRL =
22 "/home/jeanne/Nextcloud/assets/models/girl/girl-with-ground.gltf"; 24 "/home/jeanne/Nextcloud/assets/models/girl/girl-with-ground.gltf";
25static const char* BOXES =
26 "/home/jeanne/Nextcloud/assets/models/boxes/boxes.gltf";
23 27
24#define DEFAULT_SCENE_FILE GIRL 28#define DEFAULT_SCENE_FILE GIRL
25 29
@@ -167,43 +171,48 @@ void update(Game* game, State* state, double t, double dt) {
167} 171}
168 172
169/// Render the bounding boxes of all scene objects. 173/// Render the bounding boxes of all scene objects.
170static void render_bounding_boxes_rec(ImmRenderer* imm, const SceneNode* node) { 174static void render_bounding_boxes_rec(
175 ImmRenderer* imm, const Anima* anima, const mat4* parent_model_matrix,
176 const SceneNode* node) {
171 assert(imm); 177 assert(imm);
172 assert(node); 178 assert(node);
173 179
180 const mat4 model_matrix =
181 mat4_mul(*parent_model_matrix, gfx_get_node_transform(node));
182
174 const NodeType node_type = gfx_get_node_type(node); 183 const NodeType node_type = gfx_get_node_type(node);
175 184
176 if (node_type == ModelNode) { 185 if (node_type == ModelNode) {
177 const Model* model = gfx_get_node_model(node); 186 const Model* model = gfx_get_node_model(node);
178 const SceneNode* root = gfx_get_model_root(model); 187 const SceneNode* root = gfx_get_model_root(model);
179 render_bounding_boxes_rec(imm, root); 188 render_bounding_boxes_rec(imm, anima, &model_matrix, root);
189 } else if (node_type == AnimaNode) {
190 anima = gfx_get_node_anima(node);
180 } else if (node_type == ObjectNode) { 191 } else if (node_type == ObjectNode) {
181 // TODO: Look at the scene log. The JointNodes are detached from the 192 gfx_imm_set_model_matrix(imm, &model_matrix);
182 // ObjectNodes. This is why the boxes are not being transformed as expected 193
183 // here. Anima needs to animate boxes? Use OOBB in addition to AABB? 194 const SceneObject* obj = gfx_get_node_object(node);
184 // 195 const Skeleton* skeleton = gfx_get_object_skeleton(obj);
185 // TODO: Idea: when a model is loaded, compute an OOBB per joint using the 196
186 // vertices that are affected by the joint. Then transform this OOBB when 197 if (skeleton) { // Animated model.
187 // animating the skeleton. Start with AABB for simplicity. The AABB/OOBB 198 assert(anima);
188 // in the skeleton should be const. The transform AABB/OOBB is derived 199 const size_t num_joints = gfx_get_skeleton_num_joints(skeleton);
189 // on demand. Stack allocator would be best for this kind of per-frame 200 for (size_t i = 0; i < num_joints; ++i) {
190 // data. 201 if (gfx_joint_has_box(anima, skeleton, i)) {
191 // 202 const Box box = gfx_get_joint_box(anima, skeleton, i);
192 // TODO: After computing joint AABB/OOBBs, check here whether the node has 203 gfx_imm_draw_box3(imm, box.vertices);
193 // a skeleton, and if so, render the skeleton's boxes instead of the 204 }
194 // node's (the node's boxes are not animated, but computer from the rest 205 }
195 // pose). 206 } else { // Static model.
196 const mat4 model = gfx_get_node_global_transform(node); 207 const aabb3 box = gfx_get_object_aabb(obj);
197 const SceneObject* obj = gfx_get_node_object(node); 208 gfx_imm_draw_aabb3(imm, box);
198 const aabb3 box = gfx_get_object_aabb(obj); 209 }
199 gfx_imm_set_model_matrix(imm, &model);
200 gfx_imm_draw_aabb3(imm, box);
201 } 210 }
202 211
203 // Render children's boxes. 212 // Render children's boxes.
204 const SceneNode* child = gfx_get_node_child(node); 213 const SceneNode* child = gfx_get_node_child(node);
205 while (child) { 214 while (child) {
206 render_bounding_boxes_rec(imm, child); 215 render_bounding_boxes_rec(imm, anima, &model_matrix, child);
207 child = gfx_get_node_sibling(child); 216 child = gfx_get_node_sibling(child);
208 } 217 }
209} 218}
@@ -218,17 +227,20 @@ static void render_bounding_boxes(const Game* game, const State* state) {
218 assert(render_backend); 227 assert(render_backend);
219 assert(imm); 228 assert(imm);
220 229
230 const mat4 id = mat4_id();
231 Anima* anima = 0;
232
221 gfx_set_blending(render_backend, true); 233 gfx_set_blending(render_backend, true);
222 gfx_set_depth_mask(render_backend, false); 234 gfx_set_depth_mask(render_backend, false);
223 gfx_set_polygon_offset(render_backend, 0.5f, 0.5f); 235 gfx_set_polygon_offset(render_backend, -1.5f, -1.0f);
224 236
225 gfx_imm_start(imm); 237 gfx_imm_start(imm);
226 gfx_imm_set_camera(imm, gfx_get_camera_camera(state->camera)); 238 gfx_imm_set_camera(imm, gfx_get_camera_camera(state->camera));
227 gfx_imm_set_colour(imm, vec4_make(0.2, 0.2, 1.0, 0.3)); 239 gfx_imm_set_colour(imm, vec4_make(0.3, 0.3, 0.9, 0.1));
228 render_bounding_boxes_rec(imm, gfx_get_scene_root(state->scene)); 240 render_bounding_boxes_rec(imm, anima, &id, gfx_get_scene_root(state->scene));
229 gfx_imm_end(imm); 241 gfx_imm_end(imm);
230 242
231 gfx_set_polygon_offset(render_backend, 0.0f, 0.0f); 243 gfx_reset_polygon_offset(render_backend);
232 gfx_set_depth_mask(render_backend, true); 244 gfx_set_depth_mask(render_backend, true);
233 gfx_set_blending(render_backend, false); 245 gfx_set_blending(render_backend, false);
234} 246}
diff --git a/gfx/include/gfx/renderer.h b/gfx/include/gfx/renderer.h
index 78d8bbd..9236e3f 100644
--- a/gfx/include/gfx/renderer.h
+++ b/gfx/include/gfx/renderer.h
@@ -66,6 +66,19 @@ void gfx_imm_draw_aabb2(ImmRenderer*, aabb2);
66/// Draw a bounding box. 66/// Draw a bounding box.
67void gfx_imm_draw_aabb3(ImmRenderer*, aabb3); 67void gfx_imm_draw_aabb3(ImmRenderer*, aabb3);
68 68
69/// Draw a box.
70///
71/// The vertices must be given in the following order:
72///
73/// 7 ----- 6
74/// / /|
75/// 3 ----- 2 |
76/// | | |
77/// | 4 ----- 5
78/// |/ |/
79/// 0 ----- 1
80void gfx_imm_draw_box3(ImmRenderer* renderer, const vec3 vertices[8]);
81
69/// Set the camera. 82/// Set the camera.
70void gfx_imm_set_camera(ImmRenderer*, const Camera*); 83void gfx_imm_set_camera(ImmRenderer*, const Camera*);
71 84
diff --git a/gfx/include/gfx/scene/animation.h b/gfx/include/gfx/scene/animation.h
index 42c0c43..d95b895 100644
--- a/gfx/include/gfx/scene/animation.h
+++ b/gfx/include/gfx/scene/animation.h
@@ -5,6 +5,7 @@
5#include <gfx/sizes.h> 5#include <gfx/sizes.h>
6 6
7#include <cstring.h> 7#include <cstring.h>
8#include <math/aabb3.h>
8#include <math/defs.h> 9#include <math/defs.h>
9#include <math/mat4.h> 10#include <math/mat4.h>
10#include <math/quat.h> 11#include <math/quat.h>
@@ -22,21 +23,26 @@ typedef struct Joint Joint;
22typedef struct Skeleton Skeleton; 23typedef struct Skeleton Skeleton;
23 24
24/// Index type used to store relative indices into arrays. 25/// Index type used to store relative indices into arrays.
25typedef uint16_t rel_idx; 26typedef uint16_t joint_idx;
26 27
27/// Index value denoting no index. 28/// Index value denoting no index.
28static const rel_idx INDEX_NONE = (rel_idx)-1; 29static const joint_idx INDEX_NONE = (joint_idx)-1;
30
31typedef struct Box {
32 vec3 vertices[8];
33} Box;
29 34
30/// Joint descriptor. 35/// Joint descriptor.
31typedef struct JointDesc { 36typedef struct JointDesc {
32 rel_idx parent; /// Parent Joint; index into Anima's joints. 37 joint_idx parent; /// Parent Joint; index into Anima's joints.
33 mat4 inv_bind_matrix; /// Transforms the mesh into the joint's local space. 38 mat4 inv_bind_matrix; /// Transforms the mesh into the joint's local space.
39 aabb3 box; /// Bounding box.
34} JointDesc; 40} JointDesc;
35 41
36/// Skeleton descriptor. 42/// Skeleton descriptor.
37typedef struct SkeletonDesc { 43typedef struct SkeletonDesc {
38 size_t num_joints; 44 size_t num_joints;
39 rel_idx joints[GFX_MAX_NUM_JOINTS]; /// Indices into Anima's joints array. 45 joint_idx joints[GFX_MAX_NUM_JOINTS]; /// Indices into Anima's joints array.
40} SkeletonDesc; 46} SkeletonDesc;
41 47
42/// Animation interpolation mode. 48/// Animation interpolation mode.
@@ -67,7 +73,7 @@ typedef struct KeyframeDesc {
67 73
68/// Animation channel descriptor. 74/// Animation channel descriptor.
69typedef struct ChannelDesc { 75typedef struct ChannelDesc {
70 rel_idx target; /// Index into Anima's joints array. 76 joint_idx target; /// Index into Anima's joints array.
71 ChannelType type; 77 ChannelType type;
72 AnimationInterpolation interpolation; 78 AnimationInterpolation interpolation;
73 size_t num_keyframes; 79 size_t num_keyframes;
@@ -121,3 +127,16 @@ void gfx_stop_animation(Anima*);
121 127
122/// Return the anima's ith skeleton. 128/// Return the anima's ith skeleton.
123const Skeleton* gfx_get_anima_skeleton(const Anima* anima, size_t i); 129const Skeleton* gfx_get_anima_skeleton(const Anima* anima, size_t i);
130
131/// Return the number of joints in the skeleton.
132size_t gfx_get_skeleton_num_joints(const Skeleton*);
133
134/// Return true if the skeleton's ith joint has a bounding box.
135///
136/// IK joints that do not directly transform vertices have no bounding box.
137bool gfx_joint_has_box(const Anima*, const Skeleton*, size_t joint);
138
139/// Return the bounding box of the skeleton's ith joint.
140///
141/// IK joints that do not directly transform vertices have no box.
142Box gfx_get_joint_box(const Anima*, const Skeleton*, size_t joint);
diff --git a/gfx/include/gfx/scene/object.h b/gfx/include/gfx/scene/object.h
index 891c3cd..7579d29 100644
--- a/gfx/include/gfx/scene/object.h
+++ b/gfx/include/gfx/scene/object.h
@@ -29,6 +29,10 @@ void gfx_destroy_object(SceneObject**);
29/// Set the object's skeleton. 29/// Set the object's skeleton.
30void gfx_set_object_skeleton(SceneObject*, const Skeleton*); 30void gfx_set_object_skeleton(SceneObject*, const Skeleton*);
31 31
32/// Get the object's skeleton.
33/// Return null if the object has no skeleton.
34const Skeleton* gfx_get_object_skeleton(const SceneObject*);
35
32/// Gets the object's bounding box. 36/// Gets the object's bounding box.
33/// 37///
34/// The object's bounding box is the bounding box of its mesh geometries. 38/// The object's bounding box is the bounding box of its mesh geometries.
diff --git a/gfx/src/asset/asset_cache.c b/gfx/src/asset/asset_cache.c
index 1d64d66..d077421 100644
--- a/gfx/src/asset/asset_cache.c
+++ b/gfx/src/asset/asset_cache.c
@@ -152,10 +152,13 @@ static void log_model_loaded(const LoadModelCmd* cmd) {
152static Model* clone_model(const Model* model) { 152static Model* clone_model(const Model* model) {
153 assert(model); 153 assert(model);
154 154
155 // Only the Anima needs to be (shallow) cloned since everything else in the 155 // Only the Anima needs to be cloned since everything else in the model is
156 // model is static. Also note that only the Anima's joints and animation state 156 // static.
157 // need to be cloned; all other members can be shared. So a shallow clone of 157 //
158 // the anima is sufficient. 158 // The Anima can be partially shallow-cloned. Skeletons and animations are
159 // static and can be shared with the original Anima. Other members are
160 // deep-cloned. Skeletons in particular point back to their Anima, so need to
161 // be deep-cloned.
159 const SceneNode* root = mem_get_node(model->root); 162 const SceneNode* root = mem_get_node(model->root);
160 if (gfx_get_node_type(root) == AnimaNode) { 163 if (gfx_get_node_type(root) == AnimaNode) {
161 const Anima* anima = gfx_get_node_anima(root); 164 const Anima* anima = gfx_get_node_anima(root);
diff --git a/gfx/src/asset/model.c b/gfx/src/asset/model.c
index 37f129e..2053dc4 100644
--- a/gfx/src/asset/model.c
+++ b/gfx/src/asset/model.c
@@ -94,6 +94,7 @@
94#include "gfx/sizes.h" 94#include "gfx/sizes.h"
95#include "gfx/util/shader.h" 95#include "gfx/util/shader.h"
96 96
97#include "gfx_assert.h"
97#include "scene/model_impl.h" 98#include "scene/model_impl.h"
98 99
99#include "cstring.h" 100#include "cstring.h"
@@ -110,7 +111,6 @@
110#define CGLTF_IMPLEMENTATION 111#define CGLTF_IMPLEMENTATION
111#include "cgltf.h" 112#include "cgltf.h"
112 113
113#include <assert.h>
114#include <stdbool.h> 114#include <stdbool.h>
115#include <stdlib.h> 115#include <stdlib.h>
116 116
@@ -323,204 +323,231 @@ static cgltf_size get_component_size(cgltf_component_type type) {
323 return 0; 323 return 0;
324} 324}
325 325
326/// Return the number dimensionality of the given data type.
327int get_num_dimensions(cgltf_type type) {
328 switch (type) {
329 case cgltf_type_scalar:
330 return 1;
331 case cgltf_type_vec2:
332 return 2;
333 case cgltf_type_vec3:
334 return 3;
335 case cgltf_type_vec4:
336 return 4;
337 case cgltf_type_mat2:
338 return 4; // 2x2
339 case cgltf_type_mat3:
340 return 9; // 3x3
341 case cgltf_type_mat4:
342 return 16; // 4x4
343 case cgltf_type_invalid:
344 FAIL("");
345 break;
346 }
347 FAIL("");
348 return 0;
349}
350
351/// Read an int64 from the given data pointer and accessor.
352/// The largest integer in glTF is u32, so we can fit all integers in an int64.
353static int64_t read_int(const void* component, const cgltf_accessor* accessor) {
354 assert(component);
355 assert(accessor);
356
357 switch (accessor->component_type) {
358 case cgltf_component_type_r_8: {
359 const int8_t c = *((int8_t*)component);
360 return c;
361 }
362 case cgltf_component_type_r_8u: {
363 const uint8_t c = *((uint8_t*)component);
364 return c;
365 }
366 case cgltf_component_type_r_16: {
367 const int16_t c = *((int16_t*)component);
368 return c;
369 }
370 case cgltf_component_type_r_16u: {
371 const uint16_t c = *((uint16_t*)component);
372 return c;
373 }
374 case cgltf_component_type_r_32u: {
375 const uint32_t c = *((uint32_t*)component);
376 return c;
377 }
378 case cgltf_component_type_r_32f: {
379 const float c = *((float*)component);
380 return (int64_t)c;
381 }
382 case cgltf_component_type_invalid:
383 FAIL("");
384 break;
385 }
386 FAIL("");
387 return 0;
388}
389
326/// Read a float from the given data pointer and accessor. 390/// Read a float from the given data pointer and accessor.
327/// 391///
328/// This function uses the normalization equations from the spec. See the 392/// This function uses the normalization equations from the spec. See the
329/// animation section: 393/// animation section:
330/// 394///
331/// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#animations 395/// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#animations
332static float read_float(const void* data, const cgltf_accessor* accessor) { 396static float read_float(const void* component, const cgltf_accessor* accessor) {
333 assert(data); 397 assert(component);
334 assert(accessor); 398 assert(accessor);
335 399
336 switch (accessor->component_type) { 400 switch (accessor->component_type) {
337 case cgltf_component_type_r_8: { 401 case cgltf_component_type_r_8: {
338 assert(accessor->normalized); 402 // assert(accessor->normalized);
339 const int8_t c = *((int8_t*)data); 403 const int8_t c = *((int8_t*)component);
340 return max((float)c / 127.0, -1.0); 404 return max((float)c / 127.0, -1.0);
341 } 405 }
342 case cgltf_component_type_r_8u: { 406 case cgltf_component_type_r_8u: {
343 assert(accessor->normalized); 407 // assert(accessor->normalized);
344 const uint8_t c = *((uint8_t*)data); 408 const uint8_t c = *((uint8_t*)component);
345 return (float)c / 255.0; 409 return (float)c / 255.0;
346 } 410 }
347 case cgltf_component_type_r_16: { 411 case cgltf_component_type_r_16: {
348 assert(accessor->normalized); 412 // assert(accessor->normalized);
349 const int16_t c = *((int16_t*)data); 413 const int16_t c = *((int16_t*)component);
350 return max((float)c / 32767.0, -1.0); 414 return max((float)c / 32767.0, -1.0);
351 } 415 }
352 case cgltf_component_type_r_16u: { 416 case cgltf_component_type_r_16u: {
353 assert(accessor->normalized); 417 // assert(accessor->normalized);
354 const uint16_t c = *((uint16_t*)data); 418 const uint16_t c = *((uint16_t*)component);
355 return (float)c / 65535.0; 419 return (float)c / 65535.0;
356 } 420 }
357 case cgltf_component_type_r_32u: { 421 case cgltf_component_type_r_32u: {
358 assert(accessor->normalized); 422 // assert(accessor->normalized);
359 const uint32_t c = *((uint32_t*)data); 423 const uint32_t c = *((uint32_t*)component);
360 return (float)c / 4294967295.0; 424 return (float)c / 4294967295.0;
361 } 425 }
362 case cgltf_component_type_r_32f: { 426 case cgltf_component_type_r_32f: {
363 const float c = *((float*)data); 427 const float c = *((float*)component);
364 return c; 428 return c;
365 } 429 }
366 case cgltf_component_type_invalid: 430 case cgltf_component_type_invalid:
367 assert(false); 431 FAIL("");
368 break; 432 break;
369 } 433 }
370 assert(false); 434 FAIL("");
371 return 0; 435 return 0;
372} 436}
373 437
374/// Iterate over the vectors in an accessor. 438typedef struct AccessorIter {
375#define ACCESSOR_FOREACH_VEC(dimensions, accessor, body) \ 439 const cgltf_accessor* accessor;
376 { \ 440 const uint8_t* next_element;
377 assert((1 <= dimensions) && (dimensions <= 4)); \ 441 cgltf_size comp_size; // Component size in bytes.
378 assert( \ 442 cgltf_size stride; // ELement stride in bytes.
379 ((dimensions == 1) && (accessor->type == cgltf_type_scalar)) || \ 443 cgltf_size index; // Index of the next element.
380 ((dimensions == 2) && (accessor->type == cgltf_type_vec2)) || \ 444 bool is_matrix;
381 ((dimensions == 3) && (accessor->type == cgltf_type_vec3)) || \ 445} AccessorIter;
382 ((dimensions == 4) && (accessor->type == cgltf_type_vec4))); \ 446
383 const cgltf_buffer_view* view = accessor->buffer_view; \ 447typedef struct AccessorData {
384 const cgltf_buffer* buffer = view->buffer; \ 448 union {
385 const cgltf_size offset = accessor->offset + view->offset; \ 449 struct {
386 const uint8_t* bytes = (const uint8_t*)buffer->data + offset; \ 450 float x, y, z, w; // Possibly normalized.
387 /* Component size in bytes. */ \ 451 int64_t xi, yi, zi, wi; // Always unnormalized.
388 const cgltf_size comp_size = get_component_size(accessor->component_type); \ 452 };
389 /* Element size in bytes. */ \ 453 const float* floats;
390 const cgltf_size elem_size = dimensions * comp_size; \ 454 };
391 /* Stride in bytes. If the view stride is 0, then the elements are tightly \ 455} AccessorData;
392 * packed. */ \ 456
393 const cgltf_size stride = view->stride != 0 ? view->stride : elem_size; \ 457bool accessor_iter_next(AccessorIter* iter, AccessorData* data) {
394 /* There isn't an accessor stride in the spec, but cgltf still specifies \ 458 assert(iter);
395 * one. */ \ 459 assert(data);
396 assert(accessor->stride == elem_size); \ 460
397 /* Accessor data must fit inside the buffer. */ \ 461 if (iter->index < iter->accessor->count) {
398 assert( \ 462 const int dimensions = get_num_dimensions(iter->accessor->type);
399 (offset + (accessor->count * elem_size) + \ 463 const uint8_t* component = iter->next_element;
400 ((accessor->count - 1) * view->stride)) <= buffer->size); \ 464
401 /* Accessor data must fit inside the view. */ \ 465 // So that the caller can access the element's components as an array.
402 assert(accessor->count * accessor->stride <= view->size); \ 466 data->floats = (const float*)component;
403 cgltf_float x = 0, y = 0, z = 0, w = 0; \ 467
404 /* Silence unused variable warnings. */ \ 468 if (!iter->is_matrix) { // Scalar or vector.
405 (void)y; \ 469 // x
406 (void)z; \ 470 data->x = read_float(component, iter->accessor);
407 (void)w; \ 471 data->xi = read_int(component, iter->accessor);
408 /* The {component type} X {dimensions} combinations are a pain to handle. \ 472 component += iter->comp_size;
409 For floats, we switch on type first and then lay out a loop for each \ 473 // y
410 dimension to get a tight loop with a possibly inlined body. For other \ 474 if (dimensions > 1) {
411 types, we take the performance hit and perform checks and conversions \ 475 data->y = read_float(component, iter->accessor);
412 inside the loop for simplicity. */ \ 476 data->yi = read_int(component, iter->accessor);
413 if (accessor->component_type == cgltf_component_type_r_32f) { \ 477 component += iter->comp_size;
414 switch (dimensions) { \ 478 }
415 case 1: \ 479 // z
416 assert(accessor->type == cgltf_type_scalar); \ 480 if (dimensions > 2) {
417 for (cgltf_size i = 0; i < accessor->count; ++i, bytes += stride) { \ 481 data->z = read_float(component, iter->accessor);
418 const cgltf_float* floats = (const cgltf_float*)bytes; \ 482 data->zi = read_int(component, iter->accessor);
419 x = *floats; \ 483 component += iter->comp_size;
420 body; \ 484 }
421 } \ 485 // w
422 break; \ 486 if (dimensions > 3) {
423 case 2: \ 487 data->w = read_float(component, iter->accessor);
424 assert(accessor->type == cgltf_type_vec2); \ 488 data->wi = read_int(component, iter->accessor);
425 for (cgltf_size i = 0; i < accessor->count; ++i, bytes += stride) { \ 489 component += iter->comp_size;
426 const cgltf_float* floats = (const cgltf_float*)bytes; \ 490 }
427 x = *floats++; \ 491 }
428 y = *floats; \ 492
429 body; \ 493 iter->next_element += iter->stride;
430 } \ 494 iter->index++;
431 break; \ 495 return true;
432 case 3: \
433 assert(accessor->type == cgltf_type_vec3); \
434 for (cgltf_size i = 0; i < accessor->count; ++i, bytes += stride) { \
435 const cgltf_float* floats = (const cgltf_float*)bytes; \
436 x = *floats++; \
437 y = *floats++; \
438 z = *floats; \
439 body; \
440 } \
441 break; \
442 case 4: \
443 assert(accessor->type == cgltf_type_vec4); \
444 for (cgltf_size i = 0; i < accessor->count; ++i, bytes += stride) { \
445 const cgltf_float* floats = (const cgltf_float*)bytes; \
446 x = *floats++; \
447 y = *floats++; \
448 z = *floats++; \
449 w = *floats; \
450 body; \
451 } \
452 break; \
453 } \
454 } else { \
455 for (cgltf_size i = 0; i < accessor->count; ++i, bytes += stride) { \
456 const uint8_t* component = bytes; \
457 \
458 x = read_float(component, accessor); \
459 component += comp_size; \
460 if (dimensions > 1) { \
461 y = read_float(component, accessor); \
462 component += comp_size; \
463 } \
464 if (dimensions > 2) { \
465 z = read_float(component, accessor); \
466 component += comp_size; \
467 } \
468 if (dimensions > 3) { \
469 w = read_float(component, accessor); \
470 component += comp_size; \
471 } \
472 body; \
473 } \
474 } \
475 }
476
477/// Iterate over the matrices in an accessor.
478#define ACCESSOR_FOREACH_MAT(dimensions, accessor, body) \
479 { \
480 assert((2 <= dimensions) && (dimensions <= 4)); \
481 assert(!(dimensions == 2) || (accessor->type == cgltf_type_mat2)); \
482 assert(!(dimensions == 3) || (accessor->type == cgltf_type_mat3)); \
483 assert(!(dimensions == 4) || (accessor->type == cgltf_type_mat4)); \
484 const cgltf_buffer_view* view = accessor->buffer_view; \
485 const cgltf_buffer* buffer = view->buffer; \
486 const cgltf_size offset = accessor->offset + view->offset; \
487 const cgltf_size comp_size = get_component_size(accessor->component_type); \
488 const uint8_t* bytes = (const uint8_t*)buffer->data + offset; \
489 assert( \
490 (offset + accessor->count * dimensions * comp_size) < buffer->size); \
491 /* From the spec: */ \
492 /* "Buffer views with other types of data MUST NOT not define */ \
493 /* byteStride (unless such layout is explicitly enabled by an */ \
494 /* extension)."*/ \
495 assert(view->stride == 0); \
496 assert(accessor->stride == dimensions * dimensions * comp_size); \
497 assert(accessor->component_type == cgltf_component_type_r_32f); \
498 const cgltf_float* floats = (const cgltf_float*)bytes; \
499 switch (dimensions) { \
500 case 2: \
501 assert(accessor->type == cgltf_type_mat2); \
502 for (cgltf_size i = 0; i < accessor->count; ++i) { \
503 body; \
504 floats += 4; \
505 } \
506 break; \
507 case 3: \
508 assert(accessor->type == cgltf_type_mat3); \
509 for (cgltf_size i = 0; i < accessor->count; ++i) { \
510 body; \
511 floats += 9; \
512 } \
513 break; \
514 case 4: \
515 assert(accessor->type == cgltf_type_mat4); \
516 for (cgltf_size i = 0; i < accessor->count; ++i) { \
517 body; \
518 floats += 16; \
519 } \
520 break; \
521 } \
522 } 496 }
523 497
498 return false;
499}
500
501AccessorIter make_accessor_iter(const cgltf_accessor* accessor) {
502 assert(accessor);
503
504 const bool is_matrix = (accessor->type == cgltf_type_mat2) ||
505 (accessor->type == cgltf_type_mat3) ||
506 (accessor->type == cgltf_type_mat4);
507
508 const int dimensions = get_num_dimensions(accessor->type);
509 assert(
510 ((dimensions == 1) && (accessor->type == cgltf_type_scalar)) ||
511 ((dimensions == 2) && (accessor->type == cgltf_type_vec2)) ||
512 ((dimensions == 3) && (accessor->type == cgltf_type_vec3)) ||
513 ((dimensions == 4) && (accessor->type == cgltf_type_vec4)) ||
514 ((dimensions == 4) && (accessor->type == cgltf_type_mat2)) ||
515 ((dimensions == 9) && (accessor->type == cgltf_type_mat3)) ||
516 ((dimensions == 16) && (accessor->type == cgltf_type_mat4)));
517
518 const cgltf_buffer_view* view = accessor->buffer_view;
519 const cgltf_buffer* buffer = view->buffer;
520 const cgltf_size offset = accessor->offset + view->offset;
521 const uint8_t* bytes = (const uint8_t*)buffer->data + offset;
522 // Component size in bytes.
523 const cgltf_size comp_size = get_component_size(accessor->component_type);
524 // Element size in bytes.
525 const cgltf_size elem_size = dimensions * comp_size;
526 // Stride in bytes. If the view stride is 0, then the elements are tightly
527 // packed.
528 const cgltf_size stride = view->stride != 0 ? view->stride : elem_size;
529
530 // There isn't an accessor stride in the spec, but cgltf still specifies one.
531 assert(accessor->stride == elem_size);
532
533 // Accessor data must fit inside the view.
534 assert(accessor->offset + (accessor->count * accessor->stride) <= view->size);
535
536 // Accessor data must fit inside the buffer.
537 assert(
538 (offset + (accessor->count * elem_size) +
539 ((accessor->count - 1) * view->stride)) <= buffer->size);
540
541 return (AccessorIter){
542 .accessor = accessor,
543 .next_element = bytes,
544 .comp_size = comp_size,
545 .stride = stride,
546 .index = 0,
547 .is_matrix = is_matrix,
548 };
549}
550
524/// Return the total number of primitives in the scene. Each mesh may contain 551/// Return the total number of primitives in the scene. Each mesh may contain
525/// multiple primitives. 552/// multiple primitives.
526/// 553///
@@ -703,7 +730,7 @@ static bool load_texture_and_uniform(
703 730
704 textures[texture_index] = gfx_load_texture(gfx, cmd); 731 textures[texture_index] = gfx_load_texture(gfx, cmd);
705 if (!textures[texture_index]) { 732 if (!textures[texture_index]) {
706 prepend_error( 733 log_error(
707 "Failed to load texture: %s", 734 "Failed to load texture: %s",
708 mstring_cstr(&cmd->data.texture.filepath)); 735 mstring_cstr(&cmd->data.texture.filepath));
709 return false; 736 return false;
@@ -856,20 +883,25 @@ static Material* make_default_material() {
856 883
857/// Compute the bounding box of the vertices pointed to by the accessor. 884/// Compute the bounding box of the vertices pointed to by the accessor.
858/// 'dim' is the dimension of the vertices (2D or 3D). 885/// 'dim' is the dimension of the vertices (2D or 3D).
859aabb3 compute_aabb(const cgltf_accessor* accessor, int dim) { 886aabb3 compute_aabb(const cgltf_accessor* accessor) {
860 aabb3 box = {0}; 887 aabb3 box = {0};
861 if (accessor->has_min && accessor->has_max) { 888 if (accessor->has_min && accessor->has_max) {
862 box = aabb3_make( 889 box = aabb3_make(
863 vec3_from_array(accessor->min), vec3_from_array(accessor->max)); 890 vec3_from_array(accessor->min), vec3_from_array(accessor->max));
864 } else { 891 } else {
865 ACCESSOR_FOREACH_VEC(dim, accessor, { 892 AccessorIter iter = make_accessor_iter(accessor);
866 const vec3 p = vec3_make(x, y, z); 893 AccessorData vertex = {0};
894 cgltf_size i = 0;
895
896 while (accessor_iter_next(&iter, &vertex)) {
897 const vec3 p = vec3_make(vertex.x, vertex.y, vertex.z);
867 if (i == 0) { 898 if (i == 0) {
868 box = aabb3_make(p, p); 899 box = aabb3_make(p, p);
869 } else { 900 } else {
870 box = aabb3_add(box, p); 901 box = aabb3_add(box, p);
871 } 902 }
872 }); 903 ++i;
904 }
873 } 905 }
874 return box; 906 return box;
875} 907}
@@ -1000,15 +1032,15 @@ static bool load_meshes(
1000 case cgltf_type_vec2: 1032 case cgltf_type_vec2:
1001 assert(geometry_desc.positions3d.buffer == 0); 1033 assert(geometry_desc.positions3d.buffer == 0);
1002 buffer_view_2d = &geometry_desc.positions2d; 1034 buffer_view_2d = &geometry_desc.positions2d;
1003 geometry_desc.aabb = compute_aabb(accessor, 2); 1035 geometry_desc.aabb = compute_aabb(accessor);
1004 break; 1036 break;
1005 case cgltf_type_vec3: 1037 case cgltf_type_vec3:
1006 assert(geometry_desc.positions2d.buffer == 0); 1038 assert(geometry_desc.positions2d.buffer == 0);
1007 buffer_view_3d = &geometry_desc.positions3d; 1039 buffer_view_3d = &geometry_desc.positions3d;
1008 geometry_desc.aabb = compute_aabb(accessor, 3); 1040 geometry_desc.aabb = compute_aabb(accessor);
1009 break; 1041 break;
1010 default: 1042 default:
1011 LOGE( 1043 FAIL(
1012 "Unhandled accessor type %d in vertex positions", 1044 "Unhandled accessor type %d in vertex positions",
1013 accessor->type); 1045 accessor->type);
1014 assert(false); 1046 assert(false);
@@ -1186,6 +1218,77 @@ static bool load_meshes(
1186 return true; 1218 return true;
1187} 1219}
1188 1220
1221/// Compute bounding boxes for the joints in the model.
1222static void compute_joint_bounding_boxes(
1223 const cgltf_data* data, size_t num_joints, JointDesc* joint_descs) {
1224 assert(data);
1225 assert(joint_descs);
1226 assert(num_joints <= GFX_MAX_NUM_JOINTS);
1227
1228 // Initialize bounding boxes so that we can compute unions below.
1229 for (size_t i = 0; i < num_joints; ++i) {
1230 joint_descs[i].box = aabb3_make_empty();
1231 }
1232
1233 // Iterate over the meshes -> primitives -> vertices -> joint indices, and add
1234 // the vertex to the joint's bounding box.
1235 for (cgltf_size n = 0; n < data->nodes_count; ++n) {
1236 const cgltf_node* node = &data->nodes[n];
1237
1238 if (node->skin) {
1239 if (node->mesh) {
1240 const cgltf_mesh* mesh = node->mesh;
1241
1242 for (cgltf_size pr = 0; pr < mesh->primitives_count; ++pr) {
1243 const cgltf_primitive* prim = &mesh->primitives[pr];
1244
1245 // Find the indices of the positions and joints arrays in the
1246 // primitive's attributes.
1247 int positions_index = -1;
1248 int joints_index = -1;
1249 for (int a = 0; a < (int)prim->attributes_count; ++a) {
1250 const cgltf_attribute* attrib = &prim->attributes[a];
1251
1252 if (attrib->type == cgltf_attribute_type_position) {
1253 positions_index = a;
1254 } else if (attrib->type == cgltf_attribute_type_joints) {
1255 joints_index = a;
1256 }
1257 }
1258
1259 if ((positions_index != -1) && (joints_index != -1)) {
1260 const cgltf_accessor* positions =
1261 prim->attributes[positions_index].data;
1262 const cgltf_accessor* joints = prim->attributes[joints_index].data;
1263
1264 assert(positions->count == joints->count);
1265
1266 AccessorIter positions_iter = make_accessor_iter(positions);
1267 AccessorIter joints_iter = make_accessor_iter(joints);
1268 AccessorData position = {0}, joint = {0};
1269
1270 while (accessor_iter_next(&positions_iter, &position)) {
1271 const bool advance = accessor_iter_next(&joints_iter, &joint);
1272 assert(advance); // Counts should match.
1273
1274 const vec3 p = vec3_make(position.x, position.y, position.z);
1275 const int64_t j[4] = {joint.xi, joint.yi, joint.wi, joint.zi};
1276
1277 for (int i = 0; i < 4; ++i) {
1278 const size_t joint_index = j[i];
1279 assert((size_t)joint_index < num_joints);
1280
1281 joint_descs[joint_index].box =
1282 aabb3_add(joint_descs[joint_index].box, p);
1283 }
1284 }
1285 }
1286 }
1287 }
1288 }
1289 }
1290}
1291
1189/// Find the joint node with the smallest index across all skeletons. 1292/// Find the joint node with the smallest index across all skeletons.
1190/// 1293///
1191/// The channels in glTF may target arbitrary nodes in the scene (those nodes 1294/// The channels in glTF may target arbitrary nodes in the scene (those nodes
@@ -1249,8 +1352,10 @@ static size_t load_skins(
1249 *skeleton_desc = (SkeletonDesc){.num_joints = skin->joints_count}; 1352 *skeleton_desc = (SkeletonDesc){.num_joints = skin->joints_count};
1250 1353
1251 // for (cgltf_size j = 0; j < skin->joints_count; ++j) { 1354 // for (cgltf_size j = 0; j < skin->joints_count; ++j) {
1252 ACCESSOR_FOREACH_MAT(4, matrices_accessor, { 1355 AccessorIter iter = make_accessor_iter(matrices_accessor);
1253 const mat4 inv_bind_matrix = mat4_from_array(floats); 1356 AccessorData matrix = {0};
1357 for (cgltf_size i = 0; accessor_iter_next(&iter, &matrix); ++i) {
1358 const mat4 inv_bind_matrix = mat4_from_array(matrix.floats);
1254 1359
1255 // Joint is an index/pointer into the nodes array. 1360 // Joint is an index/pointer into the nodes array.
1256 const cgltf_size node_index = skin->joints[i] - data->nodes; 1361 const cgltf_size node_index = skin->joints[i] - data->nodes;
@@ -1275,7 +1380,7 @@ static size_t load_skins(
1275 joint_desc->inv_bind_matrix = inv_bind_matrix; 1380 joint_desc->inv_bind_matrix = inv_bind_matrix;
1276 1381
1277 is_joint_node[joint_index] = true; 1382 is_joint_node[joint_index] = true;
1278 }); 1383 };
1279 1384
1280 // glTF may specify a "skeleton", which is the root of the skin's 1385 // glTF may specify a "skeleton", which is the root of the skin's
1281 // (skeleton's) node hierarchy. 1386 // (skeleton's) node hierarchy.
@@ -1352,23 +1457,32 @@ static void load_animations(
1352 .num_keyframes = 0}; 1457 .num_keyframes = 0};
1353 1458
1354 // Read time inputs. 1459 // Read time inputs.
1355 ACCESSOR_FOREACH_VEC(1, sampler->input, { 1460 AccessorIter iter = make_accessor_iter(sampler->input);
1356 channel_desc->keyframes[i].time = x; 1461 AccessorData input = {0};
1462 for (cgltf_size i = 0; accessor_iter_next(&iter, &input); ++i) {
1463 channel_desc->keyframes[i].time = input.x;
1357 channel_desc->num_keyframes++; 1464 channel_desc->num_keyframes++;
1358 }); 1465 }
1359 1466
1360 // Read transform outputs. 1467 // Read transform outputs.
1468 AccessorData output = {0};
1361 switch (channel->target_path) { 1469 switch (channel->target_path) {
1362 case cgltf_animation_path_type_translation: 1470 case cgltf_animation_path_type_translation: {
1363 ACCESSOR_FOREACH_VEC(3, sampler->output, { 1471 iter = make_accessor_iter(sampler->output);
1364 channel_desc->keyframes[i].translation = vec3_make(x, y, z); 1472 for (cgltf_size i = 0; accessor_iter_next(&iter, &output); ++i) {
1365 }); 1473 channel_desc->keyframes[i].translation =
1474 vec3_make(output.x, output.y, output.z);
1475 }
1366 break; 1476 break;
1367 case cgltf_animation_path_type_rotation: 1477 }
1368 ACCESSOR_FOREACH_VEC(4, sampler->output, { 1478 case cgltf_animation_path_type_rotation: {
1369 channel_desc->keyframes[i].rotation = qmake(x, y, z, w); 1479 iter = make_accessor_iter(sampler->output);
1370 }); 1480 for (cgltf_size i = 0; accessor_iter_next(&iter, &output); ++i) {
1481 channel_desc->keyframes[i].rotation =
1482 qmake(output.x, output.y, output.z, output.w);
1483 }
1371 break; 1484 break;
1485 }
1372 default: 1486 default:
1373 // TODO: Handle other channel transformations. 1487 // TODO: Handle other channel transformations.
1374 break; 1488 break;
@@ -1421,10 +1535,6 @@ static void load_nodes(
1421 assert(skin_index < data->skins_count); 1535 assert(skin_index < data->skins_count);
1422 const Skeleton* skeleton = gfx_get_anima_skeleton(anima, skin_index); 1536 const Skeleton* skeleton = gfx_get_anima_skeleton(anima, skin_index);
1423 gfx_set_object_skeleton(object, skeleton); 1537 gfx_set_object_skeleton(object, skeleton);
1424
1425 // TODO: Compute AABBs/OOBBs for the skeleton's joints here. Iterate
1426 // over the mesh's primitives, its vertices, their joint indices, and
1427 // add the vertex to the AABB/OOBB.
1428 } 1538 }
1429 } else if (node->camera) { 1539 } else if (node->camera) {
1430 assert(next_camera < data->cameras_count); 1540 assert(next_camera < data->cameras_count);
@@ -1686,6 +1796,9 @@ static Model* load_scene(
1686 anima_desc->num_joints = load_skins(data, buffers, base, anima_desc); 1796 anima_desc->num_joints = load_skins(data, buffers, base, anima_desc);
1687 load_animations(data, base, anima_desc); 1797 load_animations(data, base, anima_desc);
1688 1798
1799 compute_joint_bounding_boxes(
1800 data, anima_desc->num_joints, anima_desc->joints);
1801
1689 anima = gfx_make_anima(anima_desc); 1802 anima = gfx_make_anima(anima_desc);
1690 gfx_construct_anima_node(root_node, anima); 1803 gfx_construct_anima_node(root_node, anima);
1691 } 1804 }
diff --git a/gfx/src/renderer/imm_renderer.c b/gfx/src/renderer/imm_renderer.c
index e8c0410..dd5e2cb 100644
--- a/gfx/src/renderer/imm_renderer.c
+++ b/gfx/src/renderer/imm_renderer.c
@@ -135,32 +135,45 @@ void gfx_imm_draw_aabb3(ImmRenderer* renderer, aabb3 box) {
135 assert(renderer); 135 assert(renderer);
136 136
137 // clang-format off 137 // clang-format off
138 const vec3 verts[8] = { 138 const vec3 vertices[8] = {
139 box.min, // 2 ----- 6 139 vec3_make(box.min.x, box.min.y, box.max.z), // 7 ----- 6
140 vec3_make(box.min.x, box.min.y, box.max.z), // / /| 140 vec3_make(box.max.x, box.min.y, box.max.z), // / /|
141 vec3_make(box.min.x, box.max.y, box.min.z), // 3 ----- 7 | 141 vec3_make(box.max.x, box.max.y, box.max.z), // 3 ----- 2 |
142 vec3_make(box.min.x, box.max.y, box.max.z), // | | | 142 vec3_make(box.min.x, box.max.y, box.max.z), // | | |
143 vec3_make(box.max.x, box.min.y, box.min.z), // | 0 ----- 4 143 vec3_make(box.min.x, box.min.y, box.min.z), // | 4 ----- 5
144 vec3_make(box.max.x, box.min.y, box.max.z), // |/ |/ 144 vec3_make(box.max.x, box.min.y, box.min.z), // |/ |/
145 vec3_make(box.max.x, box.max.y, box.min.z), // 1 ----- 5 145 vec3_make(box.max.x, box.max.y, box.min.z), // 0 ----- 1
146 box.max}; 146 vec3_make(box.min.x, box.max.y, box.min.z)};
147 // clang-format on 147 // clang-format on
148 148
149#define tri(i0, i1, i2) verts[i0], verts[i1], verts[i2] 149 gfx_imm_draw_box3(renderer, vertices);
150 // TODO: Use vertex indices in Geometry. 150}
151
152void gfx_imm_draw_box3(ImmRenderer* renderer, const vec3 vertices[8]) {
153 assert(renderer);
154 assert(vertices);
155
156 // 7 ----- 6
157 // / /|
158 // 3 ----- 2 |
159 // | | |
160 // | 4 ----- 5
161 // |/ |/
162 // 0 ----- 1
163
164#define tri(i0, i1, i2) vertices[i0], vertices[i1], vertices[i2]
151 const vec3 tris[36] = {// Front. 165 const vec3 tris[36] = {// Front.
152 tri(1, 5, 7), tri(1, 7, 3), 166 tri(0, 1, 2), tri(0, 2, 3),
153 // Right. 167 // Right.
154 tri(5, 4, 6), tri(5, 6, 7), 168 tri(1, 5, 6), tri(1, 6, 2),
155 // Back. 169 // Back.
156 tri(4, 0, 2), tri(4, 2, 6), 170 tri(5, 4, 7), tri(5, 7, 6),
157 // Left. 171 // Left.
158 tri(0, 1, 3), tri(0, 3, 2), 172 tri(4, 0, 03), tri(4, 3, 7),
159 // Top. 173 // Top.
160 tri(3, 7, 6), tri(3, 6, 2), 174 tri(3, 2, 6), tri(3, 6, 7),
161 // Bottom. 175 // Bottom.
162 tri(0, 4, 5), tri(0, 5, 1)}; 176 tri(0, 4, 5), tri(0, 5, 1)};
163#undef tri
164 177
165 gfx_imm_draw_triangles(renderer, tris, 12); 178 gfx_imm_draw_triangles(renderer, tris, 12);
166} 179}
diff --git a/gfx/src/renderer/renderer.c b/gfx/src/renderer/renderer.c
index 161eed2..2244733 100644
--- a/gfx/src/renderer/renderer.c
+++ b/gfx/src/renderer/renderer.c
@@ -197,9 +197,9 @@ static void load_skeleton(RenderState* state, skeleton_idx skeleton_index) {
197 state->num_joints = skeleton->num_joints; 197 state->num_joints = skeleton->num_joints;
198 198
199 for (size_t i = 0; i < skeleton->num_joints; ++i) { 199 for (size_t i = 0; i < skeleton->num_joints; ++i) {
200 const rel_idx joint_index = skeleton->joints[i]; 200 const joint_idx joint_index = skeleton->joints[i];
201 const Joint* joint = &state->anima->joints[joint_index]; 201 const Joint* joint = &state->anima->joints[joint_index];
202 state->joint_matrices[i] = joint->joint_matrix; 202 state->joint_matrices[i] = joint->joint_matrix;
203 } 203 }
204} 204}
205 205
diff --git a/gfx/src/scene/animation.c b/gfx/src/scene/animation.c
index 18e2a99..08d02ce 100644
--- a/gfx/src/scene/animation.c
+++ b/gfx/src/scene/animation.c
@@ -9,7 +9,7 @@
9 9
10static const R PLAYBACK_UNINITIALIZED = -1; 10static const R PLAYBACK_UNINITIALIZED = -1;
11 11
12static rel_idx get_anima_root_joint_index(Anima* anima) { 12static joint_idx get_anima_root_joint_index(Anima* anima) {
13 assert(anima); 13 assert(anima);
14 assert(anima->num_joints > 0); 14 assert(anima->num_joints > 0);
15 assert(anima->num_joints < GFX_MAX_NUM_JOINTS); 15 assert(anima->num_joints < GFX_MAX_NUM_JOINTS);
@@ -21,7 +21,7 @@ static Joint* get_anima_root_joint(Anima* anima) {
21 return &anima->joints[get_anima_root_joint_index(anima)]; 21 return &anima->joints[get_anima_root_joint_index(anima)];
22} 22}
23 23
24static Joint* get_anima_joint(Anima* anima, rel_idx index) { 24static const Joint* get_anima_joint(const Anima* anima, joint_idx index) {
25 assert(anima); 25 assert(anima);
26 assert(index < GFX_MAX_NUM_JOINTS); 26 assert(index < GFX_MAX_NUM_JOINTS);
27 assert(index != INDEX_NONE); 27 assert(index != INDEX_NONE);
@@ -29,22 +29,33 @@ static Joint* get_anima_joint(Anima* anima, rel_idx index) {
29 return &anima->joints[index]; 29 return &anima->joints[index];
30} 30}
31 31
32static Joint* get_anima_joint_mut(Anima* anima, joint_idx index) {
33 return (Joint*)get_anima_joint(anima, index);
34}
35
36static 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
32static void set_joint_parent( 43static void set_joint_parent(
33 Anima* anima, rel_idx joint_index, rel_idx parent_index) { 44 Anima* anima, joint_idx joint_index, joint_idx parent_index) {
34 assert(anima); 45 assert(anima);
35 assert(joint_index != INDEX_NONE); 46 assert(joint_index != INDEX_NONE);
36 assert(joint_index != get_anima_root_joint_index(anima)); 47 assert(joint_index != get_anima_root_joint_index(anima));
37 assert(parent_index != INDEX_NONE); 48 assert(parent_index != INDEX_NONE);
38 49
39 Joint* parent = get_anima_joint(anima, parent_index); 50 Joint* parent = get_anima_joint_mut(anima, parent_index);
40 51
41 if (parent->child == INDEX_NONE) { 52 if (parent->child == INDEX_NONE) {
42 parent->child = joint_index; 53 parent->child = joint_index;
43 } else { 54 } else {
44 // Find the last child in the chain of children. 55 // Find the last child in the chain of children.
45 Joint* child = get_anima_joint(anima, parent->child); 56 Joint* child = get_anima_joint_mut(anima, parent->child);
46 while (child->next != INDEX_NONE) { 57 while (child->next != INDEX_NONE) {
47 child = get_anima_joint(anima, child->next); 58 child = get_anima_joint_mut(anima, child->next);
48 } 59 }
49 // Wire up this joint as the last child's sibling. 60 // Wire up this joint as the last child's sibling.
50 child->next = joint_index; 61 child->next = joint_index;
@@ -64,6 +75,7 @@ static void make_joint(Anima* anima, const JointDesc* desc, Joint* joint) {
64 joint->transform = mat4_id(); 75 joint->transform = mat4_id();
65 joint->inv_bind_matrix = desc->inv_bind_matrix; 76 joint->inv_bind_matrix = desc->inv_bind_matrix;
66 joint->joint_matrix = mat4_id(); 77 joint->joint_matrix = mat4_id();
78 joint->box = desc->box;
67} 79}
68 80
69static Skeleton* make_skeleton(const SkeletonDesc* desc) { 81static Skeleton* make_skeleton(const SkeletonDesc* desc) {
@@ -88,6 +100,7 @@ static Animation* make_animation(const AnimationDesc* desc) {
88 animation->num_channels = desc->num_channels; 100 animation->num_channels = desc->num_channels;
89 R start_time = 0; 101 R start_time = 0;
90 R end_time = 0; 102 R end_time = 0;
103
91 for (size_t c = 0; c < desc->num_channels; ++c) { 104 for (size_t c = 0; c < desc->num_channels; ++c) {
92 const ChannelDesc* channel_desc = &desc->channels[c]; 105 const ChannelDesc* channel_desc = &desc->channels[c];
93 Channel* channel = &animation->channels[c]; 106 Channel* channel = &animation->channels[c];
@@ -110,6 +123,7 @@ static Animation* make_animation(const AnimationDesc* desc) {
110 end_time = keyframe->time > end_time ? keyframe->time : end_time; 123 end_time = keyframe->time > end_time ? keyframe->time : end_time;
111 } 124 }
112 } 125 }
126
113 // LOGD("Animation start/end: %f / %f", start_time, end_time); 127 // LOGD("Animation start/end: %f / %f", start_time, end_time);
114 animation->duration = end_time - start_time; 128 animation->duration = end_time - start_time;
115 assert(animation->duration >= 0); 129 assert(animation->duration >= 0);
@@ -122,7 +136,7 @@ Anima* gfx_make_anima(const AnimaDesc* desc) {
122 assert(desc->num_joints <= GFX_MAX_NUM_JOINTS); 136 assert(desc->num_joints <= GFX_MAX_NUM_JOINTS);
123 // All joints should have a parent except for the root. 137 // All joints should have a parent except for the root.
124 for (size_t i = 0; i < desc->num_joints - 1; ++i) { 138 for (size_t i = 0; i < desc->num_joints - 1; ++i) {
125 const rel_idx parent = desc->joints[i].parent; 139 const joint_idx parent = desc->joints[i].parent;
126 assert(parent != INDEX_NONE); 140 assert(parent != INDEX_NONE);
127 assert(parent < desc->num_joints); 141 assert(parent < desc->num_joints);
128 } 142 }
@@ -134,10 +148,7 @@ Anima* gfx_make_anima(const AnimaDesc* desc) {
134 // Wire the skeletons in the same order they are given in the descriptor. 148 // Wire the skeletons in the same order they are given in the descriptor.
135 Skeleton* last_skeleton = 0; 149 Skeleton* last_skeleton = 0;
136 for (size_t i = 0; i < desc->num_skeletons; ++i) { 150 for (size_t i = 0; i < desc->num_skeletons; ++i) {
137 Skeleton* skeleton = make_skeleton(&desc->skeletons[i]); 151 Skeleton* skeleton = make_skeleton(&desc->skeletons[i]);
138 // TODO: Here and everywhere else, I think it would simplify the code
139 // greatly to make mem_alloc_xyz() fail if the allocation fails. At that
140 // point the user should just bump their memory limits.
141 const skeleton_idx skeleton_index = mem_get_skeleton_index(skeleton); 152 const skeleton_idx skeleton_index = mem_get_skeleton_index(skeleton);
142 if (last_skeleton == 0) { 153 if (last_skeleton == 0) {
143 anima->skeleton = skeleton_index; 154 anima->skeleton = skeleton_index;
@@ -166,7 +177,7 @@ Anima* gfx_make_anima(const AnimaDesc* desc) {
166 // Child and sibling pointers must be initialized before wiring up the 177 // Child and sibling pointers must be initialized before wiring up the
167 // hierarchy. 178 // hierarchy.
168 for (size_t i = 0; i < desc->num_joints; ++i) { 179 for (size_t i = 0; i < desc->num_joints; ++i) {
169 Joint* joint = get_anima_joint(anima, i); 180 Joint* joint = get_anima_joint_mut(anima, i);
170 make_joint(anima, &desc->joints[i], joint); 181 make_joint(anima, &desc->joints[i], joint);
171 } 182 }
172 // Wire up joints to their parents. -1 to skip the root. 183 // Wire up joints to their parents. -1 to skip the root.
@@ -339,7 +350,7 @@ static void animate_channel(Anima* anima, const Channel* channel, R t) {
339 // work. 350 // work.
340 t = t > channel->keyframes[next].time ? channel->keyframes[next].time : t; 351 t = t > channel->keyframes[next].time ? channel->keyframes[next].time : t;
341 352
342 Joint* target = get_anima_joint(anima, channel->target); 353 Joint* target = get_anima_joint_mut(anima, channel->target);
343 354
344 switch (channel->type) { 355 switch (channel->type) {
345 case RotationChannel: { 356 case RotationChannel: {
@@ -380,7 +391,7 @@ static void compute_joint_matrices_rec(
380 391
381 // Recursively compute the joint matrices for this joint's siblings. 392 // Recursively compute the joint matrices for this joint's siblings.
382 if (joint->next != INDEX_NONE) { 393 if (joint->next != INDEX_NONE) {
383 Joint* sibling = get_anima_joint(anima, joint->next); 394 Joint* sibling = get_anima_joint_mut(anima, joint->next);
384 395
385 compute_joint_matrices_rec( 396 compute_joint_matrices_rec(
386 anima, sibling, parent_global_joint_transform, 397 anima, sibling, parent_global_joint_transform,
@@ -389,7 +400,7 @@ static void compute_joint_matrices_rec(
389 400
390 // Recursively compute the joint matrices for this joint's children. 401 // Recursively compute the joint matrices for this joint's children.
391 if (joint->child != INDEX_NONE) { 402 if (joint->child != INDEX_NONE) {
392 Joint* child = get_anima_joint(anima, joint->child); 403 Joint* child = get_anima_joint_mut(anima, joint->child);
393 404
394 compute_joint_matrices_rec( 405 compute_joint_matrices_rec(
395 anima, child, &global_joint_transform, root_inv_global_transform); 406 anima, child, &global_joint_transform, root_inv_global_transform);
@@ -431,16 +442,14 @@ void gfx_update_animation(Anima* anima, R t) {
431 442
432 // Compute joint matrices after having transformed the skeletons. 443 // Compute joint matrices after having transformed the skeletons.
433 // 444 //
434 // Skeletons are not guaranteed to have a common parent, but are generally a 445 // The anima's parent node is the common ancestor of all skeletons, and its
435 // set of disjoint trees (glTF). The anima's parent node, however, is 446 // transform maps the skeletons from object space to world space. This is the
436 // guaranteed to be the common ancestor of all skeletons. 447 // transform used as the "global transform" in the joint matrix equations.
437 // 448 //
438 // Joint matrix calculation therefore begins by descending from the anima's 449 // Joint matrix calculation begins by descending from the anima's root joint,
439 // node. This node's immediate children may not be joints, however, so we need 450 // which we have constructed to be the common root of all skeletons.
440 // to gracefully handle them and proceed recursively.
441 // 451 //
442 // Lack of a common parent aside, the procedure touches every joint exactly 452 // This procedure touches every joint exactly once.
443 // once (and potentially other non-joint intermediate nodes).
444 SceneNode* root_node = mem_get_node(anima->parent); 453 SceneNode* root_node = mem_get_node(anima->parent);
445 // LOGD("Root: %u, child: %u", anima->parent.val, root->child.val); 454 // LOGD("Root: %u, child: %u", anima->parent.val, root->child.val);
446 const mat4 root_global_transform = gfx_get_node_global_transform(root_node); 455 const mat4 root_global_transform = gfx_get_node_global_transform(root_node);
@@ -464,3 +473,52 @@ const Skeleton* gfx_get_anima_skeleton(const Anima* anima, size_t i) {
464 473
465 return skeleton; 474 return skeleton;
466} 475}
476
477size_t gfx_get_skeleton_num_joints(const Skeleton* skeleton) {
478 assert(skeleton);
479 return skeleton->num_joints;
480}
481
482bool gfx_joint_has_box(
483 const Anima* anima, const Skeleton* skeleton, size_t joint_index) {
484 assert(anima);
485 assert(skeleton);
486 assert(joint_index < skeleton->num_joints);
487
488 const Joint* joint = get_skeleton_joint(anima, skeleton, joint_index);
489 return !aabb3_is_empty(joint->box);
490}
491
492Box gfx_get_joint_box(
493 const Anima* anima, const Skeleton* skeleton, size_t joint_index) {
494 assert(anima);
495 assert(skeleton);
496
497 const Joint* joint = get_skeleton_joint(anima, skeleton, joint_index);
498
499 // Transform the box to anima space.
500 // Note that joint matrices do not usually have a translation since joints
501 // mostly just rotate with respect to their parent.
502 const vec3 pmin = joint->box.min;
503 const vec3 pmax = joint->box.max;
504 return (Box){
505 .vertices = {
506 mat4_mul_vec3(
507 joint->joint_matrix, vec3_make(pmin.x, pmin.y, pmax.z), 1),
508 mat4_mul_vec3(
509 joint->joint_matrix, vec3_make(pmax.x, pmin.y, pmax.z), 1),
510 mat4_mul_vec3(
511 joint->joint_matrix, vec3_make(pmax.x, pmax.y, pmax.z), 1),
512 mat4_mul_vec3(
513 joint->joint_matrix, vec3_make(pmin.x, pmax.y, pmax.z), 1),
514 mat4_mul_vec3(
515 joint->joint_matrix, vec3_make(pmin.x, pmin.y, pmin.z), 1),
516 mat4_mul_vec3(
517 joint->joint_matrix, vec3_make(pmax.x, pmin.y, pmin.z), 1),
518 mat4_mul_vec3(
519 joint->joint_matrix, vec3_make(pmax.x, pmax.y, pmin.z), 1),
520 mat4_mul_vec3(
521 joint->joint_matrix, vec3_make(pmin.x, pmax.y, pmin.z), 1),
522 }
523 };
524}
diff --git a/gfx/src/scene/animation_impl.h b/gfx/src/scene/animation_impl.h
index 7265858..4408158 100644
--- a/gfx/src/scene/animation_impl.h
+++ b/gfx/src/scene/animation_impl.h
@@ -24,18 +24,19 @@ typedef struct Buffer Buffer;
24/// Joints are mutable and store the transform and joint matrices that result 24/// Joints are mutable and store the transform and joint matrices that result
25/// from animation, aside from the inverse bind matrix. 25/// from animation, aside from the inverse bind matrix.
26typedef struct Joint { 26typedef struct Joint {
27 rel_idx child; /// First child Joint; index into Anima's joints. 27 joint_idx child; /// First child Joint; index into Anima's joints.
28 rel_idx next; /// Next sibling Joint; index into Anima's joints. 28 joint_idx next; /// Next sibling Joint; index into Anima's joints.
29 mat4 transform; /// Local transform relative to parent. 29 mat4 transform; /// Local transform relative to parent.
30 mat4 inv_bind_matrix; /// Transforms the mesh into the joint's local space. 30 mat4 inv_bind_matrix; /// Transforms the mesh into the joint's local space.
31 mat4 joint_matrix; /// inv(global) * global joint transform * inv(bind). 31 mat4 joint_matrix; /// inv(global) * global joint transform * inv(bind).
32 aabb3 box; /// Bounding box of vertices affected by joint.
32} Joint; 33} Joint;
33 34
34/// Animation skeleton. 35/// Animation skeleton.
35typedef struct Skeleton { 36typedef struct Skeleton {
36 skeleton_idx next; 37 skeleton_idx next;
37 size_t num_joints; 38 size_t num_joints;
38 rel_idx joints[GFX_MAX_NUM_JOINTS]; /// Indices into Anima's joints array. 39 joint_idx joints[GFX_MAX_NUM_JOINTS]; /// Indices into Anima's joints array.
39} Skeleton; 40} Skeleton;
40 41
41/// A keyframe of animation. 42/// A keyframe of animation.
@@ -49,7 +50,7 @@ typedef struct Keyframe {
49 50
50/// Animation channel. 51/// Animation channel.
51typedef struct Channel { 52typedef struct Channel {
52 rel_idx target; /// Index into Anima's joints array. 53 joint_idx target; /// Index into Anima's joints array.
53 ChannelType type; 54 ChannelType type;
54 AnimationInterpolation interpolation; 55 AnimationInterpolation interpolation;
55 size_t num_keyframes; 56 size_t num_keyframes;
diff --git a/gfx/src/scene/object.c b/gfx/src/scene/object.c
index 9291feb..406c81f 100644
--- a/gfx/src/scene/object.c
+++ b/gfx/src/scene/object.c
@@ -72,6 +72,11 @@ void gfx_set_object_skeleton(SceneObject* object, const Skeleton* skeleton) {
72 object->skeleton = mem_get_skeleton_index(skeleton); 72 object->skeleton = mem_get_skeleton_index(skeleton);
73} 73}
74 74
75const Skeleton* gfx_get_object_skeleton(const SceneObject* object) {
76 assert(object);
77 return (object->skeleton.val == 0) ? 0 : mem_get_skeleton(object->skeleton);
78}
79
75aabb3 gfx_get_object_aabb(const SceneObject* object) { 80aabb3 gfx_get_object_aabb(const SceneObject* object) {
76 assert(object); 81 assert(object);
77 return object->box; 82 return object->box;
diff --git a/gfx/src/scene/object_impl.h b/gfx/src/scene/object_impl.h
index 91e1427..88f8e31 100644
--- a/gfx/src/scene/object_impl.h
+++ b/gfx/src/scene/object_impl.h
@@ -20,7 +20,7 @@ typedef struct MeshLink {
20/// of Meshes, and the Meshes are re-used. 20/// of Meshes, and the Meshes are re-used.
21typedef struct SceneObject { 21typedef struct SceneObject {
22 mesh_link_idx mesh_link; /// First MeshLink in the list. 22 mesh_link_idx mesh_link; /// First MeshLink in the list.
23 skeleton_idx skeleton; 23 skeleton_idx skeleton; /// 0 for static objects.
24 node_idx parent; /// Parent SceneNode. 24 node_idx parent; /// Parent SceneNode.
25 aabb3 box; 25 aabb3 box;
26} SceneObject; 26} SceneObject;
diff --git a/gfx/src/scene/types.h b/gfx/src/scene/types.h
index 9752bcf..d0ffc41 100644
--- a/gfx/src/scene/types.h
+++ b/gfx/src/scene/types.h
@@ -13,7 +13,6 @@ typedef uint16_t gfx_idx;
13DEF_STRONG_INDEX(anima, gfx_idx) 13DEF_STRONG_INDEX(anima, gfx_idx)
14DEF_STRONG_INDEX(animation, gfx_idx) 14DEF_STRONG_INDEX(animation, gfx_idx)
15DEF_STRONG_INDEX(camera, gfx_idx) 15DEF_STRONG_INDEX(camera, gfx_idx)
16DEF_STRONG_INDEX(joint, gfx_idx)
17DEF_STRONG_INDEX(light, gfx_idx) 16DEF_STRONG_INDEX(light, gfx_idx)
18DEF_STRONG_INDEX(material, gfx_idx) 17DEF_STRONG_INDEX(material, gfx_idx)
19DEF_STRONG_INDEX(mesh, gfx_idx) 18DEF_STRONG_INDEX(mesh, gfx_idx)