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) |
