create font/ui text system
[fishladder.git] / vg / vg_ui.h
index 9758d9840c4add9c0fd524b4b3a056730a0aa36a..a9bea2e384baa82a96435882c1dc0453239c0d81 100644 (file)
        
 */
 
+SHADER_DEFINE( shader_ui,
+
+       // VERTEX
+       "layout (location=0) in vec2 a_co;"                     // i16, i16, .. ?
+       "layout (location=1) in vec2 a_uv;"                     // i8, i8
+       "layout (location=2) in vec4 a_colour;" // u32
+       "uniform mat3 uPv;"
+       ""
+       "out vec2 aTexCoords;"
+       "out vec4 aColour;"
+       ""
+       "void main()"
+       "{"
+               "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
+               "aTexCoords = a_uv * 0.01388888888;"
+               "aColour = a_colour;"
+       "}",
+       
+       // FRAGMENT
+       "uniform sampler2D uTexGlyphs;"
+       "out vec4 FragColor;"
+       ""
+       "in vec2 aTexCoords;"
+       "in vec4 aColour;"
+       ""
+       "void main()"
+       "{"
+               "vec4 glyph = texture( uTexGlyphs, aTexCoords );"
+               "FragColor = aColour * vec4( 1.0, 1.0, 1.0, glyph.r );"
+       "}"
+       ,
+       UNIFORMS({ "uPv", "uTexGlyphs" })
+)
+
 #define UI_AUTO_FILL 0
 
 // Types
@@ -21,6 +55,15 @@ typedef u32                          ui_colour;
 typedef ui_px                          ui_rect[4];
 typedef struct ui_ctx  ui_ctx;
 
+#pragma pack(push,1)
+struct ui_vert
+{
+       ui_px co[2];
+       u8 uv[2];
+       u32 colour;
+};
+#pragma pack(pop)
+
 struct ui_ctx
 {
        ui_px padding;
@@ -34,6 +77,11 @@ struct ui_ctx
        }
        stack[ 32 ];
        
+       struct ui_vert *verts;
+       u32 num_verts;
+       u16 *indices;
+       u32 num_indices;
+       
        ui_rect cursor;
        u32 stack_count;
        u32 capture_mouse_id;
@@ -45,6 +93,131 @@ struct ui_ctx
        int click_state;        // 0: released, 1: on down, 2: pressed, 3: on release
 };
 
