medfall

A super great game engine
Log | Files | Refs

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 }