Wed, 16 Jul 2025 23:27:34 +0200
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;