+// Opengl
+GLuint ui_glyph_texture;
+GLuint ui_vao;
+GLuint ui_vbo;
+GLuint ui_ebo;
+
+
+
+#define UI_BUFFER_SIZE 30000
+#define UI_INDEX_SIZE 20000
+
+static void ui_glyphs_load( u32 *compressed )
+{
+       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++;
+       }
+       
+       for( int j = 0; j < 70; j ++ )
+       {
+               for( int i = 0; i < 70; i ++ )
+                       printf( "%s",image[j*72+i]?"#":" " );
+               printf( "\n" );
+       }
+       
+       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 );
+}
+
+static void ui_default_init(void)
+{
+       ui_glyphs_load( (u32[]){
+               #include "fonts/weiholmir.h"
+       });
+       
+       SHADER_INIT( shader_ui );
+       
+       // Generate the buffer we are gonna be drawing to
+       glGenVertexArrays(1, &ui_vao);
+       glGenBuffers( 1, &ui_vbo );
+       glGenBuffers( 1, &ui_ebo );
+       glBindVertexArray( ui_vao );
+       
+       glBindBuffer( GL_ARRAY_BUFFER, ui_vbo );
+       
+       glBufferData( GL_ARRAY_BUFFER, UI_BUFFER_SIZE * sizeof( struct ui_vert ), NULL, GL_DYNAMIC_DRAW );
+       glBindVertexArray( ui_vao );
+       
+       glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ui_ebo );
+       glBufferData( GL_ELEMENT_ARRAY_BUFFER, UI_INDEX_SIZE * sizeof( u16 ), NULL, GL_DYNAMIC_DRAW );
+       
+       // XY
+       glVertexAttribPointer( 0, 2, GL_UNSIGNED_SHORT, GL_FALSE, sizeof( struct ui_vert ), (void *)offsetof( struct ui_vert, co ) );
+       glEnableVertexAttribArray( 0 );
+       
+       // UV
+       glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE, GL_FALSE, sizeof( struct ui_vert ), (void *)offsetof( struct ui_vert, uv ) );
+       glEnableVertexAttribArray( 1 );
+       
+       // COLOUR
+       glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof( struct ui_vert ), (void *)offsetof( struct ui_vert, colour ) );
+       glEnableVertexAttribArray( 2 );
+}
+
+static void ui_default_free(void)
+{
+       glDeleteTextures( 1, &ui_glyph_texture );
+       
+       glDeleteVertexArrays( 1, &ui_vao );
+       glDeleteBuffers( 1, &ui_vbo );
+       glDeleteBuffers( 1, &ui_ebo );
+}
+
+static void ui_draw( ui_ctx *ctx )
+{
+       glBindVertexArray( ui_vao );
+       
+       glBindBuffer( GL_ARRAY_BUFFER, ui_vbo );
+       glBufferSubData( GL_ARRAY_BUFFER, 0, ctx->num_verts * sizeof( struct ui_vert ), ctx->verts );
+       
+       glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ui_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;
+       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 );
+       
+       glActiveTexture( GL_TEXTURE0 );
+       glBindTexture( GL_TEXTURE_2D, ui_glyph_texture );
+       glUniform1i( SHADER_UNIFORM( shader_ui, "uTexGlyphs" ), 0 );
+       
+       glDrawElements( GL_TRIANGLES, ctx->num_indices, GL_UNSIGNED_SHORT, (void*)(0) );
+       
+       //vg_info( "Verts: %u, Indices: %u\n", ctx->num_verts, ctx->num_indices );
+       
+       glDisable(GL_BLEND);
+}
+
 // Rect controls
 // ==========================================================
 
@@ -119,14 +292,14 @@ static void ui_end( ui_ctx *ctx )
 
 static void ui_end_down( ui_ctx *ctx )
 {
-       ui_px height = ctx->stack[ ctx->stack_count ].rect[3];
+       ui_px height = ctx->stack[ ctx->stack_count-1 ].rect[3];
        ui_end( ctx );
        ctx->cursor[1] += height;
 }
 
 static void ui_end_right( ui_ctx *ctx )
 {
-       ui_px width = ctx->stack[ ctx->stack_count ].rect[2];
+       ui_px width = ctx->stack[ ctx->stack_count-1 ].rect[2];
        ui_end( ctx );
        ctx->cursor[0] += width;
 }
@@ -137,6 +310,12 @@ static void ui_align_right( ui_ctx *ctx )
        ctx->cursor[0] = node->rect[0] + node->rect[2] - ctx->cursor[2];
 }
 
+static void ui_align_top( ui_ctx *ctx )
+{
+       struct ui_qnode *node = &ctx->stack[ ctx->stack_count-1 ];
+       ctx->cursor[1] = node->rect[1] + node->rect[3] - ctx->cursor[3];
+}
+
 static void ui_clamp_rect( ui_rect parent, ui_rect dest )
 {
        dest[0] = vg_min( parent[0] + parent[2] - dest[2], dest[0] );
@@ -163,6 +342,122 @@ static void ui_capture_mouse( ui_ctx *ctx, u32 id )
        }
 }
 
