add text rendering and demo FPS counter

Wed, 15 Nov 2023 22:51:40 +0100

author
Mike Becker <universe@uap-core.de>
date
Wed, 15 Nov 2023 22:51:40 +0100
changeset 16
c5dde81b6fb2
parent 15
362b7659dc76
child 17
25013a35e07d

add text rendering and demo FPS counter

shader/font_frag.glsl file | annotate | diff | comparison | revisions
shader/font_vtx.glsl file | annotate | diff | comparison | revisions
src/Makefile file | annotate | diff | comparison | revisions
src/ascension/ascension.h file | annotate | diff | comparison | revisions
src/ascension/context.h file | annotate | diff | comparison | revisions
src/ascension/datatypes.h file | annotate | diff | comparison | revisions
src/ascension/error.h file | annotate | diff | comparison | revisions
src/ascension/mesh.h file | annotate | diff | comparison | revisions
src/ascension/primitives.h file | annotate | diff | comparison | revisions
src/ascension/shader.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/context.c file | annotate | diff | comparison | revisions
src/error.c file | annotate | diff | comparison | revisions
src/primitives.c file | annotate | diff | comparison | revisions
src/shader.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/sandbox.c file | annotate | diff | comparison | revisions
     1.1 --- a/shader/font_frag.glsl	Wed Nov 08 23:17:07 2023 +0100
     1.2 +++ b/shader/font_frag.glsl	Wed Nov 15 22:51:40 2023 +0100
     1.3 @@ -1,10 +1,10 @@
     1.4  #version 400 core
     1.5  
     1.6 +layout(location = 0) out vec4 diffuse;
     1.7  in vec2 texcoord;
     1.8 -out vec4 fragment;
     1.9  
    1.10  uniform sampler2DRect surface;
    1.11  
    1.12  void main(void) {
    1.13 -    fragment = texture(surface, texcoord);
    1.14 +    diffuse = texture(surface, texcoord);
    1.15  }
     2.1 --- a/shader/font_vtx.glsl	Wed Nov 08 23:17:07 2023 +0100
     2.2 +++ b/shader/font_vtx.glsl	Wed Nov 15 22:51:40 2023 +0100
     2.3 @@ -1,6 +1,6 @@
     2.4  #version 400 core
     2.5  
     2.6 -in vec2 position;
     2.7 +layout(location = 0) in vec2 position;
     2.8  out vec2 texcoord;
     2.9  
    2.10  uniform mat4 projection;
    2.11 @@ -8,5 +8,5 @@
    2.12  
    2.13  void main(void) {
    2.14      gl_Position = projection*model*vec4(position, 0.0, 1.0);
    2.15 -    texcoord = position;
    2.16 +    texcoord = vec2(model[0].x, model[1].y)*position;
    2.17  }
     3.1 --- a/src/Makefile	Wed Nov 08 23:17:07 2023 +0100
     3.2 +++ b/src/Makefile	Wed Nov 15 22:51:40 2023 +0100
     3.3 @@ -27,7 +27,7 @@
     3.4  
     3.5  BUILD_DIR=../build/lib
     3.6  
     3.7 -SRC  = context.c error.c window.c font.c files.c shader.c
     3.8 +SRC  = context.c error.c window.c files.c shader.c font.c text.c primitives.c
     3.9  
    3.10  OBJ = $(SRC:%.c=$(BUILD_DIR)/%.o)
    3.11  
    3.12 @@ -40,15 +40,16 @@
    3.13  
    3.14  FORCE:
    3.15  
    3.16 -$(BUILD_DIR)/context.o: context.c ascension/context.h ascension/window.h \
    3.17 - ascension/datatypes.h ascension/font.h ascension/error.h \
    3.18 - ascension/utils.h ascension/shader.h
    3.19 +$(BUILD_DIR)/context.o: context.c ascension/context.h \
    3.20 + ascension/datatypes.h ascension/window.h ascension/primitives.h \
    3.21 + ascension/mesh.h ascension/font.h ascension/error.h ascension/utils.h \
    3.22 + ascension/shader.h
    3.23  	@echo "Compiling $<"
    3.24  	$(CC) -o $@ $(CFLAGS) -c $<
    3.25  
    3.26 -$(BUILD_DIR)/error.o: error.c ascension/context.h ascension/window.h \
    3.27 - ascension/datatypes.h ascension/font.h ascension/error.h \
    3.28 - ascension/utils.h
    3.29 +$(BUILD_DIR)/error.o: error.c ascension/context.h ascension/datatypes.h \
    3.30 + ascension/window.h ascension/primitives.h ascension/mesh.h \
    3.31 + ascension/font.h ascension/error.h ascension/utils.h
    3.32  	@echo "Compiling $<"
    3.33  	$(CC) -o $@ $(CFLAGS) -c $<
    3.34  
    3.35 @@ -57,8 +58,15 @@
    3.36  	$(CC) -o $@ $(CFLAGS) -c $<
    3.37  
    3.38  $(BUILD_DIR)/font.o: font.c ascension/font.h ascension/context.h \
    3.39 - ascension/window.h ascension/datatypes.h ascension/font.h \
    3.40 - ascension/error.h
    3.41 + ascension/datatypes.h ascension/window.h ascension/primitives.h \
    3.42 + ascension/mesh.h ascension/font.h ascension/error.h
    3.43 +	@echo "Compiling $<"
    3.44 +	$(CC) -o $@ $(CFLAGS) -c $<
    3.45 +
    3.46 +$(BUILD_DIR)/primitives.o: primitives.c ascension/primitives.h \
    3.47 + ascension/mesh.h ascension/error.h ascension/context.h \
    3.48 + ascension/datatypes.h ascension/window.h ascension/primitives.h \
    3.49 + ascension/font.h
    3.50  	@echo "Compiling $<"
    3.51  	$(CC) -o $@ $(CFLAGS) -c $<
    3.52  
    3.53 @@ -67,9 +75,16 @@
    3.54  	@echo "Compiling $<"
    3.55  	$(CC) -o $@ $(CFLAGS) -c $<
    3.56  
    3.57 -$(BUILD_DIR)/window.o: window.c ascension/window.h ascension/datatypes.h \
    3.58 - ascension/context.h ascension/window.h ascension/font.h \
    3.59 - ascension/error.h ascension/utils.h
    3.60 +$(BUILD_DIR)/text.o: text.c ascension/text.h ascension/font.h \
    3.61 + ascension/datatypes.h ascension/context.h ascension/window.h \
    3.62 + ascension/primitives.h ascension/mesh.h ascension/error.h \
    3.63 + ascension/shader.h
    3.64  	@echo "Compiling $<"
    3.65  	$(CC) -o $@ $(CFLAGS) -c $<
    3.66  
    3.67 +$(BUILD_DIR)/window.o: window.c ascension/window.h ascension/datatypes.h \
    3.68 + ascension/primitives.h ascension/mesh.h ascension/context.h \
    3.69 + ascension/window.h ascension/font.h ascension/error.h ascension/utils.h
    3.70 +	@echo "Compiling $<"
    3.71 +	$(CC) -o $@ $(CFLAGS) -c $<
    3.72 +
     4.1 --- a/src/ascension/ascension.h	Wed Nov 08 23:17:07 2023 +0100
     4.2 +++ b/src/ascension/ascension.h	Wed Nov 15 22:51:40 2023 +0100
     4.3 @@ -31,6 +31,7 @@
     4.4  #include "error.h"
     4.5  #include "context.h"
     4.6  #include "shader.h"
     4.7 +#include "text.h"
     4.8  
     4.9  #endif /* ASCENSION_H */
    4.10  
     5.1 --- a/src/ascension/context.h	Wed Nov 08 23:17:07 2023 +0100
     5.2 +++ b/src/ascension/context.h	Wed Nov 15 22:51:40 2023 +0100
     5.3 @@ -28,6 +28,7 @@
     5.4  #ifndef ASCENSION_CONTEXT_H
     5.5  #define ASCENSION_CONTEXT_H
     5.6  
     5.7 +#include "datatypes.h"
     5.8  #include "window.h"
     5.9  #include "font.h"
    5.10  
    5.11 @@ -50,8 +51,13 @@
    5.12      unsigned int flags;
    5.13      CxBuffer error_buffer;
    5.14      AscWindow windows[ASC_MAX_WINDOWS];
    5.15 +    AscWindow const *active_window;
    5.16      AscFont fonts[ASC_MAX_FONTS];
    5.17      unsigned int fonts_loaded;
    5.18 +    AscFont const *active_font;
    5.19 +    asc_col4i ink;
    5.20 +    unsigned int elapsed_millis;
    5.21 +    float elapsed;
    5.22  } AscContext;
    5.23  
    5.24  /** Global ascension context. */
    5.25 @@ -60,5 +66,25 @@
    5.26  void asc_context_initialize(void);
    5.27  void asc_context_destroy(void);
    5.28  
    5.29 +
    5.30 +/**
    5.31 + * Dispatches events and synchronizes all initialized windows.
    5.32 + *
    5.33 + * @return false, if the application wants to quit, true otherwise
    5.34 + */
    5.35 +bool asc_loop_next(void);
    5.36 +
    5.37 +/**
    5.38 + * Sets the active drawing color.
    5.39 + */
    5.40 +#define asc_ink(color) asc_context.ink = (color)
    5.41 +#define asc_ink_rgba(r,g,b,a) asc_context.ink = (asc_col4i){(r),(g),(b),(a)}
    5.42 +#define asc_ink_rgb(r,g,b) asc_context.ink = (asc_col4i){(r),(g),(b),255u}
    5.43 +
    5.44 +/**
    5.45 + * Sets the active drawing font.
    5.46 + */
    5.47 +#define asc_set_font(font) asc_context.active_font = (font)
    5.48 +
    5.49  #endif /* ASCENSION_CONTEXT_H */
    5.50  
     6.1 --- a/src/ascension/datatypes.h	Wed Nov 08 23:17:07 2023 +0100
     6.2 +++ b/src/ascension/datatypes.h	Wed Nov 15 22:51:40 2023 +0100
     6.3 @@ -33,7 +33,7 @@
     6.4  #endif
     6.5  
     6.6  #include <stdbool.h>
     6.7 -#include <SDL2/SDL_endian.h>
     6.8 +#include <string.h>
     6.9  
    6.10  // --------------------------------------------------------------------------
    6.11  //    Datatype Definitions
    6.12 @@ -48,25 +48,15 @@
    6.13      struct { int width, height; };
    6.14  } asc_vec2i;
    6.15  
    6.16 -typedef union asc_col4i {
    6.17 -    asc_ubyte data[4];
    6.18 -#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    6.19 -    struct { asc_ubyte red, green, blue, alpha; };
    6.20 -#else
    6.21 -    struct { asc_ubyte alpha, blue, green, red; };
    6.22 -#endif
    6.23 +typedef struct asc_col4i {
    6.24 +    asc_ubyte red, green, blue, alpha;
    6.25  } asc_col4i;
    6.26  
    6.27 -typedef union asc_col4f {
    6.28 -    float data[4];
    6.29 -#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    6.30 -    struct { float red, green, blue, alpha; };
    6.31 -#else
    6.32 -    struct { float alpha, blue, green, red; };
    6.33 -#endif
    6.34 +typedef struct asc_col4f {
    6.35 +    float red, green, blue, alpha;
    6.36  } asc_col4f;
    6.37  
    6.38 -typedef float asc_mat4f[4][4];
    6.39 +typedef float asc_mat4f[16];
    6.40  
    6.41  // --------------------------------------------------------------------------
    6.42  //    General Utility Functions
    6.43 @@ -104,6 +94,21 @@
    6.44  //   Matrix Functions
    6.45  // --------------------------------------------------------------------------
    6.46  
    6.47 +/**
    6.48 + * Computes a matrix index in column-major order.
    6.49 + * @param col the column
    6.50 + * @param row the row
    6.51 + * @param rows the number of rows
    6.52 + */
    6.53 +#define asc_mat_index(col, row, rows) ((row) + (col) * (rows))
    6.54 +
    6.55 +/**
    6.56 + * Computes a 4x4 matrix index in column-major order.
    6.57 + * @param col the column
    6.58 + * @param row the row
    6.59 + */
    6.60 +#define asc_mat4_index(col, row) asc_mat_index(col, row, 4)
    6.61 +
    6.62  static inline void asc_mat4f_ortho(
    6.63          asc_mat4f mat,
    6.64          float left,
    6.65 @@ -112,11 +117,12 @@
    6.66          float top
    6.67  ) {
    6.68      memset(mat, 0, sizeof(float) * 16);
    6.69 -    mat[0][0] = 2.f / (right - left);
    6.70 -    mat[1][1] = 2.f / (top - bottom);
    6.71 -    mat[2][2] = -1;
    6.72 -    mat[3][0] = -(right + left) / (right - left);
    6.73 -    mat[3][1] = -(top + bottom) / (top - bottom);
    6.74 +    mat[asc_mat4_index(0,0)] = 2.f / (right - left);
    6.75 +    mat[asc_mat4_index(1,1)] = 2.f / (top - bottom);
    6.76 +    mat[asc_mat4_index(2,2)] = -1;
    6.77 +    mat[asc_mat4_index(3,0)] = -(right + left) / (right - left);
    6.78 +    mat[asc_mat4_index(3,1)] = -(top + bottom) / (top - bottom);
    6.79 +    mat[asc_mat4_index(3,3)] = 1;
    6.80  }
    6.81  
    6.82  #endif //ASCENSION_DATATYPES_H
     7.1 --- a/src/ascension/error.h	Wed Nov 08 23:17:07 2023 +0100
     7.2 +++ b/src/ascension/error.h	Wed Nov 15 22:51:40 2023 +0100
     7.3 @@ -42,6 +42,8 @@
     7.4      unsigned char*: asc_error_cuchar,        \
     7.5      cxstring: asc_error_cxstr)(text)
     7.6  
     7.7 +void asc_error_gl(unsigned code, cxstring message);
     7.8 +
     7.9  bool asc_has_error(void);
    7.10  char const* asc_get_error(void);
    7.11  void asc_clear_error(void);
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/src/ascension/mesh.h	Wed Nov 15 22:51:40 2023 +0100
     8.3 @@ -0,0 +1,37 @@
     8.4 +/*
     8.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     8.6 + * Copyright 2023 Mike Becker. All rights reserved.
     8.7 + *
     8.8 + * Redistribution and use in source and binary forms, with or without
     8.9 + * modification, are permitted provided that the following conditions are met:
    8.10 + *
    8.11 + *   1. Redistributions of source code must retain the above copyright
    8.12 + *      notice, this list of conditions and the following disclaimer.
    8.13 + *
    8.14 + *   2. Redistributions in binary form must reproduce the above copyright
    8.15 + *      notice, this list of conditions and the following disclaimer in the
    8.16 + *      documentation and/or other materials provided with the distribution.
    8.17 + *
    8.18 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    8.19 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    8.20 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    8.21 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    8.22 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    8.23 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    8.24 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    8.25 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    8.26 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    8.27 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    8.28 + * POSSIBILITY OF SUCH DAMAGE.
    8.29 + */
    8.30 +
    8.31 +#ifndef ASCENSION_MESH_H
    8.32 +#define ASCENSION_MESH_H
    8.33 +
    8.34 +typedef struct AscMesh {
    8.35 +    unsigned vbo;
    8.36 +    unsigned vao;
    8.37 +    unsigned vertices;
    8.38 +} AscMesh;
    8.39 +
    8.40 +#endif //ASCENSION_MESH_H
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/src/ascension/primitives.h	Wed Nov 15 22:51:40 2023 +0100
     9.3 @@ -0,0 +1,57 @@
     9.4 +/*
     9.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     9.6 + * Copyright 2023 Mike Becker. All rights reserved.
     9.7 + *
     9.8 + * Redistribution and use in source and binary forms, with or without
     9.9 + * modification, are permitted provided that the following conditions are met:
    9.10 + *
    9.11 + *   1. Redistributions of source code must retain the above copyright
    9.12 + *      notice, this list of conditions and the following disclaimer.
    9.13 + *
    9.14 + *   2. Redistributions in binary form must reproduce the above copyright
    9.15 + *      notice, this list of conditions and the following disclaimer in the
    9.16 + *      documentation and/or other materials provided with the distribution.
    9.17 + *
    9.18 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    9.19 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    9.20 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    9.21 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    9.22 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    9.23 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    9.24 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    9.25 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    9.26 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    9.27 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    9.28 + * POSSIBILITY OF SUCH DAMAGE.
    9.29 + */
    9.30 +
    9.31 +#ifndef ASCENSION_PRIMITIVES_H
    9.32 +#define ASCENSION_PRIMITIVES_H
    9.33 +
    9.34 +#include <stdbool.h>
    9.35 +
    9.36 +#include "mesh.h"
    9.37 +
    9.38 +typedef struct AscPrimitives {
    9.39 +    AscMesh plane;
    9.40 +} AscPrimitives;
    9.41 +
    9.42 +
    9.43 +void asc_primitives_draw_plane(void);
    9.44 +
    9.45 +/**
    9.46 + * Automatically called after initializing the OpenGL context.
    9.47 + *
    9.48 + * @param primitives the data structure to initialize
    9.49 + * @return true on success, false otherwise
    9.50 + */
    9.51 +bool asc_primitives_init(AscPrimitives *primitives);
    9.52 +
    9.53 +/**
    9.54 + * Automatically called when the OpenGL context is destroyed.
    9.55 + *
    9.56 + * @param primitives containing information about the OpenGL objects
    9.57 + */
    9.58 +void asc_primitives_destroy(AscPrimitives *primitives);
    9.59 +
    9.60 +#endif //ASCENSION_PRIMITIVES_H
    10.1 --- a/src/ascension/shader.h	Wed Nov 08 23:17:07 2023 +0100
    10.2 +++ b/src/ascension/shader.h	Wed Nov 15 22:51:40 2023 +0100
    10.3 @@ -34,12 +34,18 @@
    10.4  
    10.5  typedef struct AscShaderProgram {
    10.6      unsigned int id;
    10.7 -    AscShader vertex;
    10.8 -    AscShader fragment;
    10.9 +    int model;
   10.10 +    int view;
   10.11 +    int projection;
   10.12  } AscShaderProgram;
   10.13  
   10.14 +typedef struct AscShaderFont {
   10.15 +    AscShaderProgram base;
   10.16 +    int surface;
   10.17 +} AscShaderFont;
   10.18  
   10.19 -extern AscShaderProgram ASC_SHADER_FONT;
   10.20 +
   10.21 +extern AscShaderFont ASC_SHADER_FONT;
   10.22  
   10.23  
   10.24  /**
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/src/ascension/text.h	Wed Nov 15 22:51:40 2023 +0100
    11.3 @@ -0,0 +1,102 @@
    11.4 +/*
    11.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    11.6 + * Copyright 2023 Mike Becker. All rights reserved.
    11.7 + *
    11.8 + * Redistribution and use in source and binary forms, with or without
    11.9 + * modification, are permitted provided that the following conditions are met:
   11.10 + *
   11.11 + *   1. Redistributions of source code must retain the above copyright
   11.12 + *      notice, this list of conditions and the following disclaimer.
   11.13 + *
   11.14 + *   2. Redistributions in binary form must reproduce the above copyright
   11.15 + *      notice, this list of conditions and the following disclaimer in the
   11.16 + *      documentation and/or other materials provided with the distribution.
   11.17 + *
   11.18 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   11.19 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   11.20 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   11.21 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   11.22 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   11.23 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   11.24 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   11.25 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   11.26 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   11.27 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   11.28 + * POSSIBILITY OF SUCH DAMAGE.
   11.29 + */
   11.30 +
   11.31 +#ifndef ASCENSION_TEXT_H
   11.32 +#define ASCENSION_TEXT_H
   11.33 +
   11.34 +#include "font.h"
   11.35 +#include "datatypes.h"
   11.36 +#include <cx/string.h>
   11.37 +
   11.38 +typedef struct AscTextNode {
   11.39 +    asc_vec2i position;
   11.40 +    asc_vec2i dimension;
   11.41 +    unsigned tex_id;
   11.42 +} AscTextNode;
   11.43 +
   11.44 +
   11.45 +/**
   11.46 + * Draws text on screen.
   11.47 + *
   11.48 + * The current context ink and font will be used.
   11.49 + *
   11.50 + * The text must be zero-terminated (use cx_strdup() e.g. to achieve that) and can
   11.51 + * be freed after the call.
   11.52 + *
   11.53 + * @param node a previously created node or
   11.54 + * @param position the position where to draw the text
   11.55 + * @param max_width the maximum width before breaking the text into new lines
   11.56 + * @param text the text to draw (must be zero-terminated!)
   11.57 + * @see asc_ink()
   11.58 + * @see asc_font()
   11.59 + */
   11.60 +void asc_text_draw_lb(
   11.61 +        AscTextNode *node,
   11.62 +        asc_vec2i position,
   11.63 +        unsigned max_width,
   11.64 +        cxmutstr text);
   11.65 +
   11.66 +/**
   11.67 + * Draws text on screen.
   11.68 + *
   11.69 + * The current context ink and font will be used.
   11.70 + *
   11.71 + * The text must be zero-terminated (use cx_strdup() e.g. to achieve that) and can
   11.72 + * be freed after the call.
   11.73 + *
   11.74 + * @param node a previously created node or
   11.75 + * @param position the position where to draw the text
   11.76 + * @param text the text to draw (must be zero-terminated!)
   11.77 + * @see asc_ink()
   11.78 + * @see asc_font()
   11.79 + */
   11.80 +void asc_text_draw(
   11.81 +        AscTextNode *node,
   11.82 +        asc_vec2i position,
   11.83 +        cxmutstr text);
   11.84 +
   11.85 +/**
   11.86 + * Just redraw the text node in the current frame.
   11.87 + *
   11.88 + * Invoke this method, when the text did not change and the texture does not need
   11.89 + * to be re-generated. You can change the position of the node. When you change the dimension,
   11.90 + * the text will be scaled.
   11.91 + *
   11.92 + * @param node the text node
   11.93 + */
   11.94 +void asc_text_redraw(AscTextNode *node);
   11.95 +
   11.96 +/**
   11.97 + * Releases the graphics memory for this node.
   11.98 + *
   11.99 + * The structure can be reused anytime for other draw calls.
  11.100 + *
  11.101 + * @param node the text node
  11.102 + */
  11.103 +void asc_text_destroy(AscTextNode *node);
  11.104 +
  11.105 +#endif //ASCENSION_TEXT_H
    12.1 --- a/src/ascension/window.h	Wed Nov 08 23:17:07 2023 +0100
    12.2 +++ b/src/ascension/window.h	Wed Nov 15 22:51:40 2023 +0100
    12.3 @@ -31,6 +31,7 @@
    12.4  #include <SDL2/SDL.h>
    12.5  
    12.6  #include "datatypes.h"
    12.7 +#include "primitives.h"
    12.8  
    12.9  #ifndef ASC_MAX_WINDOWS
   12.10  /** The maximum number of windows that can exist simultaneously. */
   12.11 @@ -48,21 +49,14 @@
   12.12  } AscWindowSettings;
   12.13  
   12.14  typedef struct AscWindow {
   12.15 +    Uint32 id;
   12.16      SDL_Window* window;
   12.17      SDL_GLContext glctx;
   12.18 -    Uint32 id;
   12.19 +    AscPrimitives primitives;
   12.20      asc_vec2i dimensions;
   12.21      asc_mat4f projection;
   12.22  } AscWindow;
   12.23  
   12.24 -
   12.25 -/**
   12.26 - * Dispatches events and synchronizes all initialized windows.
   12.27 - *
   12.28 - * @return false, if the application wants to quit, true otherwise
   12.29 - */
   12.30 -bool asc_loop_next(void);
   12.31 -
   12.32  /**
   12.33   * Initializes the settings structure with default values.
   12.34   *
   12.35 @@ -73,6 +67,8 @@
   12.36  /**
   12.37   * Creates and initializes a new window and a corresponding OpenGL context.
   12.38   *
   12.39 + * The new window will also be automatically activated (see asc_window_activate()).
   12.40 + *
   12.41   * The index specified must not be in use by another window already.
   12.42   * The maximum number of windows is defined by #ASC_MAX_WINDOWS.
   12.43   *
   12.44 @@ -85,6 +81,8 @@
   12.45  /**
   12.46   * Destroys the window and its OpenGL context.
   12.47   *
   12.48 + * When this window is currently active, there will be \em no active window afterwards.
   12.49 + *
   12.50   * Still alive windows will also be destroyed by asc_context_destroy()
   12.51   * automatically.
   12.52   *
   12.53 @@ -102,5 +100,15 @@
   12.54   */
   12.55  void asc_window_sync(AscWindow const *window);
   12.56  
   12.57 +/**
   12.58 + * Switches the active window.
   12.59 + *
   12.60 + * In particular that makes the corresponding OpenGL context "current".
   12.61 + * When you only want to draw into one window, you'll never need this.
   12.62 + *
   12.63 + * @param the window to activate
   12.64 + */
   12.65 +void asc_window_activate(AscWindow const *window);
   12.66 +
   12.67  #endif /* ASCENSION_WINDOW_H */
   12.68  
    13.1 --- a/src/context.c	Wed Nov 08 23:17:07 2023 +0100
    13.2 +++ b/src/context.c	Wed Nov 15 22:51:40 2023 +0100
    13.3 @@ -81,3 +81,59 @@
    13.4      asc_context.flags = 0;
    13.5      asc_dprintf("Ascension context destroyed.");
    13.6  }
    13.7 +
    13.8 +static void asc_event_window_resized(Uint32 id, Sint32 width, Sint32 height) {
    13.9 +    for (unsigned int i = 0 ; i < ASC_MAX_WINDOWS ; i++) {
   13.10 +        if (asc_context.windows[i].id == id) {
   13.11 +            asc_context.windows[i].dimensions.width = width;
   13.12 +            asc_context.windows[i].dimensions.height = height;
   13.13 +            asc_mat4f_ortho(asc_context.windows[i].projection, 0, (float) width, (float) height, 0);
   13.14 +            return;
   13.15 +        }
   13.16 +    }
   13.17 +}
   13.18 +
   13.19 +bool asc_loop_next(void) {
   13.20 +    // dispatch SDL events
   13.21 +    SDL_Event event;
   13.22 +    while (SDL_PollEvent(&event)) {
   13.23 +        switch (event.type) {
   13.24 +            case SDL_QUIT:
   13.25 +                asc_set_flag(&asc_context.flags, ASC_FLAG_QUIT);
   13.26 +                break;
   13.27 +            case SDL_WINDOWEVENT: {
   13.28 +                if (event.window.event == SDL_WINDOWEVENT_RESIZED)
   13.29 +                    asc_event_window_resized(
   13.30 +                            event.window.windowID,
   13.31 +                            event.window.data1,
   13.32 +                            event.window.data2
   13.33 +                    );
   13.34 +                break;
   13.35 +            }
   13.36 +            case SDL_KEYDOWN:
   13.37 +                // TODO: remove this code and implement key press map instead
   13.38 +                if (event.key.keysym.sym == SDLK_ESCAPE)
   13.39 +                    return false;
   13.40 +                break;
   13.41 +            case SDL_KEYUP:
   13.42 +                // TODO: implement key press map
   13.43 +                break;
   13.44 +        }
   13.45 +    }
   13.46 +
   13.47 +    // sync the windows
   13.48 +    for (unsigned int i = 0 ; i < ASC_MAX_WINDOWS ; i++) {
   13.49 +        if (asc_context.windows[i].id > 0) {
   13.50 +            asc_window_sync(&asc_context.windows[i]);
   13.51 +        }
   13.52 +    }
   13.53 +
   13.54 +    // compute frame time
   13.55 +    static Uint32 ticks;
   13.56 +    Uint32 ticks_elapsed = SDL_GetTicks() - ticks;
   13.57 +    ticks = SDL_GetTicks();
   13.58 +    asc_context.elapsed_millis = ticks_elapsed;
   13.59 +    asc_context.elapsed = (float) ticks_elapsed / 1000.0f;
   13.60 +
   13.61 +    return !asc_test_flag(asc_context.flags, ASC_FLAG_QUIT);
   13.62 +}
    14.1 --- a/src/error.c	Wed Nov 08 23:17:07 2023 +0100
    14.2 +++ b/src/error.c	Wed Nov 15 22:51:40 2023 +0100
    14.3 @@ -30,6 +30,7 @@
    14.4  #include "ascension/utils.h"
    14.5  
    14.6  #include <cx/buffer.h>
    14.7 +#include <GL/gl.h>
    14.8  
    14.9  void asc_error_cchar(char const* text) {
   14.10      asc_error_cxstr(cx_str(text));
   14.11 @@ -69,3 +70,37 @@
   14.12      cxBufferClear(&asc_context.error_buffer);
   14.13      asc_clear_flag(&asc_context.flags, ASC_FLAG_HAS_ERROR);
   14.14  }
   14.15 +
   14.16 +void asc_error_gl(unsigned code, cxstring message) {
   14.17 +    const char *glerr;
   14.18 +    switch(code) {
   14.19 +        case GL_NO_ERROR:
   14.20 +            return;
   14.21 +        case GL_INVALID_ENUM:
   14.22 +            glerr = "invalid enum";
   14.23 +            break;
   14.24 +        case GL_INVALID_VALUE:
   14.25 +            glerr = "invalid value";
   14.26 +            break;
   14.27 +        case GL_INVALID_OPERATION:
   14.28 +            glerr = "invalid operation";
   14.29 +            break;
   14.30 +        case GL_INVALID_FRAMEBUFFER_OPERATION:
   14.31 +            glerr = "invalid framebuffer operation";
   14.32 +            break;
   14.33 +        case GL_OUT_OF_MEMORY:
   14.34 +            glerr = "out of memory";
   14.35 +            break;
   14.36 +        case GL_STACK_UNDERFLOW:
   14.37 +            glerr = "stack underflow";
   14.38 +            break;
   14.39 +        case GL_STACK_OVERFLOW:
   14.40 +            glerr = "stack overflow";
   14.41 +            break;
   14.42 +        default:
   14.43 +            glerr = "unknown GL error";
   14.44 +    }
   14.45 +    cxmutstr msg = cx_strcat(3, message, CX_STR(" GL Error: "), cx_str(glerr));
   14.46 +    asc_error_cxstr(cx_strcast(msg));
   14.47 +    cx_strfree(&msg);
   14.48 +}
   14.49 \ No newline at end of file
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/src/primitives.c	Wed Nov 15 22:51:40 2023 +0100
    15.3 @@ -0,0 +1,93 @@
    15.4 +/*
    15.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    15.6 + * Copyright 2023 Mike Becker. All rights reserved.
    15.7 + *
    15.8 + * Redistribution and use in source and binary forms, with or without
    15.9 + * modification, are permitted provided that the following conditions are met:
   15.10 + *
   15.11 + *   1. Redistributions of source code must retain the above copyright
   15.12 + *      notice, this list of conditions and the following disclaimer.
   15.13 + *
   15.14 + *   2. Redistributions in binary form must reproduce the above copyright
   15.15 + *      notice, this list of conditions and the following disclaimer in the
   15.16 + *      documentation and/or other materials provided with the distribution.
   15.17 + *
   15.18 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   15.19 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   15.20 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   15.21 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   15.22 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   15.23 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   15.24 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   15.25 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   15.26 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   15.27 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   15.28 + * POSSIBILITY OF SUCH DAMAGE.
   15.29 + */
   15.30 +
   15.31 +#include "ascension/primitives.h"
   15.32 +#include "ascension/error.h"
   15.33 +#include "ascension/context.h"
   15.34 +
   15.35 +#include <string.h>
   15.36 +#include <GL/glew.h>
   15.37 +
   15.38 +static void asc_primitives_init_plane(AscMesh *mesh) {
   15.39 +    asc_dprintf("Create primitive plane in VBO %u and VAO %u", mesh->vbo, mesh->vao);
   15.40 +    mesh->vertices = 4;
   15.41 +    float data[8] = {
   15.42 +            0.0f, 0.0f, // bottom left
   15.43 +            0.0f, 1.0f,    // top left
   15.44 +            1.0f, 0.0f, // bottom right
   15.45 +            1.0f, 1.0f    // top right
   15.46 +    };
   15.47 +    glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
   15.48 +    glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
   15.49 +    glBindVertexArray(mesh->vao);
   15.50 +    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
   15.51 +    glEnableVertexAttribArray(0);
   15.52 +}
   15.53 +
   15.54 +bool asc_primitives_init(AscPrimitives *primitives) {
   15.55 +    asc_dprintf("Create primitives for the GL context of active window.");
   15.56 +    // TODO: more primitives
   15.57 +
   15.58 +    GLuint buffers[1];
   15.59 +    GLuint arrays[1];
   15.60 +    glGenBuffers(1, buffers);
   15.61 +    glGenVertexArrays(1, arrays);
   15.62 +
   15.63 +    AscMesh *plane = &(primitives->plane);
   15.64 +    plane->vbo = buffers[0];
   15.65 +    plane->vao = arrays[0];
   15.66 +    asc_primitives_init_plane(plane);
   15.67 +
   15.68 +    GLenum error = glGetError();
   15.69 +    if (error == GL_NO_ERROR) {
   15.70 +        return true;
   15.71 +    } else {
   15.72 +        asc_error_gl(error, CX_STR("Initialization of primitive meshes failed."));
   15.73 +        return false;
   15.74 +    }
   15.75 +}
   15.76 +
   15.77 +void asc_primitives_destroy(AscPrimitives *primitives) {
   15.78 +    asc_dprintf("Destroy primitives in GL context of active window.");
   15.79 +
   15.80 +    GLuint buffers[1];
   15.81 +    GLuint arrays[1];
   15.82 +
   15.83 +    buffers[0] = primitives->plane.vbo;
   15.84 +    arrays[0] = primitives->plane.vao;
   15.85 +
   15.86 +    glDeleteBuffers(1, buffers);
   15.87 +    glDeleteVertexArrays(1, arrays);
   15.88 +
   15.89 +    memset(primitives, 0, sizeof(AscPrimitives));
   15.90 +}
   15.91 +
   15.92 +void asc_primitives_draw_plane(void) {
   15.93 +    AscMesh const *mesh = &(asc_context.active_window->primitives.plane);
   15.94 +    glBindVertexArray(mesh->vao);
   15.95 +    glDrawArrays(GL_TRIANGLE_STRIP, 0, mesh->vertices);
   15.96 +}
   15.97 \ No newline at end of file
    16.1 --- a/src/shader.c	Wed Nov 08 23:17:07 2023 +0100
    16.2 +++ b/src/shader.c	Wed Nov 15 22:51:40 2023 +0100
    16.3 @@ -30,8 +30,9 @@
    16.4  #include "ascension/error.h"
    16.5  
    16.6  #include <GL/glew.h>
    16.7 +#include <string.h>
    16.8  
    16.9 -AscShaderProgram ASC_SHADER_FONT;
   16.10 +AscShaderFont ASC_SHADER_FONT;
   16.11  
   16.12  
   16.13  AscShader asc_shader_compile(unsigned int type,
   16.14 @@ -93,7 +94,12 @@
   16.15      glDetachShader(id, fragment.id);
   16.16      if (success) {
   16.17          asc_dprintf("Shader Program %u linked (vtf: %u, frag: %u)", id, vertex.id, fragment.id);
   16.18 -        return (AscShaderProgram) {id};
   16.19 +        AscShaderProgram prog;
   16.20 +        prog.id = id;
   16.21 +        prog.model = glGetUniformLocation(id, "model");
   16.22 +        prog.view = glGetUniformLocation(id, "view");
   16.23 +        prog.projection = glGetUniformLocation(id, "projection");
   16.24 +        return prog;
   16.25      } else {
   16.26          char *log = malloc(1024);
   16.27          glGetProgramInfoLog(id, 1024, NULL, log);
   16.28 @@ -131,9 +137,10 @@
   16.29  }
   16.30  
   16.31  void asc_shader_initialize_predefined(void) {
   16.32 -    ASC_SHADER_FONT = asc_shader_compile_link_discard("shader/font_vtx.glsl", "shader/font_frag.glsl");
   16.33 +    ASC_SHADER_FONT.base = asc_shader_compile_link_discard("shader/font_vtx.glsl", "shader/font_frag.glsl");
   16.34 +    ASC_SHADER_FONT.surface = glGetUniformLocation(ASC_SHADER_FONT.base.id, "surface");
   16.35  }
   16.36  
   16.37  void asc_shader_destroy_predefined(void) {
   16.38 -    asc_shader_program_destroy(ASC_SHADER_FONT);
   16.39 +    asc_shader_program_destroy(ASC_SHADER_FONT.base);
   16.40  }
   16.41 \ No newline at end of file
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/src/text.c	Wed Nov 15 22:51:40 2023 +0100
    17.3 @@ -0,0 +1,129 @@
    17.4 +/*
    17.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    17.6 + * Copyright 2023 Mike Becker. All rights reserved.
    17.7 + *
    17.8 + * Redistribution and use in source and binary forms, with or without
    17.9 + * modification, are permitted provided that the following conditions are met:
   17.10 + *
   17.11 + *   1. Redistributions of source code must retain the above copyright
   17.12 + *      notice, this list of conditions and the following disclaimer.
   17.13 + *
   17.14 + *   2. Redistributions in binary form must reproduce the above copyright
   17.15 + *      notice, this list of conditions and the following disclaimer in the
   17.16 + *      documentation and/or other materials provided with the distribution.
   17.17 + *
   17.18 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   17.19 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17.20 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17.21 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   17.22 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   17.23 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   17.24 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   17.25 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   17.26 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   17.27 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   17.28 + * POSSIBILITY OF SUCH DAMAGE.
   17.29 + */
   17.30 +
   17.31 +#include "ascension/text.h"
   17.32 +#include "ascension/context.h"
   17.33 +#include "ascension/error.h"
   17.34 +#include "ascension/shader.h"
   17.35 +
   17.36 +#include <GL/glew.h>
   17.37 +
   17.38 +void asc_text_draw_lb(
   17.39 +        AscTextNode *node,
   17.40 +        asc_vec2i position,
   17.41 +        unsigned max_width,
   17.42 +        cxmutstr text) {
   17.43 +
   17.44 +    // Generate new texture, if required
   17.45 +    if (node->tex_id == 0) {
   17.46 +        glGenTextures(1, &node->tex_id);
   17.47 +        glBindTexture(GL_TEXTURE_RECTANGLE, node->tex_id);
   17.48 +        glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   17.49 +        glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   17.50 +        asc_dprintf("Generated new texture for text node: %u", node->tex_id);
   17.51 +    }
   17.52 +
   17.53 +    // Render text onto a surface
   17.54 +    SDL_Surface *surface = TTF_RenderText_Blended_Wrapped(
   17.55 +            asc_context.active_font->ptr,
   17.56 +            text.ptr,
   17.57 +            (SDL_Color) {
   17.58 +                    .r = asc_context.ink.red,
   17.59 +                    .g = asc_context.ink.green,
   17.60 +                    .b = asc_context.ink.blue,
   17.61 +                    .a = asc_context.ink.alpha
   17.62 +            },
   17.63 +            max_width
   17.64 +    );
   17.65 +    if (surface == NULL) {
   17.66 +        asc_error(SDL_GetError());
   17.67 +        return;
   17.68 +    }
   17.69 +
   17.70 +    // Store basic node information
   17.71 +    node->position = position;
   17.72 +    node->dimension.width = surface->w;
   17.73 +    node->dimension.height = surface->h;
   17.74 +
   17.75 +    // Transfer Image Data
   17.76 +    // TODO: move the image data transfer to a separate function - we will need it more often
   17.77 +    glBindTexture(GL_TEXTURE_RECTANGLE, node->tex_id);
   17.78 +    glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->pitch / surface->format->BytesPerPixel);
   17.79 +    glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA,
   17.80 +                 surface->w, surface->h,
   17.81 +                 0, GL_BGRA, GL_UNSIGNED_BYTE, surface->pixels);
   17.82 +
   17.83 +    // Free the surface
   17.84 +    SDL_FreeSurface(surface);
   17.85 +
   17.86 +    // Redraw the node
   17.87 +    asc_text_redraw(node);
   17.88 +}
   17.89 +
   17.90 +void asc_text_draw(
   17.91 +        AscTextNode *node,
   17.92 +        asc_vec2i position,
   17.93 +        cxmutstr text) {
   17.94 +    unsigned max_width = asc_context.active_window->dimensions.width - position.width;
   17.95 +    asc_text_draw_lb(node, position, max_width, text);
   17.96 +}
   17.97 +
   17.98 +void asc_text_redraw(AscTextNode *node) {
   17.99 +    if (node->tex_id == 0) {
  17.100 +        asc_error("Tried to redraw text node after destruction");
  17.101 +        return;
  17.102 +    }
  17.103 +
  17.104 +    glUseProgram(ASC_SHADER_FONT.base.id);
  17.105 +
  17.106 +    // Upload projection
  17.107 +    // TODO: when we group UI draw calls, we don't need this
  17.108 +    glUniformMatrix4fv(ASC_SHADER_FONT.base.projection, 1, GL_FALSE, asc_context.active_window->projection);
  17.109 +
  17.110 +    // Upload model matrix
  17.111 +    asc_mat4f model = {0};
  17.112 +    model[asc_mat4_index(0, 0)] = node->dimension.width;
  17.113 +    model[asc_mat4_index(1, 1)] = node->dimension.height;
  17.114 +    model[asc_mat4_index(3, 0)] = node->position.x;
  17.115 +    model[asc_mat4_index(3, 1)] = node->position.y;
  17.116 +    model[asc_mat4_index(3, 3)] = 1;
  17.117 +    glUniformMatrix4fv(ASC_SHADER_FONT.base.model, 1, GL_FALSE, model);
  17.118 +
  17.119 +    // Upload surface
  17.120 +    glActiveTexture(GL_TEXTURE0);
  17.121 +    glBindTexture(GL_TEXTURE_RECTANGLE, node->tex_id);
  17.122 +    glUniform1i(ASC_SHADER_FONT.surface, 0);
  17.123 +
  17.124 +    // Draw mesh
  17.125 +    asc_primitives_draw_plane();
  17.126 +}
  17.127 +
  17.128 +void asc_text_destroy(AscTextNode *node) {
  17.129 +    asc_dprintf("Release text node texture: %u", node->tex_id);
  17.130 +    glDeleteTextures(1, &node->tex_id);
  17.131 +    node->tex_id = 0;
  17.132 +}
  17.133 \ No newline at end of file
    18.1 --- a/src/window.c	Wed Nov 08 23:17:07 2023 +0100
    18.2 +++ b/src/window.c	Wed Nov 15 22:51:40 2023 +0100
    18.3 @@ -45,61 +45,11 @@
    18.4      if (type == GL_DEBUG_TYPE_ERROR) {
    18.5          asc_error(buf.ptr);
    18.6      } else {
    18.7 -        asc_dprintf("GL debug: %*.s", (int)buf.length, buf.ptr);
    18.8 +        asc_dprintf("GL debug: %.*s", (int)buf.length, buf.ptr);
    18.9      }
   18.10      cx_strfree(&buf);
   18.11  }
   18.12  
   18.13 -
   18.14 -static void asc_event_window_resized(Uint32 id, Sint32 width, Sint32 height) {
   18.15 -    for (unsigned int i = 0 ; i < ASC_MAX_WINDOWS ; i++) {
   18.16 -        if (asc_context.windows[i].id == id) {
   18.17 -            asc_context.windows[i].dimensions.width = width;
   18.18 -            asc_context.windows[i].dimensions.height = height;
   18.19 -            asc_mat4f_ortho(asc_context.windows[i].projection, 0, (float) width, (float) height, 0);
   18.20 -            return;
   18.21 -        }
   18.22 -    }
   18.23 -}
   18.24 -
   18.25 -bool asc_loop_next(void) {
   18.26 -    // dispatch SDL events
   18.27 -    SDL_Event event;
   18.28 -    while (SDL_PollEvent(&event)) {
   18.29 -        switch (event.type) {
   18.30 -        case SDL_QUIT:
   18.31 -            asc_set_flag(&asc_context.flags, ASC_FLAG_QUIT);
   18.32 -            break;
   18.33 -        case SDL_WINDOWEVENT: {
   18.34 -            if (event.window.type == SDL_WINDOWEVENT_RESIZED)
   18.35 -                asc_event_window_resized(
   18.36 -                        event.window.windowID,
   18.37 -                        event.window.data1,
   18.38 -                        event.window.data2
   18.39 -                );
   18.40 -            break;
   18.41 -        }
   18.42 -        case SDL_KEYDOWN:
   18.43 -            // TODO: remove this code and implement key press map instead
   18.44 -            if (event.key.keysym.sym == SDLK_ESCAPE)
   18.45 -                return false;
   18.46 -            break;
   18.47 -        case SDL_KEYUP:
   18.48 -            // TODO: implement key press map
   18.49 -            break;
   18.50 -        }
   18.51 -    }
   18.52 -
   18.53 -    // sync the windows
   18.54 -    for (unsigned int i = 0 ; i < ASC_MAX_WINDOWS ; i++) {
   18.55 -        if (asc_context.windows[i].id > 0) {
   18.56 -            asc_window_sync(&asc_context.windows[i]);
   18.57 -        }
   18.58 -    }
   18.59 -
   18.60 -    return !asc_test_flag(asc_context.flags, ASC_FLAG_QUIT);
   18.61 -}
   18.62 -
   18.63  void asc_window_settings_init_defaults(AscWindowSettings* settings) {
   18.64      settings->depth_size = 24;
   18.65      settings->vsync = 1;
   18.66 @@ -170,8 +120,14 @@
   18.67              glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   18.68              glEnable(GL_DEBUG_OUTPUT);
   18.69              glDebugMessageCallback(asc_gl_debug_callback, NULL);
   18.70 +
   18.71              asc_dprintf("Window %u initialized", window->id);
   18.72 -            return window;
   18.73 +            if (asc_primitives_init(&window->primitives)) {
   18.74 +                asc_context.active_window = window;
   18.75 +                return window;
   18.76 +            } else {
   18.77 +                asc_dprintf("!!! Creating primitives for window %u failed !!!", window->id);
   18.78 +            }
   18.79          } else {
   18.80              asc_error(glewGetErrorString(err));
   18.81          }
   18.82 @@ -191,6 +147,15 @@
   18.83      // safeguard
   18.84      if (window->id == 0) return;
   18.85  
   18.86 +    // this window cannot be active anymore
   18.87 +    if (asc_context.active_window == window) {
   18.88 +        asc_context.active_window = NULL;
   18.89 +    }
   18.90 +
   18.91 +    // release context related data (we have to make the GL context current for this)
   18.92 +    SDL_GL_MakeCurrent(window->window, window->glctx);
   18.93 +    asc_primitives_destroy(&window->primitives);
   18.94 +
   18.95      // destroy the GL context and the window
   18.96      if (window->glctx != NULL) {
   18.97          SDL_GL_DeleteContext(window->glctx);
   18.98 @@ -199,15 +164,32 @@
   18.99          SDL_DestroyWindow(window->window);
  18.100      }
  18.101  
  18.102 +    // if another window was active, make the other context current again
  18.103 +    if (asc_context.active_window != NULL) {
  18.104 +        AscWindow const *aw = asc_context.active_window;
  18.105 +        SDL_GL_MakeCurrent(aw->window, aw->glctx);
  18.106 +    }
  18.107 +
  18.108      // clean the data
  18.109      asc_dprintf("Window %u and its OpenGL context destroyed.", window->id);
  18.110      memset(window, 0, sizeof(AscWindow));
  18.111  }
  18.112  
  18.113  void asc_window_sync(AscWindow const* window) {
  18.114 -    SDL_GL_MakeCurrent(window->window, window->glctx);
  18.115 +    AscWindow const *active_window = asc_context.active_window;
  18.116 +    if (window != active_window) {
  18.117 +        asc_window_activate(window);
  18.118 +    }
  18.119      SDL_GL_SwapWindow(window->window);
  18.120      glViewport(0, 0, window->dimensions.width, window->dimensions.height);
  18.121      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  18.122      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  18.123 +    if (window != active_window) {
  18.124 +        asc_window_activate(active_window);
  18.125 +    }
  18.126  }
  18.127 +
  18.128 +void asc_window_activate(AscWindow const *window) {
  18.129 +    SDL_GL_MakeCurrent(window->window, window->glctx);
  18.130 +    asc_context.active_window = window;
  18.131 +}
    19.1 --- a/test/Makefile	Wed Nov 08 23:17:07 2023 +0100
    19.2 +++ b/test/Makefile	Wed Nov 15 22:51:40 2023 +0100
    19.3 @@ -40,8 +40,10 @@
    19.4  
    19.5  $(BUILD_DIR)/sandbox.o: sandbox.c ../src/ascension/ascension.h \
    19.6   ../src/ascension/error.h ../src/ascension/context.h \
    19.7 - ../src/ascension/window.h ../src/ascension/datatypes.h \
    19.8 - ../src/ascension/font.h ../src/ascension/shader.h
    19.9 + ../src/ascension/datatypes.h ../src/ascension/window.h \
   19.10 + ../src/ascension/primitives.h ../src/ascension/mesh.h \
   19.11 + ../src/ascension/font.h ../src/ascension/shader.h \
   19.12 + ../src/ascension/text.h
   19.13  	@echo "Compiling $<"
   19.14  	$(CC) -o $@ $(CFLAGS) -c $<
   19.15  
    20.1 --- a/test/sandbox.c	Wed Nov 08 23:17:07 2023 +0100
    20.2 +++ b/test/sandbox.c	Wed Nov 15 22:51:40 2023 +0100
    20.3 @@ -26,6 +26,7 @@
    20.4   */
    20.5  
    20.6  #include <ascension/ascension.h>
    20.7 +#include <cx/printf.h>
    20.8  
    20.9  static bool show_message_box_on_error(SDL_Window* window) {
   20.10      if (asc_has_error()) {
   20.11 @@ -50,13 +51,34 @@
   20.12  
   20.13      AscWindow *window = asc_window_initialize(0, &settings);
   20.14      asc_shader_initialize_predefined();
   20.15 +
   20.16 +    AscTextNode fps_counter = {0};
   20.17 +    unsigned last_fps = 0;
   20.18 +
   20.19      while (asc_loop_next()) {
   20.20          // quit application on any error
   20.21          if (show_message_box_on_error(window->window)) break;
   20.22  
   20.23  
   20.24 +        // fps counter
   20.25 +        if (asc_context.elapsed_millis > 0) {
   20.26 +            unsigned fps = 1000u / asc_context.elapsed_millis;
   20.27 +            if (fps != last_fps) {
   20.28 +                last_fps = fps;
   20.29 +                asc_set_font(asc_font(ASC_FONT_REGULAR, 24));
   20.30 +                asc_ink_rgb(255, 0, 0);
   20.31 +                cxmutstr fpstext = cx_asprintf("%u FPS", fps);
   20.32 +                asc_text_draw(&fps_counter, (asc_vec2i) {50, 50}, fpstext);
   20.33 +                cx_strfree(&fpstext);
   20.34 +            } else {
   20.35 +                asc_text_redraw(&fps_counter);
   20.36 +            }
   20.37 +        }
   20.38      }
   20.39  
   20.40 +    // TODO: maybe nodes should also be "garbage collected"
   20.41 +    asc_text_destroy(&fps_counter);
   20.42 +
   20.43      asc_context_destroy();
   20.44      return 0;
   20.45  }

mercurial