add behavior nodes + restructure test program

Wed, 06 Mar 2024 23:08:03 +0100

author
Mike Becker <universe@uap-core.de>
date
Wed, 06 Mar 2024 23:08:03 +0100
changeset 33
e7ddb52facd3
parent 32
86468a71dd73
child 34
45d29d7221cc

add behavior nodes + restructure test program

Also, the test program will now officially be a game of snake.

src/ascension/context.h file | annotate | diff | comparison | revisions
src/ascension/scene.h file | annotate | diff | comparison | revisions
src/ascension/text.h file | annotate | diff | comparison | revisions
src/scene.c file | annotate | diff | comparison | revisions
src/text.c file | annotate | diff | comparison | revisions
test/Makefile file | annotate | diff | comparison | revisions
test/sandbox.c file | annotate | diff | comparison | revisions
test/snake.c file | annotate | diff | comparison | revisions
--- a/src/ascension/context.h	Mon Mar 04 21:16:46 2024 +0100
+++ b/src/ascension/context.h	Wed Mar 06 23:08:03 2024 +0100
@@ -50,7 +50,7 @@
     unsigned int flags;
     CxBuffer error_buffer;
     AscWindow windows[ASC_MAX_WINDOWS];
-    AscWindow const *active_window;
+    AscWindow *active_window;
     AscFont fonts[ASC_MAX_FONTS];
     unsigned int fonts_loaded;
     AscFont const *active_font;
--- a/src/ascension/scene.h	Mon Mar 04 21:16:46 2024 +0100
+++ b/src/ascension/scene.h	Wed Mar 06 23:08:03 2024 +0100
@@ -31,22 +31,31 @@
 #include "transform.h"
 
 typedef struct AscSceneNode AscSceneNode;
+typedef struct AscBehaviorNode AscBehaviorNode;
 
 typedef void(*asc_scene_free_func)(AscSceneNode*);
 typedef void(*asc_scene_update_func)(AscSceneNode*);
 typedef void(*asc_scene_draw_func)(AscSceneNode const*);
 
