add asc_scene_node_init() - fixes #695

Wed, 16 Jul 2025 23:27:34 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 16 Jul 2025 23:27:34 +0200
changeset 207
4d184a8706b1
parent 206
26726b7a89a7
child 208
658bccb1bf73

add asc_scene_node_init() - fixes #695

src/2d.c file | annotate | diff | comparison | revisions
src/ascension/scene_node.h file | annotate | diff | comparison | revisions
src/scene_node.c file | annotate | diff | comparison | revisions
src/sprite.c file | annotate | diff | comparison | revisions
src/text.c file | annotate | diff | comparison | revisions
--- a/src/2d.c	Mon Jul 14 21:56:53 2025 +0200
+++ b/src/2d.c	Wed Jul 16 23:27:34 2025 +0200
@@ -189,17 +189,14 @@
         rectangle->border_color = rectangle->color;
     }
 
-    AscSceneNode *node = &rectangle->node;
-    node->position = ASC_VEC3F(pos_x, pos_y, ASC_SCENE_2D_DEPTH_OFFSET);
-    node->scale = ASC_VEC3F_1;
-    asc_mat4f_unit(node->rotation);
-    node->render_group = asc_context.ink.alpha < 255
+    asc_ptr_cast(AscSceneNode, node, rectangle);
+    asc_scene_node_init(node,
+        ASC_SCENE_NODE_FUNCS(asc_rectangle),
+        .pos2d = ASC_VEC2I(pos_x, pos_y),
+        .render_group = asc_context.ink.alpha < 255
                              ? ASC_RENDER_GROUP_2D_BLEND
-                             : ASC_RENDER_GROUP_2D_OPAQUE;
-    node->update_func = asc_rectangle_update;
-    node->destroy_func = asc_rectangle_destroy;
-    node->draw_func = asc_rectangle_draw;
-    asc_scene_node_update(node);
+                             : ASC_RENDER_GROUP_2D_OPAQUE
+    );
     return node;
 }
 
@@ -345,16 +342,13 @@
         ellipsis->border_color = ellipsis->color;
     }
 
-    AscSceneNode *node = &ellipsis->node;
-    node->position = ASC_VEC3F(pos_x, pos_y, ASC_SCENE_2D_DEPTH_OFFSET);
-    node->scale = ASC_VEC3F_1;
-    asc_mat4f_unit(node->rotation);
-    node->render_group = asc_context.ink.alpha < 255
+    asc_ptr_cast(AscSceneNode, node, ellipsis);
+    asc_scene_node_init(node,
+        ASC_SCENE_NODE_FUNCS(asc_ellipsis),
+        .pos2d = ASC_VEC2I(pos_x, pos_y),
+        .render_group = asc_context.ink.alpha < 255
                              ? ASC_RENDER_GROUP_2D_BLEND
-                             : ASC_RENDER_GROUP_2D_OPAQUE;
-    node->update_func = asc_ellipsis_update;
-    node->destroy_func = asc_ellipsis_destroy;
-    node->draw_func = asc_ellipsis_draw;
-    asc_scene_node_update(node);
+                             : ASC_RENDER_GROUP_2D_OPAQUE
+    );
     return node;
 }
--- a/src/ascension/scene_node.h	Mon Jul 14 21:56:53 2025 +0200
+++ b/src/ascension/scene_node.h	Wed Jul 16 23:27:34 2025 +0200
@@ -111,6 +111,47 @@
  */
 #define ASC_SCENE_NODE_HIDDEN                 0x80000000
 
