add aspect-ration independent rendering default tip

Sun, 27 Apr 2025 15:17:12 +0200

author
Mike Becker <universe@uap-core.de>
date
Sun, 27 Apr 2025 15:17:12 +0200
changeset 99
ac143db979dc
parent 98
83bec26453a8

add aspect-ration independent rendering

src/ascension/camera.h file | annotate | diff | comparison | revisions
src/ascension/datatypes.h file | annotate | diff | comparison | revisions
src/ascension/scene.h file | annotate | diff | comparison | revisions
src/camera.c file | annotate | diff | comparison | revisions
src/scene.c file | annotate | diff | comparison | revisions
src/window.c file | annotate | diff | comparison | revisions
test/snake/snake.c file | annotate | diff | comparison | revisions
--- a/src/ascension/camera.h	Sun Apr 27 13:27:27 2025 +0200
+++ b/src/ascension/camera.h	Sun Apr 27 15:17:12 2025 +0200
@@ -30,14 +30,34 @@
 
 #include "datatypes.h"
 
+typedef asc_recti(*asc_camera_viewport_update_func)(asc_vec2i window_size);
+
 typedef struct AscCamera AscCamera;
 
 struct AscCamera {
     asc_mat4f projection;
     asc_mat4f view;
+    asc_recti viewport;
+    /**
+     * Function that gets invoked whenever the window sized changed.
+     * Calculates the new drawing viewport.
+     * If @c NULL, the entire window will be used.
+     */
+    asc_camera_viewport_update_func viewport_update_func;
 };
 
 __attribute__((__nonnull__))
 void asc_camera_ortho(AscCamera *camera, asc_recti rect);
 
+/**
+ * Shorter version of updating an orthographic camera which assumes the top right corner at (0,0).
+ *
+ * @attention The camera MUST have been initialized with asc_camera_ortho() at position (0,0).
+ *
+ * @param camera the camera
+ * @param size the new size
+ */
+__attribute__((__nonnull__))
+void asc_camera_ortho_update_size(AscCamera *camera, asc_vec2i size);
+
 #endif //ASCENSION_CAMERA_H
--- a/src/ascension/datatypes.h	Sun Apr 27 13:27:27 2025 +0200
+++ b/src/ascension/datatypes.h	Sun Apr 27 15:17:12 2025 +0200
@@ -161,6 +161,24 @@
     mat[asc_mat4_index(3,3)] = 1;
 }
 
+/**
+ * Shorter version of updating an orthographic matrix which assumes the top right corner at (0,0).
+ *
+ * @attention The matrix MUST have been properly initialized with asc_mat4f_ortho() with left=0 and top=0, first!
+ *
+ * @param mat the matrix
+ * @param width the new width (right coordinate)
+ * @param height the new height (bottom coordinate)
+ */
+static inline void asc_mat4f_ortho_update_size(
+        asc_mat4f mat,
+        float width,
+        float height
+) {
+    mat[asc_mat4_index(0,0)] = 2.f / width;
+    mat[asc_mat4_index(1,1)] = -2.f / height;
+}
+
 static inline void asc_mat4f_mulst(
     asc_mat4f dest,
     asc_mat4f const left,
--- a/src/ascension/scene.h	Sun Apr 27 13:27:27 2025 +0200
+++ b/src/ascension/scene.h	Sun Apr 27 15:17:12 2025 +0200
@@ -64,10 +64,9 @@
  * If @p scene is not initialized, drawing is skipped.
  *
  * @param scene the scene graph
- * @param viewport the window viewport the scene shall be drawn to
  */
 __attribute__((__nonnull__))
-void asc_scene_draw(AscScene *scene, asc_recti viewport);
+void asc_scene_draw(AscScene *scene);
 
 __attribute__((__nonnull__))
 void asc_scene_add_node(AscScene *scene, AscSceneNode *node);
--- a/src/camera.c	Sun Apr 27 13:27:27 2025 +0200
+++ b/src/camera.c	Sun Apr 27 15:17:12 2025 +0200
@@ -35,3 +35,7 @@
     float bottom = top + (float) rect.size.height;
     asc_mat4f_ortho(camera->projection, left, right, bottom, top, -1, 1);
 }
+
+void asc_camera_ortho_update_size(AscCamera *camera, asc_vec2i size) {
+    asc_mat4f_ortho_update_size(camera->projection, (float)size.width, (float)size.height);
+}
--- a/src/scene.c	Sun Apr 27 13:27:27 2025 +0200
+++ b/src/scene.c	Sun Apr 27 15:17:12 2025 +0200
@@ -57,23 +57,30 @@
     asc_scene_node_free(scene->root);
 }
 
