add AscBehavior - prepares resolution of issue #646 default tip

Sat, 03 May 2025 19:48:57 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 03 May 2025 19:48:57 +0200
changeset 108
d619bf7dd87b
parent 107
a35b39abe2b2

add AscBehavior - prepares resolution of issue #646

src/Makefile file | annotate | diff | comparison | revisions
src/ascension/behavior.h file | annotate | diff | comparison | revisions
src/ascension/core.h file | annotate | diff | comparison | revisions
src/ascension/scene.h file | annotate | diff | comparison | revisions
src/ascension/scene_node.h file | annotate | diff | comparison | revisions
src/behavior.c file | annotate | diff | comparison | revisions
src/scene.c file | annotate | diff | comparison | revisions
src/scene_node.c file | annotate | diff | comparison | revisions
test/snake/Makefile file | annotate | diff | comparison | revisions
test/snake/snake.c file | annotate | diff | comparison | revisions
--- a/src/Makefile	Sat May 03 14:37:57 2025 +0200
+++ b/src/Makefile	Sat May 03 19:48:57 2025 +0200
@@ -31,7 +31,7 @@
       window.c shader.c mesh.c texture.c \
       sprite.c \
       primitives.c \
-      camera.c scene.c scene_node.c \
+      camera.c scene.c scene_node.c behavior.c \
       font.c text.c
 
 OBJ = $(SRC:%.c=$(BUILD_DIR)/%.o)
@@ -45,6 +45,10 @@
 
 FORCE:
 
+$(BUILD_DIR)/behavior.o: behavior.c ascension/behavior.h
+	@echo "Compiling $<"
+	$(CC) -o $@ $(CFLAGS) -c $<
+
 $(BUILD_DIR)/camera.o: camera.c ascension/error.h ascension/camera.h \
  ascension/datatypes.h
 	@echo "Compiling $<"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ascension/behavior.h	Sat May 03 19:48:57 2025 +0200
@@ -0,0 +1,58 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ * Copyright 2025 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.
+ */
+
+
+#ifndef ASC_BEHAVIOR_H
+#define ASC_BEHAVIOR_H
+
+#include "scene_node.h"
+
+typedef struct asc_behavior_s AscBehavior;
+
+typedef void(*asc_behavior_func)(AscBehavior*);
+typedef void(*asc_behavior_destroy_func)(AscBehavior*);
+
+struct asc_behavior_s {
+    asc_behavior_func func;
+    asc_behavior_destroy_func destroy_func;
+    AscSceneNode *node;
+    void *data;
+    // TODO: more useful attributes, e.g. timing settings
+};
+
+struct asc_behavior_create_args {
+    asc_behavior_func func;
+    asc_behavior_destroy_func destroy_func;
+    void *data;
+};
+
+AscBehavior *asc_behavior_add_impl(AscSceneNode *node, struct asc_behavior_create_args args);
+#define asc_behavior_add(node,...) asc_behavior_add_impl(node, (struct asc_behavior_create_args){__VA_ARGS__})
+
+// TODO: asc_behavior_remove()
+
+#endif
--- a/src/ascension/core.h	Sat May 03 14:37:57 2025 +0200
+++ b/src/ascension/core.h	Sat May 03 19:48:57 2025 +0200
@@ -30,8 +30,8 @@
 
 #include "error.h"
 #include "context.h"
-#include "shader.h"
 #include "scene.h"
+#include "behavior.h"
 
 #endif /* ASCENSION_CORE_H */
 
--- a/src/ascension/scene.h	Sat May 03 14:37:57 2025 +0200
+++ b/src/ascension/scene.h	Sat May 03 19:48:57 2025 +0200
@@ -28,7 +28,6 @@
 #ifndef ASCENSION_SCENE_H
 #define ASCENSION_SCENE_H
 
-#include "datatypes.h"
 #include "scene_node.h"
 #include "camera.h"
 
--- a/src/ascension/scene_node.h	Sat May 03 14:37:57 2025 +0200
+++ b/src/ascension/scene_node.h	Sat May 03 19:48:57 2025 +0200
@@ -51,6 +51,11 @@
     AscSceneNode *next;
     AscSceneNode *children;
     AscSceneNode *last_child;
