aboutsummaryrefslogtreecommitdiff
path: root/src/scene
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-10-30 20:08:53 -0700
committer3gg <3gg@shellblade.net>2025-10-30 20:08:53 -0700
commit6fde17649f7404e79a17d4d8f96662d03011aca1 (patch)
treea93738b26dab567fe551b62e2a4bd1b1556460b1 /src/scene
parent440b292c39162284a447b34d3a692143af9fbc87 (diff)
Move animation module outside of scene/
Diffstat (limited to 'src/scene')
-rw-r--r--src/scene/animation.c518
-rw-r--r--src/scene/animation_impl.h96
-rw-r--r--src/scene/node_impl.h2
-rw-r--r--src/scene/object_impl.h2
-rw-r--r--src/scene/scene_impl.h2
-rw-r--r--src/scene/types.h24
6 files changed, 3 insertions, 641 deletions
diff --git a/src/scene/animation.c b/src/scene/animation.c
deleted file mode 100644
index a498961..0000000
--- a/src/scene/animation.c
+++ /dev/null
@@ -1,518 +0,0 @@
1#include "animation_impl.h"
2
3#include "memory.h"
4#include "node_impl.h"
5
6#include <string.h>
7
8// #include <log/log.h> // Debugging.
9
10static const R PLAYBACK_UNINITIALIZED = -1;
11
12static joint_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
19static Joint* get_anima_root_joint(Anima* anima) {
20 assert(anima);
21 return &anima->joints[get_anima_root_joint_index(anima)];
22}
23
24static const Joint* get_anima_joint(const Anima* anima, joint_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
32static Joint* get_anima_joint_mut(Anima* anima, joint_idx index) {
33 return (Joint*)get_anima_joint(anima, index);
34}
35
36static const Joint* get_skeleton_joint(
37 const Anima* anima, const Skeleton* skeleton, joint_idx index) {
38 assert(anima);
39 assert(skeleton);
40 return get_anima_joint(anima, skeleton->joints[index]);
41}
42
43static void set_joint_parent(
44 Anima* anima, joint_idx joint_index, joint_idx parent_index) {
45 assert(anima);
46 assert(joint_index != INDEX_NONE);
47 assert(joint_index != get_anima_root_joint_index(anima));
48 assert(parent_index != INDEX_NONE);
49
50 Joint* parent = get_anima_joint_mut(anima, parent_index);
51
52 if (parent->child == INDEX_NONE) {
53 parent->child = joint_index;
54 } else {
55 // Find the last child in the chain of children.
56 Joint* child = get_anima_joint_mut(anima, parent->child);
57 while (child->next != INDEX_NONE) {
58 child = get_anima_joint_mut(anima, child->next);
59 }
60 // Wire up this joint as the last child's sibling.
61 child->next = joint_index;
62 }
63}
64
65static void make_joint(Anima* anima, const JointDesc* desc, Joint* joint) {
66 assert(anima);
67 assert(desc);
68 assert(joint);
69
70 // The joint matrix needs to be initialized so that meshes look right even if
71 // no animation is played. Initializing joint matrices to the identity makes
72 // meshes appear in their bind pose.
73 joint->child = INDEX_NONE;
74 joint->next = INDEX_NONE;
75 joint->transform = mat4_id();
76 joint->inv_bind_matrix = desc->inv_bind_matrix;
77 joint->joint_matrix = mat4_id();
78 joint->box = desc->box;
79}
80
81static Skeleton* make_skeleton(const SkeletonDesc* desc) {
82 assert(desc);
83 assert(desc->num_joints <= GFX_MAX_NUM_JOINTS);
84
85 Skeleton* skeleton = mem_alloc_skeleton();
86 skeleton->num_joints = desc->num_joints;
87 memcpy(
88 skeleton->joints, desc->joints,
89 desc->num_joints * sizeof(skeleton->joints[0]));
90 return skeleton;
91}
92
93static Animation* make_animation(const AnimationDesc* desc) {
94 assert(desc);
95 assert(desc->num_channels < GFX_MAX_NUM_CHANNELS);
96
97 Animation* animation = mem_alloc_animation();
98 animation->name = desc->name;
99 animation->duration = 0;
100 animation->num_channels = desc->num_channels;
101 R start_time = 0;
102 R end_time = 0;
103
104 for (size_t c = 0; c < desc->num_channels; ++c) {
105 const ChannelDesc* channel_desc = &desc->channels[c];
106 Channel* channel = &animation->channels[c];
107
108 channel->target = channel_desc->target;
109 channel->type = channel_desc->type;
110 channel->interpolation = channel_desc->interpolation;
111 channel->num_keyframes = channel_desc->num_keyframes;
112 assert(channel_desc->num_keyframes < GFX_MAX_NUM_KEYFRAMES);
113
114 for (size_t k = 0; k < channel_desc->num_keyframes; ++k) {
115 const KeyframeDesc* keyframe_desc = &channel_desc->keyframes[k];
116 Keyframe* keyframe = &channel->keyframes[k];
117
118 keyframe->time = keyframe_desc->time;
119 keyframe->translation = keyframe_desc->translation;
120 keyframe->rotation = keyframe_desc->rotation;
121
122 start_time = keyframe->time < start_time ? keyframe->time : start_time;
123 end_time = keyframe->time > end_time ? keyframe->time : end_time;
124 }
125 }
126
127 // LOGD("Animation start/end: %f / %f", start_time, end_time);
128 animation->duration = end_time - start_time;
129 assert(animation->duration >= 0);
130 return animation;
131}
132
133Anima* gfx_make_anima(const AnimaDesc* desc) {
134 assert(desc);
135 assert(desc->num_joints > 0);
136 assert(desc->num_joints <= GFX_MAX_NUM_JOINTS);
137 // All joints should have a parent except for the root.
138 for (size_t i = 0; i < desc->num_joints - 1; ++i) {
139 const joint_idx parent = desc->joints[i].parent;
140 assert(parent != INDEX_NONE);
141 assert(parent < desc->num_joints);
142 }
143 // The root should have no parent.
144 assert(desc->joints[desc->num_joints - 1].parent == INDEX_NONE);
145
146 Anima* anima = mem_alloc_anima();
147
148 // Wire the skeletons in the same order they are given in the descriptor.
149 Skeleton* last_skeleton = 0;
150 for (size_t i = 0; i < desc->num_skeletons; ++i) {
151 Skeleton* skeleton = make_skeleton(&desc->skeletons[i]);
152 const skeleton_idx skeleton_index = mem_get_skeleton_index(skeleton);
153 if (last_skeleton == 0) {
154 anima->skeleton = skeleton_index;
155 } else {
156 last_skeleton->next = skeleton_index;
157 }
158 last_skeleton = skeleton;
159 }
160
161 // Wire the animations in the same order they are given in the descriptor.
162 Animation* last_animation = 0;
163 for (size_t i = 0; i < desc->num_animations; ++i) {
164 Animation* animation = make_animation(&desc->animations[i]);
165 const animation_idx animation_index = mem_get_animation_index(animation);
166 if (last_animation == 0) {
167 anima->animation = animation_index;
168 } else {
169 last_animation->next = animation_index;
170 }
171 last_animation = animation;
172 }
173
174 // Create joints.
175 anima->num_joints = desc->num_joints;
176 // Initialize all joints.
177 // Child and sibling pointers must be initialized before wiring up the
178 // hierarchy.
179 for (size_t i = 0; i < desc->num_joints; ++i) {
180 Joint* joint = get_anima_joint_mut(anima, i);
181 make_joint(anima, &desc->joints[i], joint);
182 }
183 // Wire up joints to their parents. -1 to skip the root.
184 for (size_t i = 0; i < desc->num_joints - 1; ++i) {
185 set_joint_parent(anima, i, desc->joints[i].parent);
186 }
187
188 return anima;
189}
190
191void gfx_destroy_anima(Anima** anima) {
192 assert(anima);
193
194 if (*anima) {
195 for (skeleton_idx i = (*anima)->skeleton; i.val != 0;) {
196 Skeleton* skeleton = mem_get_skeleton(i);
197 i = skeleton->next;
198 mem_free_skeleton(&skeleton);
199 }
200
201 for (animation_idx i = (*anima)->animation; i.val != 0;) {
202 Animation* animation = mem_get_animation(i);
203 i = animation->next;
204 mem_free_animation(&animation);
205 }
206
207 mem_free_anima(anima);
208 }
209}
210
211static Animation* find_animation(animation_idx index, const char* name) {
212 assert(name);
213
214 while (index.val != 0) {
215 Animation* animation = mem_get_animation(index);
216 if (sstring_eq_cstr(animation->name, name)) {
217 // LOGD(
218 // "Found animation at index %u, %s - %s", index.val,
219 // sstring_cstr(&animation->name), name);
220 // LOGD("Animation has duration %f", animation->duration);
221 return animation;
222 }
223 index = animation->next;
224 }
225
226 return 0;
227}
228
229bool gfx_play_animation(Anima* anima, const AnimationPlaySettings* settings) {
230 assert(anima);
231 assert(settings);
232
233 // TODO: Should we animate at t=0 here to kickstart the animation? Otherwise
234 // the client is forced to call gfx_update_animation() to do this.
235 Animation* animation = find_animation(anima->animation, settings->name);
236 if (!animation) {
237 return false;
238 }
239 // Playback initialized on first call to update().
240 AnimationState* state = &anima->state;
241 state->start_time = PLAYBACK_UNINITIALIZED;
242 state->animation = mem_get_animation_index(animation);
243 state->loop = settings->loop;
244 return true;
245}
246
247static void gfx_set_joint_position(Joint* joint, vec3 position) {
248 assert(joint);
249 mat4_set_v3(&joint->transform, position);
250}
251
252static void gfx_set_joint_rotation(Joint* joint, quat rotation) {
253 assert(joint);
254 mat4_set_3x3(&joint->transform, mat4_from_quat(rotation));
255}
256
257static void find_keyframes(const Channel* channel, R t, int* prev, int* next) {
258 assert(channel);
259 assert(prev);
260 assert(next);
261
262 *prev = -1;
263 *next = 0;
264 while (((*next + 1) < (int)channel->num_keyframes) &&
265 (t >= channel->keyframes[*next + 1].time)) {
266 (*prev)++;
267 (*next)++;
268 }
269}
270
271static R normalize_time(R a, R b, R t) {
272 assert(a <= t);
273 assert(t <= b);
274 return (t - a) / (b - a);
275}
276
277static quat interpolate_rotation(
278 const Channel* channel, int prev, int next, R t) {
279 assert(channel);
280
281 if (next == 0) {
282 // Animation has not started at this point in time yet.
283 return channel->keyframes[next].rotation;
284 } else {
285 switch (channel->interpolation) {
286 case StepInterpolation:
287 return channel->keyframes[prev].rotation;
288 case LinearInterpolation: {
289 const R normalized_t = normalize_time(
290 channel->keyframes[prev].time, channel->keyframes[next].time, t);
291 return qnormalize(qslerp(
292 channel->keyframes[prev].rotation, channel->keyframes[next].rotation,
293 normalized_t));
294 break;
295 }
296 case CubicSplineInterpolation:
297 assert(false); // TODO
298 return qmake(0, 0, 0, 0);
299 default:
300 assert(false);
301 return qmake(0, 0, 0, 0);
302 }
303 }
304}
305
306static vec3 interpolate_translation(
307 const Channel* channel, int prev, int next, R t) {
308 assert(channel);
309
310 if (next == 0) {
311 // Animation has not started at this point in time yet.
312 return channel->keyframes[next].translation;
313 } else {
314 switch (channel->interpolation) {
315 case StepInterpolation:
316 return channel->keyframes[prev].translation;
317 case LinearInterpolation: {
318 const R normalized_t = normalize_time(
319 channel->keyframes[prev].time, channel->keyframes[next].time, t);
320 return vec3_lerp(
321 channel->keyframes[prev].translation,
322 channel->keyframes[next].translation, normalized_t);
323 break;
324 }
325 case CubicSplineInterpolation:
326 assert(false); // TODO
327 return vec3_make(0, 0, 0);
328 default:
329 assert(false);
330 return vec3_make(0, 0, 0);
331 }
332 }
333}
334
335static void animate_channel(Anima* anima, const Channel* channel, R t) {
336 assert(anima);
337 assert(channel);
338 assert(channel->target < anima->num_joints);
339
340 int prev, next;
341 find_keyframes(channel, t, &prev, &next);
342
343 // Note that not all channels extend to the duration of an animation; some
344 // channels may stop animating their targets earlier. Clamp the animation time
345 // to the channel's end keyframe to make the rest of the math (normalize_time)
346 // work.
347 t = t > channel->keyframes[next].time ? channel->keyframes[next].time : t;
348
349 Joint* target = get_anima_joint_mut(anima, channel->target);
350
351 switch (channel->type) {
352 case RotationChannel: {
353 const quat rotation = interpolate_rotation(channel, prev, next, t);
354 gfx_set_joint_rotation(target, rotation);
355 break;
356 }
357 case TranslationChannel: {
358 const vec3 translation = interpolate_translation(channel, prev, next, t);
359 gfx_set_joint_position(target, translation);
360 break;
361 }
362 // Not yet supported.
363 case ScaleChannel:
364 case WeightsChannel:
365 default:
366 // TODO: Add back the assertion or add support for scaling.
367 // assert(false);
368 break;
369 }
370}
371
372static void compute_joint_matrices_rec(
373 Anima* anima, Joint* joint, const mat4* parent_global_joint_transform,
374 const mat4* root_inv_global_transform) {
375 assert(anima);
376 assert(joint);
377 assert(parent_global_joint_transform);
378 assert(root_inv_global_transform);
379
380 const mat4 global_joint_transform =
381 mat4_mul(*parent_global_joint_transform, joint->transform);
382
383 // Compute this joint's matrix.
384 joint->joint_matrix = mat4_mul(
385 *root_inv_global_transform,
386 mat4_mul(global_joint_transform, joint->inv_bind_matrix));
387
388 // Recursively compute the joint matrices for this joint's siblings.
389 if (joint->next != INDEX_NONE) {
390 Joint* sibling = get_anima_joint_mut(anima, joint->next);
391
392 compute_joint_matrices_rec(
393 anima, sibling, parent_global_joint_transform,
394 root_inv_global_transform);
395 }
396
397 // Recursively compute the joint matrices for this joint's children.
398 if (joint->child != INDEX_NONE) {
399 Joint* child = get_anima_joint_mut(anima, joint->child);
400
401 compute_joint_matrices_rec(
402 anima, child, &global_joint_transform, root_inv_global_transform);
403 }
404}
405
406void gfx_update_animation(Anima* anima, R t) {
407 assert(anima);
408
409 AnimationState* state = &anima->state;
410 if (state->animation.val == 0) {
411 return; // No active animation.
412 }
413 const Animation* animation = mem_get_animation(state->animation);
414 assert(animation);
415
416 // On a call to play(), the start time is set to -1 to signal that the
417 // animation playback has not yet been initialized.
418 if (state->start_time == PLAYBACK_UNINITIALIZED) {
419 state->start_time = t;
420 }
421 // Locate the current time point inside the animation's timeline.
422 assert(t >= state->start_time);
423 assert(animation->duration >= 0.0);
424 const R local_time = t - state->start_time;
425 const R animation_time = state->loop
426 ? rmod(local_time, animation->duration)
427 : clamp(local_time, 0.0, animation->duration);
428
429 // LOGD(
430 // "animation_time = %f, animation duration: %f", animation_time,
431 // animation->duration);
432
433 // Play through the animation to transform skeleton nodes.
434 for (size_t i = 0; i < animation->num_channels; ++i) {
435 const Channel* channel = &animation->channels[i];
436 animate_channel(anima, channel, animation_time);
437 }
438
439 // Compute joint matrices after having transformed the skeletons.
440 //
441 // The anima's parent node is the common ancestor of all skeletons, and its
442 // transform maps the skeletons from object space to world space. This is the
443 // transform used as the "global transform" in the joint matrix equations.
444 //
445 // Joint matrix calculation begins by descending from the anima's root joint,
446 // which we have constructed to be the common root of all skeletons.
447 //
448 // This procedure touches every joint exactly once.
449 const mat4 root_global_transform = mat4_id();
450 const mat4 root_inv_global_transform = mat4_id();
451
452 Joint* root_joint = get_anima_root_joint(anima);
453 compute_joint_matrices_rec(
454 anima, root_joint, &root_global_transform, &root_inv_global_transform);
455}
456
457const Skeleton* gfx_get_anima_skeleton(const Anima* anima, size_t i) {
458 assert(anima);
459
460 skeleton_idx skeleton_index = anima->skeleton;
461 const Skeleton* skeleton = mem_get_skeleton(skeleton_index);
462
463 for (size_t j = 1; j < i; ++j) {
464 skeleton_index = skeleton->next;
465 mem_get_skeleton(skeleton_index);
466 }
467
468 return skeleton;
469}
470
471size_t gfx_get_skeleton_num_joints(const Skeleton* skeleton) {
472 assert(skeleton);
473 return skeleton->num_joints;
474}
475
476bool gfx_joint_has_box(
477 const Anima* anima, const Skeleton* skeleton, size_t joint_index) {
478 assert(anima);
479 assert(skeleton);
480 assert(joint_index < skeleton->num_joints);
481
482 const Joint* joint = get_skeleton_joint(anima, skeleton, joint_index);
483 return !aabb3_is_empty(joint->box);
484}
485
486Box gfx_get_joint_box(
487 const Anima* anima, const Skeleton* skeleton, size_t joint_index) {
488 assert(anima);
489 assert(skeleton);
490
491 const Joint* joint = get_skeleton_joint(anima, skeleton, joint_index);
492
493 // Transform the box to anima space.
494 // Note that joint matrices do not usually have a translation since joints
495 // mostly just rotate with respect to their parent.
496 const vec3 pmin = joint->box.min;
497 const vec3 pmax = joint->box.max;
498 return (Box){
499 .vertices = {
500 mat4_mul_vec3(
501 joint->joint_matrix, vec3_make(pmin.x, pmin.y, pmax.z), 1),
502 mat4_mul_vec3(
503 joint->joint_matrix, vec3_make(pmax.x, pmin.y, pmax.z), 1),
504 mat4_mul_vec3(
505 joint->joint_matrix, vec3_make(pmax.x, pmax.y, pmax.z), 1),
506 mat4_mul_vec3(
507 joint->joint_matrix, vec3_make(pmin.x, pmax.y, pmax.z), 1),
508 mat4_mul_vec3(
509 joint->joint_matrix, vec3_make(pmin.x, pmin.y, pmin.z), 1),
510 mat4_mul_vec3(
511 joint->joint_matrix, vec3_make(pmax.x, pmin.y, pmin.z), 1),
512 mat4_mul_vec3(
513 joint->joint_matrix, vec3_make(pmax.x, pmax.y, pmin.z), 1),
514 mat4_mul_vec3(
515 joint->joint_matrix, vec3_make(pmin.x, pmax.y, pmin.z), 1),
516 }
517 };
518}
diff --git a/src/scene/animation_impl.h b/src/scene/animation_impl.h
deleted file mode 100644
index ef1492c..0000000
--- a/src/scene/animation_impl.h
+++ /dev/null
@@ -1,96 +0,0 @@
1#pragma once
2
3#include <gfx/scene/animation.h>
4#include <gfx/sizes.h>
5
6#include "types.h"
7
8#include <cstring.h>
9#include <math/defs.h>
10#include <math/mat4.h>
11#include <math/quat.h>
12#include <math/vec3.h>
13
14#include <stddef.h>
15
16typedef struct Buffer Buffer;
17
18// Currently ignoring scale in skinning and animation.
19//
20// TODO: Simultaneous animation of disjoint animations.
21
22/// Skeleton joint.
23/// Joints are mutable and store the transform and joint matrices that result
24/// from animation, aside from the inverse bind matrix.
25typedef struct Joint {
26 joint_idx child; // First child Joint; index into Anima's joints.
27 joint_idx next; // Next sibling Joint; index into Anima's joints.
28 mat4 transform; // Local transform relative to parent.
29 mat4 inv_bind_matrix; // Transforms the mesh into the joint's local space.
30 mat4 joint_matrix; // inv(global) * global joint transform * inv(bind).
31 aabb3 box; // Bounding box of vertices affected by joint.
32} Joint;
33
34/// Animation skeleton.
35typedef struct Skeleton {
36 skeleton_idx next;
37 size_t num_joints;
38 joint_idx joints[GFX_MAX_NUM_JOINTS]; // Indices into Anima's joints array.
39} Skeleton;
40
41/// A keyframe of animation.
42typedef struct Keyframe {
43 R time; // Start time in [0, end animation time]
44 union {
45 vec3 translation;
46 quat rotation;
47 };
48} Keyframe;
49
50/// Animation channel.
51typedef struct Channel {
52 joint_idx target; // Index into Anima's joints array.
53 ChannelType type;
54 AnimationInterpolation interpolation;
55 size_t num_keyframes;
56 Keyframe keyframes[GFX_MAX_NUM_KEYFRAMES];
57} Channel;
58
59/// A skeletal animation.
60typedef struct Animation {
61 animation_idx next;
62 sstring name;
63 R duration;
64 size_t num_channels;
65 Channel channels[GFX_MAX_NUM_CHANNELS];
66} Animation;
67
68/// Animation state.
69///
70/// This represents the current state of an animation.
71typedef struct AnimationState {
72 R start_time; // Time when the current animation started playing. -1 means the
73 // animation playback has not yet been initialized.
74 animation_idx animation; // Current animation. 0 = no animation.
75 bool loop;
76} AnimationState;
77
78/// Animation object.
79///
80/// This is the top-level animation object that encapsulates everything
81/// necessary for animation.
82///
83/// For lack of a better name, this is called an Anima. It is short and the
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).
90typedef struct Anima {
91 skeleton_idx skeleton; // Index of first skeleton.
92 animation_idx animation; // Index of first animation.
93 AnimationState state; // Current animation state.
94 size_t num_joints; // Number of actual joints in the array.
95 Joint joints[GFX_MAX_NUM_JOINTS]; // Shared by all skeletons.
96} Anima;
diff --git a/src/scene/node_impl.h b/src/scene/node_impl.h
index 15ee232..aba8c41 100644
--- a/src/scene/node_impl.h
+++ b/src/scene/node_impl.h
@@ -2,7 +2,7 @@
2 2
3#include <gfx/scene/node.h> 3#include <gfx/scene/node.h>
4 4
5#include "types.h" 5#include "../types.h"
6 6
7#include <math/camera.h> 7#include <math/camera.h>
8#include <math/mat4.h> 8#include <math/mat4.h>
diff --git a/src/scene/object_impl.h b/src/scene/object_impl.h
index b7a0752..1b14cb5 100644
--- a/src/scene/object_impl.h
+++ b/src/scene/object_impl.h
@@ -2,7 +2,7 @@
2 2
3#include <gfx/scene/object.h> 3#include <gfx/scene/object.h>
4 4
5#include "types.h" 5#include "../types.h"
6 6
7typedef struct MeshLink { 7typedef struct MeshLink {
8 mesh_idx mesh; 8 mesh_idx mesh;
diff --git a/src/scene/scene_impl.h b/src/scene/scene_impl.h
index 992f620..06a69f0 100644
--- a/src/scene/scene_impl.h
+++ b/src/scene/scene_impl.h
@@ -2,7 +2,7 @@
2 2
3#include <gfx/scene/scene.h> 3#include <gfx/scene/scene.h>
4 4
5#include "types.h" 5#include "../types.h"
6 6
7typedef struct SceneNode SceneNode; 7typedef struct SceneNode SceneNode;
8 8
diff --git a/src/scene/types.h b/src/scene/types.h
deleted file mode 100644
index d0ffc41..0000000
--- a/src/scene/types.h
+++ /dev/null
@@ -1,24 +0,0 @@
1/// Strongly-typed indices for every kind of scene node resource.
2#pragma once
3
4#include <stdint.h>
5
6typedef uint16_t gfx_idx;
7
8#define DEF_STRONG_INDEX(TYPE_NAME, IDX_TYPE) \
9 typedef struct TYPE_NAME##_idx { \
10 IDX_TYPE val; \
11 } TYPE_NAME##_idx;
12
13DEF_STRONG_INDEX(anima, gfx_idx)
14DEF_STRONG_INDEX(animation, gfx_idx)
15DEF_STRONG_INDEX(camera, gfx_idx)
16DEF_STRONG_INDEX(light, gfx_idx)
17DEF_STRONG_INDEX(material, gfx_idx)
18DEF_STRONG_INDEX(mesh, gfx_idx)
19DEF_STRONG_INDEX(mesh_link, gfx_idx)
20DEF_STRONG_INDEX(model, gfx_idx)
21DEF_STRONG_INDEX(node, gfx_idx)
22DEF_STRONG_INDEX(object, gfx_idx)
23DEF_STRONG_INDEX(scene, gfx_idx)
24DEF_STRONG_INDEX(skeleton, gfx_idx)