add a frame to the main scene + implement the necessary asc_rectangle_set_bounds() function default tip

Sat, 02 Aug 2025 21:43:39 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 02 Aug 2025 21:43:39 +0200
changeset 244
ceab8a9f0366
parent 243
b06168253818

add a frame to the main scene + implement the necessary asc_rectangle_set_bounds() function

src/2d.c file | annotate | diff | comparison | revisions
src/ascension/2d.h file | annotate | diff | comparison | revisions
src/ascension/util.h file | annotate | diff | comparison | revisions
test/snake/snake.c file | annotate | diff | comparison | revisions
--- a/src/2d.c	Sat Aug 02 15:45:43 2025 +0200
+++ b/src/2d.c	Sat Aug 02 21:43:39 2025 +0200
@@ -29,6 +29,7 @@
 
 #include "ascension/constants.h"
 #include "ascension/shader.h"
+#include "ascension/util.h"
 
 #include <assert.h>
 
@@ -129,6 +130,7 @@
 
     float pos_x, pos_y;
     if (args.bounds.size.width + args.bounds.size.height > 0) {
+        // TODO: this is probably bugged, because it does not respect the origin
         pos_x = (float) args.bounds.pos.x;
         pos_y = (float) args.bounds.pos.y;
         rectangle->size.width = (float) args.bounds.size.width;
@@ -170,6 +172,27 @@
     return node;
 }
 
+void asc_rectangle_set_bounds(AscRectangle *rect, asc_rect bounds) {
+    // TODO: check how this harmonizes with a different origin
+    float x = (float) bounds.pos.x;
+    float y = (float) bounds.pos.y;
+    float width = (float) bounds.size.width;
+    float height = (float) bounds.size.height;
+    bool update_transform = false;
+    bool update_mesh = false;
+    update_transform |= asc_util_check_and_set(rect->node.position.x, x);
+    update_transform |= asc_util_check_and_set(rect->node.position.y, y);
+    update_mesh |= asc_util_check_and_set(rect->size.width, width);
+    update_mesh |= asc_util_check_and_set(rect->size.height, height);
+    if (update_mesh) {
+        asc_scene_node_update(&rect->node);
+    }
+    if (update_transform) {
+        asc_scene_node_update_transform(&rect->node);
+    }
+}
+
+
 static void asc_ellipsis_destroy(AscSceneNode *node) {
     asc_ptr_cast(AscEllipsis, ellipsis, node);
     asc_mesh_destroy(&ellipsis->mesh);
--- a/src/ascension/2d.h	Sat Aug 02 15:45:43 2025 +0200
+++ b/src/ascension/2d.h	Sat Aug 02 21:43:39 2025 +0200
@@ -80,6 +80,17 @@
 
 #define asc_rectangle(...) asc_rectangle_create((struct asc_rectangle_create_args) { __VA_ARGS__ })
 
+/**
+ * Sets new bounds of the rectangle.
+ *
+ * Triggers a mesh-recalculation only if the new bounds are unequal to the current bounds.
+ *
+ * @param rect the rectangle
+ * @param bounds the new bounds of the rectangle
+ */
+void asc_rectangle_set_bounds(AscRectangle *rect, asc_rect bounds);
+
+
 typedef struct asc_ellipsis_s {
     AscSceneNode node;
     AscMesh mesh;
--- a/src/ascension/util.h	Sat Aug 02 15:45:43 2025 +0200
+++ b/src/ascension/util.h	Sat Aug 02 21:43:39 2025 +0200
@@ -32,4 +32,20 @@
 
 cxmutstr asc_util_gen_name(void *obj);
 
+#define asc_util_check_and_set_impl if (*var == new_value) return false; *var = new_value; return true
+static inline bool asc_util_check_and_set_float(float *var, float new_value) {
+    asc_util_check_and_set_impl;
+}
+static inline bool asc_util_check_and_set_int(int *var, int new_value) {
+    asc_util_check_and_set_impl;
+}
+static inline bool asc_util_check_and_set_unsigned(unsigned *var, unsigned new_value) {
+    asc_util_check_and_set_impl;
+}
+
+#define asc_util_check_and_set(v, x) _Generic(x, \
+    float: asc_util_check_and_set_float, \
+    int: asc_util_check_and_set_int, \
+    unsigned: asc_util_check_and_set_unsigned)(&(v), x)
+
 #endif //ASC_UTIL_H
--- a/test/snake/snake.c	Sat Aug 02 15:45:43 2025 +0200
+++ b/test/snake/snake.c	Sat Aug 02 21:43:39 2025 +0200
@@ -96,11 +96,20 @@
 
 static void backdrop_scale(AscBehavior *behavior) {
     // scale the backdrop to the size of the window
-    if (asc_active_window->resized) {
-        asc_ptr_cast(AscSprite, sprite, behavior->node);
-        asc_vec2u window_size = asc_active_window->dimensions;
-        asc_sprite_set_size(sprite, window_size);
-    }
+    if (!asc_active_window->resized) return;
+    asc_ptr_cast(AscSprite, sprite, behavior->node);
+    asc_vec2u window_size = asc_active_window->dimensions;
+    asc_sprite_set_size(sprite, window_size);
+}
+
+static void main_scene_frame_scale(AscBehavior *behavior) {
+    // TODO: we cannot skip this behavior when window is resized,
+    //       because the viewport is updated after executing all behaviors
+    //       and then the resized flag is cleared already.
+    //       A possible solution is to add something like a post-rendering behavior.
+    //       Another solution would be a viewport-changed-event (once we implement events)
+    asc_ptr_cast(AscRectangle, frame, behavior->node);
+    asc_rectangle_set_bounds(frame, MAIN_SCENE->camera.viewport);
 }
 
 static void backdrop_create(void) {
@@ -112,6 +121,12 @@
     );
     asc_behavior_add(node, .func = backdrop_scale);
     asc_scene_add_node(BACKDROP_SCENE, node);
+
+    // also add a frame for the main scene
+    // add this to the UI layer so that the border size does not scale
+    AscSceneNode *frame = asc_rectangle(.thickness = 2, .color = ASC_RGB(66, 142, 161));
+    asc_behavior_add(frame, .func = main_scene_frame_scale);
+    asc_ui_add_node(frame);
 }
 
 static void fps_counter_update(AscBehavior *behavior) {

mercurial