make camera independent of the scene

Sun, 16 Nov 2025 22:00:13 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 16 Nov 2025 22:00:13 +0100
changeset 287
359eaf2a8bd2
parent 286
26a41832c81d
child 288
8796f03aac26

make camera independent of the scene

demo/snake/snake.c file | annotate | diff | comparison | revisions
src/Makefile file | annotate | diff | comparison | revisions
src/ascension/camera.h file | annotate | diff | comparison | revisions
src/ascension/scene.h file | annotate | diff | comparison | revisions
src/ascension/window.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
--- a/demo/snake/snake.c	Sun Nov 16 21:12:20 2025 +0100
+++ b/demo/snake/snake.c	Sun Nov 16 22:00:13 2025 +0100
@@ -139,7 +139,7 @@
 static void main_scene_frame_scale(AscBehavior *behavior) {
     if (!asc_active_window->resized) return;
     asc_ptr_cast(AscRectangle, frame, behavior->node);
-    asc_rectangle_set_bounds(frame, MAIN_SCENE->camera.viewport);
+    asc_rectangle_set_bounds(frame, asc_scene_viewport(MAIN_SCENE));
 }
 
 static void backdrop_create(void) {
@@ -196,7 +196,7 @@
 
     AscSceneNode *node = behavior->node;
     // center the "game over" text in the game field viewport
-    const asc_rect main_scene_viewport = MAIN_SCENE->camera.viewport;
+    const asc_rect main_scene_viewport = asc_scene_viewport(MAIN_SCENE);
     asc_scene_node_set_position2f(node, ASC_VEC2F(
         main_scene_viewport.pos.x + main_scene_viewport.size.x / 2.f,
         main_scene_viewport.pos.y + main_scene_viewport.size.y / 2.f - 36
@@ -543,32 +543,35 @@
     asc_window_initialize(0, asc_gl_context_settings_default(4, 0));
     asc_window_set_title(0, "Snake");
     asc_window_set_size(0, asc_vec2_ftou(
-        asc_vec2f_scale(ASC_VEC2F(1000+HUD_WIDTH, 1000), asc_ui_scale_auto())));
+        asc_vec2f_scale(ASC_VEC2F(600+HUD_WIDTH, 600), asc_ui_scale_auto())));
     asc_window_center(0);
 
     // load textures
     textures_init();
 
     // initialize backdrop scene
-    asc_scene_init(BACKDROP_SCENE, "backdrop",
+    AscCamera backdrop_camera;
+    asc_camera_init(&backdrop_camera,
         .type = ASC_CAMERA_ORTHO,
         .projection_update_func = asc_camera_ortho_update_size
     );
+    asc_scene_init(BACKDROP_SCENE, "backdrop", &backdrop_camera);
     backdrop_create();
 
-    // Initialize main scene
-    asc_scene_init(MAIN_SCENE, "main",
+    // Initialize the main scene
+    const unsigned initial_view_distance = 10;
+    AscCamera main_camera;
+    asc_camera_init(&main_camera,
         .type = ASC_CAMERA_ORTHO,
-        .ortho.rect = ASC_RECT(
-            -GAME_FIELD_TILE_SIZE,
-            -GAME_FIELD_TILE_SIZE,
-            (GAME_FIELD_SIZE+2)*GAME_FIELD_TILE_SIZE,
-            (GAME_FIELD_SIZE+2)*GAME_FIELD_TILE_SIZE
+        .ortho.rect = ASC_RECT(0, 0,
+            initial_view_distance*2*GAME_FIELD_TILE_SIZE,
+            initial_view_distance*2*GAME_FIELD_TILE_SIZE
         ),
         .viewport_clear = true,
         .clear_color = ASC_RGBi(0, 32, 16),
         .viewport_update_func = main_scene_viewport_update
     );
+    asc_scene_init(MAIN_SCENE, "main", &main_camera);
 
     // create the fps counter
     AscSceneNode *fps_counter = fps_counter_create();
--- a/src/Makefile	Sun Nov 16 21:12:20 2025 +0100
+++ b/src/Makefile	Sun Nov 16 22:00:13 2025 +0100
@@ -110,7 +110,8 @@
  ascension/datatypes.h ascension/window.h ascension/glcontext.h \
  ascension/scene.h ascension/scene_node.h ascension/transform.h \
  ascension/camera.h ascension/input.h ascension/scene.h \
- ascension/behavior.h ascension/shader.h ascension/util.h
+ ascension/camera.h ascension/behavior.h ascension/shader.h \
+ ascension/util.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
--- a/src/ascension/camera.h	Sun Nov 16 21:12:20 2025 +0100
+++ b/src/ascension/camera.h	Sun Nov 16 22:00:13 2025 +0100
@@ -38,6 +38,10 @@
 struct asc_camera_s {
     asc_mat4f projection;
     asc_mat4f view;
+    /**
+     * Does not need to be initialized.
+     * Will be updated every frame.
+     */
     asc_rect viewport;
     /**
      * Function that gets invoked whenever the window sized changed.
--- a/src/ascension/scene.h	Sun Nov 16 21:12:20 2025 +0100
+++ b/src/ascension/scene.h	Sun Nov 16 22:00:13 2025 +0100
@@ -29,13 +29,17 @@
 #define ASCENSION_SCENE_H
 
 #include "scene_node.h"
-#include "camera.h"
 
 #include <cx/list.h>
 #include <cx/string.h>
 
+#include "camera.h"
+
+// forward declare camera
+typedef struct asc_camera_s AscCamera;
+
 typedef struct asc_scene_s {
-    AscCamera camera;
+    AscCamera *camera;
     AscSceneNode *root;
     cxmutstr name;
     struct {
@@ -48,18 +52,9 @@
  *
  * @param scene the scene graph
  * @param name optional name for the scene
- * @param camera_params initial camera parameters
+ * @param camera a pointer to the main camera for this scene
  */
-void asc_scene_init_(AscScene *scene, const char *name, struct asc_camera_init_args camera_params);
-
-/**
- * Initializes a scene graph.
- *
- * @param scene the scene graph
- * @param name optional name for the scene
- * @param ... initial camera parameters
- */
-#define asc_scene_init(scene, name, ...) asc_scene_init_(scene, name, (struct asc_camera_init_args){__VA_ARGS__})
+void asc_scene_init(AscScene *scene, const char *name, AscCamera *camera);
 
 /**
  * Checks if a scene is active.
@@ -84,7 +79,11 @@
  * @param scene the scene graph
  */
 static inline const AscCamera *asc_scene_camera(const AscScene *scene) {
-    return &scene->camera;
+    return scene->camera;
+}
+
+static inline asc_rect asc_scene_viewport(const AscScene *scene) {
+    return scene->camera->viewport;
 }
 
 void asc_scene_execute_behaviors(AscScene *scene);
--- a/src/ascension/window.h	Sun Nov 16 21:12:20 2025 +0100
+++ b/src/ascension/window.h	Sun Nov 16 22:00:13 2025 +0100
@@ -53,6 +53,7 @@
     AscGLContext glctx;
     float ui_scale;
     AscScene ui;
+    AscCamera ui_camera;
     AscScene scenes[ASC_MAX_SCENES];
 } AscWindow;
 
--- a/src/camera.c	Sun Nov 16 21:12:20 2025 +0100
+++ b/src/camera.c	Sun Nov 16 22:00:13 2025 +0100
@@ -30,6 +30,7 @@
 #include "ascension/camera.h"
 
 void asc_camera_init_(AscCamera *camera, struct asc_camera_init_args args) {
+    memset(camera, 0, sizeof(AscCamera));
     if (args.type == ASC_CAMERA_ORTHO) {
         asc_rect rect = args.ortho.rect;
         if (rect.size.width <= 0 || rect.size.height <= 0) {
@@ -41,8 +42,7 @@
         // TODO: implement
         asc_wprintf("Camera type PERSPECTIVE is not yet implemented.");
     } else {
-        // at least zero all the bytes (law of the least surprise)
-        memset(camera, 0, sizeof(AscCamera));
+        asc_error("Illegal argument for asc_camera_init(): type = %d", args.type);
     }
     camera->viewport_update_func = args.viewport_update_func;
     camera->projection_update_func = args.projection_update_func;
--- a/src/scene.c	Sun Nov 16 21:12:20 2025 +0100
+++ b/src/scene.c	Sun Nov 16 22:00:13 2025 +0100
@@ -28,6 +28,7 @@
 #include "ascension/error.h"
 #include "ascension/context.h"
 #include "ascension/scene.h"
+#include "ascension/camera.h"
 #include "ascension/behavior.h"
 #include "ascension/shader.h"
 #include "ascension/util.h"
@@ -39,9 +40,9 @@
 
 #include <assert.h>
 
-void asc_scene_init_(AscScene *scene, const char *name, struct asc_camera_init_args args) {
+void asc_scene_init(AscScene *scene, const char *name, AscCamera *camera) {
     assert(scene->root == NULL);
-    asc_camera_init_(&scene->camera, args);
+    scene->camera = camera;
     scene->root = asc_scene_node_empty();
     for (unsigned i = 0 ; i < ASC_RENDER_GROUP_COUNT ; i++) {
         scene->internal.render_groups[i] = cxArrayListCreateSimple(CX_STORE_POINTERS, 32);
@@ -116,10 +117,13 @@
 }
 
 void asc_scene_draw(AscScene *scene) {
-    if (scene->root == NULL) return;
+    if (!asc_scene_active(scene)) return;
 
     // when the viewport is zero, exit immediately
-    if (scene->camera.viewport.size.width == 0 || scene->camera.viewport.size.height == 0) {
+    const AscCamera *camera = asc_scene_camera(scene);
+    if (camera == NULL ||
+            camera->viewport.size.width == 0 ||
+            camera->viewport.size.height == 0) {
         return;
     }
 
@@ -181,20 +185,20 @@
 
     // set the viewport
     glViewport(
-            scene->camera.viewport.pos.x,
-            scene->camera.viewport.pos.y,
-            scene->camera.viewport.size.width,
-            scene->camera.viewport.size.height
+            camera->viewport.pos.x,
+            camera->viewport.pos.y,
+            camera->viewport.size.width,
+            camera->viewport.size.height
     );
-    if (scene->camera.viewport_clear) {
+    if (camera->viewport_clear) {
         glScissor(
-            scene->camera.viewport.pos.x,
-            scene->camera.viewport.pos.y,
-            scene->camera.viewport.size.width,
-            scene->camera.viewport.size.height
+            camera->viewport.pos.x,
+            camera->viewport.pos.y,
+            camera->viewport.size.width,
+            camera->viewport.size.height
         );
         glEnable(GL_SCISSOR_TEST);
-        const asc_color col = scene->camera.clear_color;
+        const asc_color col = camera->clear_color;
         glClearColor(col.red, col.green, col.blue, col.alpha);
         glClear(GL_COLOR_BUFFER_BIT);
         glDisable(GL_SCISSOR_TEST);
--- a/src/window.c	Sun Nov 16 21:12:20 2025 +0100
+++ b/src/window.c	Sun Nov 16 22:00:13 2025 +0100
@@ -67,11 +67,10 @@
     if (asc_gl_context_initialize(&window->glctx, window->window, &settings)) {
         char ui_scene_name[16];
         snprintf(ui_scene_name, sizeof(ui_scene_name), "Window %u UI", index);
-        asc_scene_init(&window->ui, ui_scene_name,
-            .type = ASC_CAMERA_ORTHO,
+        asc_camera_init(&window->ui_camera, .type = ASC_CAMERA_ORTHO,
             .ortho.rect = ASC_RECT(0, 0, window->dimensions.width, window->dimensions.height),
-            .projection_update_func = asc_camera_ortho_update_size
-        );
+            .projection_update_func = asc_camera_ortho_update_size);
+        asc_scene_init(&window->ui, ui_scene_name, &window->ui_camera);
         asc_dprintf("Window %u initialized at index %u", window->id, index);
         asc_context.active_window = index;
     } else {
@@ -147,10 +146,10 @@
     if (window->resized) {
         for (unsigned int i = 0; i < ASC_MAX_SCENES; i++) {
             if (asc_scene_active(&window->scenes[i])) {
-                asc_camera_update_viewport(&window->scenes[i].camera, window->dimensions);
+                asc_camera_update_viewport(window->scenes[i].camera, window->dimensions);
             }
         }
-        asc_camera_update_viewport(&window->ui.camera, window->dimensions);
+        asc_camera_update_viewport(window->ui.camera, window->dimensions);
     }
 
     // Execute all behaviors

mercurial