Wed, 06 Mar 2024 23:08:03 +0100
add behavior nodes + restructure test program
Also, the test program will now officially be a game of snake.
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 * Copyright 2023 Mike Becker. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
28 #include "ascension/scene.h"
29 #include "ascension/error.h"
31 #include <cx/tree.h>
33 #include <assert.h>
35 #define child_list_off_ \
36 offsetof(AscSceneNode, children), offsetof(AscSceneNode, next)
38 void asc_scene_init(AscScene *scene) {
39 if (scene->root != NULL) {
40 asc_error("Scene is already initialized.");
41 return;
42 }
43 scene->root = asc_scene_node_empty();
44 }
46 void asc_scene_destroy(AscScene *scene) {
47 asc_scene_node_free(scene->root);
48 }
50 void asc_scene_add(AscScene *scene, AscSceneNode *node) {
51 asc_scene_node_link(scene->root, node);
52 asc_node_update(node);
53 }
55 void asc_scene_draw(AscScene const *scene) {
56 CxTreeIterator iter = cx_tree_iterator(scene->root, false, child_list_off_);
58 // skip the root node deliberately, we know it's just the container
59 cxIteratorNext(iter);
61 // update the children
62 cx_foreach(AscSceneNode*, node, iter) {
63 // execute behaviors, first
64 AscBehaviorNode *behavior = node->behaviors;
65 while (behavior) {
66 behavior->func(node);
67 behavior = behavior->next;
68 }
70 // check if geometry needs update
71 if (node->need_update && node->update_func != NULL) {
72 node->need_update = false;
73 asc_transform_copy(node->transform, node->parent->transform);
74 node->update_func(node);
75 }
77 // TODO: don't visit the tree for drawing, visit the render groups
78 if (node->draw_func != NULL) {
79 node->draw_func(node);
80 }
81 }
82 }
84 AscSceneNode *asc_scene_node_empty(void) {
85 AscSceneNode *node = calloc(1, sizeof(AscSceneNode));
86 assert(node != NULL);
87 node->free_func = (asc_scene_free_func) free;
88 asc_transform_identity(node->transform);
89 return node;
90 }
92 void asc_scene_node_free(AscSceneNode *node) {
93 if (node == NULL) return;
95 // remove this node from its parent
96 asc_scene_node_unlink(node);
98 // free the entire subtree
99 CxTreeIterator iter = cx_tree_iterator(node, true, child_list_off_);
100 cx_foreach(AscSceneNode*, child, iter) {
101 if (!iter.exiting) continue;
102 if (child->free_func != NULL) {
103 child->free_func(child);
104 } else {
105 free(child);
106 }
107 }
108 }
110 void asc_scene_node_link(AscSceneNode * restrict parent, AscSceneNode * restrict node) {
111 cx_tree_link(
112 parent, node,
113 offsetof(AscSceneNode, parent),
114 offsetof(AscSceneNode, children),
115 offsetof(AscSceneNode, prev),
116 offsetof(AscSceneNode, next)
117 );
118 }
120 void asc_scene_node_unlink(AscSceneNode *node) {
121 cx_tree_unlink(
122 node,
123 offsetof(AscSceneNode, parent),
124 offsetof(AscSceneNode, children),
125 offsetof(AscSceneNode, prev),
126 offsetof(AscSceneNode, next)
127 );
128 }
130 AscBehaviorNode *asc_scene_add_behavior(AscSceneNode *node, asc_scene_update_func behavior) {
131 AscBehaviorNode *behavior_node = calloc(1, sizeof(AscBehaviorNode));
132 behavior_node->func = behavior;
133 cx_tree_link(
134 node,
135 behavior_node,
136 offsetof(AscBehaviorNode, parent),
137 offsetof(AscSceneNode, behaviors),
138 offsetof(AscBehaviorNode, prev),
139 offsetof(AscBehaviorNode, next)
140 );
141 return behavior_node;
142 }
144 void asc_scene_remove_behavior(AscBehaviorNode *node) {
145 cx_tree_unlink(
146 node,
147 offsetof(AscBehaviorNode, parent),
148 offsetof(AscSceneNode, behaviors),
149 offsetof(AscBehaviorNode, prev),
150 offsetof(AscBehaviorNode, next)
151 );
152 }