# HG changeset patch # User Mike Becker # Date 1709762883 -3600 # Node ID e7ddb52facd39a4308e7bb02b545368738281a3f # Parent 86468a71dd73962c0162a58cf01775aaa1970010 add behavior nodes + restructure test program Also, the test program will now officially be a game of snake. diff -r 86468a71dd73 -r e7ddb52facd3 src/ascension/context.h --- 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; diff -r 86468a71dd73 -r e7ddb52facd3 src/ascension/scene.h --- 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 diff -r 86468a71dd73 -r e7ddb52facd3 src/ascension/text.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 diff -r 86468a71dd73 -r e7ddb52facd3 src/scene.c --- 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 -#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) + ); +} diff -r 86468a71dd73 -r e7ddb52facd3 src/text.c --- 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; } diff -r 86468a71dd73 -r e7ddb52facd3 test/Makefile --- 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 \ diff -r 86468a71dd73 -r e7ddb52facd3 test/sandbox.c --- 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 -#include - -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; -} - diff -r 86468a71dd73 -r e7ddb52facd3 test/snake.c --- /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 +#include + +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; +} +