+ui_ctx ui_global_ctx = {
+ .padding = 8,
+ .colours_main = &ui_default_colours
+};
+
+
+// Initialization
+// ===========================================================================================================
+
+static void ui_reset_colours( ui_ctx *ctx );
+static void ui_init_context( ui_ctx *ctx, int index_buffer_size )
+{
+ ui_reset_colours( ctx );
+
+ u32 vertex_buffer_size = (index_buffer_size+(index_buffer_size/2));
+
+ // Generate the buffer we are gonna be drawing to
+ {
+ glGenVertexArrays(1, &ctx->vao);
+ glGenBuffers( 1, &ctx->vbo );
+ glGenBuffers( 1, &ctx->ebo );
+ glBindVertexArray( ctx->vao );
+
+ glBindBuffer( GL_ARRAY_BUFFER, ctx->vbo );
+
+ glBufferData( GL_ARRAY_BUFFER, vertex_buffer_size * sizeof( struct ui_vert ), NULL, GL_DYNAMIC_DRAW );
+ glBindVertexArray( ctx->vao );
+
+ glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ctx->ebo );
+ glBufferData( GL_ELEMENT_ARRAY_BUFFER, index_buffer_size * sizeof( u16 ), NULL, GL_DYNAMIC_DRAW );
+
+ u32 const stride = sizeof( struct ui_vert );
+
+ // XY
+ glVertexAttribPointer( 0, 2, GL_SHORT, GL_FALSE, stride, (void *)offsetof( struct ui_vert, co ) );
+ glEnableVertexAttribArray( 0 );
+
+ // UV
+ glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE, GL_FALSE, stride, (void *)offsetof( struct ui_vert, uv ) );
+ glEnableVertexAttribArray( 1 );
+
+ // COLOUR
+ glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void *)offsetof( struct ui_vert, colour ) );
+ glEnableVertexAttribArray( 2 );
+
+ // CLIPPING
+ glVertexAttribPointer( 3, 4, GL_SHORT, GL_FALSE, stride, (void *)offsetof( struct ui_vert, clip ) );
+ glEnableVertexAttribArray( 3 );
+ }
+
+ // Initialize default context
+ {
+ ctx->verts = (struct ui_vert *)malloc( vertex_buffer_size * sizeof(struct ui_vert) );
+ ctx->indices = (u16*)malloc( index_buffer_size * sizeof(u16) );
+ }
+}
+
+static void ui_context_free( ui_ctx *ctx )
+{
+ glDeleteVertexArrays( 1, &ctx->vao );
+ glDeleteBuffers( 1, &ctx->vbo );
+ glDeleteBuffers( 1, &ctx->ebo );
+
+ free( ctx->verts );
+ free( ctx->indices );
+}
+
+static void ui_override_font( GLuint new_tex, ui_px space_x )
+{
+ if( ui_glyph_texture )
+ glDeleteTextures( 1, &ui_glyph_texture );
+
+ ui_glyph_texture = new_tex;
+ ui_glyph_override = 1;
+ ui_glyph_spacing_x = space_x;
+}
+
+static void ui_default_init(void)
+{
+ // Load default font
+ if( !ui_glyph_override )
+ {
+ u32 compressed[] = {
+ #include "fonts/weiholmir.h"
+ };
+
+ u32 pixels = 0, total = 72*72, data = 0;
+ u8 *image = malloc( total );
+
+ while( pixels < total )
+ {
+ for( int b = 31; b >= 0; b-- )
+ {
+ image[ pixels ++ ] = (compressed[data] & (0x1 << b))? 0xff: 0x00;
+
+ if( pixels >= total )
+ {
+ total = 0;
+ break;
+ }
+ }
+ data++;
+ }
+
+ glGenTextures( 1, &ui_glyph_texture );
+ glBindTexture( GL_TEXTURE_2D, ui_glyph_texture );
+
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, 72, 72, 0, GL_RED, GL_UNSIGNED_BYTE, image );
+
+ vg_tex2d_clamp();
+ vg_tex2d_nearest();
+
+ free( image );
+ }
+
+ // Setup OpenGL memory
+ SHADER_INIT( shader_ui );
+ ui_init_context( &ui_global_ctx, 20000 );
+}
+
+static void ui_default_free(void)
+{
+ if( !ui_glyph_override )
+ glDeleteTextures( 1, &ui_glyph_texture );
+
+ ui_context_free( &ui_global_ctx );
+}
+
+static struct ui_vert *ui_fill_rect_uv( ui_ctx *ctx, ui_rect rect, u32 colour, ui_px uv[4] );
+static void ui_draw( ui_ctx *ctx, m3x3f view_override )
+{
+ u32 num_indices_normal = ctx->num_indices;
+
+ // Append images to back of buffer
+ for( int i = 0; i < ctx->image_count; i ++ )
+ ui_fill_rect_uv( ctx, ctx->images[i].rc, 0xffffffff, (ui_px[4]){0,0,128,128} );
+
+ glBindVertexArray( ctx->vao );
+
+ glBindBuffer( GL_ARRAY_BUFFER, ctx->vbo );
+ glBufferSubData( GL_ARRAY_BUFFER, 0, ctx->num_verts * sizeof( struct ui_vert ), ctx->verts );
+
+ glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ctx->ebo );
+ glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, 0, ctx->num_indices * sizeof( u16 ), ctx->indices );
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendEquation(GL_FUNC_ADD);
+
+ SHADER_USE( shader_ui );
+
+ m3x3f view = M3X3_IDENTITY;
+
+ if( !view_override )
+ {
+ view_override = view;
+
+ m3x3_translate( view, (v3f){ -1.0f, 1.0f, 0.0f } );
+ m3x3_scale( view, (v3f){ 1.0f/((float)vg_window_x*0.5f), -1.0f/((float)vg_window_y*0.5f), 1.0f } );
+ }
+
+ glUniformMatrix3fv( SHADER_UNIFORM( shader_ui, "uPv" ), 1, GL_FALSE, (float *)view_override );
+
+ glActiveTexture( GL_TEXTURE0 );
+ glBindTexture( GL_TEXTURE_2D, ui_glyph_texture );
+ glUniform1i( SHADER_UNIFORM( shader_ui, "uTexGlyphs" ), 0 );
+
+ glDrawElements( GL_TRIANGLES, num_indices_normal, GL_UNSIGNED_SHORT, (void*)(0) );
+
+ // Draw image elements
+ for( int i = 0; i < ctx->image_count; i ++ )
+ {
+ struct ui_image *img = &ctx->images[i];
+
+ glBindTexture( GL_TEXTURE_2D, img->image );
+ glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*)( (num_indices_normal + 6*i)*sizeof(u16) ) );
+ }
+
+ glDisable(GL_BLEND);
+}