commit 668817ad689e352ecad59415edec3e6fba2c5c12 parent d846293606855fd89e46848cc8fb6b4a3d7bdf3d Author: Michael Savage <mikejsavage@gmail.com> Date: Sat Dec 3 21:01:50 +0200 New font renderer Diffstat:
.gitignore | | | 1 | - |
LiberationSans-Regular.ttf | | | 0 | |
Makefile | | | 4 | ++-- |
game.h | | | 8 | -------- |
hm.cc | | | 97 | +++++-------------------------------------------------------------------------- |
main.cc | | | 3 | +++ |
text_renderer.cc | | | 166 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
text_renderer.h | | | 5 | +++++ |
diff --git a/.gitignore b/.gitignore @@ -22,7 +22,6 @@ models *.parts *.xoj *.wav -*.ttf *.lz4 .deps diff --git a/LiberationSans-Regular.ttf b/LiberationSans-Regular.ttf Binary files differ. diff --git a/Makefile b/Makefile @@ -6,7 +6,7 @@ VISITORS := visitors/linear_algebra.h all: $(VISITORS) $(BINS) $(MODULES) test_lockfree # Binary dependencies -medfall: main.o gl.o log.o memory_arena.o glad.o +medfall: main.o gl.o log.o memory_arena.o renderer.o immediate.o text_renderer.o stb_truetype.o glad.o pp: pp.o log.o memory_arena.o stb_image.o lz4.o lz4hc.o strlcpy.o sound: audio.o mixer.o log.o memory_arena.o wave.o platform_audio_output.o srv: server/main.o rng/well512.o @@ -30,7 +30,7 @@ xxhash.o: CXXFLAGS += -O3 visitors/linear_algebra.h: linear_algebra.h # Common dependencies -COMMON_OBJS := log.o memory_arena.o work_queue.o immediate.o benchmark.o stb_truetype.o renderer.o strlcpy.o +COMMON_OBJS := log.o memory_arena.o work_queue.o immediate.o benchmark.o text_renderer.o stb_truetype.o renderer.o strlcpy.o # Compiler flags WARNINGS := -Wall -Wextra -Wno-unused-parameter -Wno-unused-function -Wconversion -Wshadow -Wcast-align -Wstrict-overflow -Werror=switch-enum diff --git a/game.h b/game.h @@ -8,7 +8,6 @@ #include "linear_algebra.h" #include "renderer.h" #include "benchmark.h" -#include "assets.h" #include "terrain_manager.h" #include "btt.h" #include "gpubtt.h" @@ -20,7 +19,6 @@ #include "work_queue.h" #include "memory_arena.h" #include "keys.h" -#include "stb_truetype.h" const int WIDTH = 1024; const int HEIGHT = 768; @@ -43,9 +41,6 @@ struct GameState { Shader test_shader; GLint test_un_VP; - Shader font_shader; - GLint font_un_atlas; - Shader test_tex_shader; GLint test_tex_un_tex; @@ -61,9 +56,6 @@ struct GameState { Heightmap hm; ImmediateContext test_immediate; - - Asset assets[ ASSET_COUNT ]; - stbtt_bakedchar test_chars[ 256 ]; }; struct GameMemory { diff --git a/hm.cc b/hm.cc @@ -10,7 +10,7 @@ #include "terrain_manager.h" #include "skybox.h" #include "work_queue.h" -#include "stb_truetype.h" +#include "text_renderer.h" static const char * vert_src = GLSL( in vec3 position; @@ -62,35 +62,6 @@ static const char * textured_frag_src = GLSL( } ); -static const char * font_vert_src = GLSL( - in vec3 position; - in vec4 colour; - in vec2 tex_coord0; - - out vec4 frag_colour; - out vec2 frag_uv; - - void main() { - gl_Position = vec4( position, 1.0 ); - frag_colour = colour; - frag_uv = tex_coord0; - } -); - -static const char * font_frag_src = GLSL( - in vec4 frag_colour; - in vec2 frag_uv; - - uniform sampler2D atlas; - - out vec4 screen_colour; - - void main() { - float r = texture( atlas, frag_uv ).r; - screen_colour = vec4( frag_colour.rgb, frag_colour.a * r ); - } -); - static v3 angles_to_vector( v3 angles ) { return v3( -sin( angles.y ) * sin( angles.x ), @@ -160,66 +131,11 @@ extern "C" GAME_INIT( game_init ) { red ); - // TODO - // u8 * arial = file_get_contents( "Arial.ttf" ); - // - // u8 * font_memory = memarena_push_size( &mem->persistent_arena, 512 * 256 ); - // const int offset = stbtt_GetFontOffsetForIndex( arial, 0 ); - // const int ok = stbtt_BakeFontBitmap( arial, offset, 48, font_memory, 512, 512, ' ', 127 - ' ', game->test_chars ); - // assert( ok > 0 ); - // - // free( arial ); - // - // BitmapData font_bitmap = { }; - // font_bitmap.width = 512; - // font_bitmap.height = 256; - // font_bitmap.data = font_memory; - // - // Asset font_asset = { }; - // font_asset.type = AT_BITMAP; - // font_asset.bitmap = font_bitmap; - // - // glGenTextures( 1, &font_asset.texture ); - // glBindTexture( GL_TEXTURE_2D, font_asset.texture ); - // glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - // glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - // glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, font_bitmap.width, font_bitmap.height, 0, GL_RED, GL_UNSIGNED_BYTE, font_memory ); - // - // game->assets[ ASSET_FONT ] = font_asset; - game->test_tex_shader = renderer_new_shader( textured_vert_src, textured_frag_src ); game->test_tex_un_tex = glGetUniformLocation( game->test_tex_shader, "tex" ); - game->font_shader = renderer_new_shader( font_vert_src, font_frag_src ); - game->font_un_atlas = glGetUniformLocation( game->font_shader, "atlas" ); - skybox_init( &game->skybox ); -} - -static void draw_string( const GameState * game, float x, float y, const char * str ) { - const float scale = 1.0f / 32.0f / 15.0f; - const v4 white( 1, 1, 1, 1 ); - ImmediateContext imm; - ImmediateTriangle triangles[ 256 ]; - immediate_init( &imm, triangles, ARRAY_COUNT( triangles ) ); - - while( *str != '\0' ) { - stbtt_aligned_quad q; - stbtt_GetBakedQuad( game->test_chars, 512, 256, *str - ' ', &x, &y, &q, 1 ); - - const ImmediateVertex v1A = { v3( q.x0 * scale, -q.y0 * scale, 0 ), white, v2( q.s0, q.t0 ) }; - const ImmediateVertex v2A = { v3( q.x1 * scale, -q.y0 * scale, 0 ), white, v2( q.s1, q.t0 ) }; - const ImmediateVertex v3A = { v3( q.x0 * scale, -q.y1 * scale, 0 ), white, v2( q.s0, q.t1 ) }; - const ImmediateVertex v4A = { v3( q.x1 * scale, -q.y1 * scale, 0 ), white, v2( q.s1, q.t1 ) }; - - immediate_triangle( &imm, v1A, v2A, v3A ); - immediate_triangle( &imm, v3A, v2A, v4A ); - - str++; - } - - immediate_render( &imm, game->font_shader, game->assets[ ASSET_FONT ].texture ); } static m4 camera_to_view( v3 position, v3 angles ) { @@ -264,18 +180,17 @@ extern "C" GAME_FRAME( game_frame ) { immediate_render_state.depth_func = DEPTHFUNC_DISABLED; immediate_render( &game->test_immediate, immediate_render_state ); - glDisable( GL_DEPTH_TEST ); glEnable( GL_BLEND ); - glUseProgram( game->test_tex_shader ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); ImmediateContext imm; ImmediateTriangle asdf[ 32 ]; immediate_init( &imm, asdf, 32 ); - // TODO: the positions don't make sense - // draw_string( game, -140, -100, "hello" ); + { + char buf[ 128 ]; + snprintf( buf, sizeof( buf ), "Frame time: %.1fms FPS: %.1f", dt * 1000.0f, 1.0f / dt ); + draw_text( buf, 2.0f, 2.0f, 16.0f ); + } - glUseProgram( 0 ); glDisable( GL_BLEND ); - glEnable( GL_DEPTH_TEST ); } diff --git a/main.cc b/main.cc @@ -14,6 +14,7 @@ #include "intrinsics.h" #include "gl.h" #include "keys.h" +#include "text_renderer.h" #include "platform_library.h" struct Game { @@ -107,6 +108,7 @@ int main( int argc, char ** argv ) { mem.state = state; GLFWwindow * window = gl_init(); + text_renderer_init(); #if STATIC_GAME game_init( state, &mem ); @@ -183,6 +185,7 @@ int main( int argc, char ** argv ) { unload_game( &game ); #endif + text_renderer_term(); gl_term(); printf( "FPS: %.1f\n", total_frames / program_run_time ); diff --git a/text_renderer.cc b/text_renderer.cc @@ -0,0 +1,166 @@ +#include "intrinsics.h" +#include "immediate.h" +#include "renderer.h" +#include "linear_algebra.h" +#include "stb_truetype.h" + +static const char * vert_src = GLSL( + in vec3 position; + in vec4 colour; + in vec2 tex_coord0; + + out vec4 frag_colour; + out vec2 frag_uv; + + void main() { + vec3 screen_position = vec3( + ( position.x / 1024.0 * 2.0 - 1.0 ), + -( position.y / 800.0 * 2.0 - 1.0 ), + position.z + ); + gl_Position = vec4( screen_position, 1.0 ); + frag_colour = colour; + frag_uv = tex_coord0; + } +); + +static const char * frag_src = GLSL( + in vec4 frag_colour; + in vec2 frag_uv; + + uniform sampler2D atlas; + + out vec4 screen_colour; + + void main() { + float r = texture( atlas, frag_uv ).r; + screen_colour = vec4( frag_colour.rgb, frag_colour.a * r ); + } +); + +struct FontSize { + float pixel_size; + float ascent; + Texture atlas; + stbtt_bakedchar baked_chars[ 256 ]; +}; + +static u8 * ttf; +static stbtt_fontinfo font_info; + +static const float pixel_sizes[] = { 16.0f, 32.0f, 48.0f }; +static constexpr size_t num_sizes = ARRAY_COUNT( pixel_sizes ); +static FontSize sizes[ num_sizes ]; + +static Shader shader; + +void text_renderer_init() { + ttf = file_get_contents( "LiberationSans-Regular.ttf" ); + + int offset = stbtt_GetFontOffsetForIndex( ttf, 0 ); + { + int ok = stbtt_InitFont( &font_info, ttf, offset ); + ASSERT( ok != 0 ); + } + + int ascent; + stbtt_GetFontVMetrics( &font_info, &ascent, NULL, NULL ); + + for( size_t i = 0; i < num_sizes; i++ ) { + const u32 width = 512; + const u32 height = 256; + u8 texture[ width * height ]; + float pixel_size = pixel_sizes[ i ]; + + int ok = stbtt_BakeFontBitmap( ttf, offset, pixel_size, texture, width, height, ' ', 127 - ' ', sizes[ i ].baked_chars ); + ASSERT( ok != 0 ); + + float scale = stbtt_ScaleForPixelHeight( &font_info, pixel_size ); + + TextureConfig texture_config = { }; + texture_config.width = width; + texture_config.height = height; + texture_config.format = TEXFMT_R_U8; + texture_config.data = texture; + texture_config.data_size = sizeof( texture ); + + sizes[ i ].pixel_size = pixel_size; + sizes[ i ].ascent = ascent * scale; + sizes[ i ].atlas = renderer_new_texture( texture_config ); + } + + ShaderConfig shader_config = { }; + shader_config.vertex_src = vert_src; + shader_config.fragment_src = frag_src; + shader_config.texture_uniform_names[ 0 ] = "atlas"; + shader = renderer_new_shader( shader_config ); +} + +static ImmediateTriangle triangles[ 4096 ]; + +void draw_text( const char * str, float x, float y, float pixel_size ) { + const v4 white( 1, 1, 1, 1 ); + + ImmediateContext imm; + immediate_init( &imm, triangles, ARRAY_COUNT( triangles ) ); + + size_t size_idx = 0; + for( size_t i = 0; i < num_sizes; i++ ) { + size_idx = i; + if( pixel_size <= sizes[ i ].pixel_size ) { + break; + } + } + + float scale = pixel_size / sizes[ size_idx ].pixel_size; + float ascent = sizes[ size_idx ].ascent * scale; + float left = x; + float top = y + ascent; + + while( *str != '\0' ) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad( sizes[ size_idx ].baked_chars, 512, 256, *str - ' ', &x, &y, &q, 1 ); + + ImmediateVertex tl = { + v3( left + scale * ( q.x0 - left ), top + scale * ( q.y0 - y ), 0 ), + white, + v2( q.s0, q.t0 ), + }; + ImmediateVertex tr = { + v3( left + scale * ( q.x1 - left ), top + scale * ( q.y0 - y ), 0 ), + white, + v2( q.s1, q.t0 ), + }; + ImmediateVertex bl = { + v3( left + scale * ( q.x0 - left ), top + scale * ( q.y1 - y ), 0 ), + white, + v2( q.s0, q.t1 ), + }; + ImmediateVertex br = { + v3( left + scale * ( q.x1 - left ), top + scale * ( q.y1 - y ), 0 ), + white, + v2( q.s1, q.t1 ), + }; + + immediate_triangle( &imm, tl, tr, bl ); + immediate_triangle( &imm, bl, tr, br ); + + str++; + } + + RenderState render_state = { }; + render_state.shader = shader; + render_state.textures[ 0 ] = sizes[ size_idx ].atlas; + render_state.depth_func = DEPTHFUNC_DISABLED; + // render_state.disable_depth_writes = true; + + immediate_render( &imm, render_state ); +} + +void text_renderer_term() { + free( ttf ); + // renderer_delete_shader( shader ); + for( size_t i = 0; i < num_sizes; i++ ) { + renderer_delete_texture( sizes[ i ].atlas ); + } +} diff --git a/text_renderer.h b/text_renderer.h @@ -0,0 +1,5 @@ +#pragma once + +void text_renderer_init(); +void draw_text( const char * str, float x, float y, float pixel_size ); +void text_renderer_term();