u32 elements;
};
+struct
+{
+ GLuint vao;
+ GLuint vbo;
+ GLuint ebo;
+
+ u32
+ title_start, title_count,
+ desc_start, desc_count,
+ score_start, score_count,
+ time_start, time_count
+ ;
+
+ #pragma pack(push,1)
+ struct vector_glyph_vert
+ {
+ v2f co;
+ v2f uv;
+
+ u32 colour;
+ }
+ *buffer;
+ #pragma pack(pop)
+
+ u16 *indices;
+}
+text_buffers;
+
struct world
{
#pragma pack(push,1)
*data;
#pragma pack(pop)
-
int initialzed;
int sim_run, max_runs;
}
}
+static u32 gen_text_buffer( const char *str, struct sdf_font *font, v2f origin, float size, u32 start )
+{
+ u32 count = 0;
+
+ v2f cursor;
+ v2f invUv;
+ v2_copy( origin, cursor );
+
+ float invScale = (size / (float)font->size);
+ invUv[0] = 1.0f / (float)font->width;
+ invUv[1] = 1.0f / (float)font->height;
+
+ const char *_c = str;
+ char c;
+ while( (c = *(_c ++)) )
+ {
+ if( c == '\n' )
+ {
+ cursor[1] += size * 1.25f;
+ cursor[0] = origin[0];
+ }
+ else if( c >= 32 && c <= 126 )
+ {
+ struct sdf_char *pch = &font->characters[ c - ' ' ];
+ struct vector_glyph_vert *vt = &text_buffers.buffer[ count * 4 ];
+ u16 *ind = &text_buffers.indices[ count * 6 ];
+
+ // Emit quad
+ v2f p0; v2f uv0;
+ v2f p1; v2f uv1;
+
+ v2_muladds( cursor, (v2f){ pch->originX, -pch->originY }, -invScale, p0 );
+ v2_muladds( p0, (v2f){ pch->w, -pch->h }, invScale, p1 );
+
+ v2_mul( (v2f){ pch->uvx, pch->uvy }, invUv, uv0 );
+ v2_muladd( uv0, (v2f){ pch->w, pch->h }, invUv, uv1 );
+
+ v2_copy( p0, vt[0].co );
+ v2_copy( uv0, vt[0].uv );
+ vt[0].colour = 0xffffffff;
+
+ v2_copy( (v2f){ p0[0], p1[1] }, vt[1].co );
+ v2_copy( (v2f){ uv0[0], uv1[1] }, vt[1].uv );
+ vt[1].colour = 0xffffffff;
+
+ v2_copy( p1, vt[2].co );
+ v2_copy( uv1, vt[2].uv );
+ vt[2].colour = 0xffffffff;
+
+ v2_copy( (v2f){ p1[0], p0[1] }, vt[3].co );
+ v2_copy( (v2f){ uv1[0], uv0[1] }, vt[3].uv );
+ vt[3].colour = 0xffffffff;
+
+ // Emit indices
+ ind[0] = count*4;
+ ind[1] = count*4+1;
+ ind[2] = count*4+2;
+ ind[3] = count*4;
+ ind[4] = count*4+2;
+ ind[5] = count*4+3;
+
+ cursor[0] += (float)pch->advance * invScale;
+ count ++;
+ }
+ }
+
+ glBindVertexArray( text_buffers.vao );
+
+ glBindBuffer( GL_ARRAY_BUFFER, text_buffers.vbo );
+ glBufferSubData( GL_ARRAY_BUFFER,
+ start*4*sizeof( struct vector_glyph_vert ),
+ count*4*sizeof( struct vector_glyph_vert ),
+ text_buffers.buffer
+ );
+
+ glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, text_buffers.ebo );
+ glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, start*6*sizeof(u16), count*6*sizeof( u16 ), text_buffers.indices );
+
+ return count;
+}
+
+static void gen_level_text( struct cmp_level *pLevel )
+{
+ text_buffers.title_count = gen_text_buffer( pLevel->title, &font_Ubuntu, (v2f){ 0.0f, 0.0f }, 1.0f, text_buffers.title_start );
+ text_buffers.desc_count = gen_text_buffer( pLevel->description, &font_Ubuntu, (v2f){ 0.0f, 0.0f }, 0.5f, text_buffers.desc_start );
+ text_buffers.desc_count = 0;
+}
+
static int map_load( const char *str, const char *name )
{
//TODO: It may be worthwhile, at this point, to switch to binary encoding for save data
if( lvl->serial_id == encoded.in_map )
{
if( console_changelevel( 1, &lvl->map_name ) )
+ {
world.pCmpLevel = lvl;
+ gen_level_text( world.pCmpLevel );
+ }
}
}
}
{
career_pass_level( world.pCmpLevel, world.score, 1 );
}
+
+ sfx_set_play( &audio_tones, &audio_system_balls_extra, 9 );
+ failure_this_frame = 0;
+ success_this_frame = 0;
}
}
else
if( console_changelevel( 1, &switch_level_to->map_name ) )
{
world.pCmpLevel = switch_level_to;
+ gen_level_text( world.pCmpLevel );
}
}
}
level_selection_buttons();
+ // TEXT ELEMENTS
+ // ========================================================================================================
+ SHADER_USE( shader_sdf );
+ glBindVertexArray( text_buffers.vao );
+ glUniformMatrix3fv( SHADER_UNIFORM( shader_sdf, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
+
+ vg_tex2d_bind( &tex_ubuntu, 0 );
+ glUniform1i( SHADER_UNIFORM( shader_sdf, "uTexGlyphs" ), 0 );
+
+ glDrawElements( GL_TRIANGLES, text_buffers.title_count*6, GL_UNSIGNED_SHORT, (void*)( text_buffers.title_start*6*sizeof(u16) ) );
+ glDrawElements( GL_TRIANGLES, text_buffers.desc_count*6, GL_UNSIGNED_SHORT, (void*)( text_buffers.desc_start*6*sizeof(u16) ) );
+
// WIRES
// ========================================================================================================
//glDisable(GL_BLEND);
resource_load_main();
+ // Create text buffers
+ {
+ // Work out the counts for each 'segment'
+ u32 desc_max_size = 0, title_max_size = 0,
+ score_max_size = 10,
+ time_max_size = 10
+ ;
+
+ for( int i = 0; i < vg_list_size( career_serializable ); i ++ )
+ {
+ struct serializable_set *set = &career_serializable[i];
+ for( int j = 0; j < set->count; j ++ )
+ {
+ struct cmp_level *lvl = &set->pack[j];
+
+ desc_max_size = VG_MAX( desc_max_size, strlen( lvl->description ) );
+ title_max_size = VG_MAX( title_max_size, strlen( lvl->title ) );
+ }
+ }
+
+ // Full buffer
+ u32 total_characters =
+ title_max_size +
+ desc_max_size +
+ score_max_size +
+ time_max_size;
+
+ u32 total_faces = total_characters * 2,
+ total_vertices = total_characters * 4,
+ total_indices = total_faces * 3;
+
+ // Working buffer
+ u32 work_buffer_total_chars =
+ VG_MAX( VG_MAX( desc_max_size, title_max_size ), VG_MAX( score_max_size, time_max_size ) );
+ u32 total_work_faces = work_buffer_total_chars * 2,
+ total_work_vertices = work_buffer_total_chars * 4,
+ total_work_indices = total_work_faces * 3;
+
+ text_buffers.title_count = 0;
+ text_buffers.desc_count = 0;
+ text_buffers.score_count = 0;
+ text_buffers.time_count = 0;
+
+ // Calculate offsets
+ text_buffers.title_start = 0;
+ text_buffers.desc_start = title_max_size;
+ text_buffers.score_start = text_buffers.desc_start + desc_max_size;
+ text_buffers.time_start = text_buffers.score_start + score_max_size;
+
+ // Opengl
+ glGenVertexArrays(1, &text_buffers.vao);
+ glGenBuffers( 1, &text_buffers.vbo );
+ glGenBuffers( 1, &text_buffers.ebo );
+ glBindVertexArray( text_buffers.vao );
+
+ glBindBuffer( GL_ARRAY_BUFFER, text_buffers.vbo );
+ glBufferData( GL_ARRAY_BUFFER, total_vertices * sizeof( struct vector_glyph_vert ), NULL, GL_DYNAMIC_DRAW );
+
+ glBindVertexArray( text_buffers.vao );
+
+ glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, text_buffers.ebo );
+ glBufferData( GL_ELEMENT_ARRAY_BUFFER, total_indices * sizeof( u16 ), NULL, GL_DYNAMIC_DRAW );
+
+ u32 const stride = sizeof( struct vector_glyph_vert );
+
+ // XY
+ glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, stride, (void *)offsetof( struct vector_glyph_vert, co ) );
+ glEnableVertexAttribArray( 0 );
+
+ // UV
+ glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, stride, (void *)offsetof( struct vector_glyph_vert, uv ) );
+ glEnableVertexAttribArray( 1 );
+
+ // COLOUR
+ glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void *)offsetof( struct vector_glyph_vert, colour ) );
+ glEnableVertexAttribArray( 2 );
+
+ // Offline memory
+ text_buffers.buffer = (struct vector_glyph_vert *)malloc( total_work_vertices * sizeof(struct vector_glyph_vert) );
+ text_buffers.indices = (u16*)malloc( total_work_indices * sizeof(u16) );
+ }
+
// Restore gamestate
career_local_data_init();
career_load();
console_save_map( 0, NULL );
career_serialize();
+ glDeleteVertexArrays( 1, &text_buffers.vao );
+ glDeleteBuffers( 1, &text_buffers.vbo );
+ glDeleteBuffers( 1, &text_buffers.ebo );
+
+ free( text_buffers.buffer );
+ free( text_buffers.indices );
+
resource_free_main();
glDeleteTextures( 1, &world.background_data );