aboutsummaryrefslogtreecommitdiff
path: root/src/scene
diff options
context:
space:
mode:
Diffstat (limited to 'src/scene')
-rw-r--r--src/scene/camera.c18
-rw-r--r--src/scene/light.c39
-rw-r--r--src/scene/light_impl.h20
-rw-r--r--src/scene/material.c24
-rw-r--r--src/scene/material_impl.h13
-rw-r--r--src/scene/mesh.c26
-rw-r--r--src/scene/mesh_impl.h11
-rw-r--r--src/scene/model.c45
-rw-r--r--src/scene/model_impl.h16
-rw-r--r--src/scene/node.c352
-rw-r--r--src/scene/node_impl.h40
-rw-r--r--src/scene/object.c80
-rw-r--r--src/scene/object_impl.h23
-rw-r--r--src/scene/scene.c31
-rw-r--r--src/scene/scene_graph.h144
-rw-r--r--src/scene/scene_impl.h9
16 files changed, 891 insertions, 0 deletions
diff --git a/src/scene/camera.c b/src/scene/camera.c
new file mode 100644
index 0000000..fcfc496
--- /dev/null
+++ b/src/scene/camera.c
@@ -0,0 +1,18 @@
1#include <gfx/scene.h>
2
3#include "memory.h"
4
5#include <assert.h>
6#include <math/camera.h>
7
8Camera* gfx_make_camera(void) {
9 Camera* camera = mem_alloc_camera();
10 return camera;
11}
12
13void gfx_destroy_camera(Camera** camera) {
14 assert(camera);
15 if (*camera) {
16 mem_free_camera(camera);
17 }
18}
diff --git a/src/scene/light.c b/src/scene/light.c
new file mode 100644
index 0000000..4233330
--- /dev/null
+++ b/src/scene/light.c
@@ -0,0 +1,39 @@
1#include "light_impl.h"
2
3#include "memory.h"
4
5#include <cassert.h>
6#include <error.h>
7
8static void make_environment_light(
9 Light* light, const EnvironmentLightDesc* desc) {
10 assert(light);
11 assert(desc);
12 light->type = EnvironmentLightType;
13 light->environment.environment_map = desc->environment_map;
14}
15
16Light* gfx_make_light(const LightDesc* desc) {
17 assert(desc);
18
19 Light* light = mem_alloc_light();
20
21 switch (desc->type) {
22 case EnvironmentLightType:
23 make_environment_light(light, &desc->light.environment);
24 break;
25 default:
26 log_error("Unhandled light type");
27 gfx_destroy_light(&light);
28 return 0;
29 }
30
31 return light;
32}
33
34void gfx_destroy_light(Light** light) {
35 assert(light);
36 if (*light) {
37 mem_free_light(light);
38 }
39}
diff --git a/src/scene/light_impl.h b/src/scene/light_impl.h
new file mode 100644
index 0000000..3191a50
--- /dev/null
+++ b/src/scene/light_impl.h
@@ -0,0 +1,20 @@
1#pragma once
2
3#include <gfx/scene.h>
4
5/// An environment light.
6typedef struct EnvironmentLight {
7 const Texture* environment_map;
8 const Texture* irradiance_map; // Renderer implementation.
9 const Texture* prefiltered_environment_map; // Renderer implementation.
10 int max_reflection_lod; // Mandatory when prefiltered_environment_map is
11 // given.
12} EnvironmentLight;
13
14/// A scene light.
15typedef struct Light {
16 LightType type;
17 union {
18 EnvironmentLight environment;
19 };
20} Light;
diff --git a/src/scene/material.c b/src/scene/material.c
new file mode 100644
index 0000000..9fe6c1b
--- /dev/null
+++ b/src/scene/material.c
@@ -0,0 +1,24 @@
1#include "material_impl.h"
2
3#include "memory.h"
4
5static void material_make(Material* material, const MaterialDesc* desc) {
6 assert(material);
7 assert(desc);
8 assert(desc->num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL);
9 material->alpha_mode = desc->alpha_mode;
10 material->alpha_cutoff = desc->alpha_cutoff;
11 material->num_uniforms = (int8_t)desc->num_uniforms;
12 for (int i = 0; i < desc->num_uniforms; ++i) {
13 material->uniforms[i] = desc->uniforms[i];
14 }
15}
16
17Material* gfx_make_material(const MaterialDesc* desc) {
18 assert(desc);
19 Material* material = mem_alloc_material();
20 material_make(material, desc);
21 return material;
22}
23
24void gfx_destroy_material(Material** material) { mem_free_material(material); }
diff --git a/src/scene/material_impl.h b/src/scene/material_impl.h
new file mode 100644
index 0000000..488ffc7
--- /dev/null
+++ b/src/scene/material_impl.h
@@ -0,0 +1,13 @@
1#pragma once
2
3#include <gfx/scene.h>
4#include <gfx/sizes.h>
5
6typedef struct ShaderProgram ShaderProgram;
7
8typedef struct Material {
9 AlphaMode alpha_mode;
10 float alpha_cutoff;
11 int8_t num_uniforms;
12 ShaderUniform uniforms[GFX_MAX_UNIFORMS_PER_MATERIAL];
13} Material;
diff --git a/src/scene/mesh.c b/src/scene/mesh.c
new file mode 100644
index 0000000..770c3fb
--- /dev/null
+++ b/src/scene/mesh.c
@@ -0,0 +1,26 @@
1#include "mesh_impl.h"
2
3#include <gfx/scene.h>
4
5#include "memory.h"
6
7#include <cassert.h>
8
9static void mesh_make(Mesh* mesh, const MeshDesc* desc) {
10 assert(mesh);
11 assert(desc);
12 assert(desc->geometry);
13 assert(desc->material);
14 assert(desc->shader);
15 mesh->geometry = desc->geometry;
16 mesh->material = desc->material;
17 mesh->shader = desc->shader;
18}
19
20Mesh* gfx_make_mesh(const MeshDesc* desc) {
21 Mesh* mesh = mem_alloc_mesh();
22 mesh_make(mesh, desc);
23 return mesh;
24}
25
26void gfx_destroy_mesh(Mesh** mesh) { mem_free_mesh(mesh); }
diff --git a/src/scene/mesh_impl.h b/src/scene/mesh_impl.h
new file mode 100644
index 0000000..c7e2211
--- /dev/null
+++ b/src/scene/mesh_impl.h
@@ -0,0 +1,11 @@
1#pragma once
2
3typedef struct Geometry Geometry;
4typedef struct Material Material;
5typedef struct ShaderProgram ShaderProgram;
6
7typedef struct Mesh {
8 const Geometry* geometry;
9 const Material* material;
10 ShaderProgram* shader; // TODO: Move this back to Material?
11} Mesh;
diff --git a/src/scene/model.c b/src/scene/model.c
new file mode 100644
index 0000000..1bd0112
--- /dev/null
+++ b/src/scene/model.c
@@ -0,0 +1,45 @@
1#include "model_impl.h"
2
3#include <gfx/scene.h>
4
5#include "memory.h"
6
7#include <assert.h>
8
9Model* gfx_make_model(const SceneNode* root) {
10 assert(root);
11
12 Model* model = mem_alloc_model();
13 model->root = mem_get_node_index(root);
14 return model;
15}
16
17void gfx_del_model(Model** model) {
18 assert(model);
19
20 if (*model) {
21 SceneNode* root = mem_get_node((*model)->root);
22 gfx_destroy_node(&root);
23 *model = 0;
24 }
25}
26
27Anima* gfx_get_model_anima(Model* model) {
28 assert(model);
29
30 SceneNode* root = mem_get_node(model->root);
31 if (gfx_get_node_type(root) == AnimaNode) {
32 return gfx_get_node_anima_mut(root);
33 } else {
34 return 0;
35 }
36}
37
38const SceneNode* gfx_get_model_root(const Model* model) {
39 assert(model);
40 return mem_get_node(model->root);
41}
42
43SceneNode* gfx_get_model_root_mut(Model* model) {
44 return (SceneNode*)gfx_get_model_root(model);
45}
diff --git a/src/scene/model_impl.h b/src/scene/model_impl.h
new file mode 100644
index 0000000..72cd0ab
--- /dev/null
+++ b/src/scene/model_impl.h
@@ -0,0 +1,16 @@
1#pragma once
2
3#include <gfx/scene.h>
4
5#include "memory.h"
6
7/// Model.
8typedef struct Model {
9 node_idx root;
10} Model;
11
12/// Create a new model.
13Model* gfx_make_model(const SceneNode* root);
14
15/// Destroy the model.
16void gfx_del_model(Model**);
diff --git a/src/scene/node.c b/src/scene/node.c
new file mode 100644
index 0000000..0004d27
--- /dev/null
+++ b/src/scene/node.c
@@ -0,0 +1,352 @@
1#include "node_impl.h"
2
3#include "animation_impl.h"
4#include "memory.h"
5#include "object_impl.h"
6#include "render/llr_impl.h"
7#include "scene_graph.h"
8
9#include "gfx_assert.h"
10
11#include <gfx/scene.h>
12
13#include <cstring.h>
14#include <log/log.h>
15
16static void scene_node_make(SceneNode* node) {
17 assert(node);
18 node->type = LogicalNode;
19 node->transform = mat4_id();
20}
21
22SceneNode* gfx_make_node(void) {
23 SceneNode* node = mem_alloc_node();
24 scene_node_make(node);
25 return node;
26}
27
28SceneNode* gfx_make_anima_node(Anima* anima) {
29 assert(anima);
30 SceneNode* node = gfx_make_node();
31 node->type = AnimaNode;
32 node->anima = mem_get_anima_index(anima);
33 return node;
34}
35
36SceneNode* gfx_make_camera_node(Camera* camera) {
37 assert(camera);
38 SceneNode* node = gfx_make_node();
39 node->type = CameraNode;
40 node->camera = mem_get_camera_index(camera);
41 return node;
42}
43
44SceneNode* gfx_make_light_node(Light* light) {
45 assert(light);
46 SceneNode* node = gfx_make_node();
47 node->type = LightNode;
48 node->light = mem_get_light_index(light);
49 return node;
50}
51
52SceneNode* gfx_make_model_node(Model* model) {
53 assert(model);
54 SceneNode* node = gfx_make_node();
55 node->type = ModelNode;
56 node->model = mem_get_model_index(model);
57 return node;
58}
59
60SceneNode* gfx_make_object_node(SceneObject* object) {
61 assert(object);
62 SceneNode* node = gfx_make_node();
63 node->type = ObjectNode;
64 node->object = mem_get_object_index(object);
65 return node;
66}
67
68/// Frees the node's resource.
69static void free_node_resource(SceneNode* node) {
70 assert(node);
71
72 switch (node->type) {
73 case AnimaNode: {
74 Anima* anima = mem_get_anima(node->anima);
75 gfx_destroy_anima(&anima);
76 return;
77 }
78 case CameraNode: {
79 Camera* camera = mem_get_camera(node->camera);
80 gfx_destroy_camera(&camera);
81 return;
82 }
83 case LightNode: {
84 Light* light = mem_get_light(node->light);
85 gfx_destroy_light(&light);
86 return;
87 }
88 case ModelNode: {
89 return; // Model data is owned by the asset cache.
90 }
91 case ObjectNode: {
92 SceneObject* object = mem_get_object(node->object);
93 gfx_destroy_object(&object);
94 return;
95 }
96 case LogicalNode: {
97 return; // Logical nodes have no resource.
98 }
99 }
100 FAIL("unhandled node type");
101}
102
103static void destroy_node_rec(SceneNode* node) {
104 assert(node);
105
106 // First child.
107 if (node->child.val) {
108 destroy_node_rec(mem_get_node(node->child));
109 }
110
111 // Right sibling.
112 if (node->next.val) {
113 destroy_node_rec(mem_get_node(node->next));
114 }
115
116 free_node_resource(node);
117 mem_free_node(&node);
118}
119
120void gfx_destroy_node(SceneNode** node) {
121 assert(node);
122 if (*node) {
123 // Since the node and the whole hierarchy under it gets destroyed, there is
124 // no need to individually detach every node from its hierarchy. We can
125 // simply detach the given node and then destroy it and its sub-hierarchy.
126 TREE_REMOVE(*node);
127 destroy_node_rec(*node);
128 *node = 0;
129 }
130}
131
132// TODO: Think more about ownership of nodes and resources. Should this function
133// even exist?
134void gfx_del_node(node_idx index) {
135 assert(index.val);
136 SceneNode* node = mem_get_node(index);
137 assert(node);
138 // TODO: Should destroy children recursively?
139 TREE_REMOVE(node);
140 mem_free_node(&node);
141}
142
143NodeType gfx_get_node_type(const SceneNode* node) {
144 assert(node);
145 return node->type;
146}
147
148#define NODE_GET(node, field, expected_type) \
149 { \
150 assert(node); \
151 assert(node->type == expected_type); \
152 return mem_get_##field(node->field); \
153 }
154
155const Anima* gfx_get_node_anima(const SceneNode* node) {
156 NODE_GET(node, anima, AnimaNode);
157}
158
159Anima* gfx_get_node_anima_mut(SceneNode* node) {
160 NODE_GET(node, anima, AnimaNode);
161}
162
163const Camera* gfx_get_node_camera(const SceneNode* node) {
164 NODE_GET(node, camera, CameraNode);
165}
166
167Camera* gfx_get_node_camera_mut(SceneNode* node) {
168 NODE_GET(node, camera, CameraNode);
169}
170
171const Light* gfx_get_node_light(const SceneNode* node) {
172 NODE_GET(node, light, LightNode);
173}
174
175Light* gfx_get_node_light_mut(SceneNode* node) {
176 NODE_GET(node, light, LightNode);
177}
178
179const Model* gfx_get_node_model(const SceneNode* node) {
180 NODE_GET(node, model, ModelNode);
181}
182
183Model* gfx_get_node_model_mut(SceneNode* node) {
184 NODE_GET(node, model, ModelNode);
185}
186
187const SceneObject* gfx_get_node_object(const SceneNode* node) {
188 NODE_GET(node, object, ObjectNode);
189}
190
191SceneObject* gfx_get_node_object_mut(SceneNode* node) {
192 NODE_GET(node, object, ObjectNode);
193}
194
195const SceneNode* gfx_get_node_parent(const SceneNode* node) {
196 assert(node);
197 return mem_get_node(node->parent);
198}
199
200SceneNode* gfx_get_node_parent_mut(SceneNode* node) {
201 assert(node);
202 return mem_get_node(node->parent);
203}
204
205const SceneNode* gfx_get_node_child(const SceneNode* node) {
206 assert(node);
207 if (node->child.val) {
208 return mem_get_node(node->child);
209 } else {
210 return 0;
211 }
212}
213
214SceneNode* gfx_get_node_child_mut(SceneNode* node) {
215 return (SceneNode*)gfx_get_node_child(node);
216}
217
218const SceneNode* gfx_get_node_sibling(const SceneNode* node) {
219 assert(node);
220 if (node->next.val) {
221 return mem_get_node(node->next);
222 } else {
223 return 0;
224 }
225}
226
227SceneNode* gfx_get_node_sibling_mut(SceneNode* node) {
228 return (SceneNode*)gfx_get_node_sibling(node);
229}
230
231mat4 gfx_get_node_transform(const SceneNode* node) {
232 assert(node);
233 return node->transform;
234}
235
236mat4 gfx_get_node_global_transform(const SceneNode* node) {
237 assert(node);
238 mat4 transform = node->transform;
239 node_idx parent_index = node->parent;
240 while (parent_index.val != 0) {
241 const SceneNode* parent = mem_get_node(parent_index);
242 transform = mat4_mul(parent->transform, transform);
243 parent_index = parent->parent;
244 }
245 return transform;
246}
247
248void gfx_set_node_parent(SceneNode* child, SceneNode* parent_node) {
249 assert(child);
250 // Parent can be null.
251 SET_PARENT(child, parent_node);
252}
253
254void gfx_set_node_transform(SceneNode* node, const mat4* transform) {
255 assert(node);
256 assert(transform);
257 node->transform = *transform;
258}
259
260void gfx_set_node_position(SceneNode* node, const vec3* position) {
261 assert(node);
262 assert(position);
263 mat4_set_v3(&node->transform, *position);
264}
265
266void gfx_set_node_rotation(SceneNode* node, const quat* rotation) {
267 assert(node);
268 assert(rotation);
269 mat4_set_3x3(&node->transform, mat4_from_quat(*rotation));
270}
271
272void gfx_set_node_rotation_mat(SceneNode* node, const mat4* rotation) {
273 assert(node);
274 assert(rotation);
275 mat4_set_3x3(&node->transform, *rotation);
276}
277
278static const char* get_node_type_str(NodeType type) {
279 switch (type) {
280 case LogicalNode:
281 return "LogicalNode";
282 case AnimaNode:
283 return "AnimaNode";
284 case CameraNode:
285 return "CameraNode";
286 case LightNode:
287 return "LightNode";
288 case ModelNode:
289 return "ModelNode";
290 case ObjectNode:
291 return "ObjectNode";
292 }
293 FAIL("Unhandled node type");
294 return "";
295}
296
297static void log_node_hierarchy_rec(const SceneNode* node, const sstring* pad) {
298 assert(node);
299 assert(pad);
300
301 LOGI(
302 "%s%s (%u)", sstring_cstr(pad), get_node_type_str(node->type),
303 mem_get_node_index(node).val);
304
305 // Log the children.
306 if (node->child.val) {
307 const sstring new_pad = sstring_concat_cstr(*pad, " ");
308 log_node_hierarchy_rec(mem_get_node(node->child), &new_pad);
309 }
310
311 // Then log the siblings.
312 if (node->next.val) {
313 log_node_hierarchy_rec(mem_get_node(node->next), pad);
314 }
315}
316
317void gfx_log_node_hierarchy(const SceneNode* node) {
318 const sstring pad = sstring_make("");
319 log_node_hierarchy_rec(node, &pad);
320}
321
322static SceneNode* clone_scene_rec(const SceneNode* node) {
323 assert(node);
324
325 SceneNode* copy = mem_alloc_node();
326 *copy = *node; // Shallow clone of the node's resource.
327
328 if (node->child.val) {
329 SceneNode* child = mem_get_node(node->child);
330 SceneNode* child_copy = clone_scene_rec(child);
331 copy->child = mem_get_node_index(child_copy);
332 child_copy->parent = mem_get_node_index(copy);
333 }
334
335 if (node->next.val) {
336 SceneNode* next = mem_get_node(node->next);
337 SceneNode* next_copy = clone_scene_rec(next);
338 copy->next = mem_get_node_index(next_copy);
339 next_copy->prev = mem_get_node_index(copy);
340 }
341
342 return copy;
343}
344
345SceneNode* gfx_clone_scene_shallow(const SceneNode* node) {
346 assert(node);
347 // Must be a root node; not allowed to have siblings.
348 assert(!node->prev.val);
349 assert(!node->next.val);
350
351 return clone_scene_rec(node);
352}
diff --git a/src/scene/node_impl.h b/src/scene/node_impl.h
new file mode 100644
index 0000000..9e65588
--- /dev/null
+++ b/src/scene/node_impl.h
@@ -0,0 +1,40 @@
1#pragma once
2
3#include <gfx/scene.h>
4
5#include "../types.h"
6
7#include <math/camera.h>
8#include <math/mat4.h>
9
10/// Scene node.
11///
12/// The SceneNode owns its cameras, objects, lights and child nodes. These
13/// together form a strict tree hierarchy and not a more general DAG.
14typedef struct SceneNode {
15 NodeType type;
16 union {
17 anima_idx anima;
18 camera_idx camera;
19 light_idx light;
20 model_idx model;
21 object_idx object;
22 };
23 mat4 transform; // Transformation for this node and its children.
24 node_idx parent; // Parent SceneNode.
25 node_idx child; // First child SceneNode.
26 node_idx next; // Next sibling SceneNode.
27 node_idx prev; // Previous sibling SceneNode.
28} SceneNode;
29
30/// Recursively destroy a node given its index but without destroying the node
31/// resources.
32///
33/// The node and its children are removed from the scene graph.
34///
35/// This function is for the library's internal use only.
36void gfx_del_node(node_idx);
37
38/// Return a shallow clone of the scene rooted at the given node.
39/// The given node must have no siblings (must be a root node).
40SceneNode* gfx_clone_scene_shallow(const SceneNode*);
diff --git a/src/scene/object.c b/src/scene/object.c
new file mode 100644
index 0000000..ac86b39
--- /dev/null
+++ b/src/scene/object.c
@@ -0,0 +1,80 @@
1#include "object_impl.h"
2
3#include <gfx/core.h>
4
5#include "memory.h"
6#include "render/llr_impl.h"
7#include "scene/mesh_impl.h"
8#include "scene/node_impl.h"
9
10#include <assert.h>
11
12static aabb3 calc_object_aabb(const SceneObject* object) {
13 assert(object);
14
15 bool first = true;
16 aabb3 box;
17
18 mesh_link_idx ml = object->mesh_link;
19 while (ml.val) {
20 const MeshLink* mesh_link = mem_get_mesh_link(ml);
21 const mesh_idx mi = mesh_link->mesh;
22 if (mi.val) {
23 const Mesh* mesh = mem_get_mesh(mi);
24 const aabb3 mesh_box = gfx_get_geometry_aabb(mesh->geometry);
25 if (first) {
26 box = mesh_box;
27 first = false;
28 } else {
29 box = aabb3_sum(box, mesh_box);
30 }
31 }
32 ml = mesh_link->next;
33 }
34
35 return box;
36}
37
38static void add_object_mesh(SceneObject* object, Mesh* mesh) {
39 assert(object);
40 assert(mesh);
41
42 MeshLink* link = mem_alloc_mesh_link();
43 link->mesh = mem_get_mesh_index(mesh);
44 link->next = object->mesh_link;
45 object->mesh_link = mem_get_mesh_link_index(link);
46}
47
48SceneObject* gfx_make_object(const ObjectDesc* desc) {
49 assert(desc);
50
51 SceneObject* object = mem_alloc_object();
52 for (size_t i = 0; i < desc->num_meshes; ++i) {
53 add_object_mesh(object, desc->meshes[i]);
54 }
55 object->box = calc_object_aabb(object);
56 return object;
57}
58
59void gfx_destroy_object(SceneObject** object) {
60 assert(object);
61 if (*object) {
62 mem_free_object(object);
63 }
64}
65
66void gfx_set_object_skeleton(SceneObject* object, const Skeleton* skeleton) {
67 assert(object);
68 assert(skeleton);
69 object->skeleton = mem_get_skeleton_index(skeleton);
70}
71
72const Skeleton* gfx_get_object_skeleton(const SceneObject* object) {
73 assert(object);
74 return (object->skeleton.val == 0) ? 0 : mem_get_skeleton(object->skeleton);
75}
76
77aabb3 gfx_get_object_aabb(const SceneObject* object) {
78 assert(object);
79 return object->box;
80}
diff --git a/src/scene/object_impl.h b/src/scene/object_impl.h
new file mode 100644
index 0000000..345d615
--- /dev/null
+++ b/src/scene/object_impl.h
@@ -0,0 +1,23 @@
1#pragma once
2
3#include <gfx/scene.h>
4
5#include "../types.h"
6
7typedef struct MeshLink {
8 mesh_idx mesh;
9 mesh_link_idx next; // Next MeshLink in the list.
10} MeshLink;
11
12/// Scene object.
13///
14/// A SceneObject does not own its Meshes, and they are instead shared for
15/// re-use. The SceneObject consequently embeds a list of MeshLinks as opposed
16/// to a list of Meshes. The MeshLinks define a list of Meshes, which can be
17/// different for each SceneObject. Each SceneObject may then have a unique list
18/// of Meshes, and the Meshes are re-used.
19typedef struct SceneObject {
20 mesh_link_idx mesh_link; /// First MeshLink in the list.
21 skeleton_idx skeleton; /// 0 for static objects.
22 aabb3 box;
23} SceneObject;
diff --git a/src/scene/scene.c b/src/scene/scene.c
new file mode 100644
index 0000000..52ddb58
--- /dev/null
+++ b/src/scene/scene.c
@@ -0,0 +1,31 @@
1#include "scene_impl.h"
2
3#include "memory.h"
4#include "node_impl.h"
5
6#include <assert.h>
7
8Scene* gfx_make_scene(void) {
9 Scene* scene = mem_alloc_scene();
10 scene->root = mem_get_node_index(gfx_make_node());
11 return scene;
12}
13
14void gfx_destroy_scene(Scene** scene) {
15 assert(scene);
16 if (*scene) {
17 SceneNode* node = mem_get_node((*scene)->root);
18 gfx_destroy_node(&node);
19 mem_free_scene(scene);
20 }
21}
22
23const SceneNode* gfx_get_scene_root(const Scene* scene) {
24 assert(scene);
25 return mem_get_node(scene->root);
26}
27
28SceneNode* gfx_get_scene_root_mut(Scene* scene) {
29 assert(scene);
30 return (SceneNode*)gfx_get_scene_root(scene);
31}
diff --git a/src/scene/scene_graph.h b/src/scene/scene_graph.h
new file mode 100644
index 0000000..36c3a98
--- /dev/null
+++ b/src/scene/scene_graph.h
@@ -0,0 +1,144 @@
1/// Functions for list manipulation.
2#pragma once
3
4#include "memory.h"
5
6// NOTE: SceneMemory guarantees that index 0 can be regarded as an invalid
7// index.
8
9#define MEM_GET(INDEX) \
10 _Generic( \
11 (INDEX), \
12 camera_idx: mem_get_camera, \
13 material_idx: mem_get_material, \
14 mesh_idx: mem_get_mesh, \
15 mesh_link_idx: mem_get_mesh_link, \
16 node_idx: mem_get_node, \
17 object_idx: mem_get_object, \
18 scene_idx: mem_get_scene)(INDEX)
19
20#define MEM_GET_INDEX(ITEM) \
21 _Generic( \
22 (ITEM), \
23 Camera *: mem_get_camera_index, \
24 Material *: mem_get_material_index, \
25 Mesh *: mem_get_mesh_index, \
26 MeshLink *: mem_get_mesh_link_index, \
27 SceneNode *: mem_get_node_index, \
28 SceneObject *: mem_get_object_index, \
29 Scene *: mem_get_scene_index)(ITEM)
30
31/// Assert the list node invariant.
32///
33/// - A node does not point to itself.
34#if NDEBUG
35#define ASSERT_LIST_NODE_INVARIANT(ITEM)
36#else
37#define ASSERT_LIST_NODE_INVARIANT(ITEM) \
38 { \
39 const gfx_idx item_idx = MEM_GET_INDEX(ITEM).val; \
40 assert((ITEM)->prev.val != item_idx); \
41 assert((ITEM)->next.val != item_idx); \
42 }
43#endif
44
45/// Assert the tree node invariant.
46///
47/// - A node does not point to itself.
48/// - The node's left and right siblings cannot be equal, unless both are 0.
49/// - The node's left/right sibling cannot be its child, unless both are 0.
50/// - The node's parent cannot be the node's child or sibling, unless it's 0.
51/// - If the node has a parent and the node is the leftmost sibling, then the
52/// parent's child is the node.
53#define ASSERT_TREE_NODE_INVARIANT(ITEM) \
54 { \
55 const gfx_idx item_idx = MEM_GET_INDEX(ITEM).val; \
56 assert((ITEM)->prev.val != item_idx); \
57 assert((ITEM)->next.val != item_idx); \
58 if ((ITEM)->prev.val) { \
59 assert((ITEM)->prev.val != (ITEM)->next.val); \
60 } \
61 if ((ITEM)->child.val) { \
62 assert((ITEM)->child.val != (ITEM)->prev.val); \
63 assert((ITEM)->child.val != (ITEM)->next.val); \
64 } \
65 assert((ITEM)->parent.val != item_idx); \
66 if ((ITEM)->parent.val && !(ITEM)->prev.val) { \
67 assert((ITEM)->parent.val != (ITEM)->prev.val); \
68 assert((ITEM)->parent.val != (ITEM)->next.val); \
69 const __typeof__(ITEM) item_parent = MEM_GET((ITEM)->parent); \
70 assert(item_parent->child.val == item_idx); \
71 } \
72 }
73
74/// Prepend an item to a list.
75/// Modify HEAD_INDEX to equal the index of the new head.
76#define LIST_PREPEND(HEAD_INDEX, ITEM) \
77 (ITEM)->next = HEAD_INDEX; \
78 if (HEAD_INDEX.val) { \
79 __typeof__(ITEM) old_head = MEM_GET(HEAD_INDEX); \
80 old_head->prev = MEM_GET_INDEX(ITEM); \
81 } \
82 HEAD_INDEX = MEM_GET_INDEX(ITEM); \
83 ASSERT_LIST_NODE_INVARIANT(ITEM);
84
85/// Disconnect an item from its siblings.
86#define LIST_REMOVE(ITEM) \
87 if ((ITEM)->prev.val) { \
88 __typeof__(ITEM) prev_sibling = MEM_GET((ITEM)->prev); \
89 prev_sibling->next = (ITEM)->next; \
90 } \
91 if ((ITEM)->next.val) { \
92 __typeof__(ITEM) next_sibling = MEM_GET((ITEM)->next); \
93 next_sibling->prev = (ITEM)->prev; \
94 } \
95 (ITEM)->prev.val = 0; \
96 (ITEM)->next.val = 0; \
97 ASSERT_LIST_NODE_INVARIANT(ITEM);
98
99/// Set the child's parent.
100///
101/// The hierarchy is a strict tree hierarchy and a parent node points to its
102/// first/leftmost child only. To add a new child, the new child becomes the
103/// leftmost node in the list of siblings, the one that the parent then points
104/// to.
105///
106/// The child is also completely disconnected from its previous hierarchy. This
107/// is because siblings in a hierarchy must all point to the same parent.
108#define SET_PARENT(CHILD, PARENT) \
109 assert(CHILD); \
110 assert(CHILD != PARENT); \
111 ASSERT_TREE_NODE_INVARIANT(CHILD); \
112 ASSERT_TREE_NODE_INVARIANT(PARENT); \
113 TREE_REMOVE(CHILD); /* Disconnect CHILD from its previous hierarchy. */ \
114 if (PARENT) { \
115 LIST_PREPEND((PARENT)->child, CHILD); \
116 (CHILD)->parent = MEM_GET_INDEX(PARENT); \
117 } else { \
118 (CHILD)->parent.val = 0; \
119 } \
120 ASSERT_TREE_NODE_INVARIANT(CHILD); \
121 if (PARENT) { \
122 ASSERT_TREE_NODE_INVARIANT(PARENT); \
123 }
124
125/// Remove an item from its hierarchy.
126///
127/// The item is disconnected from its parents and siblings. The hierarchy rooted
128/// under the item remains intact.
129#define TREE_REMOVE(ITEM) \
130 assert(ITEM); \
131 if ((ITEM)->parent.val) { \
132 /* The parent points only to its first/leftmost child. If this item is */ \
133 /* the leftmost sibling, then we need to rewire the parent to point to */ \
134 /* the next sibling to keep the parent connected to its children. */ \
135 __typeof__(ITEM) parent = MEM_GET((ITEM)->parent); \
136 const __typeof__(ITEM) parent_child = MEM_GET(parent->child); \
137 if (parent_child == ITEM) { \
138 assert((ITEM)->prev.val == 0); \
139 parent->child = (ITEM)->next; \
140 } \
141 } \
142 (ITEM)->parent.val = 0; \
143 LIST_REMOVE(ITEM); /* Disconnect ITEM from its siblings. */ \
144 ASSERT_TREE_NODE_INVARIANT(ITEM);
diff --git a/src/scene/scene_impl.h b/src/scene/scene_impl.h
new file mode 100644
index 0000000..ad2e892
--- /dev/null
+++ b/src/scene/scene_impl.h
@@ -0,0 +1,9 @@
1#pragma once
2
3#include <gfx/scene.h>
4
5#include "../types.h"
6
7typedef struct Scene {
8 node_idx root;
9} Scene;