+
+struct asc_scene_node_init_args {
+    asc_scene_node_draw_func draw_func;
+    asc_scene_node_update_func update_func;
+    asc_scene_node_destroy_func destroy_func;
+    asc_transform rotation;
+    asc_vec3f pos3d;
+    asc_vec3f scale3d;
+    asc_vec3f origin3d;
+    asc_vec2i pos2d;
+    asc_vec2u scale2d;
+    asc_vec2i origin2d;
+    enum AscRenderGroup render_group;
+    const char *name;
+};
+
+/**
+ * Convenience macro to specify draw, update, and destroy func with a common prefix.
+ *
+ * @param prefix prefix for the draw, update, destroy funcs
+ */
+#define ASC_SCENE_NODE_FUNCS(prefix) .draw_func = prefix##_draw, .update_func = prefix##_update, .destroy_func = prefix##_destroy
+
+/**
+ * See asc_scene_node_init().
+ *
+ * @param node the node to initialize
+ * @param args the arguments
+ */
+void asc_scene_node_init_(AscSceneNode *node, struct asc_scene_node_init_args args);
+
+/**
+ * Initializes a node with default arguments.
+ *
+ * The minimum set of mandatory arguments are the draw, update, and destroy functions, as well as the render group.
+ *
+ * @param node the node to initialize
+ * @param ... the arguments
+ */
+#define asc_scene_node_init(node, ...) asc_scene_node_init_(node, (struct asc_scene_node_init_args){__VA_ARGS__})
+
 /**
  * Creates an empty node that may serve as a container for other nodes.
  *
--- a/src/scene_node.c	Mon Jul 14 21:56:53 2025 +0200
+++ b/src/scene_node.c	Wed Jul 16 23:27:34 2025 +0200
@@ -27,12 +27,13 @@
 
 #include "ascension/scene_node.h"
 #include "ascension/context.h"
+#include "ascension/error.h"
 
 #include <cx/tree.h>
 #include <cx/linked_list.h>
 #include <cx/printf.h>
 
-#include "ascension/error.h"
+#include <assert.h>
 
 static CxTreeIterator asc_scene_node_iterator(
         AscSceneNode *node,
@@ -82,6 +83,47 @@
     }
 }
 
+void asc_scene_node_init_(AscSceneNode *node, struct asc_scene_node_init_args args) {
+    if (args.name != NULL) {
+        asc_scene_node_name(node, args.name);
+    }
+    node->render_group = args.render_group;
+    assert(args.update_func != NULL);
+    assert(args.draw_func != NULL);
+    assert(args.destroy_func != NULL);
+    node->update_func = args.update_func;
+    node->destroy_func = args.destroy_func;
+    node->draw_func = args.draw_func;
+
+    if (args.pos2d.x != 0 || args.pos2d.y != 0) {
+        node->position = ASC_VEC3F(args.pos2d.x, args.pos2d.y, ASC_SCENE_2D_DEPTH_OFFSET);
+    } else if (args.pos3d.x != 0 || args.pos3d.y != 0 || args.pos3d.z != 0) {
+        node->position = args.pos3d;
+    }
+
+    if (args.origin2d.x != 0 || args.origin2d.y != 0) {
+        node->origin = ASC_VEC3F(args.origin2d.x, args.origin2d.y, 0.f);
+    } else if (args.origin3d.x != 0 || args.origin3d.y != 0 || args.origin3d.z != 0) {
+        node->origin = args.origin3d;
+    }
+
+    if (args.scale2d.width != 0 && args.scale2d.height != 0) {
+        node->scale = ASC_VEC3F(args.scale2d.width, args.scale2d.height, 1.f);
+    } else if (args.scale3d.x != 0 && args.scale3d.height != 0 && args.scale3d.depth != 0) {
+        node->scale = args.scale3d;
+    } else {
+        node->scale = ASC_VEC3F_1;
+    }
+
+    if (asc_memcmpz(args.rotation, ASC_TRANSFORM_SIZE)) {
+        asc_mat4f_unit(node->rotation);
+    } else {
+        asc_transform_copy(node->rotation, args.rotation);
+    }
+
+    asc_scene_node_update(node);
+}
+
 void asc_scene_node_calculate_transform(AscSceneNode *node) {
     asc_transform temp, temp2;
 
--- a/src/sprite.c	Mon Jul 14 21:56:53 2025 +0200
+++ b/src/sprite.c	Wed Jul 16 23:27:34 2025 +0200
@@ -124,21 +124,17 @@
     sprite->texture_scale.v = ASC_NONZERO_OR(1.f, args.texture_scale_y);
 
     // basic node parameters
-    AscSceneNode *node = (AscSceneNode *) sprite;
-    asc_scene_node_name(node, args.name);
-    node->render_group = args.opaque
+    asc_ptr_cast(AscSceneNode, node, sprite);
+    asc_scene_node_init(node,
+        ASC_SCENE_NODE_FUNCS(asc_sprite),
+        .name = args.name,
+        .render_group = args.opaque
                              ? ASC_RENDER_GROUP_2D_OPAQUE
-                             : ASC_RENDER_GROUP_2D_BLEND;
-    node->update_func = asc_sprite_update;
-    node->destroy_func = asc_sprite_destroy;
-    node->draw_func = asc_sprite_draw;
+                             : ASC_RENDER_GROUP_2D_BLEND,
+        .pos2d = ASC_VEC2I(args.x, args.y),
+        .origin2d = ASC_VEC2I(args.origin_x, args.origin_y)
+    );
 
-    node->position = ASC_VEC3F(args.x, args.y, ASC_SCENE_2D_DEPTH_OFFSET);
-    node->origin = ASC_VEC3F(args.origin_x, args.origin_y, 0);
-    node->scale = ASC_VEC3F_1;
-    asc_mat4f_unit(node->rotation);
-
-    asc_scene_node_update(node);
     return node;
 }
 
--- a/src/text.c	Mon Jul 14 21:56:53 2025 +0200
+++ b/src/text.c	Wed Jul 16 23:27:34 2025 +0200
@@ -70,7 +70,11 @@
 
     // Render text onto a surface
     TTF_Font *font = asc_font_load(text->font);
-    if (font == NULL) return;
+    if (font == NULL) {
+        // cannot load font - hide the text node to avoid errors when trying to draw
+        asc_set_flag(node->flags, ASC_SCENE_NODE_HIDDEN);
+        return;
+    }
     static int alignments[] = {
             TTF_WRAPPED_ALIGN_LEFT,
             TTF_WRAPPED_ALIGN_CENTER,
@@ -141,17 +145,15 @@
 
 AscSceneNode *asc_text_create(struct asc_text_create_args args) {
     AscText *text = cxZallocDefault(sizeof(AscText));
-    AscSceneNode *node = &text->base;
+    asc_ptr_cast(AscSceneNode, node, text);
 
     // node properties
-    asc_scene_node_name(node, args.name);
-    node->render_group = ASC_RENDER_GROUP_2D_BLEND;
-    node->destroy_func = asc_text_destroy;
-    node->update_func = asc_text_update;
-    node->draw_func = asc_text_draw;
-    node->position = ASC_VEC3F(args.x, args.y, ASC_SCENE_2D_DEPTH_OFFSET);
-    node->scale = ASC_VEC3F_1;
-    asc_mat4f_unit(node->rotation);
+    asc_scene_node_init(node,
+        ASC_SCENE_NODE_FUNCS(asc_text),
+        .name = args.name,
+        .render_group = ASC_RENDER_GROUP_2D_BLEND,
+        .pos2d = ASC_VEC2I(args.x, args.y)
+    );
 
     // text properties
     node->flags = args.alignment; // use flags variable to save some space
@@ -171,6 +173,7 @@
     // mesh will be created in the update func
     text->texture = cxMallocDefault(sizeof(AscTexture));
     asc_texture_init_rectangle(text->texture, 1);
+    // TODO: check why we can't just wait for the first normal update to happen
     asc_text_update(node);
 
     return node;

mercurial