+static void ui_fill_rect( ui_ctx *ctx, ui_rect rect, u32 colour )
+{
+       ui_px uv[2];
+       uv[0] = 66;
+       uv[1] = 66;
+
+       struct ui_vert *vertices = &ctx->verts[ ctx->num_verts ];
+       vertices[0].co[0] = rect[0];
+       vertices[0].co[1] = rect[1];
+       vertices[0].uv[0] = uv[0];
+       vertices[0].uv[1] = uv[1];
+       vertices[0].colour = colour;
+       vertices[1].co[0] = rect[0]+rect[2];
+       vertices[1].co[1] = rect[1];
+       vertices[1].uv[0] = uv[0];
+       vertices[1].uv[1] = uv[1];
+       vertices[1].colour = colour;
+       vertices[2].co[0] = rect[0]+rect[2];
+       vertices[2].co[1] = rect[1]+rect[3];
+       vertices[2].uv[0] = uv[0];
+       vertices[2].uv[1] = uv[1];
+       vertices[2].colour = colour;
+       vertices[3].co[0] = rect[0];
+       vertices[3].co[1] = rect[1]+rect[3];
+       vertices[3].uv[0] = uv[0];
+       vertices[3].uv[1] = uv[1];
+       vertices[3].colour = colour;
+       u16 ind_start = ctx->num_verts;
+       u16 *indices = &ctx->indices[ ctx->num_indices ];
+       
+       indices[0] = ind_start+0;
+       indices[1] = ind_start+2;
+       indices[2] = ind_start+1;
+       
+       indices[3] = ind_start+0;
+       indices[4] = ind_start+3;
+       indices[5] = ind_start+2;
+       
+       ctx->num_indices += 6;
+       ctx->num_verts += 4;
+}
+
+static void ui_text( ui_ctx *ctx, const char *str, ui_px scale, int alignment )
+{
+       ui_rect text_cursor;
+
+       text_cursor[0] = ctx->cursor[0];
+       text_cursor[1] = ctx->cursor[1];
+       text_cursor[2] = 7*scale;
+       text_cursor[3] = 7*scale;
+
+       const char *_c = str;
+       char c;
+       while( (c = *(_c ++)) )
+       {
+               if( c == '\n' )
+               {
+                       text_cursor[1] += 10*scale;
+                       text_cursor[0] = ctx->cursor[0];
+                       continue;
+               }
+               else if( c >= 33 && c <= 126 )
+               {
+                       u8 glyph_base[2];
+                       u8 glyph_index = c - 32;
+                       glyph_base[0] = glyph_index%10;
+                       glyph_base[1] = (glyph_index-glyph_base[0])/10;
+                       
+                       glyph_base[0] *= 7;
+                       glyph_base[1] *= 7;
+                       
+                       
+                       struct ui_vert *vertices = &ctx->verts[ctx->num_verts];
+                       vertices[0].co[0] = text_cursor[0];
+                       vertices[0].co[1] = text_cursor[1];
+                       vertices[0].uv[0] = glyph_base[0];
+                       vertices[0].uv[1] = glyph_base[1];
+                       vertices[0].colour = 0xffffffff;
+                       
+                       vertices[1].co[0] = text_cursor[0]+text_cursor[2];
+                       vertices[1].co[1] = text_cursor[1];
+                       vertices[1].uv[0] = glyph_base[0]+7;
+                       vertices[1].uv[1] = glyph_base[1];
+                       vertices[1].colour = 0xffffffff;
+                       
+                       vertices[2].co[0] = text_cursor[0]+text_cursor[2];
+                       vertices[2].co[1] = text_cursor[1]+text_cursor[3];
+                       vertices[2].uv[0] = glyph_base[0]+7;
+                       vertices[2].uv[1] = glyph_base[1]+7;
+                       vertices[2].colour = 0xffffffff;
+                       
+                       vertices[3].co[0] = text_cursor[0];
+                       vertices[3].co[1] = text_cursor[1]+text_cursor[3];              
+                       vertices[3].uv[0] = glyph_base[0];
+                       vertices[3].uv[1] = glyph_base[1]+7;    
+                       vertices[3].colour = 0xffffffff;
+
+                       u16 ind_start = ctx->num_verts;
+                       u16 *indices = &ctx->indices[ ctx->num_indices ];
+                       
+                       indices[0] = ind_start+0;
+                       indices[1] = ind_start+2;
+                       indices[2] = ind_start+1;
+                       
+                       indices[3] = ind_start+0;
+                       indices[4] = ind_start+3;
+                       indices[5] = ind_start+2;
+                       
+                       ctx->num_indices += 6;
+                       ctx->num_verts += 4;
+               }
+               
+               text_cursor[0] += 6*scale;
+       }
+}
+
 // API control
 // ====================================================================
 
