remove separate vectors for position, rotation, scale from scene node

Mon, 07 Jul 2025 22:51:06 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 07 Jul 2025 22:51:06 +0200
changeset 189
2c063b225183
parent 188
5ece6e3acc84
child 190
7f72375bedc8

remove separate vectors for position, rotation, scale from scene node

src/2d.c file | annotate | diff | comparison | revisions
src/ascension/scene_node.h file | annotate | diff | comparison | revisions
src/ascension/transform.h file | annotate | diff | comparison | revisions
src/scene.c 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
test/snake/snake.c file | annotate | diff | comparison | revisions
--- a/src/2d.c	Mon Jul 07 22:41:48 2025 +0200
+++ b/src/2d.c	Mon Jul 07 22:51:06 2025 +0200
@@ -157,14 +157,15 @@
 AscSceneNode *asc_rectangle_create(struct asc_rectangle_create_args args) {
     AscRectangle *rectangle = cxZallocDefault(sizeof(AscRectangle));
 
+    float pos_x, pos_y;
     if (args.bounds.size.width + args.bounds.size.height > 0) {
-        rectangle->node.position.x = (float) args.bounds.pos.x;
-        rectangle->node.position.y = (float) args.bounds.pos.y;
+        pos_x = (float) args.bounds.pos.x;
+        pos_y = (float) args.bounds.pos.y;
         rectangle->size.width = (float) args.bounds.size.width;
         rectangle->size.height = (float) args.bounds.size.height;
     } else {
-        rectangle->node.position.x = (float) args.x;
-        rectangle->node.position.y = (float) args.y;
+        pos_x = (float) args.x;
+        pos_y = (float) args.y;
         rectangle->size.width = (float) args.width;
         rectangle->size.height = (float) args.height;
     }
@@ -187,8 +188,9 @@
     }
 
     AscSceneNode *node = &rectangle->node;
-    node->position.z = ASC_SCENE_2D_DEPTH_OFFSET;
-    node->scale = ASC_VEC3F_1;
+    asc_transform_identity(node->transform);
+    asc_transform_translate3f(node->transform,
+        ASC_VEC3F(pos_x, pos_y, ASC_SCENE_2D_DEPTH_OFFSET));
     node->render_group = asc_context.ink.alpha < 255
                              ? ASC_RENDER_GROUP_2D_BLEND
                              : ASC_RENDER_GROUP_2D_OPAQUE;
@@ -308,9 +310,10 @@
 AscSceneNode *asc_ellipsis_create(struct asc_ellipsis_create_args args) {
     AscEllipsis *ellipsis = cxZallocDefault(sizeof(AscEllipsis));
 
+    float pos_x, pos_y;
     if (args.bounds.size.width + args.bounds.size.height > 0) {
-        ellipsis->node.position.x = (float) args.bounds.pos.x;
-        ellipsis->node.position.y = (float) args.bounds.pos.y;
+        pos_x = (float) args.bounds.pos.x;
+        pos_y = (float) args.bounds.pos.y;
         ellipsis->radii.x = (float) args.bounds.size.width / 2.f;
         ellipsis->radii.y = (float) args.bounds.size.height / 2.f;
     } else {
@@ -318,8 +321,8 @@
         const unsigned cy = ASC_NONZERO_OR(args.y, args.center.y);
         const unsigned rx = ASC_NONZERO_OR(args.radius, args.radius_x);
         const unsigned ry = ASC_NONZERO_OR(args.radius, args.radius_y);
-        ellipsis->node.position.x = (float) (cx-rx);
-        ellipsis->node.position.y = (float) (cy-ry);
+        pos_x = (float) (cx-rx);
+        pos_y = (float) (cy-ry);
         ellipsis->radii.x = (float) rx;
         ellipsis->radii.y = (float) ry;
     }
@@ -341,8 +344,9 @@
     }
 
     AscSceneNode *node = &ellipsis->node;
