Wed, 15 Nov 2023 22:51:40 +0100
add text rendering and demo FPS counter
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 }