diff options
| author | 3gg <3gg@shellblade.net> | 2024-02-17 11:17:30 -0800 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2024-02-17 11:17:30 -0800 |
| commit | 13c7adb42168a566c97f36db76080d80e02a6aae (patch) | |
| tree | 45699619ee40d38733848364c5f0a12851c945e6 | |
| parent | 54f2b7a097c8d11984c351057f33be8c79a2d5eb (diff) | |
Pack joints into array to simplify animation data structure and make it easier to clone.
| -rw-r--r-- | gfx/include/gfx/scene/animation.h | 48 | ||||
| -rw-r--r-- | gfx/include/gfx/scene/node.h | 13 | ||||
| -rw-r--r-- | gfx/src/asset/scene.c | 163 | ||||
| -rw-r--r-- | gfx/src/renderer/renderer.c | 21 | ||||
| -rw-r--r-- | gfx/src/scene/animation.c | 185 | ||||
| -rw-r--r-- | gfx/src/scene/animation_impl.h | 42 | ||||
| -rw-r--r-- | gfx/src/scene/node.c | 32 | ||||
| -rw-r--r-- | gfx/src/scene/node_impl.h | 1 | ||||
| -rw-r--r-- | gfx/src/scene/scene_memory.c | 6 | ||||
| -rw-r--r-- | gfx/src/scene/scene_memory.h | 1 |
10 files changed, 309 insertions, 203 deletions
diff --git a/gfx/include/gfx/scene/animation.h b/gfx/include/gfx/scene/animation.h index eeb21fd..42c0c43 100644 --- a/gfx/include/gfx/scene/animation.h +++ b/gfx/include/gfx/scene/animation.h | |||
| @@ -17,25 +17,26 @@ | |||
| 17 | typedef struct Buffer Buffer; | 17 | typedef struct Buffer Buffer; |
| 18 | typedef struct SceneNode SceneNode; | 18 | typedef struct SceneNode SceneNode; |
| 19 | 19 | ||
| 20 | typedef struct Anima Anima; | 20 | typedef struct Anima Anima; |
| 21 | typedef struct Joint Joint; | 21 | typedef struct Joint Joint; |
| 22 | // TODO: Remove this when removing gfx_get_anima_skeleton(). | ||
| 23 | typedef struct Skeleton Skeleton; | 22 | typedef struct Skeleton Skeleton; |
| 24 | 23 | ||
| 24 | /// Index type used to store relative indices into arrays. | ||
| 25 | typedef uint16_t rel_idx; | ||
| 26 | |||
| 27 | /// Index value denoting no index. | ||
| 28 | static const rel_idx INDEX_NONE = (rel_idx)-1; | ||
| 29 | |||
| 25 | /// Joint descriptor. | 30 | /// Joint descriptor. |
| 26 | typedef struct JointDesc { | 31 | typedef struct JointDesc { |
| 27 | mat4 inv_bind_matrix; | 32 | rel_idx parent; /// Parent Joint; index into Anima's joints. |
| 33 | mat4 inv_bind_matrix; /// Transforms the mesh into the joint's local space. | ||
| 28 | } JointDesc; | 34 | } JointDesc; |
| 29 | 35 | ||
| 30 | /// Skeleton descriptor. | 36 | /// Skeleton descriptor. |
| 31 | /// | ||
| 32 | /// The last element of the joints array is the root of the hierarchy. For | ||
| 33 | /// flexibility (glTF), the root need not be the immediate parent of the | ||
| 34 | /// top-level joints of the skeleton, but rather, intermediate non-joint nodes | ||
| 35 | /// are allowed between the root and the skeleton. | ||
| 36 | typedef struct SkeletonDesc { | 37 | typedef struct SkeletonDesc { |
| 37 | size_t num_joints; // Number of joints and matrices. | 38 | size_t num_joints; |
| 38 | const SceneNode* joints[GFX_MAX_NUM_JOINTS]; | 39 | rel_idx joints[GFX_MAX_NUM_JOINTS]; /// Indices into Anima's joints array. |
| 39 | } SkeletonDesc; | 40 | } SkeletonDesc; |
| 40 | 41 | ||
| 41 | /// Animation interpolation mode. | 42 | /// Animation interpolation mode. |
| @@ -66,7 +67,7 @@ typedef struct KeyframeDesc { | |||
| 66 | 67 | ||
| 67 | /// Animation channel descriptor. | 68 | /// Animation channel descriptor. |
| 68 | typedef struct ChannelDesc { | 69 | typedef struct ChannelDesc { |
| 69 | SceneNode* target; | 70 | rel_idx target; /// Index into Anima's joints array. |
| 70 | ChannelType type; | 71 | ChannelType type; |
| 71 | AnimationInterpolation interpolation; | 72 | AnimationInterpolation interpolation; |
| 72 | size_t num_keyframes; | 73 | size_t num_keyframes; |
| @@ -75,19 +76,25 @@ typedef struct ChannelDesc { | |||
| 75 | 76 | ||
| 76 | /// Animation descriptor. | 77 | /// Animation descriptor. |
| 77 | typedef struct AnimationDesc { | 78 | typedef struct AnimationDesc { |
| 78 | // TODO: Replace the name with a hash for a smaller footprint and faster | 79 | // TODO: Store a name hash for faster comparisons. |
| 79 | // comparisons. | ||
| 80 | sstring name; // Animation name. Required for playback. | 80 | sstring name; // Animation name. Required for playback. |
| 81 | size_t num_channels; // Number of channels. | 81 | size_t num_channels; // Number of channels. |
| 82 | ChannelDesc channels[GFX_MAX_NUM_CHANNELS]; | 82 | ChannelDesc channels[GFX_MAX_NUM_CHANNELS]; |
| 83 | } AnimationDesc; | 83 | } AnimationDesc; |
| 84 | 84 | ||
| 85 | /// Anima object descriptor. | 85 | /// Anima object descriptor. |
| 86 | /// | ||
| 87 | /// The last joint of the joints array at index 'num_joints - 1' must be the | ||
| 88 | /// root of all skeletons; specifically, the root of all joints that otherwise | ||
| 89 | /// would have no parent (a skeleton need not have its own root and can be a set | ||
| 90 | /// of disjoint node hierarchies). | ||
| 86 | typedef struct AnimaDesc { | 91 | typedef struct AnimaDesc { |
| 87 | size_t num_skeletons; | 92 | size_t num_skeletons; |
| 88 | size_t num_animations; | 93 | size_t num_animations; |
| 94 | size_t num_joints; | ||
| 89 | SkeletonDesc skeletons[GFX_MAX_NUM_SKELETONS]; | 95 | SkeletonDesc skeletons[GFX_MAX_NUM_SKELETONS]; |
| 90 | AnimationDesc animations[GFX_MAX_NUM_ANIMATIONS]; | 96 | AnimationDesc animations[GFX_MAX_NUM_ANIMATIONS]; |
| 97 | JointDesc joints[GFX_MAX_NUM_JOINTS]; | ||
| 91 | } AnimaDesc; | 98 | } AnimaDesc; |
| 92 | 99 | ||
| 93 | /// Animation play settings. | 100 | /// Animation play settings. |
| @@ -97,15 +104,7 @@ typedef struct AnimationPlaySettings { | |||
| 97 | // TODO: Add animation speed. | 104 | // TODO: Add animation speed. |
| 98 | } AnimationPlaySettings; | 105 | } AnimationPlaySettings; |
| 99 | 106 | ||
| 100 | /// Create a joint. | ||
| 101 | Joint* gfx_make_joint(const JointDesc*); | ||
| 102 | |||
| 103 | /// Destroy the joint. | ||
| 104 | void gfx_destroy_joint(Joint**); | ||
| 105 | |||
| 106 | /// Create an anima object. | 107 | /// Create an anima object. |
| 107 | /// | ||
| 108 | /// The anima owns its skeletons and animations. | ||
| 109 | Anima* gfx_make_anima(const AnimaDesc*); | 108 | Anima* gfx_make_anima(const AnimaDesc*); |
| 110 | 109 | ||
| 111 | /// Destroy the anima. | 110 | /// Destroy the anima. |
| @@ -120,8 +119,5 @@ void gfx_update_animation(Anima*, R t); | |||
| 120 | /// Stop the current animation. | 119 | /// Stop the current animation. |
| 121 | void gfx_stop_animation(Anima*); | 120 | void gfx_stop_animation(Anima*); |
| 122 | 121 | ||
| 123 | // TODO: Remove this, it's ugly. Things would be much simpler if scene nodes | 122 | /// Return the anima's ith skeleton. |
| 124 | // were allocated in arrays. Then our descs can take indices instead of | ||
| 125 | // pointers, and locating a node is simply a matter of indexing the array. The | ||
| 126 | // same is true for skeletons here and other objects. | ||
| 127 | const Skeleton* gfx_get_anima_skeleton(const Anima* anima, size_t i); | 123 | const Skeleton* gfx_get_anima_skeleton(const Anima* anima, size_t i); |
diff --git a/gfx/include/gfx/scene/node.h b/gfx/include/gfx/scene/node.h index aedac92..7976ae8 100644 --- a/gfx/include/gfx/scene/node.h +++ b/gfx/include/gfx/scene/node.h | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | #include <stdint.h> | 8 | #include <stdint.h> |
| 9 | 9 | ||
| 10 | typedef struct Anima Anima; | 10 | typedef struct Anima Anima; |
| 11 | typedef struct Joint Joint; | ||
| 12 | typedef struct Light Light; | 11 | typedef struct Light Light; |
| 13 | typedef struct SceneCamera SceneCamera; | 12 | typedef struct SceneCamera SceneCamera; |
| 14 | typedef struct SceneObject SceneObject; | 13 | typedef struct SceneObject SceneObject; |
| @@ -21,7 +20,6 @@ typedef enum NodeType { | |||
| 21 | LogicalNode, | 20 | LogicalNode, |
| 22 | AnimaNode, | 21 | AnimaNode, |
| 23 | CameraNode, | 22 | CameraNode, |
| 24 | JointNode, | ||
| 25 | LightNode, | 23 | LightNode, |
| 26 | ObjectNode | 24 | ObjectNode |
| 27 | } NodeType; | 25 | } NodeType; |
| @@ -48,9 +46,6 @@ SceneNode* gfx_make_anima_node(Anima*); | |||
| 48 | /// Create a new camera node. | 46 | /// Create a new camera node. |
| 49 | SceneNode* gfx_make_camera_node(SceneCamera*); | 47 | SceneNode* gfx_make_camera_node(SceneCamera*); |
| 50 | 48 | ||
| 51 | /// Create a joint node. | ||
| 52 | SceneNode* gfx_make_joint_node(Joint*); | ||
| 53 | |||
| 54 | /// Create a new light node. | 49 | /// Create a new light node. |
| 55 | SceneNode* gfx_make_light_node(Light*); | 50 | SceneNode* gfx_make_light_node(Light*); |
| 56 | 51 | ||
| @@ -63,9 +58,6 @@ void gfx_construct_anima_node(SceneNode*, Anima*); | |||
| 63 | /// Make the node a camera node. | 58 | /// Make the node a camera node. |
| 64 | void gfx_construct_camera_node(SceneNode*, SceneCamera*); | 59 | void gfx_construct_camera_node(SceneNode*, SceneCamera*); |
| 65 | 60 | ||
| 66 | /// Make the node a joint node. | ||
| 67 | void gfx_construct_joint_node(SceneNode*, Joint*); | ||
| 68 | |||
| 69 | /// Make the node a light node. | 61 | /// Make the node a light node. |
| 70 | void gfx_construct_light_node(SceneNode*, Light*); | 62 | void gfx_construct_light_node(SceneNode*, Light*); |
| 71 | 63 | ||
| @@ -96,11 +88,6 @@ Anima* gfx_get_node_anima(const SceneNode*); | |||
| 96 | /// The node must be of type CameraNode. | 88 | /// The node must be of type CameraNode. |
| 97 | SceneCamera* gfx_get_node_camera(const SceneNode* node); | 89 | SceneCamera* gfx_get_node_camera(const SceneNode* node); |
| 98 | 90 | ||
| 99 | /// Get the node's joint. | ||
| 100 | /// | ||
| 101 | /// The node must be of type JointNode. | ||
| 102 | Joint* gfx_get_node_joint(const SceneNode*); | ||
| 103 | |||
| 104 | /// Get the node's light. | 91 | /// Get the node's light. |
| 105 | /// | 92 | /// |
| 106 | /// The node must be of type LightNode. | 93 | /// The node must be of type LightNode. |
diff --git a/gfx/src/asset/scene.c b/gfx/src/asset/scene.c index 156e7b6..ef2328c 100644 --- a/gfx/src/asset/scene.c +++ b/gfx/src/asset/scene.c | |||
| @@ -1177,63 +1177,147 @@ static bool load_meshes( | |||
| 1177 | return true; | 1177 | return true; |
| 1178 | } | 1178 | } |
| 1179 | 1179 | ||
| 1180 | /// Find the joint node with the smallest index across all skeletons. | ||
| 1181 | /// | ||
| 1182 | /// The channels in glTF may target arbitrary nodes in the scene (those nodes | ||
| 1183 | /// are the joints). However, we want to map the "base joint" (the joint/node | ||
| 1184 | /// with the smallest index) to 0 in the AnimaDesc's joint array. We can do this | ||
| 1185 | /// by subtracting the "base node index" from every joint index or channel | ||
| 1186 | /// target. | ||
| 1187 | /// | ||
| 1188 | /// There is an assumption in the animation library that joints are contiguous | ||
| 1189 | /// anyway, so this "base joint index" works provided the joint nodes are also | ||
| 1190 | /// contiguous in the glTF. The glTF does not guarantee this, but I think it's | ||
| 1191 | /// a reasonable assumption that exporters write glTF files in such a way, and | ||
| 1192 | /// Blender does appear to do so. | ||
| 1193 | cgltf_size find_base_joint_index(const cgltf_data* data) { | ||
| 1194 | assert(data); | ||
| 1195 | |||
| 1196 | cgltf_size base_joint_index = (cgltf_size)-1; | ||
| 1197 | |||
| 1198 | for (cgltf_size s = 0; s < data->skins_count; ++s) { | ||
| 1199 | const cgltf_skin* skin = &data->skins[s]; | ||
| 1200 | for (cgltf_size j = 0; j < skin->joints_count; ++j) { | ||
| 1201 | // Joint is an index/pointer into the nodes array. | ||
| 1202 | const cgltf_size node_index = skin->joints[j] - data->nodes; | ||
| 1203 | assert(node_index < data->nodes_count); | ||
| 1204 | // Min. | ||
| 1205 | if (node_index < base_joint_index) { | ||
| 1206 | base_joint_index = node_index; | ||
| 1207 | } | ||
| 1208 | } | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | return base_joint_index; | ||
| 1212 | } | ||
| 1213 | |||
| 1180 | /// Load all skins (Gfx skeletons) from the glTF scene. | 1214 | /// Load all skins (Gfx skeletons) from the glTF scene. |
| 1181 | static void load_skins( | 1215 | /// Return the total number of joints. |
| 1182 | const cgltf_data* data, Buffer* const* buffers, SceneNode** nodes, | 1216 | static size_t load_skins( |
| 1183 | SkeletonDesc* descs) { | 1217 | const cgltf_data* data, Buffer* const* buffers, cgltf_size base_joint_index, |
| 1218 | AnimaDesc* anima_desc) { | ||
| 1184 | assert(data); | 1219 | assert(data); |
| 1185 | assert(buffers); | 1220 | assert(buffers); |
| 1186 | assert(nodes); | 1221 | assert(anima_desc); |
| 1187 | assert(descs); | 1222 | assert(base_joint_index < data->nodes_count); |
| 1223 | |||
| 1224 | // Determines whether the ith joint in the node hierarchy is a joint node. | ||
| 1225 | // This is then used to determine whether a joint is a root of the joint | ||
| 1226 | // hierarchy. | ||
| 1227 | bool is_joint_node[GFX_MAX_NUM_JOINTS] = {false}; | ||
| 1228 | |||
| 1229 | size_t num_joints = 0; | ||
| 1188 | 1230 | ||
| 1189 | for (cgltf_size s = 0; s < data->skins_count; ++s) { | 1231 | for (cgltf_size s = 0; s < data->skins_count; ++s) { |
| 1190 | const cgltf_skin* skin = &data->skins[s]; | 1232 | const cgltf_skin* skin = &data->skins[s]; |
| 1191 | const cgltf_accessor* matrices_accessor = skin->inverse_bind_matrices; | 1233 | const cgltf_accessor* matrices_accessor = skin->inverse_bind_matrices; |
| 1192 | assert(matrices_accessor->count == skin->joints_count); | 1234 | assert(matrices_accessor->count == skin->joints_count); |
| 1193 | SkeletonDesc* desc = &descs[s]; | ||
| 1194 | 1235 | ||
| 1195 | *desc = (SkeletonDesc){.num_joints = skin->joints_count}; | 1236 | num_joints += skin->joints_count; |
| 1237 | assert(num_joints < GFX_MAX_NUM_JOINTS); | ||
| 1238 | |||
| 1239 | SkeletonDesc* skeleton_desc = &anima_desc->skeletons[s]; | ||
| 1240 | *skeleton_desc = (SkeletonDesc){.num_joints = skin->joints_count}; | ||
| 1196 | 1241 | ||
| 1197 | assert(skin->joints_count < GFX_MAX_NUM_JOINTS); | ||
| 1198 | // for (cgltf_size j = 0; j < skin->joints_count; ++j) { | 1242 | // for (cgltf_size j = 0; j < skin->joints_count; ++j) { |
| 1199 | ACCESSOR_FOREACH_MAT(4, matrices_accessor, { | 1243 | ACCESSOR_FOREACH_MAT(4, matrices_accessor, { |
| 1200 | const mat4 inv_bind_matrix = mat4_from_array(floats); | 1244 | const mat4 inv_bind_matrix = mat4_from_array(floats); |
| 1201 | 1245 | ||
| 1246 | // Joint is an index/pointer into the nodes array. | ||
| 1202 | const cgltf_size node_index = skin->joints[i] - data->nodes; | 1247 | const cgltf_size node_index = skin->joints[i] - data->nodes; |
| 1203 | assert(node_index < data->nodes_count); | 1248 | assert(node_index < data->nodes_count); |
| 1204 | SceneNode* node = nodes[node_index]; | ||
| 1205 | 1249 | ||
| 1206 | // Transform the node into a joint node. | 1250 | const cgltf_size parent_node_index = |
| 1207 | const JointDesc joint_desc = | 1251 | skin->joints[i]->parent - data->nodes; |
| 1208 | (JointDesc){.inv_bind_matrix = inv_bind_matrix}; | 1252 | assert(parent_node_index < data->nodes_count); |
| 1209 | Joint* joint = gfx_make_joint(&joint_desc); | 1253 | |
| 1210 | gfx_construct_joint_node(node, joint); | 1254 | // Subtract the base index to pack the joints as tightly as possible in |
| 1255 | // the AnimaDesc. | ||
| 1256 | assert(node_index >= base_joint_index); | ||
| 1257 | const cgltf_size joint_index = node_index - base_joint_index; | ||
| 1211 | 1258 | ||
| 1212 | desc->joints[i] = node; | 1259 | assert(parent_node_index >= base_joint_index); |
| 1260 | const cgltf_size parent_index = parent_node_index - base_joint_index; | ||
| 1261 | |||
| 1262 | skeleton_desc->joints[i] = joint_index; | ||
| 1263 | |||
| 1264 | JointDesc* joint_desc = &anima_desc->joints[joint_index]; | ||
| 1265 | joint_desc->parent = parent_index; | ||
| 1266 | joint_desc->inv_bind_matrix = inv_bind_matrix; | ||
| 1267 | |||
| 1268 | is_joint_node[joint_index] = true; | ||
| 1213 | }); | 1269 | }); |
| 1214 | 1270 | ||
| 1215 | // glTF may specify a "skeleton", which is the root of the skeleton's node | 1271 | // glTF may specify a "skeleton", which is the root of the skin's |
| 1216 | // hierarchy. TODO: We could use this root node to optimize the descend in | 1272 | // (skeleton's) node hierarchy. |
| 1217 | // joint matrix computation. The root node should be passed with the | ||
| 1218 | // AnimaDesc. | ||
| 1219 | // if (skin->skeleton) { | 1273 | // if (skin->skeleton) { |
| 1220 | // cgltf_size root_index = skin->skeleton - data->nodes; | 1274 | // // cgltf_size root_index = skin->skeleton - data->nodes; |
| 1221 | // assert(root_index <= data->nodes_count); | 1275 | // // assert(root_index <= data->nodes_count); |
| 1222 | // root_node = nodes[root_index]; | 1276 | // // root_node = nodes[root_index]; |
| 1223 | // } | 1277 | // assert(false); |
| 1278 | //} | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | // Animation library assumes that joints are contiguous. | ||
| 1282 | for (size_t i = 0; i < num_joints; ++i) { | ||
| 1283 | assert(is_joint_node[i]); | ||
| 1224 | } | 1284 | } |
| 1285 | |||
| 1286 | // Insert the root joint. | ||
| 1287 | // This is the root of all skeletons. It is, specifically, the root of all | ||
| 1288 | // joints that do not have a parent; skins (skeletons) in glTF are not | ||
| 1289 | // guaranteed to have a common parent, but are generally a set of disjoint | ||
| 1290 | // trees. | ||
| 1291 | const size_t root_index = num_joints; | ||
| 1292 | assert(root_index < GFX_MAX_NUM_JOINTS); | ||
| 1293 | anima_desc->joints[root_index] = (JointDesc){.parent = INDEX_NONE}; | ||
| 1294 | num_joints++; | ||
| 1295 | |||
| 1296 | // Make root joints point to the root joint at index N. | ||
| 1297 | // The root joints are the ones that have a non-joint node in the glTF as a | ||
| 1298 | // parent. | ||
| 1299 | for (size_t i = 0; i < root_index; ++i) { | ||
| 1300 | JointDesc* joint = &anima_desc->joints[i]; | ||
| 1301 | if ((joint->parent >= root_index) || !is_joint_node[joint->parent]) { | ||
| 1302 | joint->parent = root_index; | ||
| 1303 | } | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | return num_joints; | ||
| 1225 | } | 1307 | } |
| 1226 | 1308 | ||
| 1227 | /// Load all animations from the glTF scene. | 1309 | /// Load all animations from the glTF scene. |
| 1228 | static void load_animations( | 1310 | static void load_animations( |
| 1229 | const cgltf_data* data, SceneNode** nodes, AnimationDesc* descs) { | 1311 | const cgltf_data* data, cgltf_size base_joint_index, |
| 1312 | AnimaDesc* anima_desc) { | ||
| 1230 | assert(data); | 1313 | assert(data); |
| 1231 | assert(nodes); | 1314 | assert(anima_desc); |
| 1232 | assert(descs); | 1315 | assert(base_joint_index < data->nodes_count); |
| 1316 | assert(data->animations_count <= GFX_MAX_NUM_ANIMATIONS); | ||
| 1233 | 1317 | ||
| 1234 | for (cgltf_size a = 0; a < data->animations_count; ++a) { | 1318 | for (cgltf_size a = 0; a < data->animations_count; ++a) { |
| 1235 | const cgltf_animation* animation = &data->animations[a]; | 1319 | const cgltf_animation* animation = &data->animations[a]; |
| 1236 | AnimationDesc* animation_desc = &descs[a]; | 1320 | AnimationDesc* animation_desc = &anima_desc->animations[a]; |
| 1237 | 1321 | ||
| 1238 | *animation_desc = (AnimationDesc){ | 1322 | *animation_desc = (AnimationDesc){ |
| 1239 | .name = sstring_make(animation->name), | 1323 | .name = sstring_make(animation->name), |
| @@ -1244,13 +1328,16 @@ static void load_animations( | |||
| 1244 | const cgltf_animation_channel* channel = &animation->channels[c]; | 1328 | const cgltf_animation_channel* channel = &animation->channels[c]; |
| 1245 | ChannelDesc* channel_desc = &animation_desc->channels[c]; | 1329 | ChannelDesc* channel_desc = &animation_desc->channels[c]; |
| 1246 | const cgltf_animation_sampler* sampler = channel->sampler; | 1330 | const cgltf_animation_sampler* sampler = channel->sampler; |
| 1247 | const size_t node_index = channel->target_node - data->nodes; | 1331 | |
| 1248 | assert(node_index < data->nodes_count); | 1332 | const size_t target_index = channel->target_node - data->nodes; |
| 1249 | SceneNode* target = nodes[node_index]; | 1333 | assert(target_index < data->nodes_count); |
| 1250 | assert(target); | 1334 | |
| 1335 | assert(target_index >= base_joint_index); | ||
| 1336 | const size_t tight_target_index = target_index - base_joint_index; | ||
| 1337 | assert(tight_target_index < anima_desc->num_joints); | ||
| 1251 | 1338 | ||
| 1252 | *channel_desc = (ChannelDesc){ | 1339 | *channel_desc = (ChannelDesc){ |
| 1253 | .target = target, | 1340 | .target = tight_target_index, |
| 1254 | .type = from_gltf_animation_path_type(channel->target_path), | 1341 | .type = from_gltf_animation_path_type(channel->target_path), |
| 1255 | .interpolation = from_gltf_interpolation_type(sampler->interpolation), | 1342 | .interpolation = from_gltf_interpolation_type(sampler->interpolation), |
| 1256 | .num_keyframes = 0}; | 1343 | .num_keyframes = 0}; |
| @@ -1316,8 +1403,8 @@ static void load_nodes( | |||
| 1316 | const cgltf_size mesh_index = node->mesh - data->meshes; | 1403 | const cgltf_size mesh_index = node->mesh - data->meshes; |
| 1317 | assert(mesh_index < data->meshes_count); | 1404 | assert(mesh_index < data->meshes_count); |
| 1318 | SceneObject* object = objects[mesh_index]; | 1405 | SceneObject* object = objects[mesh_index]; |
| 1319 | |||
| 1320 | gfx_construct_object_node(nodes[n], object); | 1406 | gfx_construct_object_node(nodes[n], object); |
| 1407 | |||
| 1321 | if (node->skin) { | 1408 | if (node->skin) { |
| 1322 | assert(anima); | 1409 | assert(anima); |
| 1323 | 1410 | ||
| @@ -1522,12 +1609,14 @@ static SceneNode* load_scene( | |||
| 1522 | goto cleanup; | 1609 | goto cleanup; |
| 1523 | } | 1610 | } |
| 1524 | 1611 | ||
| 1525 | load_skins(data, buffers, scene_nodes, anima_desc->skeletons); | 1612 | const cgltf_size base = find_base_joint_index(data); |
| 1526 | load_animations(data, scene_nodes, anima_desc->animations); | ||
| 1527 | 1613 | ||
| 1528 | anima_desc->num_skeletons = data->skins_count; | 1614 | anima_desc->num_skeletons = data->skins_count; |
| 1529 | anima_desc->num_animations = data->animations_count; | 1615 | anima_desc->num_animations = data->animations_count; |
| 1530 | anima = gfx_make_anima(anima_desc); | 1616 | anima_desc->num_joints = load_skins(data, buffers, base, anima_desc); |
| 1617 | load_animations(data, base, anima_desc); | ||
| 1618 | |||
| 1619 | anima = gfx_make_anima(anima_desc); | ||
| 1531 | gfx_construct_anima_node(root_node, anima); | 1620 | gfx_construct_anima_node(root_node, anima); |
| 1532 | } | 1621 | } |
| 1533 | gfx_set_node_parent(root_node, parent_node); | 1622 | gfx_set_node_parent(root_node, parent_node); |
| @@ -1535,6 +1624,8 @@ static SceneNode* load_scene( | |||
| 1535 | // The root node becomes the root of all scene nodes. | 1624 | // The root node becomes the root of all scene nodes. |
| 1536 | load_nodes(data, root_node, scene_objects, scene_cameras, anima, scene_nodes); | 1625 | load_nodes(data, root_node, scene_objects, scene_cameras, anima, scene_nodes); |
| 1537 | 1626 | ||
| 1627 | // TODO: Clean up scene nodes that correspond to joints in the glTF. | ||
| 1628 | |||
| 1538 | success = true; | 1629 | success = true; |
| 1539 | 1630 | ||
| 1540 | cleanup: | 1631 | cleanup: |
diff --git a/gfx/src/renderer/renderer.c b/gfx/src/renderer/renderer.c index ac6a45f..5cc06d6 100644 --- a/gfx/src/renderer/renderer.c +++ b/gfx/src/renderer/renderer.c | |||
| @@ -175,9 +175,10 @@ typedef struct RenderState { | |||
| 175 | const mat4* camera_rotation; // From camera to world space, rotation only. | 175 | const mat4* camera_rotation; // From camera to world space, rotation only. |
| 176 | const mat4* view_matrix; | 176 | const mat4* view_matrix; |
| 177 | const mat4* projection; | 177 | const mat4* projection; |
| 178 | Light* environment_light; | ||
| 179 | const float fovy; | 178 | const float fovy; |
| 180 | const float aspect; | 179 | const float aspect; |
| 180 | Light* environment_light; | ||
| 181 | Anima* anima; | ||
| 181 | size_t num_joints; | 182 | size_t num_joints; |
| 182 | mat4 joint_matrices[GFX_MAX_NUM_JOINTS]; | 183 | mat4 joint_matrices[GFX_MAX_NUM_JOINTS]; |
| 183 | } RenderState; | 184 | } RenderState; |
| @@ -191,15 +192,13 @@ static void load_skeleton(RenderState* state, skeleton_idx skeleton_index) { | |||
| 191 | assert(skeleton); | 192 | assert(skeleton); |
| 192 | assert(skeleton->num_joints <= GFX_MAX_NUM_JOINTS); | 193 | assert(skeleton->num_joints <= GFX_MAX_NUM_JOINTS); |
| 193 | 194 | ||
| 195 | state->num_joints = skeleton->num_joints; | ||
| 196 | |||
| 194 | for (size_t i = 0; i < skeleton->num_joints; ++i) { | 197 | for (size_t i = 0; i < skeleton->num_joints; ++i) { |
| 195 | const SceneNode* node = mem_get_node(skeleton->joints[i]); | 198 | const rel_idx joint_index = skeleton->joints[i]; |
| 196 | assert(node); | 199 | const Joint* joint = &state->anima->joints[joint_index]; |
| 197 | assert(node->type == JointNode); | 200 | state->joint_matrices[i] = joint->joint_matrix; |
| 198 | const Joint* joint = mem_get_joint(node->joint); | ||
| 199 | assert(joint); | ||
| 200 | state->joint_matrices[i] = joint->joint_matrix; | ||
| 201 | } | 201 | } |
| 202 | state->num_joints = skeleton->num_joints; | ||
| 203 | } | 202 | } |
| 204 | 203 | ||
| 205 | /// Draw the scene recursively. | 204 | /// Draw the scene recursively. |
| @@ -209,7 +208,9 @@ static void draw_recursively( | |||
| 209 | const mat4 node_transform = mat4_mul(parent_transform, node->transform); | 208 | const mat4 node_transform = mat4_mul(parent_transform, node->transform); |
| 210 | 209 | ||
| 211 | // Activate light. | 210 | // Activate light. |
| 212 | if (node->type == LightNode) { | 211 | if (node->type == AnimaNode) { |
| 212 | state->anima = gfx_get_node_anima(node); | ||
| 213 | } else if (node->type == LightNode) { | ||
| 213 | Light* light = mem_get_light(node->light); | 214 | Light* light = mem_get_light(node->light); |
| 214 | assert(light); | 215 | assert(light); |
| 215 | 216 | ||
| @@ -349,6 +350,6 @@ void gfx_render_scene(Renderer* renderer, const RenderSceneParams* params) { | |||
| 349 | // Assuming a perspective matrix. | 350 | // Assuming a perspective matrix. |
| 350 | .fovy = atan(1.0 / (mat4_at(projection, 1, 1))) * 2, | 351 | .fovy = atan(1.0 / (mat4_at(projection, 1, 1))) * 2, |
| 351 | .aspect = aspect}; | 352 | .aspect = aspect}; |
| 352 | 353 | ||
| 353 | draw_recursively(&state, mat4_id(), scene->root); | 354 | draw_recursively(&state, mat4_id(), scene->root); |
| 354 | } | 355 | } |
diff --git a/gfx/src/scene/animation.c b/gfx/src/scene/animation.c index 459e864..18e2a99 100644 --- a/gfx/src/scene/animation.c +++ b/gfx/src/scene/animation.c | |||
| @@ -3,42 +3,78 @@ | |||
| 3 | #include "node_impl.h" | 3 | #include "node_impl.h" |
| 4 | #include "scene_memory.h" | 4 | #include "scene_memory.h" |
| 5 | 5 | ||
| 6 | #include <string.h> | ||
| 7 | |||
| 6 | // #include <log/log.h> // Debugging. | 8 | // #include <log/log.h> // Debugging. |
| 7 | 9 | ||
| 8 | static const R PLAYBACK_UNINITIALIZED = -1; | 10 | static const R PLAYBACK_UNINITIALIZED = -1; |
| 9 | 11 | ||
| 10 | Joint* gfx_make_joint(const JointDesc* desc) { | 12 | static rel_idx get_anima_root_joint_index(Anima* anima) { |
| 13 | assert(anima); | ||
| 14 | assert(anima->num_joints > 0); | ||
| 15 | assert(anima->num_joints < GFX_MAX_NUM_JOINTS); | ||
| 16 | return anima->num_joints - 1; | ||
| 17 | } | ||
| 18 | |||
| 19 | static Joint* get_anima_root_joint(Anima* anima) { | ||
| 20 | assert(anima); | ||
| 21 | return &anima->joints[get_anima_root_joint_index(anima)]; | ||
| 22 | } | ||
| 23 | |||
| 24 | static Joint* get_anima_joint(Anima* anima, rel_idx index) { | ||
| 25 | assert(anima); | ||
| 26 | assert(index < GFX_MAX_NUM_JOINTS); | ||
| 27 | assert(index != INDEX_NONE); | ||
| 28 | assert(index < anima->num_joints); | ||
| 29 | return &anima->joints[index]; | ||
| 30 | } | ||
| 31 | |||
| 32 | static void set_joint_parent( | ||
| 33 | Anima* anima, rel_idx joint_index, rel_idx parent_index) { | ||
| 34 | assert(anima); | ||
| 35 | assert(joint_index != INDEX_NONE); | ||
| 36 | assert(joint_index != get_anima_root_joint_index(anima)); | ||
| 37 | assert(parent_index != INDEX_NONE); | ||
| 38 | |||
| 39 | Joint* parent = get_anima_joint(anima, parent_index); | ||
| 40 | |||
| 41 | if (parent->child == INDEX_NONE) { | ||
| 42 | parent->child = joint_index; | ||
| 43 | } else { | ||
| 44 | // Find the last child in the chain of children. | ||
| 45 | Joint* child = get_anima_joint(anima, parent->child); | ||
| 46 | while (child->next != INDEX_NONE) { | ||
| 47 | child = get_anima_joint(anima, child->next); | ||
| 48 | } | ||
| 49 | // Wire up this joint as the last child's sibling. | ||
| 50 | child->next = joint_index; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | static void make_joint(Anima* anima, const JointDesc* desc, Joint* joint) { | ||
| 55 | assert(anima); | ||
| 11 | assert(desc); | 56 | assert(desc); |
| 57 | assert(joint); | ||
| 12 | 58 | ||
| 13 | // The joint matrix needs to be initialized so that meshes look right even if | 59 | // The joint matrix needs to be initialized so that meshes look right even if |
| 14 | // no animation is played. Initializing joint matrices to the identity makes | 60 | // no animation is played. Initializing joint matrices to the identity makes |
| 15 | // meshes appear in their bind pose. | 61 | // meshes appear in their bind pose. |
| 16 | Joint* joint = mem_alloc_joint(); | 62 | joint->child = INDEX_NONE; |
| 63 | joint->next = INDEX_NONE; | ||
| 64 | joint->transform = mat4_id(); | ||
| 17 | joint->inv_bind_matrix = desc->inv_bind_matrix; | 65 | joint->inv_bind_matrix = desc->inv_bind_matrix; |
| 18 | joint->joint_matrix = mat4_id(); | 66 | joint->joint_matrix = mat4_id(); |
| 19 | return joint; | ||
| 20 | } | ||
| 21 | |||
| 22 | void gfx_destroy_joint(Joint** joint) { | ||
| 23 | assert(joint); | ||
| 24 | |||
| 25 | if (*joint) { | ||
| 26 | if ((*joint)->parent.val) { | ||
| 27 | gfx_del_node((*joint)->parent); | ||
| 28 | } | ||
| 29 | mem_free_joint(joint); | ||
| 30 | } | ||
| 31 | } | 67 | } |
| 32 | 68 | ||
| 33 | static Skeleton* make_skeleton(const SkeletonDesc* desc) { | 69 | static Skeleton* make_skeleton(const SkeletonDesc* desc) { |
| 34 | assert(desc); | 70 | assert(desc); |
| 35 | assert(desc->num_joints < GFX_MAX_NUM_JOINTS); | 71 | assert(desc->num_joints <= GFX_MAX_NUM_JOINTS); |
| 36 | 72 | ||
| 37 | Skeleton* skeleton = mem_alloc_skeleton(); | 73 | Skeleton* skeleton = mem_alloc_skeleton(); |
| 38 | skeleton->num_joints = desc->num_joints; | 74 | skeleton->num_joints = desc->num_joints; |
| 39 | for (size_t i = 0; i < desc->num_joints; ++i) { | 75 | memcpy( |
| 40 | skeleton->joints[i] = mem_get_node_index(desc->joints[i]); | 76 | skeleton->joints, desc->joints, |
| 41 | } | 77 | desc->num_joints * sizeof(skeleton->joints[0])); |
| 42 | return skeleton; | 78 | return skeleton; |
| 43 | } | 79 | } |
| 44 | 80 | ||
| @@ -56,7 +92,7 @@ static Animation* make_animation(const AnimationDesc* desc) { | |||
| 56 | const ChannelDesc* channel_desc = &desc->channels[c]; | 92 | const ChannelDesc* channel_desc = &desc->channels[c]; |
| 57 | Channel* channel = &animation->channels[c]; | 93 | Channel* channel = &animation->channels[c]; |
| 58 | 94 | ||
| 59 | channel->target = mem_get_node_index(channel_desc->target); | 95 | channel->target = channel_desc->target; |
| 60 | channel->type = channel_desc->type; | 96 | channel->type = channel_desc->type; |
| 61 | channel->interpolation = channel_desc->interpolation; | 97 | channel->interpolation = channel_desc->interpolation; |
| 62 | channel->num_keyframes = channel_desc->num_keyframes; | 98 | channel->num_keyframes = channel_desc->num_keyframes; |
| @@ -82,6 +118,16 @@ static Animation* make_animation(const AnimationDesc* desc) { | |||
| 82 | 118 | ||
| 83 | Anima* gfx_make_anima(const AnimaDesc* desc) { | 119 | Anima* gfx_make_anima(const AnimaDesc* desc) { |
| 84 | assert(desc); | 120 | assert(desc); |
| 121 | assert(desc->num_joints > 0); | ||
| 122 | assert(desc->num_joints <= GFX_MAX_NUM_JOINTS); | ||
| 123 | // All joints should have a parent except for the root. | ||
| 124 | for (size_t i = 0; i < desc->num_joints - 1; ++i) { | ||
| 125 | const rel_idx parent = desc->joints[i].parent; | ||
| 126 | assert(parent != INDEX_NONE); | ||
| 127 | assert(parent < desc->num_joints); | ||
| 128 | } | ||
| 129 | // The root should have no parent. | ||
| 130 | assert(desc->joints[desc->num_joints - 1].parent == INDEX_NONE); | ||
| 85 | 131 | ||
| 86 | Anima* anima = mem_alloc_anima(); | 132 | Anima* anima = mem_alloc_anima(); |
| 87 | 133 | ||
| @@ -114,6 +160,20 @@ Anima* gfx_make_anima(const AnimaDesc* desc) { | |||
| 114 | last_animation = animation; | 160 | last_animation = animation; |
| 115 | } | 161 | } |
| 116 | 162 | ||
| 163 | // Create joints. | ||
| 164 | anima->num_joints = desc->num_joints; | ||
| 165 | // Initialize all joints. | ||
| 166 | // Child and sibling pointers must be initialized before wiring up the | ||
| 167 | // hierarchy. | ||
| 168 | for (size_t i = 0; i < desc->num_joints; ++i) { | ||
| 169 | Joint* joint = get_anima_joint(anima, i); | ||
| 170 | make_joint(anima, &desc->joints[i], joint); | ||
| 171 | } | ||
| 172 | // Wire up joints to their parents. -1 to skip the root. | ||
| 173 | for (size_t i = 0; i < desc->num_joints - 1; ++i) { | ||
| 174 | set_joint_parent(anima, i, desc->joints[i].parent); | ||
| 175 | } | ||
| 176 | |||
| 117 | return anima; | 177 | return anima; |
| 118 | } | 178 | } |
| 119 | 179 | ||
| @@ -177,6 +237,16 @@ bool gfx_play_animation(Anima* anima, const AnimationPlaySettings* settings) { | |||
| 177 | return true; | 237 | return true; |
| 178 | } | 238 | } |
| 179 | 239 | ||
| 240 | static void gfx_set_joint_position(Joint* joint, vec3 position) { | ||
| 241 | assert(joint); | ||
| 242 | mat4_set_v3(&joint->transform, position); | ||
| 243 | } | ||
| 244 | |||
| 245 | static void gfx_set_joint_rotation(Joint* joint, quat rotation) { | ||
| 246 | assert(joint); | ||
| 247 | mat4_set_3x3(&joint->transform, mat4_from_quat(rotation)); | ||
| 248 | } | ||
| 249 | |||
| 180 | static void find_keyframes(const Channel* channel, R t, int* prev, int* next) { | 250 | static void find_keyframes(const Channel* channel, R t, int* prev, int* next) { |
| 181 | assert(channel); | 251 | assert(channel); |
| 182 | assert(prev); | 252 | assert(prev); |
| @@ -255,9 +325,10 @@ static vec3 interpolate_translation( | |||
| 255 | } | 325 | } |
| 256 | } | 326 | } |
| 257 | 327 | ||
| 258 | static void animate_channel(const Channel* channel, R t) { | 328 | static void animate_channel(Anima* anima, const Channel* channel, R t) { |
| 329 | assert(anima); | ||
| 259 | assert(channel); | 330 | assert(channel); |
| 260 | assert(channel->target.val != 0); | 331 | assert(channel->target < anima->num_joints); |
| 261 | 332 | ||
| 262 | int prev, next; | 333 | int prev, next; |
| 263 | find_keyframes(channel, t, &prev, &next); | 334 | find_keyframes(channel, t, &prev, &next); |
| @@ -268,17 +339,17 @@ static void animate_channel(const Channel* channel, R t) { | |||
| 268 | // work. | 339 | // work. |
| 269 | t = t > channel->keyframes[next].time ? channel->keyframes[next].time : t; | 340 | t = t > channel->keyframes[next].time ? channel->keyframes[next].time : t; |
| 270 | 341 | ||
| 271 | SceneNode* target = mem_get_node(channel->target); | 342 | Joint* target = get_anima_joint(anima, channel->target); |
| 272 | 343 | ||
| 273 | switch (channel->type) { | 344 | switch (channel->type) { |
| 274 | case RotationChannel: { | 345 | case RotationChannel: { |
| 275 | const quat rotation = interpolate_rotation(channel, prev, next, t); | 346 | const quat rotation = interpolate_rotation(channel, prev, next, t); |
| 276 | gfx_set_node_rotation(target, &rotation); | 347 | gfx_set_joint_rotation(target, rotation); |
| 277 | break; | 348 | break; |
| 278 | } | 349 | } |
| 279 | case TranslationChannel: { | 350 | case TranslationChannel: { |
| 280 | const vec3 translation = interpolate_translation(channel, prev, next, t); | 351 | const vec3 translation = interpolate_translation(channel, prev, next, t); |
| 281 | gfx_set_node_position(target, &translation); | 352 | gfx_set_joint_position(target, translation); |
| 282 | break; | 353 | break; |
| 283 | } | 354 | } |
| 284 | // Not yet supported. | 355 | // Not yet supported. |
| @@ -292,35 +363,36 @@ static void animate_channel(const Channel* channel, R t) { | |||
| 292 | } | 363 | } |
| 293 | 364 | ||
| 294 | static void compute_joint_matrices_rec( | 365 | static void compute_joint_matrices_rec( |
| 295 | node_idx node_index, mat4 parent_global_joint_transform, | 366 | Anima* anima, Joint* joint, const mat4* parent_global_joint_transform, |
| 296 | const mat4* root_inv_global_transform) { | 367 | const mat4* root_inv_global_transform) { |
| 297 | if (node_index.val == 0) { | 368 | assert(anima); |
| 298 | return; | 369 | assert(joint); |
| 299 | } | 370 | assert(parent_global_joint_transform); |
| 371 | assert(root_inv_global_transform); | ||
| 300 | 372 | ||
| 301 | const SceneNode* node = mem_get_node(node_index); | 373 | const mat4 global_joint_transform = |
| 302 | const mat4 global_joint_transform = | 374 | mat4_mul(*parent_global_joint_transform, joint->transform); |
| 303 | mat4_mul(parent_global_joint_transform, node->transform); | ||
| 304 | 375 | ||
| 305 | // For flexibility (glTF), we allow non-joint nodes between the root of the | 376 | // Compute this joint's matrix. |
| 306 | // skeleton and its joint nodes. We check the node type as opposed to assert | 377 | joint->joint_matrix = mat4_mul( |
| 307 | // it. | 378 | *root_inv_global_transform, |
| 308 | if (node->type == JointNode) { | 379 | mat4_mul(global_joint_transform, joint->inv_bind_matrix)); |
| 309 | // Compute this node's joint matrix. | ||
| 310 | Joint* joint = mem_get_joint(node->joint); | ||
| 311 | 380 | ||
| 312 | joint->joint_matrix = mat4_mul( | 381 | // Recursively compute the joint matrices for this joint's siblings. |
| 313 | *root_inv_global_transform, | 382 | if (joint->next != INDEX_NONE) { |
| 314 | mat4_mul(global_joint_transform, joint->inv_bind_matrix)); | 383 | Joint* sibling = get_anima_joint(anima, joint->next); |
| 384 | |||
| 385 | compute_joint_matrices_rec( | ||
| 386 | anima, sibling, parent_global_joint_transform, | ||
| 387 | root_inv_global_transform); | ||
| 315 | } | 388 | } |
| 316 | 389 | ||
| 317 | // Recursively compute the joint matrices for this node's children. | 390 | // Recursively compute the joint matrices for this joint's children. |
| 318 | node_idx child = node->child; | 391 | if (joint->child != INDEX_NONE) { |
| 319 | while (child.val != 0) { | 392 | Joint* child = get_anima_joint(anima, joint->child); |
| 393 | |||
| 320 | compute_joint_matrices_rec( | 394 | compute_joint_matrices_rec( |
| 321 | child, global_joint_transform, root_inv_global_transform); | 395 | anima, child, &global_joint_transform, root_inv_global_transform); |
| 322 | node = mem_get_node(child); | ||
| 323 | child = node->next; // Next sibling. | ||
| 324 | } | 396 | } |
| 325 | } | 397 | } |
| 326 | 398 | ||
| @@ -354,7 +426,7 @@ void gfx_update_animation(Anima* anima, R t) { | |||
| 354 | // Play through the animation to transform skeleton nodes. | 426 | // Play through the animation to transform skeleton nodes. |
| 355 | for (size_t i = 0; i < animation->num_channels; ++i) { | 427 | for (size_t i = 0; i < animation->num_channels; ++i) { |
| 356 | const Channel* channel = &animation->channels[i]; | 428 | const Channel* channel = &animation->channels[i]; |
| 357 | animate_channel(channel, animation_time); | 429 | animate_channel(anima, channel, animation_time); |
| 358 | } | 430 | } |
| 359 | 431 | ||
| 360 | // Compute joint matrices after having transformed the skeletons. | 432 | // Compute joint matrices after having transformed the skeletons. |
| @@ -369,19 +441,14 @@ void gfx_update_animation(Anima* anima, R t) { | |||
| 369 | // | 441 | // |
| 370 | // Lack of a common parent aside, the procedure touches every joint exactly | 442 | // Lack of a common parent aside, the procedure touches every joint exactly |
| 371 | // once (and potentially other non-joint intermediate nodes). | 443 | // once (and potentially other non-joint intermediate nodes). |
| 372 | node_idx root_index = anima->parent; | 444 | SceneNode* root_node = mem_get_node(anima->parent); |
| 373 | SceneNode* root = mem_get_node(root_index); | 445 | // LOGD("Root: %u, child: %u", anima->parent.val, root->child.val); |
| 374 | // LOGD("Root: %u, child: %u", root_index.val, root->child.val); | 446 | const mat4 root_global_transform = gfx_get_node_global_transform(root_node); |
| 375 | const mat4 root_global_transform = gfx_get_node_global_transform(root); | ||
| 376 | const mat4 root_inv_global_transform = mat4_inverse(root_global_transform); | 447 | const mat4 root_inv_global_transform = mat4_inverse(root_global_transform); |
| 377 | // Step over root's children (siblings of the first child). | 448 | |
| 378 | node_idx child = root->child; | 449 | Joint* root_joint = get_anima_root_joint(anima); |
| 379 | while (child.val != 0) { | 450 | compute_joint_matrices_rec( |
| 380 | compute_joint_matrices_rec( | 451 | anima, root_joint, &root_global_transform, &root_inv_global_transform); |
| 381 | child, root_global_transform, &root_inv_global_transform); | ||
| 382 | SceneNode* node = mem_get_node(child); | ||
| 383 | child = node->next; // Next sibling. | ||
| 384 | } | ||
| 385 | } | 452 | } |
| 386 | 453 | ||
| 387 | const Skeleton* gfx_get_anima_skeleton(const Anima* anima, size_t i) { | 454 | const Skeleton* gfx_get_anima_skeleton(const Anima* anima, size_t i) { |
diff --git a/gfx/src/scene/animation_impl.h b/gfx/src/scene/animation_impl.h index ceebbbf..7265858 100644 --- a/gfx/src/scene/animation_impl.h +++ b/gfx/src/scene/animation_impl.h | |||
| @@ -20,32 +20,27 @@ typedef struct Buffer Buffer; | |||
| 20 | // | 20 | // |
| 21 | // TODO: Simultaneous animation of disjoint animations. | 21 | // TODO: Simultaneous animation of disjoint animations. |
| 22 | 22 | ||
| 23 | /// Skeleton joint. | ||
| 24 | /// Joints are mutable and store the transform and joint matrices that result | ||
| 25 | /// from animation, aside from the inverse bind matrix. | ||
| 23 | typedef struct Joint { | 26 | typedef struct Joint { |
| 24 | node_idx parent; // Parent SceneNode. | 27 | rel_idx child; /// First child Joint; index into Anima's joints. |
| 25 | mat4 inv_bind_matrix; | 28 | rel_idx next; /// Next sibling Joint; index into Anima's joints. |
| 26 | mat4 joint_matrix; | 29 | mat4 transform; /// Local transform relative to parent. |
| 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). | ||
| 27 | } Joint; | 32 | } Joint; |
| 28 | 33 | ||
| 29 | /// Animation skeleton. | 34 | /// Animation skeleton. |
| 30 | /// | ||
| 31 | /// The joints array maps joint indices to scene nodes. | ||
| 32 | /// | ||
| 33 | /// The last element of the joints array is the root of the hierarchy. Storing | ||
| 34 | /// the root of the hierarchy allows us to compute joint matrices only for scene | ||
| 35 | /// nodes that are actually part of a skeleton, since the root identifies the | ||
| 36 | /// skeleton's node hierarchy. | ||
| 37 | /// | ||
| 38 | /// Some model formats (glTF) may not actually specify a root. In that case, the | ||
| 39 | /// client must create one to form a strict tree hierarchy. | ||
| 40 | typedef struct Skeleton { | 35 | typedef struct Skeleton { |
| 41 | skeleton_idx next; | 36 | skeleton_idx next; |
| 42 | size_t num_joints; | 37 | size_t num_joints; |
| 43 | node_idx joints[GFX_MAX_NUM_JOINTS]; // Last = root. | 38 | rel_idx joints[GFX_MAX_NUM_JOINTS]; /// Indices into Anima's joints array. |
| 44 | } Skeleton; | 39 | } Skeleton; |
| 45 | 40 | ||
| 46 | /// A keyframe of animation. | 41 | /// A keyframe of animation. |
| 47 | typedef struct Keyframe { | 42 | typedef struct Keyframe { |
| 48 | R time; // Start time in [0, end animation time] | 43 | R time; /// Start time in [0, end animation time] |
| 49 | union { | 44 | union { |
| 50 | vec3 translation; | 45 | vec3 translation; |
| 51 | quat rotation; | 46 | quat rotation; |
| @@ -54,7 +49,7 @@ typedef struct Keyframe { | |||
| 54 | 49 | ||
| 55 | /// Animation channel. | 50 | /// Animation channel. |
| 56 | typedef struct Channel { | 51 | typedef struct Channel { |
| 57 | node_idx target; | 52 | rel_idx target; /// Index into Anima's joints array. |
| 58 | ChannelType type; | 53 | ChannelType type; |
| 59 | AnimationInterpolation interpolation; | 54 | AnimationInterpolation interpolation; |
| 60 | size_t num_keyframes; | 55 | size_t num_keyframes; |
| @@ -87,9 +82,16 @@ typedef struct AnimationState { | |||
| 87 | /// | 82 | /// |
| 88 | /// For lack of a better name, this is called an Anima. It is short and the | 83 | /// For lack of a better name, this is called an Anima. It is short and the |
| 89 | /// Latin root of animation. | 84 | /// Latin root of animation. |
| 85 | /// | ||
| 86 | /// The last joint of the joints array at index 'num_joints - 1' is the root of | ||
| 87 | /// all skeletons; specifically, the root of all joints that otherwise would | ||
| 88 | /// have no parent (a skeleton need not have its own root and can be a set of | ||
| 89 | /// disjoint node hierarchies). | ||
| 90 | typedef struct Anima { | 90 | typedef struct Anima { |
| 91 | AnimationState state; | 91 | node_idx parent; /// Parent SceneNode. |
| 92 | skeleton_idx skeleton; | 92 | skeleton_idx skeleton; /// Index of first skeleton. |
| 93 | animation_idx animation; // Index of first animation. | 93 | animation_idx animation; /// Index of first animation. |
| 94 | node_idx parent; // Parent SceneNode. | 94 | AnimationState state; /// Current animation state. |
| 95 | size_t num_joints; /// Number of actual joints in the array. | ||
| 96 | Joint joints[GFX_MAX_NUM_JOINTS]; /// Shared by all skeletons. | ||
| 95 | } Anima; | 97 | } Anima; |
diff --git a/gfx/src/scene/node.c b/gfx/src/scene/node.c index 333dc28..1a68216 100644 --- a/gfx/src/scene/node.c +++ b/gfx/src/scene/node.c | |||
| @@ -41,15 +41,6 @@ SceneNode* gfx_make_camera_node(SceneCamera* camera) { | |||
| 41 | return node; | 41 | return node; |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | SceneNode* gfx_make_joint_node(Joint* joint) { | ||
| 45 | assert(joint); | ||
| 46 | SceneNode* node = gfx_make_node(); | ||
| 47 | node->type = JointNode; | ||
| 48 | node->joint = mem_get_joint_index(joint); | ||
| 49 | joint->parent = mem_get_node_index(node); | ||
| 50 | return node; | ||
| 51 | } | ||
| 52 | |||
| 53 | SceneNode* gfx_make_light_node(Light* light) { | 44 | SceneNode* gfx_make_light_node(Light* light) { |
| 54 | assert(light); | 45 | assert(light); |
| 55 | SceneNode* node = gfx_make_node(); | 46 | SceneNode* node = gfx_make_node(); |
| @@ -87,12 +78,6 @@ static void free_node_resource(SceneNode* node) { | |||
| 87 | gfx_destroy_camera(&camera); | 78 | gfx_destroy_camera(&camera); |
| 88 | break; | 79 | break; |
| 89 | } | 80 | } |
| 90 | case JointNode: { | ||
| 91 | Joint* joint = mem_get_joint(node->joint); | ||
| 92 | joint->parent.val = 0; | ||
| 93 | gfx_destroy_joint(&joint); | ||
| 94 | break; | ||
| 95 | } | ||
| 96 | case LightNode: { | 81 | case LightNode: { |
| 97 | Light* light = mem_get_light(node->light); | 82 | Light* light = mem_get_light(node->light); |
| 98 | light->parent.val = 0; | 83 | light->parent.val = 0; |
| @@ -129,16 +114,7 @@ void gfx_construct_camera_node(SceneNode* node, SceneCamera* camera) { | |||
| 129 | } | 114 | } |
| 130 | 115 | ||
| 131 | // TODO: Add a common helper function between each gfx_make_xyz_node() and | 116 | // TODO: Add a common helper function between each gfx_make_xyz_node() and |
| 132 | // gfx_construct_xyz_node() pair. | 117 | // gfx_construct_xyz_node() pair. |
| 133 | void gfx_construct_joint_node(SceneNode* node, Joint* joint) { | ||
| 134 | assert(node); | ||
| 135 | assert(joint); | ||
| 136 | free_node_resource(node); | ||
| 137 | node->type = JointNode; | ||
| 138 | node->joint = mem_get_joint_index(joint); | ||
| 139 | joint->parent = mem_get_node_index(node); | ||
| 140 | } | ||
| 141 | |||
| 142 | void gfx_construct_light_node(SceneNode* node, Light* light) { | 118 | void gfx_construct_light_node(SceneNode* node, Light* light) { |
| 143 | assert(node); | 119 | assert(node); |
| 144 | assert(light); | 120 | assert(light); |
| @@ -217,10 +193,6 @@ SceneCamera* gfx_get_node_camera(const SceneNode* node) { | |||
| 217 | NODE_GET(node, camera, CameraNode); | 193 | NODE_GET(node, camera, CameraNode); |
| 218 | } | 194 | } |
| 219 | 195 | ||
| 220 | Joint* gfx_get_node_joint(const SceneNode* node) { | ||
| 221 | NODE_GET(node, joint, JointNode); | ||
| 222 | } | ||
| 223 | |||
| 224 | Light* gfx_get_node_light(const SceneNode* node) { | 196 | Light* gfx_get_node_light(const SceneNode* node) { |
| 225 | NODE_GET(node, light, LightNode); | 197 | NODE_GET(node, light, LightNode); |
| 226 | } | 198 | } |
| @@ -309,8 +281,6 @@ static const char* get_node_type_str(NodeType type) { | |||
| 309 | return "AnimaNode"; | 281 | return "AnimaNode"; |
| 310 | case CameraNode: | 282 | case CameraNode: |
| 311 | return "CameraNode"; | 283 | return "CameraNode"; |
| 312 | case JointNode: | ||
| 313 | return "JointNode"; | ||
| 314 | case LightNode: | 284 | case LightNode: |
| 315 | return "LightNode"; | 285 | return "LightNode"; |
| 316 | case ObjectNode: | 286 | case ObjectNode: |
diff --git a/gfx/src/scene/node_impl.h b/gfx/src/scene/node_impl.h index e8d546f..4592fce 100644 --- a/gfx/src/scene/node_impl.h +++ b/gfx/src/scene/node_impl.h | |||
| @@ -16,7 +16,6 @@ typedef struct SceneNode { | |||
| 16 | union { | 16 | union { |
| 17 | anima_idx anima; | 17 | anima_idx anima; |
| 18 | camera_idx camera; | 18 | camera_idx camera; |
| 19 | joint_idx joint; | ||
| 20 | light_idx light; | 19 | light_idx light; |
| 21 | object_idx object; | 20 | object_idx object; |
| 22 | }; | 21 | }; |
diff --git a/gfx/src/scene/scene_memory.c b/gfx/src/scene/scene_memory.c index cace654..2b900b9 100644 --- a/gfx/src/scene/scene_memory.c +++ b/gfx/src/scene/scene_memory.c | |||
| @@ -16,7 +16,6 @@ | |||
| 16 | DEF_MEMPOOL(anima_pool, Anima, GFX_MAX_NUM_ANIMAS) | 16 | DEF_MEMPOOL(anima_pool, Anima, GFX_MAX_NUM_ANIMAS) |
| 17 | DEF_MEMPOOL(animation_pool, Animation, GFX_MAX_NUM_ANIMATIONS) | 17 | DEF_MEMPOOL(animation_pool, Animation, GFX_MAX_NUM_ANIMATIONS) |
| 18 | DEF_MEMPOOL(camera_pool, SceneCamera, GFX_MAX_NUM_CAMERAS) | 18 | DEF_MEMPOOL(camera_pool, SceneCamera, GFX_MAX_NUM_CAMERAS) |
| 19 | DEF_MEMPOOL(joint_pool, Joint, GFX_MAX_NUM_JOINTS) | ||
| 20 | DEF_MEMPOOL(light_pool, Light, GFX_MAX_NUM_LIGHTS) | 19 | DEF_MEMPOOL(light_pool, Light, GFX_MAX_NUM_LIGHTS) |
| 21 | DEF_MEMPOOL(material_pool, Material, GFX_MAX_NUM_MATERIALS) | 20 | DEF_MEMPOOL(material_pool, Material, GFX_MAX_NUM_MATERIALS) |
| 22 | DEF_MEMPOOL(mesh_pool, Mesh, GFX_MAX_NUM_MESHES) | 21 | DEF_MEMPOOL(mesh_pool, Mesh, GFX_MAX_NUM_MESHES) |
| @@ -33,7 +32,6 @@ typedef struct SceneMemory { | |||
| 33 | anima_pool animas; | 32 | anima_pool animas; |
| 34 | animation_pool animations; | 33 | animation_pool animations; |
| 35 | camera_pool cameras; | 34 | camera_pool cameras; |
| 36 | joint_pool joints; | ||
| 37 | light_pool lights; | 35 | light_pool lights; |
| 38 | material_pool materials; | 36 | material_pool materials; |
| 39 | mesh_pool meshs; // Purposeful typo to make the PLURAL() macro work. | 37 | mesh_pool meshs; // Purposeful typo to make the PLURAL() macro work. |
| @@ -59,7 +57,6 @@ void scene_mem_init() { | |||
| 59 | mempool_make(&mem.animas); | 57 | mempool_make(&mem.animas); |
| 60 | mempool_make(&mem.animations); | 58 | mempool_make(&mem.animations); |
| 61 | mempool_make(&mem.cameras); | 59 | mempool_make(&mem.cameras); |
| 62 | mempool_make(&mem.joints); | ||
| 63 | mempool_make(&mem.lights); | 60 | mempool_make(&mem.lights); |
| 64 | mempool_make(&mem.materials); | 61 | mempool_make(&mem.materials); |
| 65 | mempool_make(&mem.meshs); | 62 | mempool_make(&mem.meshs); |
| @@ -74,7 +71,6 @@ void scene_mem_init() { | |||
| 74 | ALLOC_DUMMY(&mem.animas); | 71 | ALLOC_DUMMY(&mem.animas); |
| 75 | ALLOC_DUMMY(&mem.animations); | 72 | ALLOC_DUMMY(&mem.animations); |
| 76 | ALLOC_DUMMY(&mem.cameras); | 73 | ALLOC_DUMMY(&mem.cameras); |
| 77 | ALLOC_DUMMY(&mem.joints); | ||
| 78 | ALLOC_DUMMY(&mem.lights); | 74 | ALLOC_DUMMY(&mem.lights); |
| 79 | ALLOC_DUMMY(&mem.materials); | 75 | ALLOC_DUMMY(&mem.materials); |
| 80 | ALLOC_DUMMY(&mem.meshs); | 76 | ALLOC_DUMMY(&mem.meshs); |
| @@ -107,7 +103,6 @@ void scene_mem_destroy() { | |||
| 107 | DESTROY(anima); | 103 | DESTROY(anima); |
| 108 | // Animations are owned by animas and do not have a destructor. | 104 | // Animations are owned by animas and do not have a destructor. |
| 109 | DESTROY(camera); | 105 | DESTROY(camera); |
| 110 | DESTROY(joint); | ||
| 111 | DESTROY(light); | 106 | DESTROY(light); |
| 112 | DESTROY(material); | 107 | DESTROY(material); |
| 113 | DESTROY(mesh); | 108 | DESTROY(mesh); |
| @@ -136,7 +131,6 @@ void scene_mem_destroy() { | |||
| 136 | DEF_MEMORY(anima, Anima) | 131 | DEF_MEMORY(anima, Anima) |
| 137 | DEF_MEMORY(animation, Animation) | 132 | DEF_MEMORY(animation, Animation) |
| 138 | DEF_MEMORY(camera, SceneCamera) | 133 | DEF_MEMORY(camera, SceneCamera) |
| 139 | DEF_MEMORY(joint, Joint) | ||
| 140 | DEF_MEMORY(light, Light) | 134 | DEF_MEMORY(light, Light) |
| 141 | DEF_MEMORY(material, Material) | 135 | DEF_MEMORY(material, Material) |
| 142 | DEF_MEMORY(mesh, Mesh) | 136 | DEF_MEMORY(mesh, Mesh) |
diff --git a/gfx/src/scene/scene_memory.h b/gfx/src/scene/scene_memory.h index 9486ee6..04dbd20 100644 --- a/gfx/src/scene/scene_memory.h +++ b/gfx/src/scene/scene_memory.h | |||
| @@ -29,7 +29,6 @@ void scene_mem_destroy(); | |||
| 29 | DECL_MEMORY(anima, Anima) | 29 | DECL_MEMORY(anima, Anima) |
| 30 | DECL_MEMORY(animation, Animation) | 30 | DECL_MEMORY(animation, Animation) |
| 31 | DECL_MEMORY(camera, SceneCamera) | 31 | DECL_MEMORY(camera, SceneCamera) |
| 32 | DECL_MEMORY(joint, Joint) | ||
| 33 | DECL_MEMORY(light, Light) | 32 | DECL_MEMORY(light, Light) |
| 34 | DECL_MEMORY(material, Material) | 33 | DECL_MEMORY(material, Material) |
| 35 | DECL_MEMORY(mesh, Mesh) | 34 | DECL_MEMORY(mesh, Mesh) |