+    // TODO: think about how to make nodes identifiable - by name? by int?
+    /**
+     * List of AscBehavior structs.
+     * Not a pointer list!
+     */
     CxList *behaviors;
     asc_scene_free_func free_func;
     asc_scene_update_func update_func;
@@ -79,22 +84,25 @@
 /**
  * Set when a graphics updated happened last frame.
  */
-#define ASC_SCENE_NODE_GRAPHICS_UPDATED       0x10000000
+#define ASC_SCENE_NODE_GRAPHICS_UPDATED       0x02000000
 /**
  * Set when a transform update is needed in this frame.
  */
-#define ASC_SCENE_NODE_UPDATE_TRANSFORM       0x02000000
+#define ASC_SCENE_NODE_UPDATE_TRANSFORM       0x04000000
 /**
  * Set when a transform update happened last frame.
  */
-#define ASC_SCENE_NODE_TRANSFORM_UPDATED      0x20000000
+#define ASC_SCENE_NODE_TRANSFORM_UPDATED      0x08000000
+/**
+ * Set when the node is not supposed to be shown on screen.
+ */
+#define ASC_SCENE_BEHAVIOR_PAUSED             0x40000000
 /**
  * Set when the node is not supposed to be shown on screen.
  */
 #define ASC_SCENE_NODE_HIDDEN                 0x80000000
 
 
-
 /**
  * Creates an empty node that may serve as a container for other nodes.
  *
@@ -137,36 +145,6 @@
 __attribute__((__nonnull__))
 void asc_scene_node_unlink(AscSceneNode *node);
 
-/**
- * Adds a behavior function to the node.
- *
- * A behavior function MUST NOT be added more than once to the same node.
- * This will not be checked.
- *
- * @param node the node
- * @param behavior the behavior function
- */
-__attribute__((__nonnull__))
-void asc_scene_add_behavior(
-        AscSceneNode *node,
-        asc_scene_update_func behavior
-);
-
-/**
- * Removes a behavior function from the node.
- *
- * If the behavior function is not attached to this node, this function
- * does nothing.
- *
- * @param node the node
- * @param behavior the behavior function
- */
-__attribute__((__nonnull__))
-void asc_scene_remove_behavior(
-        AscSceneNode *node,
-        asc_scene_update_func behavior
-);
-
 __attribute__((__nonnull__))
 void asc_node_update(AscSceneNode *node);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/behavior.c	Sat May 03 19:48:57 2025 +0200
@@ -0,0 +1,64 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ * Copyright 2025 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/behavior.h"
+#include "ascension/error.h"
+
+#include <cx/array_list.h>
+
+static void asc_behavior_destroy(void *b) {
+    AscBehavior *behavior = b;
+    if (behavior->destroy_func) {
+        behavior->destroy_func(behavior);
+    }
+}
+
+static CxList *asc_behavior_new_list(void) {
+    // TODO: add comparator when we know how to identifier behaviors in order to remove them
+    CxList *list = cxArrayListCreate(NULL, NULL, sizeof(AscBehavior), 4);
+    cxDefineDestructor(list, asc_behavior_destroy);
+    return list;
+}
+
+AscBehavior *asc_behavior_add_impl(AscSceneNode *node, struct asc_behavior_create_args args) {
+    // TODO: replace cxListAdd() with cxListEmplace() when upgrading to UCX 3.2
+    AscBehavior behavior = {0};
+    behavior.node = node;
+    behavior.func = args.func;
+    behavior.destroy_func = args.destroy_func;
+    behavior.data = args.data;
+    if (node->behaviors == NULL) {
+        node->behaviors = asc_behavior_new_list();
+    }
+    if (cxListAdd(node->behaviors, &behavior)) {
+        // TODO: output ID of node once we have implemented that
+        asc_error("Failed to add behavior to scene node.");
+        return NULL;
+    }
+    // TODO: replace with cxListLast() when upgrading to UCX 3.2
+    return cxListAt(node->behaviors, cxListSize(node->behaviors) - 1);
+}
--- a/src/scene.c	Sat May 03 14:37:57 2025 +0200
+++ b/src/scene.c	Sat May 03 19:48:57 2025 +0200
@@ -28,6 +28,7 @@
 #include "ascension/error.h"
 #include "ascension/context.h"
 #include "ascension/scene.h"
