aboutsummaryrefslogtreecommitdiff
path: root/src/scene/scene_graph.h
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-06-27 10:18:39 -0700
committer3gg <3gg@shellblade.net>2025-06-27 10:18:39 -0700
commitbd57f345ed9dbed1d81683e48199626de2ea9044 (patch)
tree4221f2f2a7ad2244d2e93052bd68187ec91b8ea9 /src/scene/scene_graph.h
parent9a82ce0083437a4f9f58108b2c23b957d2249ad8 (diff)
Restructure projectHEADmain
Diffstat (limited to 'src/scene/scene_graph.h')
-rw-r--r--src/scene/scene_graph.h138
1 files changed, 138 insertions, 0 deletions
diff --git a/src/scene/scene_graph.h b/src/scene/scene_graph.h
new file mode 100644
index 0000000..a26f828
--- /dev/null
+++ b/src/scene/scene_graph.h
@@ -0,0 +1,138 @@
1/// Functions for list manipulation.
2#pragma once
3
4#include "scene_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((INDEX), camera_idx \
11 : mem_get_camera, material_idx \
12 : mem_get_material, mesh_idx \
13 : mem_get_mesh, mesh_link_idx \
14 : mem_get_mesh_link, node_idx \
15 : mem_get_node, object_idx \
16 : mem_get_object, scene_idx \
17 : mem_get_scene)(INDEX)
18
19#define MEM_GET_INDEX(ITEM) \
20 _Generic((ITEM), SceneCamera * \
21 : mem_get_camera_index, Material * \
22 : mem_get_material_index, Mesh * \
23 : mem_get_mesh_index, MeshLink * \
24 : mem_get_mesh_link_index, SceneNode * \
25 : mem_get_node_index, SceneObject * \
26 : mem_get_object_index, Scene * \
27 : mem_get_scene_index)(ITEM)
28
29/// Assert the list node invariant.
30///
31/// - A node does not point to itself.
32#define ASSERT_LIST_NODE_INVARIANT(ITEM) \
33 { \
34 const gfx_idx item_idx = MEM_GET_INDEX(ITEM).val; \
35 assert((ITEM)->prev.val != item_idx); \
36 assert((ITEM)->next.val != item_idx); \
37 }
38
39/// Assert the tree node invariant.
40///
41/// - A node does not point to itself.
42/// - The node's left and right siblings cannot be equal, unless both are 0.
43/// - The node's left/right sibling cannot be its child, unless both are 0.
44/// - The node's parent cannot be the node's child or sibling, unless it's 0.
45/// - If the node has a parent and the node is the leftmost sibling, then the
46/// parent's child is the node.
47#define ASSERT_TREE_NODE_INVARIANT(ITEM) \
48 { \
49 const gfx_idx item_idx = MEM_GET_INDEX(ITEM).val; \
50 assert((ITEM)->prev.val != item_idx); \
51 assert((ITEM)->next.val != item_idx); \
52 if ((ITEM)->prev.val) { \
53 assert((ITEM)->prev.val != (ITEM)->next.val); \
54 } \
55 if ((ITEM)->child.val) { \
56 assert((ITEM)->child.val != (ITEM)->prev.val); \
57 assert((ITEM)->child.val != (ITEM)->next.val); \
58 } \
59 assert((ITEM)->parent.val != item_idx); \
60 if ((ITEM)->parent.val && !(ITEM)->prev.val) { \
61 assert((ITEM)->parent.val != (ITEM)->prev.val); \
62 assert((ITEM)->parent.val != (ITEM)->next.val); \
63 const __typeof__(ITEM) item_parent = MEM_GET((ITEM)->parent); \
64 assert(item_parent->child.val == item_idx); \
65 } \
66 }
67
68/// Prepend an item to a list.
69/// Modify HEAD_INDEX to equal the index of the new head.
70#define LIST_PREPEND(HEAD_INDEX, ITEM) \
71 (ITEM)->next = HEAD_INDEX; \
72 if (HEAD_INDEX.val) { \
73 __typeof__(ITEM) old_head = MEM_GET(HEAD_INDEX); \
74 old_head->prev = MEM_GET_INDEX(ITEM); \
75 } \
76 HEAD_INDEX = MEM_GET_INDEX(ITEM); \
77 ASSERT_LIST_NODE_INVARIANT(ITEM);
78
79/// Disconnect an item from its siblings.
80#define LIST_REMOVE(ITEM) \
81 if ((ITEM)->prev.val) { \
82 __typeof__(ITEM) prev_sibling = MEM_GET((ITEM)->prev); \
83 prev_sibling->next = (ITEM)->next; \
84 } \
85 if ((ITEM)->next.val) { \
86 __typeof__(ITEM) next_sibling = MEM_GET((ITEM)->next); \
87 next_sibling->prev = (ITEM)->prev; \
88 } \
89 (ITEM)->prev.val = 0; \
90 (ITEM)->next.val = 0; \
91 ASSERT_LIST_NODE_INVARIANT(ITEM);
92
93/// Set the child's parent.
94///
95/// The hierarchy is a strict tree hierarchy and a parent node points to its
96/// first/leftmost child only. To add a new child, the new child becomes the
97/// leftmost node in the list of siblings, the one that the parent then points
98/// to.
99///
100/// The child is also completely disconnected from its previous hierarchy. This
101/// is because siblings in a hierarchy must all point to the same parent.
102#define SET_PARENT(CHILD, PARENT) \
103 assert(CHILD); \
104 assert(CHILD != PARENT); \
105 ASSERT_TREE_NODE_INVARIANT(CHILD); \
106 ASSERT_TREE_NODE_INVARIANT(PARENT); \
107 TREE_REMOVE(CHILD); /* Disconnect CHILD from its previous hierarchy. */ \
108 if (PARENT) { \
109 LIST_PREPEND((PARENT)->child, CHILD); \
110 (CHILD)->parent = MEM_GET_INDEX(PARENT); \
111 } else { \
112 (CHILD)->parent.val = 0; \
113 } \
114 ASSERT_TREE_NODE_INVARIANT(CHILD); \
115 if (PARENT) { \
116 ASSERT_TREE_NODE_INVARIANT(PARENT); \
117 }
118
119/// Remove an item from its hierarchy.
120///
121/// The item is disconnected from its parents and siblings. The hierarchy rooted
122/// under the item remains intact.
123#define TREE_REMOVE(ITEM) \
124 assert(ITEM); \
125 if ((ITEM)->parent.val) { \
126 /* The parent points only to its first/leftmost child. If this item is */ \
127 /* the leftmost sibling, then we need to rewire the parent to point to */ \
128 /* the next sibling to keep the parent connected to its children. */ \
129 __typeof__(ITEM) parent = MEM_GET((ITEM)->parent); \
130 const __typeof__(ITEM) parent_child = MEM_GET(parent->child); \
131 if (parent_child == ITEM) { \
132 assert((ITEM)->prev.val == 0); \
133 parent->child = (ITEM)->next; \
134 } \
135 } \
136 (ITEM)->parent.val = 0; \
137 LIST_REMOVE(ITEM); /* Disconnect ITEM from its siblings. */ \
138 ASSERT_TREE_NODE_INVARIANT(ITEM);