basic world text
[fishladder.git] / fishladder.c
index fcdd58d4fe014c5d4ae3de204315e036e7a29af6..9a567df86c7874e6f65b3cc3a6228a33a1e8ed21 100644 (file)
@@ -132,6 +132,34 @@ struct mesh
        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)
@@ -145,7 +173,6 @@ struct world
        *data;
 #pragma pack(pop)
        
-       
        int initialzed;
        
        int sim_run, max_runs;
@@ -479,6 +506,94 @@ static void map_reclassify( v2i start, v2i end, int update_texbuffer )
        }
 }
 
+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
@@ -1044,7 +1159,10 @@ static void career_load(void)
                        if( lvl->serial_id == encoded.in_map )
                        {
                                if( console_changelevel( 1, &lvl->map_name ) )
+                               {
                                        world.pCmpLevel = lvl;
+                                       gen_level_text( world.pCmpLevel );
+                               }
                        }
                }
        }
@@ -1768,6 +1886,10 @@ void vg_update(void)
                                                {
                                                        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
@@ -2187,6 +2309,7 @@ static void level_selection_buttons(void)
                if( console_changelevel( 1, &switch_level_to->map_name ) )
                {
                        world.pCmpLevel = switch_level_to;
+                       gen_level_text( world.pCmpLevel );
                }
        }
 }
@@ -2397,6 +2520,18 @@ void vg_render(void)
        
        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);
@@ -3393,6 +3528,88 @@ void vg_start(void)
        
        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();
@@ -3404,6 +3621,13 @@ void vg_free(void)
        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 );