+struct AscBehaviorNode {
+    AscSceneNode *parent;
+    AscBehaviorNode *prev;
+    AscBehaviorNode *next;
+    asc_scene_update_func func;
+};
+
 struct AscSceneNode {
     AscSceneNode *parent;
     AscSceneNode *prev;
     AscSceneNode *next;
     AscSceneNode *children;
+    AscBehaviorNode *behaviors;
+    void *data;
     asc_scene_free_func free_func;
     asc_scene_update_func update_func;
     asc_scene_draw_func draw_func;
     asc_transform transform;
     bool need_update;
-    // TODO: add more node contents
 };
 
 /**
@@ -87,10 +96,22 @@
 void asc_scene_node_free(AscSceneNode *node);
 
 __attribute__((__nonnull__))
-void asc_scene_node_link(AscSceneNode * restrict parent, AscSceneNode * restrict node);
+void asc_scene_node_link(
+        AscSceneNode *restrict parent,
+        AscSceneNode *restrict node
+);
 
 __attribute__((__nonnull__))
 void asc_scene_node_unlink(AscSceneNode *node);
 
+__attribute__((__nonnull__))
+AscBehaviorNode *asc_scene_add_behavior(
+        AscSceneNode *node,
+        asc_scene_update_func behavior
+);
+
+__attribute__((__nonnull__))
+void asc_scene_remove_behavior(AscBehaviorNode *node);
+
 #endif // ASCENSION_SCENE_H
 
--- a/src/ascension/text.h	Mon Mar 04 21:16:46 2024 +0100
+++ b/src/ascension/text.h	Wed Mar 06 23:08:03 2024 +0100
@@ -49,10 +49,9 @@
 /**
  * Creates a text node.
  *
- * The current context ink and font will be used.
- *
- * To allow more adjustments before initializing the internal structure
- * this function does NOT invoke asc_text_update() automatically.
+ * The current context ink and font will be used and the
+ * node will be automatically added to the UI scene of the
+ * currently active window.
  *
  * @param x the position where to draw the text
  * @param y the position where to draw the text
--- a/src/scene.c	Mon Mar 04 21:16:46 2024 +0100
+++ b/src/scene.c	Wed Mar 06 23:08:03 2024 +0100
@@ -32,9 +32,6 @@
 
 #include <assert.h>
 
-#define node_layout_ \
-    offsetof(AscSceneNode, parent), offsetof(AscSceneNode, children), \
-    offsetof(AscSceneNode, prev), offsetof(AscSceneNode, next)
 #define child_list_off_ \
     offsetof(AscSceneNode, children), offsetof(AscSceneNode, next)
 
@@ -61,13 +58,22 @@
     // skip the root node deliberately, we know it's just the container
     cxIteratorNext(iter);
 
-    // draw the children
+    // update the children
     cx_foreach(AscSceneNode*, node, iter) {
+        // execute behaviors, first
+        AscBehaviorNode *behavior = node->behaviors;
+        while (behavior) {
+            behavior->func(node);
+            behavior = behavior->next;
+        }
+
+        // check if geometry needs update
         if (node->need_update && node->update_func != NULL) {
             node->need_update = false;
             asc_transform_copy(node->transform, node->parent->transform);
             node->update_func(node);
         }
+
         // TODO: don't visit the tree for drawing, visit the render groups
         if (node->draw_func != NULL) {
             node->draw_func(node);
@@ -102,9 +108,45 @@
 }
 
 void asc_scene_node_link(AscSceneNode * restrict parent, AscSceneNode * restrict node) {
-    cx_tree_link(parent, node, node_layout_);
+    cx_tree_link(
+            parent, node,
+            offsetof(AscSceneNode, parent),
+            offsetof(AscSceneNode, children),
+            offsetof(AscSceneNode, prev),
+            offsetof(AscSceneNode, next)
+    );
 }
 
 void asc_scene_node_unlink(AscSceneNode *node) {
-    cx_tree_unlink(node, node_layout_);
-}
\ No newline at end of file
+    cx_tree_unlink(
+            node,
+            offsetof(AscSceneNode, parent),
+            offsetof(AscSceneNode, children),
+            offsetof(AscSceneNode, prev),
+            offsetof(AscSceneNode, next)
+    );
+}
+
+AscBehaviorNode *asc_scene_add_behavior(AscSceneNode *node, asc_scene_update_func behavior) {
+    AscBehaviorNode *behavior_node = calloc(1, sizeof(AscBehaviorNode));
+    behavior_node->func = behavior;
+    cx_tree_link(
+            node,
+            behavior_node,
+            offsetof(AscBehaviorNode, parent),
+            offsetof(AscSceneNode, behaviors),
+            offsetof(AscBehaviorNode, prev),
+            offsetof(AscBehaviorNode, next)
+    );
+    return behavior_node;
+}
+
+void asc_scene_remove_behavior(AscBehaviorNode *node) {
+    cx_tree_unlink(
+            node,
+            offsetof(AscBehaviorNode, parent),
+            offsetof(AscSceneNode, behaviors),
+            offsetof(AscBehaviorNode, prev),
+            offsetof(AscBehaviorNode, next)
+    );
+}
--- a/src/text.c	Mon Mar 04 21:16:46 2024 +0100
+++ b/src/text.c	Wed Mar 06 23:08:03 2024 +0100
@@ -118,6 +118,8 @@
         node->text = strdup(text);
     }
 
+    asc_scene_add(&asc_context.active_window->ui, &node->base);
+
     return node;
 }
 
--- a/test/Makefile	Mon Mar 04 21:16:46 2024 +0100
+++ b/test/Makefile	Wed Mar 06 23:08:03 2024 +0100
@@ -41,7 +41,7 @@
 
 FORCE:
 
-$(BUILD_DIR)/sandbox.o: sandbox.c ../src/ascension/ascension.h \
+$(BUILD_DIR)/sandbox.o: snake.c ../src/ascension/ascension.h \
  ../src/ascension/error.h ../src/ascension/context.h \
  ../src/ascension/datatypes.h ../src/ascension/window.h \
  ../src/ascension/primitives.h ../src/ascension/mesh.h \
--- a/test/sandbox.c	Mon Mar 04 21:16:46 2024 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- * Copyright 2023 Mike Becker. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *   1. Redistributions of source code must retain the above copyright
- *      notice, this list of conditions and the following disclaimer.
- *
- *   2. Redistributions in binary form must reproduce the above copyright
- *      notice, this list of conditions and the following disclaimer in the
- *      documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <ascension/ascension.h>
-#include <cx/printf.h>
-
-static bool show_message_box_on_error(SDL_Window* window) {
-    if (asc_has_error()) {
-        SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
-                "Fatal Error",
-                asc_get_error(),
-                window);
-        asc_clear_error();
-        return true;
-    } else {
-        return false;
-    }
-}
-
-int main(int argc, char** argv) {
-    asc_context_initialize();
-    if (show_message_box_on_error(NULL)) return 1;
-
-    AscWindowSettings settings;
-    asc_window_settings_init_defaults(&settings);
-    settings.title = "Sandbox Application";
-
-    AscWindow *window = asc_window_initialize(0, &settings);
-    asc_shader_initialize_predefined();
-
-    // create fps counter and add it to the UI
-    asc_set_font(asc_font(ASC_FONT_REGULAR, 24));
-    asc_ink_rgb(255, 0, 0);
-    AscText *fps_counter = asc_text(50, 50, "XXXXX FPS");
-    unsigned last_fps = 0;
-    asc_scene_add(&window->ui, asc_node(fps_counter));
-
-    do {
-        // quit application on any error
-        if (show_message_box_on_error(window->window)) break;
-
-        // update fps counter
-        if (asc_context.elapsed_millis > 0) {
-            unsigned fps = 1000u / asc_context.elapsed_millis;
-            if (fps != last_fps) {
-                last_fps = fps;
-                snprintf(fps_counter->text, 9, "%u FPS", fps);
-                asc_node_update(fps_counter);
-            }
-        }
-    } while (asc_loop_next());
-
-    asc_context_destroy();
-    return 0;
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/snake.c	Wed Mar 06 23:08:03 2024 +0100
@@ -0,0 +1,88 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ * Copyright 2023 Mike Becker. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ascension/ascension.h>
+#include <cx/printf.h>
+
+static void update_fps_counter(AscSceneNode *node) {
+    // addition and multiplication is more efficient testing for zero
+    // at an unnoticible cost of imprecision
+    static unsigned last_fps = 0u;
+    static unsigned debounce = 999u;
+    unsigned fps = 1000u;
+    debounce += asc_context.elapsed_millis;
+    if (debounce >= 1000u) {
+        debounce = 0;
+        fps /= asc_context.elapsed_millis;
+        if (fps != last_fps) {
+            last_fps = fps;
+            snprintf(((AscText*)node)->text, 9, "%u FPS", fps);
+            asc_node_update(node);
+        }
+    }
+}
+
+static void create_fps_counter(void) {
+    asc_set_font(asc_font(ASC_FONT_REGULAR, 24));
+    asc_ink_rgb(255, 0, 0);
+    AscText* text = asc_text(10, 10, "XXXXX FPS");
+    asc_scene_add_behavior(asc_node(text), update_fps_counter);
+}
+
+int main(int argc, char** argv) {
+    asc_context_initialize();
+    if (asc_has_error()) {
+        SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
+                "Fatal Error",asc_get_error(),NULL);
+        return 1;
+    }
+
+    AscWindowSettings settings;
+    asc_window_settings_init_defaults(&settings);
+    settings.title = "Sandbox Application";
+
+    AscWindow *window = asc_window_initialize(0, &settings);
+    asc_shader_initialize_predefined();
+
+    // create fps counter
+    create_fps_counter();
+
+    // Main Loop
+    do {
+        // quit application on any error
+        if (asc_has_error()) {
+            SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
+                    "Fatal Error", asc_get_error(), window->window);
+            asc_clear_error();
+            break;
+        }
+    } while (asc_loop_next());
+
+    asc_context_destroy();
+    return 0;
+}
+

mercurial