-    node->position.z = ASC_SCENE_2D_DEPTH_OFFSET;
-    node->scale = ASC_VEC3F_1;
+    asc_transform_identity(node->transform);
+    asc_transform_translate3f(node->transform,
+        ASC_VEC3F(pos_x, pos_y, ASC_SCENE_2D_DEPTH_OFFSET));
     node->render_group = asc_context.ink.alpha < 255
                              ? ASC_RENDER_GROUP_2D_BLEND
                              : ASC_RENDER_GROUP_2D_OPAQUE;
--- a/src/ascension/scene_node.h	Mon Jul 07 22:41:48 2025 +0200
+++ b/src/ascension/scene_node.h	Mon Jul 07 22:51:06 2025 +0200
@@ -64,9 +64,6 @@
     asc_scene_node_destroy_func destroy_func;
     asc_scene_node_update_func update_func;
     asc_scene_node_draw_func draw_func;
-    asc_vec3f position;
-    asc_vec3f rotation;
-    asc_vec3f scale;
     asc_transform transform;
     asc_transform world_transform;
     enum AscRenderGroup render_group;
@@ -99,16 +96,6 @@
  */
 #define ASC_SCENE_NODE_TRANSFORM_UPDATED      0x08000000
 /**
- * Set when the node's transform is directly manipulated via matrix operations.
- * When this flag is set, position/rotation/scale values won't be used to
- * calculate the transform matrix.
- */
-#define ASC_SCENE_NODE_CUSTOM_TRANSFORM       0x10000000
-/**
- * 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
@@ -123,7 +110,7 @@
  */
 AscSceneNode *asc_scene_node_empty(void);
 
-// TODO: create a common init-function that all "subclasses" use, which also debug-logs the assigned name
+// TODO: issue #695 - create a common init-function that all "subclasses" use, which also debug-logs the assigned name
 
 /**
  * Unlinks the node from its parent and frees the entire subtree.
@@ -185,76 +172,37 @@
  */
 #define ASC_SCENE_2D_DEPTH_OFFSET 0.0078125f
 
-ASC_TRANFORM_FUNC void asc_set_position(AscSceneNode *node, float x, float y, float z) {
-    node->position.x = x;
-    node->position.y = y;
-    node->position.z = z;
-    asc_clear_flag(node->flags, ASC_SCENE_NODE_CUSTOM_TRANSFORM);
-    asc_node_update_transform(node);
-}
-
-ASC_TRANFORM_FUNC void asc_set_position2d(AscSceneNode *node, int x, int y) {
-    node->position.x = (float)x;
-    node->position.y = (float)y;
-    asc_clear_flag(node->flags, ASC_SCENE_NODE_CUSTOM_TRANSFORM);
-    asc_node_update_transform(node);
-}
-
-ASC_TRANFORM_FUNC asc_vec2i asc_get_position2d(AscSceneNode *node) {
-    return ASC_VEC2I(node->position.x, node->position.y);
-}
-
-ASC_TRANFORM_FUNC void asc_set_rotation(AscSceneNode *node, float pitch, float yaw, float roll) {
-    node->rotation.pitch = pitch;
-    node->rotation.yaw = yaw;
-    node->rotation.roll = roll;
-    asc_clear_flag(node->flags, ASC_SCENE_NODE_CUSTOM_TRANSFORM);
-    asc_node_update_transform(node);
-}
-
-ASC_TRANFORM_FUNC void asc_set_scale(AscSceneNode *node, float width, float height, float depth) {
-    node->scale.width = width;
-    node->scale.height = height;
-    node->scale.depth = depth;
-    asc_clear_flag(node->flags, ASC_SCENE_NODE_CUSTOM_TRANSFORM);
-    asc_node_update_transform(node);
-}
-
-
 /**
- * Directly modifies the transformation matrix by multiplying it with another matrix.
- * This will set the node to use the custom transform mode.
+ * Applies an affine transformation to a scene node.
  *
  * @param node the node to modify
- * @param matrix the matrix to multiply with the current transform
+ * @param matrix the matrix to multiply with the current transformation matrix
  */
