Sun, 27 Apr 2025 15:17:12 +0200
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();