diff options
author | 3gg <3gg@shellblade.net> | 2025-06-27 10:18:39 -0700 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2025-06-27 10:18:39 -0700 |
commit | bd57f345ed9dbed1d81683e48199626de2ea9044 (patch) | |
tree | 4221f2f2a7ad2244d2e93052bd68187ec91b8ea9 /src/scene/node.c | |
parent | 9a82ce0083437a4f9f58108b2c23b957d2249ad8 (diff) |
Diffstat (limited to 'src/scene/node.c')
-rw-r--r-- | src/scene/node.c | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/src/scene/node.c b/src/scene/node.c new file mode 100644 index 0000000..67ce93c --- /dev/null +++ b/src/scene/node.c | |||
@@ -0,0 +1,409 @@ | |||
1 | #include "node_impl.h" | ||
2 | |||
3 | #include "animation_impl.h" | ||
4 | #include "camera_impl.h" | ||
5 | #include "light_impl.h" | ||
6 | #include "model_impl.h" | ||
7 | #include "object_impl.h" | ||
8 | #include "scene_graph.h" | ||
9 | #include "scene_memory.h" | ||
10 | |||
11 | #include "gfx_assert.h" | ||
12 | |||
13 | #include <cstring.h> | ||
14 | #include <log/log.h> | ||
15 | |||
16 | static void scene_node_make(SceneNode* node) { | ||
17 | assert(node); | ||
18 | node->type = LogicalNode; | ||
19 | node->transform = mat4_id(); | ||
20 | } | ||
21 | |||
22 | SceneNode* gfx_make_node() { | ||
23 | SceneNode* node = mem_alloc_node(); | ||
24 | scene_node_make(node); | ||
25 | return node; | ||
26 | } | ||
27 | |||
28 | SceneNode* 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 | anima->parent = mem_get_node_index(node); | ||
34 | return node; | ||
35 | } | ||
36 | |||
37 | SceneNode* gfx_make_camera_node(SceneCamera* camera) { | ||
38 | assert(camera); | ||
39 | SceneNode* node = gfx_make_node(); | ||
40 | node->type = CameraNode; | ||
41 | node->camera = mem_get_camera_index(camera); | ||
42 | camera->parent = mem_get_node_index(node); | ||
43 | return node; | ||
44 | } | ||
45 | |||
46 | SceneNode* gfx_make_light_node(Light* light) { | ||
47 | assert(light); | ||
48 | SceneNode* node = gfx_make_node(); | ||
49 | node->type = LightNode; | ||
50 | node->light = mem_get_light_index(light); | ||
51 | light->parent = mem_get_node_index(node); | ||
52 | return node; | ||
53 | } | ||
54 | |||
55 | SceneNode* gfx_make_model_node(Model* model) { | ||
56 | assert(model); | ||
57 | SceneNode* node = gfx_make_node(); | ||
58 | node->type = ModelNode; | ||
59 | node->model = mem_get_model_index(model); | ||
60 | model->parent = mem_get_node_index(node); | ||
61 | return node; | ||
62 | } | ||
63 | |||
64 | SceneNode* gfx_make_object_node(SceneObject* object) { | ||
65 | assert(object); | ||
66 | SceneNode* node = gfx_make_node(); | ||
67 | node->type = ObjectNode; | ||
68 | node->object = mem_get_object_index(object); | ||
69 | object->parent = mem_get_node_index(node); | ||
70 | return node; | ||
71 | } | ||
72 | |||
73 | /// Frees the node's resource. | ||
74 | static void free_node_resource(SceneNode* node) { | ||
75 | assert(node); | ||
76 | |||
77 | // Set the resource's parent node back to 0 to avoid a recursive call into | ||
78 | // gfx_del_node(). | ||
79 | switch (node->type) { | ||
80 | case AnimaNode: { | ||
81 | Anima* anima = mem_get_anima(node->anima); | ||
82 | anima->parent.val = 0; | ||
83 | gfx_destroy_anima(&anima); | ||
84 | return; | ||
85 | } | ||
86 | case CameraNode: { | ||
87 | SceneCamera* camera = mem_get_camera(node->camera); | ||
88 | camera->parent.val = 0; | ||
89 | gfx_destroy_camera(&camera); | ||
90 | return; | ||
91 | } | ||
92 | case LightNode: { | ||
93 | Light* light = mem_get_light(node->light); | ||
94 | light->parent.val = 0; | ||
95 | gfx_destroy_light(&light); | ||
96 | return; | ||
97 | } | ||
98 | case ModelNode: { | ||
99 | return; // Model data is owned by the asset cache. | ||
100 | } | ||
101 | case ObjectNode: { | ||
102 | SceneObject* object = mem_get_object(node->object); | ||
103 | object->parent.val = 0; | ||
104 | gfx_destroy_object(&object); | ||
105 | return; | ||
106 | } | ||
107 | case LogicalNode: | ||
108 | return; // Logical nodes have no resource. | ||
109 | } | ||
110 | FAIL("unhandled node type"); | ||
111 | } | ||
112 | |||
113 | void gfx_construct_anima_node(SceneNode* node, Anima* anima) { | ||
114 | assert(node); | ||
115 | assert(anima); | ||
116 | free_node_resource(node); | ||
117 | node->type = AnimaNode; | ||
118 | node->anima = mem_get_anima_index(anima); | ||
119 | anima->parent = mem_get_node_index(node); | ||
120 | } | ||
121 | |||
122 | void gfx_construct_camera_node(SceneNode* node, SceneCamera* camera) { | ||
123 | assert(node); | ||
124 | assert(camera); | ||
125 | free_node_resource(node); | ||
126 | node->type = CameraNode; | ||
127 | node->camera = mem_get_camera_index(camera); | ||
128 | camera->parent = mem_get_node_index(node); | ||
129 | } | ||
130 | |||
131 | // TODO: Add a common helper function between each gfx_make_xyz_node() and | ||
132 | // gfx_construct_xyz_node() pair. | ||
133 | void gfx_construct_light_node(SceneNode* node, Light* light) { | ||
134 | assert(node); | ||
135 | assert(light); | ||
136 | free_node_resource(node); | ||
137 | node->type = LightNode; | ||
138 | node->light = mem_get_light_index(light); | ||
139 | light->parent = mem_get_node_index(node); | ||
140 | } | ||
141 | |||
142 | void gfx_construct_model_node(SceneNode* node, Model* model) { | ||
143 | assert(node); | ||
144 | assert(model); | ||
145 | free_node_resource(node); | ||
146 | node->type = ModelNode; | ||
147 | node->model = mem_get_model_index(model); | ||
148 | model->parent = mem_get_node_index(node); | ||
149 | } | ||
150 | |||
151 | void gfx_construct_object_node(SceneNode* node, SceneObject* object) { | ||
152 | assert(node); | ||
153 | assert(object); | ||
154 | free_node_resource(node); | ||
155 | node->type = ObjectNode; | ||
156 | node->object = mem_get_object_index(object); | ||
157 | object->parent = mem_get_node_index(node); | ||
158 | } | ||
159 | |||
160 | static void destroy_node_rec(SceneNode* node) { | ||
161 | assert(node); | ||
162 | |||
163 | // First child. | ||
164 | if (node->child.val) { | ||
165 | destroy_node_rec(mem_get_node(node->child)); | ||
166 | } | ||
167 | |||
168 | // Right sibling. | ||
169 | if (node->next.val) { | ||
170 | destroy_node_rec(mem_get_node(node->next)); | ||
171 | } | ||
172 | |||
173 | free_node_resource(node); | ||
174 | mem_free_node(&node); | ||
175 | } | ||
176 | |||
177 | void gfx_destroy_node(SceneNode** node) { | ||
178 | assert(node); | ||
179 | if (*node) { | ||
180 | // Since the node and the whole hierarchy under it gets destroyed, there is | ||
181 | // no need to individually detach every node from its hierarchy. We can | ||
182 | // simply detach the given node and then destroy it and its sub-hierarchy. | ||
183 | TREE_REMOVE(*node); | ||
184 | destroy_node_rec(*node); | ||
185 | *node = 0; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | // TODO: Think more about ownership of nodes and resources. Should this function | ||
190 | // even exist? | ||
191 | void gfx_del_node(node_idx index) { | ||
192 | assert(index.val); | ||
193 | SceneNode* node = mem_get_node(index); | ||
194 | assert(node); | ||
195 | // TODO: Should destroy children recursively? | ||
196 | TREE_REMOVE(node); | ||
197 | mem_free_node(&node); | ||
198 | } | ||
199 | |||
200 | NodeType gfx_get_node_type(const SceneNode* node) { | ||
201 | assert(node); | ||
202 | return node->type; | ||
203 | } | ||
204 | |||
205 | #define NODE_GET(node, field, expected_type) \ | ||
206 | { \ | ||
207 | assert(node); \ | ||
208 | assert(node->type == expected_type); \ | ||
209 | return mem_get_##field(node->field); \ | ||
210 | } | ||
211 | |||
212 | const Anima* gfx_get_node_anima(const SceneNode* node) { | ||
213 | NODE_GET(node, anima, AnimaNode); | ||
214 | } | ||
215 | |||
216 | Anima* gfx_get_node_anima_mut(SceneNode* node) { | ||
217 | NODE_GET(node, anima, AnimaNode); | ||
218 | } | ||
219 | |||
220 | const SceneCamera* gfx_get_node_camera(const SceneNode* node) { | ||
221 | NODE_GET(node, camera, CameraNode); | ||
222 | } | ||
223 | |||
224 | SceneCamera* gfx_get_node_camera_mut(SceneNode* node) { | ||
225 | NODE_GET(node, camera, CameraNode); | ||
226 | } | ||
227 | |||
228 | const Light* gfx_get_node_light(const SceneNode* node) { | ||
229 | NODE_GET(node, light, LightNode); | ||
230 | } | ||
231 | |||
232 | Light* gfx_get_node_light_mut(SceneNode* node) { | ||
233 | NODE_GET(node, light, LightNode); | ||
234 | } | ||
235 | |||
236 | const Model* gfx_get_node_model(const SceneNode* node) { | ||
237 | NODE_GET(node, model, ModelNode); | ||
238 | } | ||
239 | |||
240 | Model* gfx_get_node_model_mut(SceneNode* node) { | ||
241 | NODE_GET(node, model, ModelNode); | ||
242 | } | ||
243 | |||
244 | const SceneObject* gfx_get_node_object(const SceneNode* node) { | ||
245 | NODE_GET(node, object, ObjectNode); | ||
246 | } | ||
247 | |||
248 | SceneObject* gfx_get_node_object_mut(SceneNode* node) { | ||
249 | NODE_GET(node, object, ObjectNode); | ||
250 | } | ||
251 | |||
252 | const SceneNode* gfx_get_node_parent(const SceneNode* node) { | ||
253 | assert(node); | ||
254 | return mem_get_node(node->parent); | ||
255 | } | ||
256 | |||
257 | SceneNode* gfx_get_node_parent_mut(SceneNode* node) { | ||
258 | assert(node); | ||
259 | return mem_get_node(node->parent); | ||
260 | } | ||
261 | |||
262 | const SceneNode* gfx_get_node_child(const SceneNode* node) { | ||
263 | assert(node); | ||
264 | if (node->child.val) { | ||
265 | return mem_get_node(node->child); | ||
266 | } else { | ||
267 | return 0; | ||
268 | } | ||
269 | } | ||
270 | |||
271 | SceneNode* gfx_get_node_child_mut(SceneNode* node) { | ||
272 | return (SceneNode*)gfx_get_node_child(node); | ||
273 | } | ||
274 | |||
275 | const SceneNode* gfx_get_node_sibling(const SceneNode* node) { | ||
276 | assert(node); | ||
277 | if (node->next.val) { | ||
278 | return mem_get_node(node->next); | ||
279 | } else { | ||
280 | return 0; | ||
281 | } | ||
282 | } | ||
283 | |||
284 | SceneNode* gfx_get_node_sibling_mut(SceneNode* node) { | ||
285 | return (SceneNode*)gfx_get_node_sibling(node); | ||
286 | } | ||
287 | |||
288 | mat4 gfx_get_node_transform(const SceneNode* node) { | ||
289 | assert(node); | ||
290 | return node->transform; | ||
291 | } | ||
292 | |||
293 | mat4 gfx_get_node_global_transform(const SceneNode* node) { | ||
294 | assert(node); | ||
295 | mat4 transform = node->transform; | ||
296 | node_idx parent_index = node->parent; | ||
297 | while (parent_index.val != 0) { | ||
298 | const SceneNode* parent = mem_get_node(parent_index); | ||
299 | transform = mat4_mul(parent->transform, transform); | ||
300 | parent_index = parent->parent; | ||
301 | } | ||
302 | return transform; | ||
303 | } | ||
304 | |||
305 | void gfx_set_node_parent(SceneNode* child, SceneNode* parent_node) { | ||
306 | assert(child); | ||
307 | // Parent can be null. | ||
308 | SET_PARENT(child, parent_node); | ||
309 | } | ||
310 | |||
311 | void gfx_set_node_transform(SceneNode* node, const mat4* transform) { | ||
312 | assert(node); | ||
313 | assert(transform); | ||
314 | node->transform = *transform; | ||
315 | } | ||
316 | |||
317 | void gfx_set_node_position(SceneNode* node, const vec3* position) { | ||
318 | assert(node); | ||
319 | assert(position); | ||
320 | mat4_set_v3(&node->transform, *position); | ||
321 | } | ||
322 | |||
323 | void gfx_set_node_rotation(SceneNode* node, const quat* rotation) { | ||
324 | assert(node); | ||
325 | assert(rotation); | ||
326 | mat4_set_3x3(&node->transform, mat4_from_quat(*rotation)); | ||
327 | } | ||
328 | |||
329 | void gfx_set_node_rotation_mat(SceneNode* node, const mat4* rotation) { | ||
330 | assert(node); | ||
331 | assert(rotation); | ||
332 | mat4_set_3x3(&node->transform, *rotation); | ||
333 | } | ||
334 | |||
335 | static const char* get_node_type_str(NodeType type) { | ||
336 | switch (type) { | ||
337 | case LogicalNode: | ||
338 | return "LogicalNode"; | ||
339 | case AnimaNode: | ||
340 | return "AnimaNode"; | ||
341 | case CameraNode: | ||
342 | return "CameraNode"; | ||
343 | case LightNode: | ||
344 | return "LightNode"; | ||
345 | case ModelNode: | ||
346 | return "ModelNode"; | ||
347 | case ObjectNode: | ||
348 | return "ObjectNode"; | ||
349 | } | ||
350 | FAIL("Unhandled node type"); | ||
351 | return ""; | ||
352 | } | ||
353 | |||
354 | static void log_node_hierarchy_rec(const SceneNode* node, const sstring* pad) { | ||
355 | assert(node); | ||
356 | assert(pad); | ||
357 | |||
358 | LOGI( | ||
359 | "%s%s (%u)", sstring_cstr(pad), get_node_type_str(node->type), | ||
360 | mem_get_node_index(node).val); | ||
361 | |||
362 | // Log the children. | ||
363 | if (node->child.val) { | ||
364 | const sstring new_pad = sstring_concat_cstr(*pad, " "); | ||
365 | log_node_hierarchy_rec(mem_get_node(node->child), &new_pad); | ||
366 | } | ||
367 | |||
368 | // Then log the siblings. | ||
369 | if (node->next.val) { | ||
370 | log_node_hierarchy_rec(mem_get_node(node->next), pad); | ||
371 | } | ||
372 | } | ||
373 | |||
374 | void gfx_log_node_hierarchy(const SceneNode* node) { | ||
375 | const sstring pad = sstring_make(""); | ||
376 | log_node_hierarchy_rec(node, &pad); | ||
377 | } | ||
378 | |||
379 | static SceneNode* clone_scene_rec(const SceneNode* node) { | ||
380 | assert(node); | ||
381 | |||
382 | SceneNode* copy = mem_alloc_node(); | ||
383 | *copy = *node; // Shallow clone of the node's resource. | ||
384 | |||
385 | if (node->child.val) { | ||
386 | SceneNode* child = mem_get_node(node->child); | ||
387 | SceneNode* child_copy = clone_scene_rec(child); | ||
388 | copy->child = mem_get_node_index(child_copy); | ||
389 | child_copy->parent = mem_get_node_index(copy); | ||
390 | } | ||
391 | |||
392 | if (node->next.val) { | ||
393 | SceneNode* next = mem_get_node(node->next); | ||
394 | SceneNode* next_copy = clone_scene_rec(next); | ||
395 | copy->next = mem_get_node_index(next_copy); | ||
396 | next_copy->prev = mem_get_node_index(copy); | ||
397 | } | ||
398 | |||
399 | return copy; | ||
400 | } | ||
401 | |||
402 | SceneNode* gfx_clone_scene_shallow(const SceneNode* node) { | ||
403 | assert(node); | ||
404 | // Must be a root node; not allowed to have siblings. | ||
405 | assert(!node->prev.val); | ||
406 | assert(!node->next.val); | ||
407 | |||
408 | return clone_scene_rec(node); | ||
409 | } | ||