# HG changeset patch # User Mike Becker # Date 1709583406 -3600 # Node ID 86468a71dd73962c0162a58cf01775aaa1970010 # Parent 8324037e01487ae52ddcf637d862d02c6c753b98 add transformation matrix diff -r 8324037e0148 -r 86468a71dd73 src/Makefile --- a/src/Makefile Mon Feb 26 21:16:00 2024 +0100 +++ b/src/Makefile Mon Mar 04 21:16:46 2024 +0100 @@ -43,14 +43,15 @@ $(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/font.h ascension/error.h \ - ascension/utils.h ascension/shader.h + ascension/mesh.h ascension/scene.h ascension/transform.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/font.h ascension/error.h ascension/utils.h + ascension/scene.h ascension/transform.h ascension/font.h \ + ascension/error.h ascension/utils.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -60,18 +61,20 @@ $(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/font.h ascension/error.h + ascension/mesh.h ascension/scene.h ascension/transform.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/font.h + ascension/scene.h ascension/transform.h ascension/font.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< -$(BUILD_DIR)/scene.o: scene.c ascension/scene.h ascension/error.h +$(BUILD_DIR)/scene.o: scene.c ascension/scene.h ascension/transform.h \ + ascension/datatypes.h ascension/error.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -81,16 +84,16 @@ $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/text.o: text.c ascension/text.h ascension/font.h \ - ascension/datatypes.h ascension/scene.h ascension/context.h \ - ascension/window.h ascension/primitives.h ascension/mesh.h \ - ascension/error.h ascension/shader.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 @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/context.h ascension/window.h ascension/font.h \ - ascension/error.h ascension/utils.h + ascension/transform.h ascension/context.h ascension/window.h \ + ascension/font.h ascension/error.h ascension/utils.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< diff -r 8324037e0148 -r 86468a71dd73 src/ascension/datatypes.h --- a/src/ascension/datatypes.h Mon Feb 26 21:16:00 2024 +0100 +++ b/src/ascension/datatypes.h Mon Mar 04 21:16:46 2024 +0100 @@ -92,7 +92,7 @@ } static inline SDL_Color asc_col_sdl(asc_col4i col) { - return (SDL_Color) {.r = col.red, .g = col.green, .b =col.blue, .a = col.alpha}; + return (SDL_Color) {.r = col.red, .g = col.green, .b = col.blue, .a = col.alpha}; } // -------------------------------------------------------------------------- diff -r 8324037e0148 -r 86468a71dd73 src/ascension/scene.h --- a/src/ascension/scene.h Mon Feb 26 21:16:00 2024 +0100 +++ b/src/ascension/scene.h Mon Mar 04 21:16:46 2024 +0100 @@ -28,9 +28,12 @@ #ifndef ASCENSION_SCENE_H #define ASCENSION_SCENE_H +#include "transform.h" + typedef struct AscSceneNode AscSceneNode; typedef void(*asc_scene_free_func)(AscSceneNode*); +typedef void(*asc_scene_update_func)(AscSceneNode*); typedef void(*asc_scene_draw_func)(AscSceneNode const*); struct AscSceneNode { @@ -39,16 +42,21 @@ AscSceneNode *next; AscSceneNode *children; asc_scene_free_func free_func; + asc_scene_update_func update_func; asc_scene_draw_func draw_func; + asc_transform transform; + bool need_update; // TODO: add more node contents }; /** * Place this as first member of a structure that shall be usable as a scene node. */ -#define extend_asc_scene_node AscSceneNode node +#define extend_asc_scene_node AscSceneNode base -#define asc_node(obj) (&((obj)->node)) +#define asc_node(obj) ((AscSceneNode*)obj) + +#define asc_node_update(node) ((AscSceneNode*)node)->need_update = true typedef struct AscScene { AscSceneNode *root; diff -r 8324037e0148 -r 86468a71dd73 src/ascension/text.h --- a/src/ascension/text.h Mon Feb 26 21:16:00 2024 +0100 +++ b/src/ascension/text.h Mon Mar 04 21:16:46 2024 +0100 @@ -29,7 +29,6 @@ #define ASCENSION_TEXT_H #include "font.h" -#include "datatypes.h" #include "scene.h" typedef struct AscText { @@ -42,7 +41,6 @@ bool hidden; bool centered; struct { - asc_vec2i dimension; unsigned tex_id; } internal; } AscText; @@ -65,16 +63,6 @@ AscText *asc_text(int x, int y, char const* text); /** - * Updates the internal state of the text node. - * - * You must invoke this method after changing the state of the struct, - * but not in every frame. - * - * @param node the text node - */ -void asc_text_update(AscText *node); - -/** * Releases all the memory of this node. * * @param node the text node diff -r 8324037e0148 -r 86468a71dd73 src/ascension/transform.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ascension/transform.h Mon Mar 04 21:16:46 2024 +0100 @@ -0,0 +1,98 @@ +/* + * 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_TRANSFORM_H +#define ASCENSION_TRANSFORM_H + +#include "datatypes.h" + +typedef asc_mat4f asc_transform; + +#define ASC_TRANSFORM_SIZE (sizeof(float)*16) + +#ifdef __GNUC__ +#define ASC_TRANFORM_FUNC_ATTRIBUTES \ + __attribute__((__nonnull__, __always_inline__)) +#else +#define ASC_TRANFORM_FUNC_ATTRIBUTES +#endif +#define ASC_TRANFORM_FUNC ASC_TRANFORM_FUNC_ATTRIBUTES static inline + + +ASC_TRANFORM_FUNC void asc_transform_identity(asc_transform transform) { + memset(transform, 0, ASC_TRANSFORM_SIZE); + transform[asc_mat4_index(0, 0)] = 1; + transform[asc_mat4_index(1, 1)] = 1; + transform[asc_mat4_index(2, 2)] = 1; + transform[asc_mat4_index(3, 3)] = 1; +} + +ASC_TRANFORM_FUNC void asc_transform_copy(asc_transform dest, asc_transform src) { + memcpy(dest, src, ASC_TRANSFORM_SIZE); +} + +ASC_TRANFORM_FUNC void asc_transform_translate( + asc_transform transform, + float x, float y, float z +) { + transform[asc_mat4_index(3, 0)] += x; + transform[asc_mat4_index(3, 1)] += y; + transform[asc_mat4_index(3, 2)] += z; +} + +ASC_TRANFORM_FUNC void asc_transform_translate2i( + asc_transform transform, + asc_vec2i position +) { + asc_transform_translate( + transform, + (float) position.x, (float) position.y, 0 + ); +} + +ASC_TRANFORM_FUNC void asc_transform_scale( + asc_transform transform, + float x, float y, float z +) { + for (unsigned i = 0 ; i < 3 ; i++) { + transform[asc_mat4_index(0, i)] *= x; + transform[asc_mat4_index(1, i)] *= y; + transform[asc_mat4_index(2, i)] *= z; + } +} + +ASC_TRANFORM_FUNC void asc_transform_scale2i( + asc_transform transform, + asc_vec2i dimensions +) { + asc_transform_scale( + transform, + (float) dimensions.width, (float) dimensions.height, 0 + ); +} + +#endif //ASCENSION_TRANSFORM_H diff -r 8324037e0148 -r 86468a71dd73 src/scene.c --- a/src/scene.c Mon Feb 26 21:16:00 2024 +0100 +++ b/src/scene.c Mon Mar 04 21:16:46 2024 +0100 @@ -52,12 +52,23 @@ void asc_scene_add(AscScene *scene, AscSceneNode *node) { asc_scene_node_link(scene->root, node); + asc_node_update(node); } void asc_scene_draw(AscScene const *scene) { - // TODO: don't visit the tree, visit the render groups CxTreeIterator iter = cx_tree_iterator(scene->root, false, child_list_off_); + + // skip the root node deliberately, we know it's just the container + cxIteratorNext(iter); + + // draw the children cx_foreach(AscSceneNode*, node, iter) { + if (node->need_update && node->update_func != NULL) { + node->need_update = false; + asc_transform_copy(node->transform, node->parent->transform); + node->update_func(node); + } + // TODO: don't visit the tree for drawing, visit the render groups if (node->draw_func != NULL) { node->draw_func(node); } @@ -65,10 +76,10 @@ } AscSceneNode *asc_scene_node_empty(void) { - // TODO: check if this can remain a calloc or if it's too expensive AscSceneNode *node = calloc(1, sizeof(AscSceneNode)); assert(node != NULL); node->free_func = (asc_scene_free_func) free; + asc_transform_identity(node->transform); return node; } diff -r 8324037e0148 -r 86468a71dd73 src/text.c --- a/src/text.c Mon Feb 26 21:16:00 2024 +0100 +++ b/src/text.c Mon Mar 04 21:16:46 2024 +0100 @@ -37,21 +37,15 @@ return; } + // TODO: when we group draw calls, we don't need to activate shader here glUseProgram(ASC_SHADER_FONT.base.id); - // Upload projection - // TODO: when we group UI draw calls, we don't need this + // 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 - asc_mat4f model = {0}; - model[asc_mat4_index(0, 0)] = node->internal.dimension.width; - model[asc_mat4_index(1, 1)] = node->internal.dimension.height; - model[asc_mat4_index(3, 0)] = node->position.x; - model[asc_mat4_index(3, 1)] = node->position.y; - model[asc_mat4_index(3, 3)] = 1; - glUniformMatrix4fv(ASC_SHADER_FONT.base.model, 1, GL_FALSE, model); + glUniformMatrix4fv(ASC_SHADER_FONT.base.model, 1, + GL_FALSE, node->base.transform); // Upload surface glActiveTexture(GL_TEXTURE0); @@ -62,27 +56,7 @@ asc_primitives_draw_plane(); } -AscText *asc_text(int x, int y, char const *text) { - AscText *node = calloc(1, sizeof(AscText)); - if (node == NULL) { - asc_error("Out of memory."); - return NULL; - } - - node->node.free_func = (asc_scene_free_func) asc_text_free; - node->node.draw_func = (asc_scene_draw_func) asc_text_draw; - - node->position = (asc_vec2i) {x, y}; - node->font = asc_context.active_font; - node->color = asc_context.ink; - if (text != NULL) { - node->text = strdup(text); - } - - return node; -} - -void asc_text_update(AscText *node) { +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) { return; @@ -109,10 +83,9 @@ return; } - // Store basic node information - node->position = node->position; - node->internal.dimension.width = surface->w; - node->internal.dimension.height = surface->h; + // Transform + asc_transform_scale(node->base.transform, (float) surface->w, (float) surface->h, 0); + asc_transform_translate2i(node->base.transform, node->position); // Transfer Image Data // TODO: move the image data transfer to a separate function - we will need it more often @@ -126,6 +99,28 @@ SDL_FreeSurface(surface); } +AscText *asc_text(int x, int y, char const *text) { + AscText *node = calloc(1, sizeof(AscText)); + if (node == NULL) { + asc_error("Out of memory."); + return NULL; + } + + node->base.free_func = (asc_scene_free_func) asc_text_free; + node->base.update_func = (asc_scene_update_func) asc_text_update; + node->base.draw_func = (asc_scene_draw_func) asc_text_draw; + + node->position.x = x; + node->position.y = y; + node->font = asc_context.active_font; + node->color = asc_context.ink; + if (text != NULL) { + node->text = strdup(text); + } + + return node; +} + void asc_text_free(AscText *node) { asc_dprintf("Release text node texture: %u", node->internal.tex_id); glDeleteTextures(1, &node->internal.tex_id); diff -r 8324037e0148 -r 86468a71dd73 test/Makefile --- a/test/Makefile Mon Feb 26 21:16:00 2024 +0100 +++ b/test/Makefile Mon Mar 04 21:16:46 2024 +0100 @@ -45,8 +45,9 @@ ../src/ascension/error.h ../src/ascension/context.h \ ../src/ascension/datatypes.h ../src/ascension/window.h \ ../src/ascension/primitives.h ../src/ascension/mesh.h \ - ../src/ascension/scene.h ../src/ascension/font.h \ - ../src/ascension/shader.h ../src/ascension/text.h + ../src/ascension/scene.h ../src/ascension/transform.h \ + ../src/ascension/font.h ../src/ascension/shader.h \ + ../src/ascension/text.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< diff -r 8324037e0148 -r 86468a71dd73 test/sandbox.c --- a/test/sandbox.c Mon Feb 26 21:16:00 2024 +0100 +++ b/test/sandbox.c Mon Mar 04 21:16:46 2024 +0100 @@ -69,7 +69,7 @@ if (fps != last_fps) { last_fps = fps; snprintf(fps_counter->text, 9, "%u FPS", fps); - asc_text_update(fps_counter); + asc_node_update(fps_counter); } } } while (asc_loop_next());