Fri, 15 Mar 2024 00:06:59 +0100
add camera and render groups
src/Makefile | file | annotate | diff | comparison | revisions | |
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/ascension/text.h | file | annotate | diff | comparison | revisions | |
src/ascension/window.h | file | annotate | diff | comparison | revisions | |
src/camera.c | file | annotate | diff | comparison | revisions | |
src/context.c | file | annotate | diff | comparison | revisions | |
src/scene.c | file | annotate | diff | comparison | revisions | |
src/text.c | file | annotate | diff | comparison | revisions | |
src/window.c | file | annotate | diff | comparison | revisions | |
test/Makefile | file | annotate | diff | comparison | revisions | |
test/snake.c | file | annotate | diff | comparison | revisions |
1.1 --- a/src/Makefile Wed Mar 06 23:38:17 2024 +0100 1.2 +++ b/src/Makefile Fri Mar 15 00:06:59 2024 +0100 1.3 @@ -27,8 +27,8 @@ 1.4 1.5 BUILD_DIR=../build/lib 1.6 1.7 -SRC = context.c error.c window.c files.c shader.c font.c text.c scene.c \ 1.8 - primitives.c 1.9 +SRC = context.c error.c window.c files.c shader.c font.c text.c \ 1.10 + scene.c camera.c primitives.c 1.11 1.12 OBJ = $(SRC:%.c=$(BUILD_DIR)/%.o) 1.13 1.14 @@ -41,17 +41,22 @@ 1.15 1.16 FORCE: 1.17 1.18 +$(BUILD_DIR)/camera.o: camera.c ascension/camera.h ascension/datatypes.h 1.19 + @echo "Compiling $<" 1.20 + $(CC) -o $@ $(CFLAGS) -c $< 1.21 + 1.22 $(BUILD_DIR)/context.o: context.c ascension/context.h \ 1.23 ascension/datatypes.h ascension/window.h ascension/primitives.h \ 1.24 ascension/mesh.h ascension/scene.h ascension/transform.h \ 1.25 - ascension/font.h ascension/error.h ascension/utils.h ascension/shader.h 1.26 + ascension/camera.h ascension/font.h ascension/error.h ascension/utils.h \ 1.27 + ascension/shader.h 1.28 @echo "Compiling $<" 1.29 $(CC) -o $@ $(CFLAGS) -c $< 1.30 1.31 $(BUILD_DIR)/error.o: error.c ascension/context.h ascension/datatypes.h \ 1.32 ascension/window.h ascension/primitives.h ascension/mesh.h \ 1.33 - ascension/scene.h ascension/transform.h ascension/font.h \ 1.34 - ascension/error.h ascension/utils.h 1.35 + ascension/scene.h ascension/transform.h ascension/camera.h \ 1.36 + ascension/font.h ascension/error.h ascension/utils.h 1.37 @echo "Compiling $<" 1.38 $(CC) -o $@ $(CFLAGS) -c $< 1.39 1.40 @@ -62,19 +67,22 @@ 1.41 $(BUILD_DIR)/font.o: font.c ascension/font.h ascension/context.h \ 1.42 ascension/datatypes.h ascension/window.h ascension/primitives.h \ 1.43 ascension/mesh.h ascension/scene.h ascension/transform.h \ 1.44 - ascension/font.h ascension/error.h 1.45 + ascension/camera.h ascension/font.h ascension/error.h 1.46 @echo "Compiling $<" 1.47 $(CC) -o $@ $(CFLAGS) -c $< 1.48 1.49 $(BUILD_DIR)/primitives.o: primitives.c ascension/primitives.h \ 1.50 ascension/mesh.h ascension/error.h ascension/context.h \ 1.51 ascension/datatypes.h ascension/window.h ascension/primitives.h \ 1.52 - ascension/scene.h ascension/transform.h ascension/font.h 1.53 + ascension/scene.h ascension/transform.h ascension/camera.h \ 1.54 + ascension/font.h 1.55 @echo "Compiling $<" 1.56 $(CC) -o $@ $(CFLAGS) -c $< 1.57 1.58 -$(BUILD_DIR)/scene.o: scene.c ascension/scene.h ascension/transform.h \ 1.59 - ascension/datatypes.h ascension/error.h 1.60 +$(BUILD_DIR)/scene.o: scene.c ascension/scene.h ascension/datatypes.h \ 1.61 + ascension/transform.h ascension/camera.h ascension/error.h \ 1.62 + ascension/context.h ascension/window.h ascension/primitives.h \ 1.63 + ascension/mesh.h ascension/scene.h ascension/font.h ascension/shader.h 1.64 @echo "Compiling $<" 1.65 $(CC) -o $@ $(CFLAGS) -c $< 1.66 1.67 @@ -84,16 +92,17 @@ 1.68 $(CC) -o $@ $(CFLAGS) -c $< 1.69 1.70 $(BUILD_DIR)/text.o: text.c ascension/text.h ascension/font.h \ 1.71 - ascension/scene.h ascension/transform.h ascension/datatypes.h \ 1.72 - ascension/context.h ascension/window.h ascension/primitives.h \ 1.73 - ascension/mesh.h ascension/error.h ascension/shader.h 1.74 + ascension/scene.h ascension/datatypes.h ascension/transform.h \ 1.75 + ascension/camera.h ascension/context.h ascension/window.h \ 1.76 + ascension/primitives.h ascension/mesh.h ascension/error.h \ 1.77 + ascension/shader.h 1.78 @echo "Compiling $<" 1.79 $(CC) -o $@ $(CFLAGS) -c $< 1.80 1.81 $(BUILD_DIR)/window.o: window.c ascension/window.h ascension/datatypes.h \ 1.82 ascension/primitives.h ascension/mesh.h ascension/scene.h \ 1.83 - ascension/transform.h ascension/context.h ascension/window.h \ 1.84 - ascension/font.h ascension/error.h ascension/utils.h 1.85 + ascension/transform.h ascension/camera.h ascension/context.h \ 1.86 + ascension/window.h ascension/font.h ascension/error.h ascension/utils.h 1.87 @echo "Compiling $<" 1.88 $(CC) -o $@ $(CFLAGS) -c $< 1.89
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/src/ascension/camera.h Fri Mar 15 00:06:59 2024 +0100 2.3 @@ -0,0 +1,59 @@ 2.4 +/* 2.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 2.6 + * Copyright 2023 Mike Becker. All rights reserved. 2.7 + * 2.8 + * Redistribution and use in source and binary forms, with or without 2.9 + * modification, are permitted provided that the following conditions are met: 2.10 + * 2.11 + * 1. Redistributions of source code must retain the above copyright 2.12 + * notice, this list of conditions and the following disclaimer. 2.13 + * 2.14 + * 2. Redistributions in binary form must reproduce the above copyright 2.15 + * notice, this list of conditions and the following disclaimer in the 2.16 + * documentation and/or other materials provided with the distribution. 2.17 + * 2.18 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 2.19 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2.20 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2.21 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 2.22 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2.23 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2.24 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2.25 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2.26 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2.27 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2.28 + * POSSIBILITY OF SUCH DAMAGE. 2.29 + */ 2.30 + 2.31 +#ifndef ASCENSION_CAMERA_H 2.32 +#define ASCENSION_CAMERA_H 2.33 + 2.34 +#include "datatypes.h" 2.35 + 2.36 +enum AscCameraType { 2.37 + ASC_CAMERA_DISABLED, 2.38 + ASC_CAMERA_ORTHO, 2.39 + ASC_CAMERA_PERSPECTIVE, 2.40 +}; 2.41 + 2.42 +typedef struct AscCamera AscCamera; 2.43 +typedef void(*asc_camera_update_func)(AscCamera*); 2.44 + 2.45 +struct AscCamera { 2.46 + asc_camera_update_func update; 2.47 + asc_mat4f projection; 2.48 + asc_mat4f view; 2.49 + asc_vec3f right; 2.50 + asc_vec3f up; 2.51 + asc_vec3f direction; 2.52 + asc_recti rect; 2.53 + // TODO: fov? 2.54 +}; 2.55 + 2.56 +__attribute__((__nonnull__)) 2.57 +void asc_camera_ortho(AscCamera *camera, asc_recti rect); 2.58 + 2.59 +__attribute__((__nonnull__)) 2.60 +void asc_camera_disable(AscCamera *camera); 2.61 + 2.62 +#endif //ASCENSION_CAMERA_H
3.1 --- a/src/ascension/datatypes.h Wed Mar 06 23:38:17 2024 +0100 3.2 +++ b/src/ascension/datatypes.h Fri Mar 15 00:06:59 2024 +0100 3.3 @@ -44,11 +44,22 @@ 3.4 typedef signed char asc_sbyte; 3.5 3.6 typedef union asc_vec2i { 3.7 - int data[2]; 3.8 struct { int x, y; }; 3.9 struct { int width, height; }; 3.10 + int data[2]; 3.11 } asc_vec2i; 3.12 3.13 +typedef struct asc_recti { 3.14 + asc_vec2i pos; 3.15 + asc_vec2i size; 3.16 +} asc_recti; 3.17 + 3.18 +typedef union asc_vec3f { 3.19 + struct { float x, y, z; }; 3.20 + struct { float width, height, depth; }; 3.21 + float data[3]; 3.22 +} asc_vec3f; 3.23 + 3.24 typedef struct asc_col4i { 3.25 asc_ubyte red, green, blue, alpha; 3.26 } asc_col4i; 3.27 @@ -114,6 +125,14 @@ 3.28 */ 3.29 #define asc_mat4_index(col, row) asc_mat_index(col, row, 4) 3.30 3.31 +static inline void asc_mat4f_unit(asc_mat4f mat) { 3.32 + memset(mat, 0, sizeof(float) * 16); 3.33 + mat[asc_mat4_index(0,0)] = 1; 3.34 + mat[asc_mat4_index(1,1)] = 1; 3.35 + mat[asc_mat4_index(2,2)] = 1; 3.36 + mat[asc_mat4_index(3,3)] = 1; 3.37 +} 3.38 + 3.39 static inline void asc_mat4f_ortho( 3.40 asc_mat4f mat, 3.41 float left,
4.1 --- a/src/ascension/scene.h Wed Mar 06 23:38:17 2024 +0100 4.2 +++ b/src/ascension/scene.h Fri Mar 15 00:06:59 2024 +0100 4.3 @@ -28,7 +28,11 @@ 4.4 #ifndef ASCENSION_SCENE_H 4.5 #define ASCENSION_SCENE_H 4.6 4.7 +#include "datatypes.h" 4.8 #include "transform.h" 4.9 +#include "camera.h" 4.10 + 4.11 +#include <cx/array_list.h> 4.12 4.13 typedef struct AscSceneNode AscSceneNode; 4.14 typedef struct AscBehaviorNode AscBehaviorNode; 4.15 @@ -44,6 +48,11 @@ 4.16 asc_scene_update_func func; 4.17 }; 4.18 4.19 +enum AscRenderGroup { 4.20 + ASC_RENDER_GROUP_NONE, 4.21 + ASC_RENDER_GROUP_FONTS 4.22 +}; 4.23 + 4.24 struct AscSceneNode { 4.25 AscSceneNode *parent; 4.26 AscSceneNode *prev; 4.27 @@ -53,9 +62,12 @@ 4.28 void *data; 4.29 asc_scene_free_func free_func; 4.30 asc_scene_update_func update_func; 4.31 + asc_scene_update_func transform_update_func; 4.32 asc_scene_draw_func draw_func; 4.33 asc_transform transform; 4.34 - bool need_update; 4.35 + enum AscRenderGroup render_group; 4.36 + bool need_full_update; 4.37 + bool need_transform_update; 4.38 }; 4.39 4.40 /** 4.41 @@ -65,13 +77,32 @@ 4.42 4.43 #define asc_node(obj) ((AscSceneNode*)obj) 4.44 4.45 -#define asc_node_update(node) ((AscSceneNode*)node)->need_update = true 4.46 +#define asc_node_update(node) \ 4.47 + ((AscSceneNode*)node)->need_full_update = true 4.48 +#define asc_node_update_transform(node) \ 4.49 + ((AscSceneNode*)node)->need_transform_update = true 4.50 + 4.51 +struct asc_render_group_entry { 4.52 + asc_scene_draw_func draw; 4.53 + AscSceneNode const *node; 4.54 +}; 4.55 + 4.56 +#define ASC_SCENE_CAMERAS_MAX 4 4.57 4.58 typedef struct AscScene { 4.59 AscSceneNode *root; 4.60 - // TODO: add render groups for batching 4.61 + asc_recti viewport; 4.62 + AscCamera cameras[ASC_SCENE_CAMERAS_MAX]; 4.63 + CX_ARRAY_DECLARE(struct asc_render_group_entry, rg_none); 4.64 + CX_ARRAY_DECLARE(struct asc_render_group_entry, rg_fonts); 4.65 } AscScene; 4.66 4.67 +/** 4.68 + * Initializes the scene using the active window as reference. 4.69 + * 4.70 + * @param scene the scene to initialize 4.71 + * @param type determines the type of camera to use 4.72 + */ 4.73 __attribute__((__nonnull__)) 4.74 void asc_scene_init(AscScene *scene); 4.75 4.76 @@ -79,7 +110,7 @@ 4.77 void asc_scene_destroy(AscScene *scene); 4.78 4.79 __attribute__((__nonnull__)) 4.80 -void asc_scene_draw(AscScene const *scene); 4.81 +void asc_scene_draw(AscScene *scene); 4.82 4.83 __attribute__((__nonnull__)) 4.84 void asc_scene_add(AscScene *scene, AscSceneNode *node);
5.1 --- a/src/ascension/text.h Wed Mar 06 23:38:17 2024 +0100 5.2 +++ b/src/ascension/text.h Fri Mar 15 00:06:59 2024 +0100 5.3 @@ -40,9 +40,8 @@ 5.4 unsigned max_width; 5.5 bool hidden; 5.6 bool centered; 5.7 - struct { 5.8 - unsigned tex_id; 5.9 - } internal; 5.10 + asc_vec2i dimension; 5.11 + unsigned tex_id; 5.12 } AscText; 5.13 5.14
6.1 --- a/src/ascension/window.h Wed Mar 06 23:38:17 2024 +0100 6.2 +++ b/src/ascension/window.h Fri Mar 15 00:06:59 2024 +0100 6.3 @@ -55,11 +55,11 @@ 6.4 SDL_GLContext glctx; 6.5 AscPrimitives primitives; 6.6 asc_vec2i dimensions; 6.7 - asc_mat4f projection; 6.8 + bool resized; 6.9 AscScene ui; 6.10 } AscWindow; 6.11 6.12 -#define asc_window_active_ui &(asc_context.active_window->ui) 6.13 +#define asc_window_active asc_context.active_window 6.14 6.15 /** 6.16 * Initializes the settings structure with default values. 6.17 @@ -102,7 +102,7 @@ 6.18 * 6.19 * @param window the window 6.20 */ 6.21 -void asc_window_sync(AscWindow const *window); 6.22 +void asc_window_sync(AscWindow *window); 6.23 6.24 /** 6.25 * Switches the active window.
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/src/camera.c Fri Mar 15 00:06:59 2024 +0100 7.3 @@ -0,0 +1,46 @@ 7.4 +/* 7.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 7.6 + * Copyright 2023 Mike Becker. All rights reserved. 7.7 + * 7.8 + * Redistribution and use in source and binary forms, with or without 7.9 + * modification, are permitted provided that the following conditions are met: 7.10 + * 7.11 + * 1. Redistributions of source code must retain the above copyright 7.12 + * notice, this list of conditions and the following disclaimer. 7.13 + * 7.14 + * 2. Redistributions in binary form must reproduce the above copyright 7.15 + * notice, this list of conditions and the following disclaimer in the 7.16 + * documentation and/or other materials provided with the distribution. 7.17 + * 7.18 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 7.19 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 7.20 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 7.21 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 7.22 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 7.23 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 7.24 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 7.25 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 7.26 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 7.27 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 7.28 + * POSSIBILITY OF SUCH DAMAGE. 7.29 + */ 7.30 + 7.31 +#include "ascension/camera.h" 7.32 + 7.33 +static void asc_camera_update_ortho(AscCamera *camera) { 7.34 + float left = (float) camera->rect.pos.x; 7.35 + float right = left + (float) camera->rect.size.width; 7.36 + float top = (float) camera->rect.pos.y; 7.37 + float bottom = top + (float) camera->rect.size.height; 7.38 + asc_mat4f_ortho(camera->projection, left, right, bottom, top); 7.39 +} 7.40 + 7.41 +void asc_camera_ortho(AscCamera *camera, asc_recti rect) { 7.42 + asc_mat4f_unit(camera->view); 7.43 + camera->update = asc_camera_update_ortho; 7.44 + camera->rect = rect; 7.45 +} 7.46 + 7.47 +void asc_camera_disable(AscCamera *camera) { 7.48 + camera->update = NULL; 7.49 +}
8.1 --- a/src/context.c Wed Mar 06 23:38:17 2024 +0100 8.2 +++ b/src/context.c Fri Mar 15 00:06:59 2024 +0100 8.3 @@ -83,9 +83,11 @@ 8.4 static void asc_event_window_resized(Uint32 id, Sint32 width, Sint32 height) { 8.5 for (unsigned int i = 0 ; i < ASC_MAX_WINDOWS ; i++) { 8.6 if (asc_context.windows[i].id == id) { 8.7 - asc_context.windows[i].dimensions.width = width; 8.8 - asc_context.windows[i].dimensions.height = height; 8.9 - asc_mat4f_ortho(asc_context.windows[i].projection, 0, (float) width, (float) height, 0); 8.10 + asc_vec2i dimensions = (asc_vec2i) {width, height}; 8.11 + asc_context.windows[i].resized = true; 8.12 + asc_context.windows[i].dimensions = dimensions; 8.13 + asc_context.windows[i].ui.viewport.size = dimensions; 8.14 + asc_context.windows[i].ui.cameras[0].rect.size = dimensions; 8.15 return; 8.16 } 8.17 }
9.1 --- a/src/scene.c Wed Mar 06 23:38:17 2024 +0100 9.2 +++ b/src/scene.c Fri Mar 15 00:06:59 2024 +0100 9.3 @@ -28,19 +28,34 @@ 9.4 #include "ascension/scene.h" 9.5 #include "ascension/error.h" 9.6 9.7 +#include "ascension/context.h" 9.8 + 9.9 #include <cx/tree.h> 9.10 +#include <cx/utils.h> 9.11 + 9.12 +#include "ascension/shader.h" 9.13 +#include <GL/glew.h> 9.14 9.15 #include <assert.h> 9.16 9.17 -#define child_list_off_ \ 9.18 - offsetof(AscSceneNode, children), offsetof(AscSceneNode, next) 9.19 - 9.20 void asc_scene_init(AscScene *scene) { 9.21 if (scene->root != NULL) { 9.22 asc_error("Scene is already initialized."); 9.23 return; 9.24 } 9.25 + 9.26 + // zero everything, first 9.27 + memset(scene, 0, sizeof(AscScene)); 9.28 + 9.29 + // default viewport is the entire viewport of the active window 9.30 + scene->viewport.size = asc_context.active_window->dimensions; 9.31 + 9.32 + // create the root node 9.33 scene->root = asc_scene_node_empty(); 9.34 + 9.35 + // initialize the render groups 9.36 + cx_array_initialize(scene->rg_none, 8); 9.37 + cx_array_initialize(scene->rg_fonts, 8); 9.38 } 9.39 9.40 void asc_scene_destroy(AscScene *scene) { 9.41 @@ -52,13 +67,25 @@ 9.42 asc_node_update(node); 9.43 } 9.44 9.45 -void asc_scene_draw(AscScene const *scene) { 9.46 - CxTreeIterator iter = cx_tree_iterator(scene->root, false, child_list_off_); 9.47 +#define asc_scene_draw_render_group(rg) \ 9.48 + cx_for_n(i, rg##_size) { \ 9.49 + rg[i].draw(rg[i].node); \ 9.50 + } (void)0 9.51 + 9.52 +void asc_scene_draw(AscScene *scene) { 9.53 + // reset render groups 9.54 + scene->rg_none_size = 0; 9.55 + scene->rg_fonts_size = 0; 9.56 9.57 // skip the root node deliberately, we know it's just the container 9.58 + CxTreeIterator iter = cx_tree_iterator( 9.59 + scene->root, false, 9.60 + offsetof(AscSceneNode, children), 9.61 + offsetof(AscSceneNode, next) 9.62 + ); 9.63 cxIteratorNext(iter); 9.64 9.65 - // update the children 9.66 + // update the children and add them to the render groups 9.67 cx_foreach(AscSceneNode*, node, iter) { 9.68 // execute behaviors, first 9.69 AscBehaviorNode *behavior = node->behaviors; 9.70 @@ -68,22 +95,65 @@ 9.71 } 9.72 9.73 // check if geometry needs update 9.74 - if (node->need_update && node->update_func != NULL) { 9.75 - node->need_update = false; 9.76 - asc_transform_copy(node->transform, node->parent->transform); 9.77 + if (node->need_full_update) { 9.78 + assert(node->update_func != NULL); 9.79 + node->need_full_update = false; 9.80 node->update_func(node); 9.81 } 9.82 + if (node->need_transform_update) { 9.83 + assert(node->transform_update_func != NULL); 9.84 + node->need_transform_update = false; 9.85 + asc_transform_identity(node->transform); 9.86 + node->transform_update_func(node); 9.87 + } 9.88 9.89 - // TODO: don't visit the tree for drawing, visit the render groups 9.90 + // add to render group 9.91 if (node->draw_func != NULL) { 9.92 - node->draw_func(node); 9.93 + struct asc_render_group_entry entry = { 9.94 + node->draw_func, node 9.95 + }; 9.96 + switch (node->render_group) { 9.97 + case ASC_RENDER_GROUP_NONE: 9.98 + cx_array_simple_add(scene->rg_none, entry); 9.99 + break; 9.100 + case ASC_RENDER_GROUP_FONTS: 9.101 + cx_array_simple_add(scene->rg_fonts, entry); 9.102 + break; 9.103 + } 9.104 } 9.105 } 9.106 + 9.107 + // set the viewport (in OpenGL we need to invert the Y axis) 9.108 + glViewport( 9.109 + scene->viewport.pos.x, 9.110 + -scene->viewport.pos.y, 9.111 + scene->viewport.size.width, 9.112 + scene->viewport.size.height 9.113 + ); 9.114 + 9.115 + // ----------------------------------------- 9.116 + // process the render groups for each camera 9.117 + // ----------------------------------------- 9.118 + cx_for_n(cam_id, ASC_SCENE_CAMERAS_MAX) { 9.119 + // update camera parameters, first 9.120 + AscCamera *camera = &scene->cameras[cam_id]; 9.121 + if (camera->update == NULL) continue; 9.122 + camera->update(camera); 9.123 + 9.124 + // for the NONE group, the draw func is expected to do everything 9.125 + asc_scene_draw_render_group(scene->rg_none); 9.126 + 9.127 + // draw the FONTS group 9.128 + // TODO: see if we can really always ignore the view matrix 9.129 + glUseProgram(ASC_SHADER_FONT.base.id); 9.130 + glUniformMatrix4fv(ASC_SHADER_FONT.base.projection, 1, 9.131 + GL_FALSE, camera->projection); 9.132 + asc_scene_draw_render_group(scene->rg_fonts); 9.133 + } 9.134 } 9.135 9.136 AscSceneNode *asc_scene_node_empty(void) { 9.137 AscSceneNode *node = calloc(1, sizeof(AscSceneNode)); 9.138 - assert(node != NULL); 9.139 node->free_func = (asc_scene_free_func) free; 9.140 asc_transform_identity(node->transform); 9.141 return node; 9.142 @@ -96,7 +166,11 @@ 9.143 asc_scene_node_unlink(node); 9.144 9.145 // free the entire subtree 9.146 - CxTreeIterator iter = cx_tree_iterator(node, true, child_list_off_); 9.147 + CxTreeIterator iter = cx_tree_iterator( 9.148 + node, true, 9.149 + offsetof(AscSceneNode, children), 9.150 + offsetof(AscSceneNode, next) 9.151 + ); 9.152 cx_foreach(AscSceneNode*, child, iter) { 9.153 if (!iter.exiting) continue; 9.154 if (child->free_func != NULL) {
10.1 --- a/src/text.c Wed Mar 06 23:38:17 2024 +0100 10.2 +++ b/src/text.c Fri Mar 15 00:06:59 2024 +0100 10.3 @@ -33,29 +33,28 @@ 10.4 #include <GL/glew.h> 10.5 10.6 static void asc_text_draw(AscText const *node) { 10.7 - if (node->color.alpha == 0 || node->hidden || node->internal.tex_id == 0) { 10.8 + if (node->color.alpha == 0 || node->hidden || node->tex_id == 0) { 10.9 return; 10.10 } 10.11 10.12 - // TODO: when we group draw calls, we don't need to activate shader here 10.13 - glUseProgram(ASC_SHADER_FONT.base.id); 10.14 - 10.15 - // TODO: when we group UI draw calls, we don't need to upload matrices here 10.16 - // Upload matrices 10.17 - glUniformMatrix4fv(ASC_SHADER_FONT.base.projection, 1, 10.18 - GL_FALSE, asc_context.active_window->projection); 10.19 + // Upload model matrix 10.20 glUniformMatrix4fv(ASC_SHADER_FONT.base.model, 1, 10.21 GL_FALSE, node->base.transform); 10.22 10.23 // Upload surface 10.24 glActiveTexture(GL_TEXTURE0); 10.25 - glBindTexture(GL_TEXTURE_RECTANGLE, node->internal.tex_id); 10.26 + glBindTexture(GL_TEXTURE_RECTANGLE, node->tex_id); 10.27 glUniform1i(ASC_SHADER_FONT.surface, 0); 10.28 10.29 // Draw mesh 10.30 asc_primitives_draw_plane(); 10.31 } 10.32 10.33 +static void asc_text_update_transform(AscText *node) { 10.34 + asc_transform_scale(node->base.transform, (float) node->dimension.width, (float) node->dimension.height, 0); 10.35 + asc_transform_translate2i(node->base.transform, node->position); 10.36 +} 10.37 + 10.38 static void asc_text_update(AscText *node) { 10.39 // short circuit if fully transparent or hidden, we don't need anything 10.40 if (node->color.alpha == 0 || node->hidden) { 10.41 @@ -63,12 +62,12 @@ 10.42 } 10.43 10.44 // Generate new texture, if required 10.45 - if (node->internal.tex_id == 0) { 10.46 - glGenTextures(1, &node->internal.tex_id); 10.47 - glBindTexture(GL_TEXTURE_RECTANGLE, node->internal.tex_id); 10.48 + if (node->tex_id == 0) { 10.49 + glGenTextures(1, &node->tex_id); 10.50 + glBindTexture(GL_TEXTURE_RECTANGLE, node->tex_id); 10.51 glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 10.52 glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 10.53 - asc_dprintf("Generated new texture for text node: %u", node->internal.tex_id); 10.54 + asc_dprintf("Generated new texture for text node: %u", node->tex_id); 10.55 } 10.56 10.57 // Render text onto a surface 10.58 @@ -82,14 +81,13 @@ 10.59 asc_error(SDL_GetError()); 10.60 return; 10.61 } 10.62 - 10.63 - // Transform 10.64 - asc_transform_scale(node->base.transform, (float) surface->w, (float) surface->h, 0); 10.65 - asc_transform_translate2i(node->base.transform, node->position); 10.66 + node->dimension.width = surface->w; 10.67 + node->dimension.height = surface->h; 10.68 + asc_node_update_transform(node); 10.69 10.70 // Transfer Image Data 10.71 // TODO: move the image data transfer to a separate function - we will need it more often 10.72 - glBindTexture(GL_TEXTURE_RECTANGLE, node->internal.tex_id); 10.73 + glBindTexture(GL_TEXTURE_RECTANGLE, node->tex_id); 10.74 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->pitch / surface->format->BytesPerPixel); 10.75 glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, 10.76 surface->w, surface->h, 10.77 @@ -106,8 +104,10 @@ 10.78 return NULL; 10.79 } 10.80 10.81 + node->base.render_group = ASC_RENDER_GROUP_FONTS; 10.82 node->base.free_func = (asc_scene_free_func) asc_text_free; 10.83 node->base.update_func = (asc_scene_update_func) asc_text_update; 10.84 + node->base.transform_update_func = (asc_scene_update_func) asc_text_update_transform; 10.85 node->base.draw_func = (asc_scene_draw_func) asc_text_draw; 10.86 10.87 node->position.x = x; 10.88 @@ -118,12 +118,16 @@ 10.89 node->text = strdup(text); 10.90 } 10.91 10.92 + // initialize 10.93 + asc_text_update(node); 10.94 + asc_text_update_transform(node); 10.95 + 10.96 return &node->base; 10.97 } 10.98 10.99 void asc_text_free(AscText *node) { 10.100 - asc_dprintf("Release text node texture: %u", node->internal.tex_id); 10.101 - glDeleteTextures(1, &node->internal.tex_id); 10.102 + asc_dprintf("Release text node texture: %u", node->tex_id); 10.103 + glDeleteTextures(1, &node->tex_id); 10.104 free(node->text); 10.105 free(node); 10.106 } 10.107 \ No newline at end of file
11.1 --- a/src/window.c Wed Mar 06 23:38:17 2024 +0100 11.2 +++ b/src/window.c Fri Mar 15 00:06:59 2024 +0100 11.3 @@ -63,6 +63,9 @@ 11.4 11.5 static void asc_window_init_scenes(AscWindow *window) { 11.6 asc_scene_init(&window->ui); 11.7 + asc_camera_ortho(&window->ui.cameras[0], (asc_recti){ 11.8 + 0, 0, window->dimensions 11.9 + }); 11.10 } 11.11 11.12 AscWindow *asc_window_initialize(unsigned int index, AscWindowSettings const *settings) { 11.13 @@ -98,13 +101,7 @@ 11.14 &window->dimensions.width, 11.15 &window->dimensions.height 11.16 ); 11.17 - asc_mat4f_ortho( 11.18 - window->projection, 11.19 - 0, 11.20 - (float) window->dimensions.width, 11.21 - (float) window->dimensions.height, 11.22 - 0 11.23 - ); 11.24 + window->resized = true; // count initial sizing as resize 11.25 11.26 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 11.27 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, settings->gl_major_version); 11.28 @@ -127,8 +124,8 @@ 11.29 11.30 asc_dprintf("Window %u initialized", window->id); 11.31 if (asc_primitives_init(&window->primitives)) { 11.32 + asc_context.active_window = window; 11.33 asc_window_init_scenes(window); 11.34 - asc_context.active_window = window; 11.35 return window; 11.36 } else { 11.37 asc_dprintf("!!! Creating primitives for window %u failed !!!", window->id); 11.38 @@ -183,14 +180,13 @@ 11.39 memset(window, 0, sizeof(AscWindow)); 11.40 } 11.41 11.42 -void asc_window_sync(AscWindow const* window) { 11.43 +void asc_window_sync(AscWindow* window) { 11.44 AscWindow const *active_window = asc_context.active_window; 11.45 if (window != active_window) { 11.46 asc_window_activate(window); 11.47 } 11.48 11.49 - // Clear viewport for new frame 11.50 - glViewport(0, 0, window->dimensions.width, window->dimensions.height); 11.51 + // Clear for new frame 11.52 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 11.53 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 11.54 11.55 @@ -200,6 +196,9 @@ 11.56 // Swap Buffers 11.57 SDL_GL_SwapWindow(window->window); 11.58 11.59 + // Clear Flags 11.60 + window->resized = false; 11.61 + 11.62 if (window != active_window) { 11.63 asc_window_activate(active_window); 11.64 }
12.1 --- a/test/Makefile Wed Mar 06 23:38:17 2024 +0100 12.2 +++ b/test/Makefile Fri Mar 15 00:06:59 2024 +0100 12.3 @@ -46,8 +46,8 @@ 12.4 ../src/ascension/datatypes.h ../src/ascension/window.h \ 12.5 ../src/ascension/primitives.h ../src/ascension/mesh.h \ 12.6 ../src/ascension/scene.h ../src/ascension/transform.h \ 12.7 - ../src/ascension/font.h ../src/ascension/shader.h \ 12.8 - ../src/ascension/text.h 12.9 + ../src/ascension/camera.h ../src/ascension/font.h \ 12.10 + ../src/ascension/shader.h ../src/ascension/text.h 12.11 @echo "Compiling $<" 12.12 $(CC) -o $@ $(CFLAGS) -c $< 12.13
13.1 --- a/test/snake.c Wed Mar 06 23:38:17 2024 +0100 13.2 +++ b/test/snake.c Fri Mar 15 00:06:59 2024 +0100 13.3 @@ -30,7 +30,7 @@ 13.4 13.5 static void update_fps_counter(AscSceneNode *node) { 13.6 // addition and multiplication is more efficient testing for zero 13.7 - // at an unnoticible cost of imprecision 13.8 + // at an unnoticeable cost of imprecision 13.9 static unsigned last_fps = 0u; 13.10 static unsigned debounce = 999u; 13.11 unsigned fps = 1000u; 13.12 @@ -51,7 +51,27 @@ 13.13 asc_ink_rgb(255, 0, 0); 13.14 AscSceneNode* node = asc_text(10, 10, "XXXXX FPS"); 13.15 asc_scene_add_behavior(node, update_fps_counter); 13.16 - asc_scene_add(asc_window_active_ui, node); 13.17 + asc_scene_add(&asc_window_active->ui, node); 13.18 +} 13.19 + 13.20 +static void update_score_counter(AscSceneNode *node) { 13.21 + AscText *text = asc_text_data(node); 13.22 + 13.23 + // tie to bottom left of the screen 13.24 + if (asc_window_active->resized) { 13.25 + asc_vec2i bottom_left = asc_window_active->dimensions; 13.26 + text->position.x = bottom_left.x - text->dimension.width - 10; 13.27 + text->position.y = bottom_left.y - text->dimension.height - 10; 13.28 + asc_node_update_transform(text); 13.29 + } 13.30 +} 13.31 + 13.32 +static void create_score_counter(void) { 13.33 + asc_set_font(asc_font(ASC_FONT_BOLD, 14)); 13.34 + asc_ink_rgb(0, 255, 0); 13.35 + AscSceneNode* node = asc_text(0, 0, "Score: 0"); 13.36 + asc_scene_add_behavior(node, update_score_counter); 13.37 + asc_scene_add(&asc_window_active->ui, node); 13.38 } 13.39 13.40 int main(int argc, char** argv) { 13.41 @@ -69,8 +89,10 @@ 13.42 AscWindow *window = asc_window_initialize(0, &settings); 13.43 asc_shader_initialize_predefined(); 13.44 13.45 - // create fps counter 13.46 + // create UI elements 13.47 create_fps_counter(); 13.48 + create_score_counter(); 13.49 + 13.50 13.51 // Main Loop 13.52 do {