-void asc_scene_draw(AscScene *scene, asc_recti viewport) {
+void asc_scene_draw(AscScene *scene) {
     if (scene->root == NULL) return;
 
+    // if the window resized, we must update the viewport
+    if (asc_active_window->resized) {
+        if (scene->camera.viewport_update_func == NULL) {
+            // this assumes the viewport was initialized with zeros!
+            scene->camera.viewport.size = asc_active_window->dimensions;
+        } else {
+            scene->camera.viewport = scene->camera.viewport_update_func(asc_active_window->dimensions);
+        }
+    }
+
     // create render groups
     CxList *render_group[ASC_RENDER_GROUP_COUNT];
     cx_for_n(i, ASC_RENDER_GROUP_COUNT) {
         render_group[i] = cxArrayListCreateSimple(CX_STORE_POINTERS, 32);
     }
 
-    // skip the root node deliberately; we know it's just the container
+    // update the scene graph and add nodes to their render groups
     CxTreeVisitor iter = cx_tree_visitor(scene->root,
-            offsetof(AscSceneNode, children),
-            offsetof(AscSceneNode, next)
+        offsetof(AscSceneNode, children),
+        offsetof(AscSceneNode, next)
     );
-    cxIteratorNext(iter);
-
-    // update the children and add them to the render groups
     cx_foreach(AscSceneNode*, node, iter) {
         node->depth = iter.depth;
 
@@ -126,10 +133,10 @@
 
     // set the viewport (in OpenGL we need to invert the Y axis)
     glViewport(
-            viewport.pos.x,
-            -viewport.pos.y,
-            viewport.size.width,
-            viewport.size.height
+            scene->camera.viewport.pos.x,
+            -scene->camera.viewport.pos.y,
+            scene->camera.viewport.size.width,
+            scene->camera.viewport.size.height
     );
 
     // -------------------------
--- a/src/window.c	Sun Apr 27 13:27:27 2025 +0200
+++ b/src/window.c	Sun Apr 27 15:17:12 2025 +0200
@@ -77,6 +77,9 @@
 
     if (asc_gl_context_initialize(&window->glctx, window->window, &settings->glsettings)) {
         asc_scene_init(&window->ui);
+        asc_camera_ortho(&window->ui.camera, (asc_recti){
+            0, 0, window->dimensions.width, window->dimensions.height
+        });
         asc_dprintf("Window %u initialized at index %u", window->id, index);
         asc_context.active_window = index;
     } else {
@@ -149,12 +152,11 @@
     glClear(GL_COLOR_BUFFER_BIT);
 
     // Draw all scenes
-    asc_recti viewport = {0, 0, window_width, window_height};
     for (unsigned int i = 0; i < ASC_MAX_SCENES; i++) {
-        asc_scene_draw(&window->scenes[i], viewport);
+        asc_scene_draw(&window->scenes[i]);
     }
-    asc_camera_ortho(&window->ui.camera, viewport);
-    asc_scene_draw(&window->ui, viewport);
+    asc_camera_ortho_update_size(&window->ui.camera, (asc_vec2i){window_width, window_height});
+    asc_scene_draw(&window->ui);
 
     // Swap Buffers
     SDL_GL_SwapWindow(window->window);
--- a/test/snake/snake.c	Sun Apr 27 13:27:27 2025 +0200
+++ b/test/snake/snake.c	Sun Apr 27 15:17:12 2025 +0200
@@ -108,6 +108,28 @@
     // TODO: return something
 }
 
+static asc_recti update_viewport_for_window_resize(asc_vec2i window_size) {
+    // Compute scaling and offsets
+    int viewport_size, offset_x = 0, offset_y = 0;
+    if (window_size.width > window_size.height) {
+        // Wider window: letterbox (black bars on top/bottom)
+        offset_x = (window_size.width - window_size.height) / 2;
+        viewport_size = window_size.height;
+    } else {
+        // Taller window: pillarbox (black bars on sides)
+        offset_y = (window_size.height - window_size.width) / 2;
+        viewport_size = window_size.width;
+    }
+
+    // Set the viewport to the scaled and centered region
+    return (asc_recti){
+        offset_x,
+        offset_y,
+        viewport_size,
+        viewport_size
+    };
+}
+
 int main(int argc, char** argv) {
     asc_context_initialize();
     if (asc_has_error()) {
@@ -129,8 +151,8 @@
 
     // initialize the main scene (a 500x500 game field)
     asc_scene_init(MAIN_SCENE);
-    // TODO: this is intentionally bullshit at the moment to force me to care about aspect ratio
     asc_camera_ortho(&MAIN_SCENE->camera, (asc_recti){0, 0, 500, 500});
+    MAIN_SCENE->camera.viewport_update_func = update_viewport_for_window_resize;
 
     // load textures
     init_textures();

mercurial