Sat, 03 May 2025 19:48:57 +0200
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); }