diff options
author | 3gg <3gg@shellblade.net> | 2024-03-02 07:47:29 -0800 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2024-03-02 07:47:29 -0800 |
commit | 4bc4ca2796bd434880b77d3c4bcbb56107456777 (patch) | |
tree | 6fc4280d14740108a6ace0963d9a4b0ef99f38ef | |
parent | daaa3ef68705da389d39ef625840bf5278b25f22 (diff) |
Compute joint bounding boxes.
-rw-r--r-- | game/src/plugins/viewer.c | 66 | ||||
-rw-r--r-- | gfx/include/gfx/renderer.h | 13 | ||||
-rw-r--r-- | gfx/include/gfx/scene/animation.h | 33 | ||||
-rw-r--r-- | gfx/include/gfx/scene/object.h | 4 | ||||
-rw-r--r-- | gfx/src/asset/asset_cache.c | 11 | ||||
-rw-r--r-- | gfx/src/asset/model.c | 493 | ||||
-rw-r--r-- | gfx/src/renderer/imm_renderer.c | 45 | ||||
-rw-r--r-- | gfx/src/renderer/renderer.c | 6 | ||||
-rw-r--r-- | gfx/src/scene/animation.c | 104 | ||||
-rw-r--r-- | gfx/src/scene/animation_impl.h | 15 | ||||
-rw-r--r-- | gfx/src/scene/object.c | 5 | ||||
-rw-r--r-- | gfx/src/scene/object_impl.h | 4 | ||||
-rw-r--r-- | gfx/src/scene/types.h | 1 |
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"; |
21 | static const char* GIRL = | 23 | static 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"; |
25 | static 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. |
170 | static void render_bounding_boxes_rec(ImmRenderer* imm, const SceneNode* node) { | 174 | static 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. |
67 | void gfx_imm_draw_aabb3(ImmRenderer*, aabb3); | 67 | void 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 | ||
80 | void gfx_imm_draw_box3(ImmRenderer* renderer, const vec3 vertices[8]); | ||
81 | |||
69 | /// Set the camera. | 82 | /// Set the camera. |
70 | void gfx_imm_set_camera(ImmRenderer*, const Camera*); | 83 | void 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; | |||
22 | typedef struct Skeleton Skeleton; | 23 | typedef 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. |
25 | typedef uint16_t rel_idx; | 26 | typedef uint16_t joint_idx; |
26 | 27 | ||
27 | /// Index value denoting no index. | 28 | /// Index value denoting no index. |
28 | static const rel_idx INDEX_NONE = (rel_idx)-1; | 29 | static const joint_idx INDEX_NONE = (joint_idx)-1; |
30 | |||
31 | typedef struct Box { | ||
32 | vec3 vertices[8]; | ||
33 | } Box; | ||
29 | 34 | ||
30 | /// Joint descriptor. | 35 | /// Joint descriptor. |
31 | typedef struct JointDesc { | 36 | typedef 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. |
37 | typedef struct SkeletonDesc { | 43 | typedef 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. |
69 | typedef struct ChannelDesc { | 75 | typedef 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. |
123 | const Skeleton* gfx_get_anima_skeleton(const Anima* anima, size_t i); | 129 | const Skeleton* gfx_get_anima_skeleton(const Anima* anima, size_t i); |
130 | |||
131 | /// Return the number of joints in the skeleton. | ||
132 | size_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. | ||
137 | bool 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. | ||
142 | Box 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. |
30 | void gfx_set_object_skeleton(SceneObject*, const Skeleton*); | 30 | void gfx_set_object_skeleton(SceneObject*, const Skeleton*); |
31 | 31 | ||
32 | /// Get the object's skeleton. | ||
33 | /// Return null if the object has no skeleton. | ||
34 | const 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) { | |||
152 | static Model* clone_model(const Model* model) { | 152 | static 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. | ||
327 | int 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. | ||
353 | static 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 |
332 | static float read_float(const void* data, const cgltf_accessor* accessor) { | 396 | static 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. | 438 | typedef 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; \ | 447 | typedef 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; \ | 457 | bool 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 | |||
501 | AccessorIter 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). |
859 | aabb3 compute_aabb(const cgltf_accessor* accessor, int dim) { | 886 | aabb3 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. | ||
1222 | static 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 | |||
152 | void 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 | ||
10 | static const R PLAYBACK_UNINITIALIZED = -1; | 10 | static const R PLAYBACK_UNINITIALIZED = -1; |
11 | 11 | ||
12 | static rel_idx get_anima_root_joint_index(Anima* anima) { | 12 | static 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 | ||
24 | static Joint* get_anima_joint(Anima* anima, rel_idx index) { | 24 | static 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 | ||
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 | |||
32 | static void set_joint_parent( | 43 | static 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 | ||
69 | static Skeleton* make_skeleton(const SkeletonDesc* desc) { | 81 | static 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 | |||
477 | size_t gfx_get_skeleton_num_joints(const Skeleton* skeleton) { | ||
478 | assert(skeleton); | ||
479 | return skeleton->num_joints; | ||
480 | } | ||
481 | |||
482 | bool 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 | |||
492 | Box 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. |
26 | typedef struct Joint { | 26 | typedef 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. |
35 | typedef struct Skeleton { | 36 | typedef 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. |
51 | typedef struct Channel { | 52 | typedef 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 | ||
75 | const 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 | |||
75 | aabb3 gfx_get_object_aabb(const SceneObject* object) { | 80 | aabb3 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. |
21 | typedef struct SceneObject { | 21 | typedef 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; | |||
13 | DEF_STRONG_INDEX(anima, gfx_idx) | 13 | DEF_STRONG_INDEX(anima, gfx_idx) |
14 | DEF_STRONG_INDEX(animation, gfx_idx) | 14 | DEF_STRONG_INDEX(animation, gfx_idx) |
15 | DEF_STRONG_INDEX(camera, gfx_idx) | 15 | DEF_STRONG_INDEX(camera, gfx_idx) |
16 | DEF_STRONG_INDEX(joint, gfx_idx) | ||
17 | DEF_STRONG_INDEX(light, gfx_idx) | 16 | DEF_STRONG_INDEX(light, gfx_idx) |
18 | DEF_STRONG_INDEX(material, gfx_idx) | 17 | DEF_STRONG_INDEX(material, gfx_idx) |
19 | DEF_STRONG_INDEX(mesh, gfx_idx) | 18 | DEF_STRONG_INDEX(mesh, gfx_idx) |