+#include "ascension/behavior.h"
 #include "ascension/shader.h"
 #include "ascension/2d.h"
 
@@ -93,11 +94,10 @@
         }
 
         // execute behaviors, first
-        if (node->behaviors != NULL) {
-            CxIterator behavior_iter = cxListIterator(node->behaviors);
-            cx_foreach(asc_scene_update_func, behavior, behavior_iter) {
-                behavior(node);
-            }
+        // TODO: move to a separate iteration that is decoupled from drawing - issue #646
+        CxIterator behavior_iter = cxListIterator(node->behaviors);
+        cx_foreach(AscBehavior*, behavior, behavior_iter) {
+            behavior->func(behavior);
         }
 
         // TODO: implement culling
--- a/src/scene_node.c	Sat May 03 14:37:57 2025 +0200
+++ b/src/scene_node.c	Sat May 03 19:48:57 2025 +0200
@@ -62,9 +62,7 @@
     CxTreeIterator iter = asc_scene_node_iterator(node, true);
     cx_foreach(AscSceneNode*, child, iter) {
         if (!iter.exiting) continue;
-        if (child->behaviors != NULL) {
-            cxListFree(child->behaviors);
-        }
+        cxListFree(child->behaviors);
         if (child->free_func != NULL) {
             child->free_func(child);
         } else {
--- a/test/snake/Makefile	Sat May 03 14:37:57 2025 +0200
+++ b/test/snake/Makefile	Sat May 03 19:48:57 2025 +0200
@@ -52,9 +52,9 @@
  ../../src/ascension/texture.h ../../src/ascension/scene.h \
  ../../src/ascension/scene_node.h ../../src/ascension/transform.h \
  ../../src/ascension/camera.h ../../src/ascension/input.h \
- ../../src/ascension/ui/font.h ../../src/ascension/ui.h \
- ../../src/ascension/ui/text.h ../../src/ascension/ui/font.h \
- ../../src/ascension/ui/../2d/sprite.h \
+ ../../src/ascension/ui/font.h ../../src/ascension/behavior.h \
+ ../../src/ascension/ui.h ../../src/ascension/ui/text.h \
+ ../../src/ascension/ui/font.h ../../src/ascension/ui/../2d/sprite.h \
  ../../src/ascension/ui/../2d/../scene_node.h \
  ../../src/ascension/ui/../2d/../texture.h
 	@echo "Compiling $<"
--- a/test/snake/snake.c	Sat May 03 14:37:57 2025 +0200
+++ b/test/snake/snake.c	Sat May 03 19:48:57 2025 +0200
@@ -53,7 +53,8 @@
     cxMempoolRegister(asc_active_glctx_mpool, tex2d, destroy_textures);
 }
 
-static void update_fps_counter(AscSceneNode *node) {
+static void update_fps_counter(AscBehavior *behavior) {
+    AscSceneNode *node = behavior->node;
     static uint64_t last_fps = 0;
     static uint64_t debounce = ASC_NANOS_SECOND - 1;
     debounce += asc_context.frame_nanos;
@@ -81,9 +82,10 @@
     }
 }
 
-static void scale_backdrop(AscSceneNode *node) {
+static void scale_backdrop(AscBehavior *behavior) {
     // scale the backdrop to the size of the window
     if (asc_active_window->resized) {
+        AscSceneNode *node = behavior->node;
         asc_vec2u window_size = asc_active_window->dimensions;
         // TODO: replace scale with asc_sprite_set_size()
         asc_set_scale(node, (float)window_size.width, (float)window_size.height, 1);
@@ -93,7 +95,7 @@
 
 static void create_backdrop(void) {
     AscSceneNode *node = asc_sprite(.texture = TEXTURE_BACKDROP);
-    asc_scene_add_behavior(node, scale_backdrop);
+    asc_behavior_add(node, .func = scale_backdrop);
     asc_scene_add_node(BACKDROP_SCENE, node);
 }
 
@@ -101,7 +103,7 @@
     asc_font(ASC_FONT_REGULAR, 12);
     asc_ink_rgba(224, 224, 224, 196);
     AscSceneNode *node = asc_text();
-    asc_scene_add_behavior(node, update_fps_counter);
+    asc_behavior_add(node, .func = update_fps_counter);
     asc_add_ui_node(node);
 }
 

mercurial