-ASC_TRANFORM_FUNC void asc_transform_multiply(AscSceneNode *node, const asc_transform matrix) {
-    asc_set_flag(node->flags, ASC_SCENE_NODE_CUSTOM_TRANSFORM);
+ASC_TRANFORM_FUNC void asc_node_transform_apply(AscSceneNode *node, const asc_transform matrix) {
     asc_mat4f_mulst(node->transform, node->transform, matrix);
     asc_node_update_transform(node);
 }
 
 /**
- * Directly sets the transformation matrix.
- * This will set the node to use the custom transform mode.
+ * Overwrites the current local transformation matrix.
  *
  * @param node the node to modify
- * @param matrix the matrix to set as the transform
+ * @param matrix the matrix to set as the new local transformation matrix
  */
-ASC_TRANFORM_FUNC void asc_transform_set_matrix(AscSceneNode *node, const asc_transform matrix) {
-    asc_set_flag(node->flags, ASC_SCENE_NODE_CUSTOM_TRANSFORM);
-    memcpy(node->transform, matrix, sizeof(asc_transform));
+ASC_TRANFORM_FUNC void asc_node_transform_set(AscSceneNode *node, const asc_transform matrix) {
+    asc_transform_copy(node->transform, matrix);
     asc_node_update_transform(node);
 }
 
 /**
- * Resets the node to use component-based transformation.
- * This will recalculate the transform matrix from position, rotation, and scale.
+ * Resets the node to use an identity transformation matrix.
+ *
+ * This is, for example, useful when you want to recalculate a chain of transformations from scratch.
  *
- * @param node the node to modify
+ * @param node the node for which to reset the local transformation
  */
-ASC_TRANFORM_FUNC void asc_transform_reset_to_components(AscSceneNode *node) {
-    asc_clear_flag(node->flags, ASC_SCENE_NODE_CUSTOM_TRANSFORM);
+ASC_TRANFORM_FUNC void asc_node_transform_reset(AscSceneNode *node) {
+    asc_transform_identity(node->transform);
     asc_node_update_transform(node);
 }
 
--- a/src/ascension/transform.h	Mon Jul 07 22:41:48 2025 +0200
+++ b/src/ascension/transform.h	Mon Jul 07 22:51:06 2025 +0200
@@ -42,14 +42,10 @@
 
 
 ASC_TRANFORM_FUNC void asc_transform_identity(asc_transform transform) {
-    memset(transform, 0, ASC_TRANSFORM_SIZE);
-    transform[asc_mat4_index(0, 0)] = 1;
-    transform[asc_mat4_index(1, 1)] = 1;
-    transform[asc_mat4_index(2, 2)] = 1;
-    transform[asc_mat4_index(3, 3)] = 1;
+    asc_mat4f_unit(transform);
 }
 
-ASC_TRANFORM_FUNC void asc_transform_copy(asc_transform dest, asc_transform src) {
+ASC_TRANFORM_FUNC void asc_transform_copy(asc_transform dest, const asc_transform src) {
     memcpy(dest, src, ASC_TRANSFORM_SIZE);
 }
 
--- a/src/scene.c	Mon Jul 07 22:41:48 2025 +0200
+++ b/src/scene.c	Mon Jul 07 22:51:06 2025 +0200
@@ -149,35 +149,31 @@
 
         // TODO: implement culling
 
-        // check if geometry needs update
+        // clear information flags from the previous frame
         asc_clear_flag(node->flags,
                        ASC_SCENE_NODE_GRAPHICS_UPDATED
                        | ASC_SCENE_NODE_TRANSFORM_UPDATED);
+        // check if geometry needs an update
         if (asc_test_flag(node->flags, ASC_SCENE_NODE_UPDATE_GRAPHICS)) {
             asc_set_flag(node->flags, ASC_SCENE_NODE_GRAPHICS_UPDATED);
             asc_clear_flag(node->flags, ASC_SCENE_NODE_UPDATE_GRAPHICS);
             assert(node->update_func != NULL);
             node->update_func(node);
         }
+        // check if transform needs an update
         if (asc_test_flag(node->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) {
             asc_set_flag(node->flags, ASC_SCENE_NODE_TRANSFORM_UPDATED);
             asc_clear_flag(node->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM);
-
-            // Only recalculate from components if not using custom transform
-            if (!asc_test_flag(node->flags, ASC_SCENE_NODE_CUSTOM_TRANSFORM)) {
-                asc_transform_from_vec3f(
+            if (node->parent == scene->root) {
+                // skip unnecessary multiplication with unity matrix
+                asc_transform_copy(node->world_transform, node->transform);
+            } else {
+                asc_mat4f_mulst(
+                        node->world_transform,
                         node->transform,
-                        node->position,
-                        node->scale,
-                        node->rotation
+                        node->parent->world_transform
                 );
             }
-
-            asc_mat4f_mulst(
-                    node->world_transform,
-                    node->transform,
-                    node->parent->world_transform
-            );
         }
 
         // add to render group
--- a/src/scene_node.c	Mon Jul 07 22:41:48 2025 +0200
+++ b/src/scene_node.c	Mon Jul 07 22:51:06 2025 +0200
@@ -48,7 +48,6 @@
 AscSceneNode *asc_scene_node_empty(void) {
     AscSceneNode *node = cxZallocDefault(sizeof(AscSceneNode));
     node->render_group = ASC_RENDER_GROUP_NONE;
-    node->scale = ASC_VEC3F_1;
     asc_transform_identity(node->transform);
     asc_transform_identity(node->world_transform);
     return node;
--- a/src/sprite.c	Mon Jul 07 22:41:48 2025 +0200
+++ b/src/sprite.c	Mon Jul 07 22:51:06 2025 +0200
@@ -133,8 +133,9 @@
     node->destroy_func = asc_sprite_destroy;
     node->draw_func = asc_sprite_draw;
 
-    node->position = ASC_VEC3F(args.x, args.y, ASC_SCENE_2D_DEPTH_OFFSET);
-    node->scale = ASC_VEC3F_1;
+    asc_transform_identity(node->transform);
+    asc_transform_translate3f(node->transform,
+        ASC_VEC3F(args.x, args.y, ASC_SCENE_2D_DEPTH_OFFSET));
 
     asc_node_update(node);
     return node;
--- a/src/text.c	Mon Jul 07 22:41:48 2025 +0200
+++ b/src/text.c	Mon Jul 07 22:51:06 2025 +0200
@@ -57,7 +57,7 @@
     }
     if (asc_test_flag(text->base.data.flags, ASC_TEXT_CENTERED_FLAG)) {
         unsigned short newoffx = surface->w / 2;
-        node->position.x = node->position.x + (float)(text->offx - newoffx);
+        asc_transform_translate2f(node->transform, ASC_VEC2F(text->offx - newoffx, 0));
         text->offx = newoffx;
     }
 
@@ -102,8 +102,9 @@
     node->destroy_func = asc_text_destroy;
     node->update_func = asc_text_update;
     node->draw_func = asc_sprite_draw;
-    node->position = ASC_VEC3F(args.x, args.y, ASC_SCENE_2D_DEPTH_OFFSET);
-    node->scale = ASC_VEC3F_1;
+    asc_transform_identity(node->transform);
+    asc_transform_translate3f(node->transform,
+        ASC_VEC3F(args.x, args.y, ASC_SCENE_2D_DEPTH_OFFSET));
 
     // text properties
     node->flags = args.alignment; // use flags variable to save some space
--- a/test/snake/snake.c	Mon Jul 07 22:41:48 2025 +0200
+++ b/test/snake/snake.c	Mon Jul 07 22:51:06 2025 +0200
@@ -70,11 +70,13 @@
     if (asc_test_flag(node->flags, ASC_SCENE_NODE_GRAPHICS_UPDATED) || asc_active_window->resized) {
         asc_vec2u bottom_right = asc_active_window->dimensions;
         asc_vec2u text_size = ((AscText*)node)->dimension;
-        asc_set_position2d(
-                node,
-                (int)bottom_right.x - (int)text_size.width - 10,
-                (int)bottom_right.y - (int)text_size.height - 10
-        );
+        asc_transform new_transform;
+        asc_transform_identity(new_transform);
+        asc_transform_translate2f(new_transform, ASC_VEC2F(
+                (int) bottom_right.x - (int) text_size.width - 10,
+                (int) bottom_right.y - (int) text_size.height - 10
+        ));
+        asc_node_transform_set(node, new_transform);
     }
 }
 

mercurial