aboutsummaryrefslogtreecommitdiff
path: root/src/scene/scene_graph.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/scene/scene_graph.h')
-rw-r--r--src/scene/scene_graph.h144
1 files changed, 144 insertions, 0 deletions
diff --git a/src/scene/scene_graph.h b/src/scene/scene_graph.h
new file mode 100644
index 0000000..0b1f7d0
--- /dev/null
+++ b/src/scene/scene_graph.h
@@ -0,0 +1,144 @@
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( \
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 SceneCamera *: 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);