@@ -177,6 +472,9 @@ static void ui_begin( ui_ctx *ctx, ui_px res_x, ui_px res_y )
        ctx->stack[0].mouse_over = 1;
        
        ctx->stack_count = 1;
+       
+       ctx->num_verts = 0;
+       ctx->num_indices = 0;
 }
 
 static void ui_resolve( ui_ctx *ctx )
@@ -255,18 +553,27 @@ static int ui_window( ui_ctx *ctx, struct ui_window *window, u32 control_group )
        ui_new_node( ctx );
        {
                ui_capture_mouse( ctx, __COUNTER__ );
-
+               
+               ui_fill_rect( ctx, ctx->cursor, 0xff333333 );
+               
                // Drag bar
                ctx->cursor[3] = 25;            
                ui_new_node( ctx );
                {
                        ui_capture_mouse( ctx, __COUNTER__ );
                        
+                       ui_fill_rect( ctx, ctx->cursor, 0xff555555 );
+                       
                        // title..
+                       ctx->cursor[0] += 2;
+                       ctx->cursor[1] += 2;
+                       ui_text( ctx, window->title, 2, 0 );
                        
                        // Close button
+                       ctx->cursor[3] = 25;
                        ctx->cursor[2] = 25;
                        ui_align_right( ctx );
+                       ui_align_top( ctx );
                        ui_rect_pad( ctx, ctx->cursor, 4 );
                        
                        if( ui_button( ctx, __COUNTER__ ) )
@@ -286,12 +593,26 @@ static int ui_window( ui_ctx *ctx, struct ui_window *window, u32 control_group )
                                }
                        }
                }
-               ui_end( ctx );
+               ui_end_down( ctx );
        }
        
        return 1;
 }
 
+ui_ctx test_ctx = { .padding = 8 };
+
+static void ui_test_init(void)
+{
+       test_ctx.verts = (struct ui_vert *)malloc( UI_BUFFER_SIZE * sizeof(struct ui_vert) );
+       test_ctx.indices = (u16*)malloc( UI_INDEX_SIZE * sizeof(u16) );
+}
+
+static void ui_test_free(void)
+{
+       free( test_ctx.verts );
+       free( test_ctx.indices );
+}
+
 static void ui_test(void)
 {
        /*
@@ -311,10 +632,8 @@ static void ui_test(void)
                |      |--------------| | [] Eternal flames        6 | |
                +------+--------------+-+----------------------------+-+
        */
-       
-       static ui_ctx ctx = { .padding = 8 };
-       
-       ui_begin( &ctx, vg_window_x, vg_window_y );
+
+       ui_begin( &test_ctx, vg_window_x, vg_window_y );
        
        // TODO: Find a more elegent form for this
        int mouse_state = 0;
@@ -322,7 +641,7 @@ static void ui_test(void)
        if( vg_get_button_down( "primary" ) ) mouse_state = 1;
        if( vg_get_button_up( "primary" ) ) mouse_state = 3;
                
-       ui_set_mouse( &ctx, vg_mouse[0], vg_mouse[1], mouse_state );
+       ui_set_mouse( &test_ctx, vg_mouse[0], vg_mouse[1], mouse_state );
        
        static struct ui_window window =
        {
@@ -330,16 +649,19 @@ static void ui_test(void)
                .title = "Central Market"
        };
        
-       if( ui_window( &ctx, &window, __COUNTER__ ) )
+       if( ui_window( &test_ctx, &window, __COUNTER__ ) )
        {
                // Contents
+               ui_text( &test_ctx, "Just say anything, George, say what ever's natural,\nthe first thing that comes to your mind.\nTake that you mutated son-of-a-bitch.", 1, 0 );
        }
-       ui_end( &ctx );
+       ui_end( &test_ctx );
 
-       ui_resolve( &ctx );
+       ui_resolve( &test_ctx );
        
        m3x3f view = M3X3_IDENTITY;
        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 } );
        vg_lines_drawall( (float*)view );
+       
+       ui_draw( &test_ctx );
 }