text_renderer.cc (4344B)
1 #include "intrinsics.h" 2 #include "memory_arena.h" 3 #include "immediate.h" 4 #include "gl.h" 5 #include "renderer.h" 6 #include "shaders.h" 7 #include "linear_algebra.h" 8 #include "liberation.h" 9 10 #include "libs/stb/stb_truetype.h" 11 12 struct FontSize { 13 float pixel_size; 14 float ascent; 15 stbtt_packedchar baked_chars[ 256 ]; 16 }; 17 18 constexpr u32 atlas_width = 1024; 19 constexpr u32 atlas_height = 512; 20 static Texture atlas; 21 22 static const float pixel_sizes[] = { 16.0f, 32.0f, 48.0f }; 23 static FontSize sizes[ ARRAY_COUNT( pixel_sizes ) ]; 24 25 void text_renderer_init( MemoryArena * arena ) { 26 MEMARENA_SCOPED_CHECKPOINT( arena ); 27 28 u8 * ttf = ( u8 * ) LiberationSans_Regular_ttf; 29 30 stbtt_fontinfo font_info; 31 int offset = stbtt_GetFontOffsetForIndex( ttf, 0 ); 32 { 33 int ok = stbtt_InitFont( &font_info, ttf, offset ); 34 ASSERT( ok != 0 ); 35 } 36 37 int ascent; 38 stbtt_GetFontVMetrics( &font_info, &ascent, NULL, NULL ); 39 40 u8 * texture = memarena_push_many( arena, u8, atlas_width * atlas_height ); 41 stbtt_pack_context pack_context; 42 int begin_ok = stbtt_PackBegin( &pack_context, texture, atlas_width, atlas_height, 0, 1, NULL ); 43 if( begin_ok == 0 ) 44 FATAL( "stbtt_PackBegin" ); 45 46 for( size_t i = 0; i < ARRAY_COUNT( sizes ); i++ ) { 47 stbtt_PackSetOversampling( &pack_context, 4, 1 ); 48 int ok = stbtt_PackFontRange( &pack_context, ttf, 0, pixel_sizes[ i ], ' ', 127 - ' ', sizes[ i ].baked_chars ); 49 ASSERT( ok != 0 ); 50 51 float scale = stbtt_ScaleForPixelHeight( &font_info, pixel_sizes[ i ] ); 52 sizes[ i ].pixel_size = pixel_sizes[ i ]; 53 sizes[ i ].ascent = ascent * scale; 54 } 55 56 stbtt_PackEnd( &pack_context ); 57 58 TextureConfig texture_config; 59 texture_config.width = atlas_width; 60 texture_config.height = atlas_height; 61 texture_config.format = TEXFMT_R_U8; 62 texture_config.data = texture; 63 texture_config.data_size = sizeof( texture ); 64 atlas = renderer_new_texture( texture_config ); 65 } 66 67 void text_renderer_term() { 68 renderer_delete_texture( atlas ); 69 } 70 71 float text_width( const char * str, float pixel_size ) { 72 size_t size_idx = 0; 73 for( size_t i = 0; i < ARRAY_COUNT( sizes ); i++ ) { 74 size_idx = i; 75 if( pixel_size <= sizes[ i ].pixel_size ) { 76 break; 77 } 78 } 79 80 float scale = pixel_size / sizes[ size_idx ].pixel_size; 81 float width = 0; 82 83 while( *str != '\0' ) { 84 stbtt_aligned_quad q; 85 float y = 0; 86 stbtt_GetPackedQuad( sizes[ size_idx ].baked_chars, atlas_width, atlas_height, *str - ' ', &width, &y, &q, 0 ); 87 88 str++; 89 } 90 91 return width * scale; 92 } 93 94 void draw_text( u8 pass, const char * str, int x, int y, float pixel_size ) { 95 const v4 white( 1, 1, 1, 1 ); 96 97 size_t size_idx = 0; 98 for( size_t i = 0; i < ARRAY_COUNT( sizes ); i++ ) { 99 size_idx = i; 100 if( pixel_size <= sizes[ i ].pixel_size ) { 101 break; 102 } 103 } 104 105 float scale = pixel_size / sizes[ size_idx ].pixel_size; 106 float ascent = sizes[ size_idx ].ascent * scale; 107 float left = float( x ); 108 float top = float( y ) + ascent; 109 110 float fx = float( x ); 111 float fy = float( y ); 112 113 while( *str != '\0' ) { 114 stbtt_aligned_quad q; 115 stbtt_GetPackedQuad( sizes[ size_idx ].baked_chars, atlas_width, atlas_height, *str - ' ', &fx, &fy, &q, 0 ); 116 117 ImmediateVertex tl = { 118 v3( left + scale * ( q.x0 - left ), top + scale * ( q.y0 - y ), 0 ), 119 v3( 0 ), 120 white, 121 v2( q.s0, q.t0 ), 122 }; 123 ImmediateVertex tr = { 124 v3( left + scale * ( q.x1 - left ), top + scale * ( q.y0 - y ), 0 ), 125 v3( 0 ), 126 white, 127 v2( q.s1, q.t0 ), 128 }; 129 ImmediateVertex bl = { 130 v3( left + scale * ( q.x0 - left ), top + scale * ( q.y1 - y ), 0 ), 131 v3( 0 ), 132 white, 133 v2( q.s0, q.t1 ), 134 }; 135 ImmediateVertex br = { 136 v3( left + scale * ( q.x1 - left ), top + scale * ( q.y1 - y ), 0 ), 137 v3( 0 ), 138 white, 139 v2( q.s1, q.t1 ), 140 }; 141 142 immediate_triangle( tr, tl, bl ); 143 immediate_triangle( bl, br, tr ); 144 145 str++; 146 } 147 148 RenderState render_state; 149 render_state.pass = pass; 150 render_state.shader = get_shader( SHADER_TEXT ); 151 render_state.set_uniform( "window", renderer_uniforms( get_window_size() ) ); 152 render_state.set_texture( "atlas", atlas ); 153 render_state.depth_func = DEPTHFUNC_DISABLED; 154 render_state.enable_alpha_blending = true; 155 // render_state.disable_depth_writes = true; 156 157 immediate_render( render_state ); 158 } 159 160 void draw_centered_text( u8 pass, const char * str, int x, int y, float pixel_size ) { 161 float w = text_width( str, pixel_size ); 162 draw_text( pass, str, x - w / 2.0f, y, pixel_size ); 163 }