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 |
--- a/src/Makefile Wed Mar 06 23:38:17 2024 +0100 +++ b/src/Makefile Fri Mar 15 00:06:59 2024 +0100 @@ -27,8 +27,8 @@ BUILD_DIR=../build/lib -SRC = context.c error.c window.c files.c shader.c font.c text.c scene.c \ - primitives.c +SRC = context.c error.c window.c files.c shader.c font.c text.c \ + scene.c camera.c primitives.c OBJ = $(SRC:%.c=$(BUILD_DIR)/%.o) @@ -41,17 +41,22 @@ FORCE: +$(BUILD_DIR)/camera.o: camera.c ascension/camera.h ascension/datatypes.h + @echo "Compiling $<" + $(CC) -o $@ $(CFLAGS) -c $< + $(BUILD_DIR)/context.o: context.c ascension/context.h \ ascension/datatypes.h ascension/window.h ascension/primitives.h \ ascension/mesh.h ascension/scene.h ascension/transform.h \ - ascension/font.h ascension/error.h ascension/utils.h ascension/shader.h + ascension/camera.h ascension/font.h ascension/error.h ascension/utils.h \ + ascension/shader.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/error.o: error.c ascension/context.h ascension/datatypes.h \ ascension/window.h ascension/primitives.h ascension/mesh.h \ - ascension/scene.h ascension/transform.h ascension/font.h \ - ascension/error.h ascension/utils.h + ascension/scene.h ascension/transform.h ascension/camera.h \ + ascension/font.h ascension/error.h ascension/utils.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -62,19 +67,22 @@ $(BUILD_DIR)/font.o: font.c ascension/font.h ascension/context.h \ ascension/datatypes.h ascension/window.h ascension/primitives.h \ ascension/mesh.h ascension/scene.h ascension/transform.h \ - ascension/font.h ascension/error.h + ascension/camera.h ascension/font.h ascension/error.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/primitives.o: primitives.c ascension/primitives.h \ ascension/mesh.h ascension/error.h ascension/context.h \ ascension/datatypes.h ascension/window.h ascension/primitives.h \ - ascension/scene.h ascension/transform.h ascension/font.h + ascension/scene.h ascension/transform.h ascension/camera.h \ + ascension/font.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< -$(BUILD_DIR)/scene.o: scene.c ascension/scene.h ascension/transform.h \ - ascension/datatypes.h ascension/error.h +$(BUILD_DIR)/scene.o: scene.c ascension/scene.h ascension/datatypes.h \ + ascension/transform.h ascension/camera.h ascension/error.h \ + ascension/context.h ascension/window.h ascension/primitives.h \ + ascension/mesh.h ascension/scene.h ascension/font.h ascension/shader.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -84,16 +92,17 @@ $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/text.o: text.c ascension/text.h ascension/font.h \ - ascension/scene.h ascension/transform.h ascension/datatypes.h \ - ascension/context.h ascension/window.h ascension/primitives.h \ - ascension/mesh.h ascension/error.h ascension/shader.h + ascension/scene.h ascension/datatypes.h ascension/transform.h \ + ascension/camera.h ascension/context.h ascension/window.h \ + ascension/primitives.h ascension/mesh.h ascension/error.h \ + ascension/shader.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/window.o: window.c ascension/window.h ascension/datatypes.h \ ascension/primitives.h ascension/mesh.h ascension/scene.h \ - ascension/transform.h ascension/context.h ascension/window.h \ - ascension/font.h ascension/error.h ascension/utils.h + ascension/transform.h ascension/camera.h ascension/context.h \ + ascension/window.h ascension/font.h ascension/error.h ascension/utils.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $<
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ascension/camera.h Fri Mar 15 00:06:59 2024 +0100 @@ -0,0 +1,59 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * Copyright 2023 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ASCENSION_CAMERA_H +#define ASCENSION_CAMERA_H + +#include "datatypes.h" + +enum AscCameraType { + ASC_CAMERA_DISABLED, + ASC_CAMERA_ORTHO, + ASC_CAMERA_PERSPECTIVE, +}; + +typedef struct AscCamera AscCamera; +typedef void(*asc_camera_update_func)(AscCamera*); + +struct AscCamera { + asc_camera_update_func update; + asc_mat4f projection; + asc_mat4f view; + asc_vec3f right; + asc_vec3f up; + asc_vec3f direction; + asc_recti rect; + // TODO: fov? +}; + +__attribute__((__nonnull__)) +void asc_camera_ortho(AscCamera *camera, asc_recti rect); + +__attribute__((__nonnull__)) +void asc_camera_disable(AscCamera *camera); + +#endif //ASCENSION_CAMERA_H
--- a/src/ascension/datatypes.h Wed Mar 06 23:38:17 2024 +0100 +++ b/src/ascension/datatypes.h Fri Mar 15 00:06:59 2024 +0100 @@ -44,11 +44,22 @@ typedef signed char asc_sbyte; typedef union asc_vec2i { - int data[2]; struct { int x, y; }; struct { int width, height; }; + int data[2]; } asc_vec2i; +typedef struct asc_recti { + asc_vec2i pos; + asc_vec2i size; +} asc_recti; + +typedef union asc_vec3f { + struct { float x, y, z; }; + struct { float width, height, depth; }; + float data[3]; +} asc_vec3f; + typedef struct asc_col4i { asc_ubyte red, green, blue, alpha; } asc_col4i; @@ -114,6 +125,14 @@ */ #define asc_mat4_index(col, row) asc_mat_index(col, row, 4) +static inline void asc_mat4f_unit(asc_mat4f mat) { + memset(mat, 0, sizeof(float) * 16); + mat[asc_mat4_index(0,0)] = 1; + mat[asc_mat4_index(1,1)] = 1; + mat[asc_mat4_index(2,2)] = 1; + mat[asc_mat4_index(3,3)] = 1; +} + static inline void asc_mat4f_ortho( asc_mat4f mat, float left,
--- a/src/ascension/scene.h Wed Mar 06 23:38:17 2024 +0100 +++ b/src/ascension/scene.h Fri Mar 15 00:06:59 2024 +0100 @@ -28,7 +28,11 @@ #ifndef ASCENSION_SCENE_H #define ASCENSION_SCENE_H +#include "datatypes.h" #include "transform.h" +#include "camera.h" + +#include <cx/array_list.h> typedef struct AscSceneNode AscSceneNode; typedef struct AscBehaviorNode AscBehaviorNode; @@ -44,6 +48,11 @@ asc_scene_update_func func; }; +enum AscRenderGroup { + ASC_RENDER_GROUP_NONE, + ASC_RENDER_GROUP_FONTS +}; + struct AscSceneNode { AscSceneNode *parent; AscSceneNode *prev; @@ -53,9 +62,12 @@ void *data; asc_scene_free_func free_func; asc_scene_update_func update_func; + asc_scene_update_func transform_update_func; asc_scene_draw_func draw_func; asc_transform transform; - bool need_update; + enum AscRenderGroup render_group; + bool need_full_update; + bool need_transform_update; }; /** @@ -65,13 +77,32 @@ #define asc_node(obj) ((AscSceneNode*)obj) -#define asc_node_update(node) ((AscSceneNode*)node)->need_update = true +#define asc_node_update(node) \ + ((AscSceneNode*)node)->need_full_update = true +#define asc_node_update_transform(node) \ + ((AscSceneNode*)node)->need_transform_update = true + +struct asc_render_group_entry { + asc_scene_draw_func draw; + AscSceneNode const *node; +}; + +#define ASC_SCENE_CAMERAS_MAX 4 typedef struct AscScene { AscSceneNode *root; - // TODO: add render groups for batching + asc_recti viewport; + AscCamera cameras[ASC_SCENE_CAMERAS_MAX]; + CX_ARRAY_DECLARE(struct asc_render_group_entry, rg_none); + CX_ARRAY_DECLARE(struct asc_render_group_entry, rg_fonts); } AscScene; +/** + * Initializes the scene using the active window as reference. + * + * @param scene the scene to initialize + * @param type determines the type of camera to use + */ __attribute__((__nonnull__)) void asc_scene_init(AscScene *scene); @@ -79,7 +110,7 @@ void asc_scene_destroy(AscScene *scene); __attribute__((__nonnull__)) -void asc_scene_draw(AscScene const *scene); +void asc_scene_draw(AscScene *scene); __attribute__((__nonnull__)) void asc_scene_add(AscScene *scene, AscSceneNode *node);
--- a/src/ascension/text.h Wed Mar 06 23:38:17 2024 +0100 +++ b/src/ascension/text.h Fri Mar 15 00:06:59 2024 +0100 @@ -40,9 +40,8 @@ unsigned max_width; bool hidden; bool centered; - struct { - unsigned tex_id; - } internal; + asc_vec2i dimension; + unsigned tex_id; } AscText;
--- a/src/ascension/window.h Wed Mar 06 23:38:17 2024 +0100 +++ b/src/ascension/window.h Fri Mar 15 00:06:59 2024 +0100 @@ -55,11 +55,11 @@ SDL_GLContext glctx; AscPrimitives primitives; asc_vec2i dimensions; - asc_mat4f projection; + bool resized; AscScene ui; } AscWindow; -#define asc_window_active_ui &(asc_context.active_window->ui) +#define asc_window_active asc_context.active_window /** * Initializes the settings structure with default values. @@ -102,7 +102,7 @@ * * @param window the window */ -void asc_window_sync(AscWindow const *window); +void asc_window_sync(AscWindow *window); /** * Switches the active window.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/camera.c Fri Mar 15 00:06:59 2024 +0100 @@ -0,0 +1,46 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * Copyright 2023 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ascension/camera.h" + +static void asc_camera_update_ortho(AscCamera *camera) { + float left = (float) camera->rect.pos.x; + float right = left + (float) camera->rect.size.width; + float top = (float) camera->rect.pos.y; + float bottom = top + (float) camera->rect.size.height; + asc_mat4f_ortho(camera->projection, left, right, bottom, top); +} + +void asc_camera_ortho(AscCamera *camera, asc_recti rect) { + asc_mat4f_unit(camera->view); + camera->update = asc_camera_update_ortho; + camera->rect = rect; +} + +void asc_camera_disable(AscCamera *camera) { + camera->update = NULL; +}
--- a/src/context.c Wed Mar 06 23:38:17 2024 +0100 +++ b/src/context.c Fri Mar 15 00:06:59 2024 +0100 @@ -83,9 +83,11 @@ static void asc_event_window_resized(Uint32 id, Sint32 width, Sint32 height) { for (unsigned int i = 0 ; i < ASC_MAX_WINDOWS ; i++) { if (asc_context.windows[i].id == id) { - asc_context.windows[i].dimensions.width = width; - asc_context.windows[i].dimensions.height = height; - asc_mat4f_ortho(asc_context.windows[i].projection, 0, (float) width, (float) height, 0); + asc_vec2i dimensions = (asc_vec2i) {width, height}; + asc_context.windows[i].resized = true; + asc_context.windows[i].dimensions = dimensions; + asc_context.windows[i].ui.viewport.size = dimensions; + asc_context.windows[i].ui.cameras[0].rect.size = dimensions; return; } }
--- a/src/scene.c Wed Mar 06 23:38:17 2024 +0100 +++ b/src/scene.c Fri Mar 15 00:06:59 2024 +0100 @@ -28,19 +28,34 @@ #include "ascension/scene.h" #include "ascension/error.h" +#include "ascension/context.h" + #include <cx/tree.h> +#include <cx/utils.h> + +#include "ascension/shader.h" +#include <GL/glew.h> #include <assert.h> -#define child_list_off_ \ - offsetof(AscSceneNode, children), offsetof(AscSceneNode, next) - void asc_scene_init(AscScene *scene) { if (scene->root != NULL) { asc_error("Scene is already initialized."); return; } + + // zero everything, first + memset(scene, 0, sizeof(AscScene)); + + // default viewport is the entire viewport of the active window + scene->viewport.size = asc_context.active_window->dimensions; + + // create the root node scene->root = asc_scene_node_empty(); + + // initialize the render groups + cx_array_initialize(scene->rg_none, 8); + cx_array_initialize(scene->rg_fonts, 8); } void asc_scene_destroy(AscScene *scene) { @@ -52,13 +67,25 @@ asc_node_update(node); } -void asc_scene_draw(AscScene const *scene) { - CxTreeIterator iter = cx_tree_iterator(scene->root, false, child_list_off_); +#define asc_scene_draw_render_group(rg) \ + cx_for_n(i, rg##_size) { \ + rg[i].draw(rg[i].node); \ + } (void)0 + +void asc_scene_draw(AscScene *scene) { + // reset render groups + scene->rg_none_size = 0; + scene->rg_fonts_size = 0; // skip the root node deliberately, we know it's just the container + CxTreeIterator iter = cx_tree_iterator( + scene->root, false, + offsetof(AscSceneNode, children), + offsetof(AscSceneNode, next) + ); cxIteratorNext(iter); - // update the children + // update the children and add them to the render groups cx_foreach(AscSceneNode*, node, iter) { // execute behaviors, first AscBehaviorNode *behavior = node->behaviors; @@ -68,22 +95,65 @@ } // check if geometry needs update - if (node->need_update && node->update_func != NULL) { - node->need_update = false; - asc_transform_copy(node->transform, node->parent->transform); + if (node->need_full_update) { + assert(node->update_func != NULL); + node->need_full_update = false; node->update_func(node); } + if (node->need_transform_update) { + assert(node->transform_update_func != NULL); + node->need_transform_update = false; + asc_transform_identity(node->transform); + node->transform_update_func(node); + } - // TODO: don't visit the tree for drawing, visit the render groups + // add to render group if (node->draw_func != NULL) { - node->draw_func(node); + struct asc_render_group_entry entry = { + node->draw_func, node + }; + switch (node->render_group) { + case ASC_RENDER_GROUP_NONE: + cx_array_simple_add(scene->rg_none, entry); + break; + case ASC_RENDER_GROUP_FONTS: + cx_array_simple_add(scene->rg_fonts, entry); + break; + } } } + + // set the viewport (in OpenGL we need to invert the Y axis) + glViewport( + scene->viewport.pos.x, + -scene->viewport.pos.y, + scene->viewport.size.width, + scene->viewport.size.height + ); + + // ----------------------------------------- + // process the render groups for each camera + // ----------------------------------------- + cx_for_n(cam_id, ASC_SCENE_CAMERAS_MAX) { + // update camera parameters, first + AscCamera *camera = &scene->cameras[cam_id]; + if (camera->update == NULL) continue; + camera->update(camera); + + // for the NONE group, the draw func is expected to do everything + asc_scene_draw_render_group(scene->rg_none); + + // draw the FONTS group + // TODO: see if we can really always ignore the view matrix + glUseProgram(ASC_SHADER_FONT.base.id); + glUniformMatrix4fv(ASC_SHADER_FONT.base.projection, 1, + GL_FALSE, camera->projection); + asc_scene_draw_render_group(scene->rg_fonts); + } } AscSceneNode *asc_scene_node_empty(void) { AscSceneNode *node = calloc(1, sizeof(AscSceneNode)); - assert(node != NULL); node->free_func = (asc_scene_free_func) free; asc_transform_identity(node->transform); return node; @@ -96,7 +166,11 @@ asc_scene_node_unlink(node); // free the entire subtree - CxTreeIterator iter = cx_tree_iterator(node, true, child_list_off_); + CxTreeIterator iter = cx_tree_iterator( + node, true, + offsetof(AscSceneNode, children), + offsetof(AscSceneNode, next) + ); cx_foreach(AscSceneNode*, child, iter) { if (!iter.exiting) continue; if (child->free_func != NULL) {
--- a/src/text.c Wed Mar 06 23:38:17 2024 +0100 +++ b/src/text.c Fri Mar 15 00:06:59 2024 +0100 @@ -33,29 +33,28 @@ #include <GL/glew.h> static void asc_text_draw(AscText const *node) { - if (node->color.alpha == 0 || node->hidden || node->internal.tex_id == 0) { + if (node->color.alpha == 0 || node->hidden || node->tex_id == 0) { return; } - // TODO: when we group draw calls, we don't need to activate shader here - glUseProgram(ASC_SHADER_FONT.base.id); - - // TODO: when we group UI draw calls, we don't need to upload matrices here - // Upload matrices - glUniformMatrix4fv(ASC_SHADER_FONT.base.projection, 1, - GL_FALSE, asc_context.active_window->projection); + // Upload model matrix glUniformMatrix4fv(ASC_SHADER_FONT.base.model, 1, GL_FALSE, node->base.transform); // Upload surface glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_RECTANGLE, node->internal.tex_id); + glBindTexture(GL_TEXTURE_RECTANGLE, node->tex_id); glUniform1i(ASC_SHADER_FONT.surface, 0); // Draw mesh asc_primitives_draw_plane(); } +static void asc_text_update_transform(AscText *node) { + asc_transform_scale(node->base.transform, (float) node->dimension.width, (float) node->dimension.height, 0); + asc_transform_translate2i(node->base.transform, node->position); +} + static void asc_text_update(AscText *node) { // short circuit if fully transparent or hidden, we don't need anything if (node->color.alpha == 0 || node->hidden) { @@ -63,12 +62,12 @@ } // Generate new texture, if required - if (node->internal.tex_id == 0) { - glGenTextures(1, &node->internal.tex_id); - glBindTexture(GL_TEXTURE_RECTANGLE, node->internal.tex_id); + if (node->tex_id == 0) { + glGenTextures(1, &node->tex_id); + glBindTexture(GL_TEXTURE_RECTANGLE, node->tex_id); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - asc_dprintf("Generated new texture for text node: %u", node->internal.tex_id); + asc_dprintf("Generated new texture for text node: %u", node->tex_id); } // Render text onto a surface @@ -82,14 +81,13 @@ asc_error(SDL_GetError()); return; } - - // Transform - asc_transform_scale(node->base.transform, (float) surface->w, (float) surface->h, 0); - asc_transform_translate2i(node->base.transform, node->position); + node->dimension.width = surface->w; + node->dimension.height = surface->h; + asc_node_update_transform(node); // Transfer Image Data // TODO: move the image data transfer to a separate function - we will need it more often - glBindTexture(GL_TEXTURE_RECTANGLE, node->internal.tex_id); + glBindTexture(GL_TEXTURE_RECTANGLE, node->tex_id); glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->pitch / surface->format->BytesPerPixel); glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, surface->w, surface->h, @@ -106,8 +104,10 @@ return NULL; } + node->base.render_group = ASC_RENDER_GROUP_FONTS; node->base.free_func = (asc_scene_free_func) asc_text_free; node->base.update_func = (asc_scene_update_func) asc_text_update; + node->base.transform_update_func = (asc_scene_update_func) asc_text_update_transform; node->base.draw_func = (asc_scene_draw_func) asc_text_draw; node->position.x = x; @@ -118,12 +118,16 @@ node->text = strdup(text); } + // initialize + asc_text_update(node); + asc_text_update_transform(node); + return &node->base; } void asc_text_free(AscText *node) { - asc_dprintf("Release text node texture: %u", node->internal.tex_id); - glDeleteTextures(1, &node->internal.tex_id); + asc_dprintf("Release text node texture: %u", node->tex_id); + glDeleteTextures(1, &node->tex_id); free(node->text); free(node); } \ No newline at end of file
--- a/src/window.c Wed Mar 06 23:38:17 2024 +0100 +++ b/src/window.c Fri Mar 15 00:06:59 2024 +0100 @@ -63,6 +63,9 @@ static void asc_window_init_scenes(AscWindow *window) { asc_scene_init(&window->ui); + asc_camera_ortho(&window->ui.cameras[0], (asc_recti){ + 0, 0, window->dimensions + }); } AscWindow *asc_window_initialize(unsigned int index, AscWindowSettings const *settings) { @@ -98,13 +101,7 @@ &window->dimensions.width, &window->dimensions.height ); - asc_mat4f_ortho( - window->projection, - 0, - (float) window->dimensions.width, - (float) window->dimensions.height, - 0 - ); + window->resized = true; // count initial sizing as resize SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, settings->gl_major_version); @@ -127,8 +124,8 @@ asc_dprintf("Window %u initialized", window->id); if (asc_primitives_init(&window->primitives)) { + asc_context.active_window = window; asc_window_init_scenes(window); - asc_context.active_window = window; return window; } else { asc_dprintf("!!! Creating primitives for window %u failed !!!", window->id); @@ -183,14 +180,13 @@ memset(window, 0, sizeof(AscWindow)); } -void asc_window_sync(AscWindow const* window) { +void asc_window_sync(AscWindow* window) { AscWindow const *active_window = asc_context.active_window; if (window != active_window) { asc_window_activate(window); } - // Clear viewport for new frame - glViewport(0, 0, window->dimensions.width, window->dimensions.height); + // Clear for new frame glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -200,6 +196,9 @@ // Swap Buffers SDL_GL_SwapWindow(window->window); + // Clear Flags + window->resized = false; + if (window != active_window) { asc_window_activate(active_window); }
--- a/test/Makefile Wed Mar 06 23:38:17 2024 +0100 +++ b/test/Makefile Fri Mar 15 00:06:59 2024 +0100 @@ -46,8 +46,8 @@ ../src/ascension/datatypes.h ../src/ascension/window.h \ ../src/ascension/primitives.h ../src/ascension/mesh.h \ ../src/ascension/scene.h ../src/ascension/transform.h \ - ../src/ascension/font.h ../src/ascension/shader.h \ - ../src/ascension/text.h + ../src/ascension/camera.h ../src/ascension/font.h \ + ../src/ascension/shader.h ../src/ascension/text.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $<
--- a/test/snake.c Wed Mar 06 23:38:17 2024 +0100 +++ b/test/snake.c Fri Mar 15 00:06:59 2024 +0100 @@ -30,7 +30,7 @@ static void update_fps_counter(AscSceneNode *node) { // addition and multiplication is more efficient testing for zero - // at an unnoticible cost of imprecision + // at an unnoticeable cost of imprecision static unsigned last_fps = 0u; static unsigned debounce = 999u; unsigned fps = 1000u; @@ -51,7 +51,27 @@ asc_ink_rgb(255, 0, 0); AscSceneNode* node = asc_text(10, 10, "XXXXX FPS"); asc_scene_add_behavior(node, update_fps_counter); - asc_scene_add(asc_window_active_ui, node); + asc_scene_add(&asc_window_active->ui, node); +} + +static void update_score_counter(AscSceneNode *node) { + AscText *text = asc_text_data(node); + + // tie to bottom left of the screen + if (asc_window_active->resized) { + asc_vec2i bottom_left = asc_window_active->dimensions; + text->position.x = bottom_left.x - text->dimension.width - 10; + text->position.y = bottom_left.y - text->dimension.height - 10; + asc_node_update_transform(text); + } +} + +static void create_score_counter(void) { + asc_set_font(asc_font(ASC_FONT_BOLD, 14)); + asc_ink_rgb(0, 255, 0); + AscSceneNode* node = asc_text(0, 0, "Score: 0"); + asc_scene_add_behavior(node, update_score_counter); + asc_scene_add(&asc_window_active->ui, node); } int main(int argc, char** argv) { @@ -69,8 +89,10 @@ AscWindow *window = asc_window_initialize(0, &settings); asc_shader_initialize_predefined(); - // create fps counter + // create UI elements create_fps_counter(); + create_score_counter(); + // Main Loop do {