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