board maker continues
authorhgn <hgodden00@gmail.com>
Thu, 20 Mar 2025 03:14:31 +0000 (03:14 +0000)
committerhgn <hgodden00@gmail.com>
Thu, 20 Mar 2025 03:14:31 +0000 (03:14 +0000)
content_skaterift/models/workshop/regular.mdl
shaders/workshop_compositor.fs
shaders/workshop_compositor.vs
src/board_maker.c
src/board_maker.h
src/ent_skateshop.c
src/skaterift.c

index dc4952f39e6f1f1c070c555ed9abfc1a07ac77ed..c4d1e99ea67970bd44c2e887ad764ce0d926e0c6 100644 (file)
Binary files a/content_skaterift/models/workshop/regular.mdl and b/content_skaterift/models/workshop/regular.mdl differ
index 7011c33ad3d8eda16359de13546a74525f6834d4..fd5ddf8d8463ef222b721b492bd53f73634b33bc 100644 (file)
@@ -1,11 +1,28 @@
 out vec4 FragColor;
 uniform sampler2D uTexMain;
+uniform sampler2D uTexDecal;
 
 uniform vec4 uColour;
+uniform int uDecal;
 
-in vec2 aUv;
+in vec4 aUv;
 
 void main()
 {
-   FragColor = texture( uTexMain, aUv ) * uColour;
+   vec4 c = texture( uTexMain, aUv.xy );
+
+   if( uDecal == 1 )
+   {
+      vec4 d = texture( uTexDecal, aUv.zw );
+      vec2 mask = smoothstep( 0.5, 0.495, abs(aUv.zw - vec2(0.5,0.5)) );
+      c.rgb = mix( c.rgb*uColour.rgb, c.rgb*d.rgb, d.a * min(mask.x,mask.y) );
+   }
+
+   if( uDecal == 2 )
+   {
+      vec4 d = texture( uTexDecal, aUv.zw );
+      c.rgb = mix( c.rgb*uColour.rgb, c.rgb*d.rgb, d.a );
+   }
+
+   FragColor = c;
 }
index 2593725364951e47a10d6af27922f8cf90505f99..7eafaa730d6137bffd8a6cee2b57f3af7fd06f6a 100644 (file)
@@ -1,8 +1,19 @@
 layout (location=0) in vec2 a_co;
-out vec2 aUv;
+
+out vec4 aUv;
+
+uniform vec2 uOffset;
+uniform vec2 uScale;
+uniform float uRotation;
 
 void main()
 {
-   gl_Position = vec4(a_co*2.0-1.0,0.0,1.0);
-   aUv = a_co;
+   vec2 co = a_co*2.0-1.0;
+   gl_Position = vec4(co,0.0,1.0);
+   
+   mat3 R = mat3(cos(uRotation)*uScale.x, -sin(uRotation)*uScale.x, 0,
+                 sin(uRotation)*uScale.y,  cos(uRotation)*uScale.y, 0,
+                 uOffset.x,                uOffset.y,               1 );
+
+   aUv = vec4( a_co, (inverse(R)*vec3(co,1.0)).xy*0.5+0.5 );
 }
index e8b8a1f4e3daef370625298f8769f849ddaad21b..45a4fe4112f5e6fc50493975085c4d1827107a98 100644 (file)
@@ -1,5 +1,6 @@
 #include "board_maker.h"
 #include "shaders/workshop_compositor.h"
+#include "vg/vg_ui/filebrowser.h"
 
 struct _board_maker _board_maker = 
 {
@@ -24,6 +25,52 @@ struct _board_maker _board_maker =
    }
 };
 
+/* image loader thread 
+ * ---------------------------------------- */
+
+struct async_board_maker_image_load_thread_data
+{
+   void *data;
+   i32 w, h;
+};
+
+static void _async_board_maker_image_finish( void *payload, u32 size )
+{
+   struct async_board_maker_image_load_thread_data *inf = payload;
+
+   struct board_maker_decal *decal = &_board_maker.decals[ _board_maker.ui_target_part ];
+
+   if( decal->loaded )
+      glDeleteTextures( 1, &decal->texture );
+
+   decal->loaded = 1;
+
+   glGenTextures( 1, &decal->texture );
+   glBindTexture( GL_TEXTURE_2D, decal->texture );
+   glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, inf->w, inf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, inf->data );
+   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
+   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
+   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
+   glGenerateMipmap( GL_TEXTURE_2D );
+
+   stbi_image_free( inf->data );
+
+   _board_maker.state = k_board_maker_state_none;
+   _board_maker.compositor_state = k_board_maker_compositor_dirty;
+}
+
+static void _board_maker_image_thread( void *_ )
+{
+   vg_async_item *call = vg_async_alloc( sizeof(struct async_board_maker_image_load_thread_data) );
+   struct async_board_maker_image_load_thread_data *inf = call->payload;
+
+   i32 nc;
+   stbi_set_flip_vertically_on_load(1);
+   inf->data = stbi_load( _board_maker.browser->current_path, &inf->w, &inf->h, &nc, 4 );
+   vg_async_dispatch( call, _async_board_maker_image_finish );
+}
+
 /* model loader thread 
  * ----------------------------------------- */
 
@@ -103,8 +150,8 @@ void _board_maker_pre_update(void)
 
             shader_workshop_compositor_use();
             shader_workshop_compositor_uTexMain( 0 );
+            shader_workshop_compositor_uTexDecal( 1 );
 
-            glActiveTexture( GL_TEXTURE0 );
 
             if( _board_maker.base_shader )
             {
@@ -115,8 +162,29 @@ void _board_maker_pre_update(void)
                   if( tex_id )
                   {
                      mdl_texture *tex = &_board_maker.template_mdl.textures[ tex_id-1 ];
-                     shader_workshop_compositor_uColour( _board_maker.colours[i] );
+
+                     glActiveTexture( GL_TEXTURE0 );
                      glBindTexture( GL_TEXTURE_2D, tex->glname );
+                     shader_workshop_compositor_uColour( _board_maker.colours[i] );
+
+                     struct board_maker_decal *decal = &_board_maker.decals[ i ];
+                     if( decal->loaded )
+                     {
+                        glActiveTexture( GL_TEXTURE1 );
+                        glBindTexture( GL_TEXTURE_2D, decal->texture );
+                        shader_workshop_compositor_uDecal( 1 + decal->wrap );
+                        shader_workshop_compositor_uOffset( decal->offset );
+                        shader_workshop_compositor_uScale( decal->scale );
+                        shader_workshop_compositor_uRotation( decal->rotation * (VG_PIf/180.0f) );
+                     }
+                     else
+                     {
+                        shader_workshop_compositor_uDecal( 0 );
+                        shader_workshop_compositor_uOffset( (v2f){0,0} );
+                        shader_workshop_compositor_uScale( (v2f){1,1} );
+                        shader_workshop_compositor_uRotation( 0.0f );
+                     }
+
                      vg_render_fullscreen_quad();
                   }
                }
@@ -145,7 +213,14 @@ void _board_maker_pre_update(void)
          {
             _board_maker.state = k_board_maker_state_initializing;
             vg_loader_start( _board_maker_init_thread, NULL );
-            return;
+         }
+      }
+      else if( _board_maker.state == k_board_maker_state_load_image )
+      {
+         if( vg_loader_availible() )
+         {
+            vg_loader_start( _board_maker_image_thread, NULL );
+            _board_maker.state = k_board_maker_state_loading_image;
          }
       }
    }
@@ -209,9 +284,11 @@ void _board_maker_render( world_instance *world, vg_camera *cam )
    }
 }
 
-static void _board_maker_colour_button( ui_context *ctx, ui_rect rect, f32 *colour_pointer )
+static void _board_maker_colour_button( ui_context *ctx, ui_rect rect, f32 *colour_pointer, bool clickable )
 {
-   enum ui_button_state state = ui_button_base( ctx, rect );
+   enum ui_button_state state = k_ui_button_none;
+   if( clickable )
+      state = ui_button_base( ctx, rect );
 
    u32 colour = v4f_u32_colour( colour_pointer );
    ui_fill( ctx, rect, colour );
@@ -223,7 +300,7 @@ static void _board_maker_colour_button( ui_context *ctx, ui_rect rect, f32 *colo
    {
       _board_maker.ui_target_colour = colour_pointer;
       _board_maker.ui_state = k_board_maker_ui_state_pick_colour;
-      _board_maker.picker_panel[1] = rect[1];
+      _board_maker.pop_panel[1] = rect[1];
    }
 
    if( state == k_ui_button_holding_outside )
@@ -244,6 +321,76 @@ static void _board_maker_colour_button( ui_context *ctx, ui_rect rect, f32 *colo
    }
 }
 
+/* Only allowed in decal editor panel. */
+static void _board_maker_image_button( ui_context *ctx, ui_rect rect, enum workshop_shader_part part, bool clickable )
+{
+   enum ui_button_state state = k_ui_button_none;
+   if( clickable )
+      state = ui_button_text( ctx, rect, "Open\xb2", 1 );
+   else
+   {
+      // TODO: standardize me
+      ui_fill( ctx, rect, ui_colour( ctx, k_ui_bg ) );
+      ui_text( ctx, rect, "Open image\xb2", 1, k_ui_align_middle_center, ui_colour( ctx, k_ui_bg+3 ) );
+   }
+
+   if( state == k_ui_button_click )
+   {
+      vg_filebrowser_populate( _board_maker.browser );
+      _board_maker.ui_target_part = part;
+      _board_maker.ui_image_picker_return_state = _board_maker.ui_state;
+      _board_maker.ui_state = k_board_maker_ui_state_pick_image;
+   }
+}
+
+static void _board_maker_decal_button( ui_context *ctx, ui_rect rect, enum workshop_shader_part part, bool clickable )
+{
+   ctx->font = &vgf_default_large;
+   enum ui_button_state state = k_ui_button_none;
+   if( clickable )
+      state = ui_button_text( ctx, rect, "\xb9", 1 );
+   else
+   {
+      // TODO: standardize me
+      ui_fill( ctx, rect, ui_colour( ctx, k_ui_bg ) );
+      ui_text( ctx, rect, "\xb9", 1, k_ui_align_middle_center, ui_colour( ctx, k_ui_bg+3 ) );
+   }
+   ctx->font = &vgf_default_small;
+
+   if( state == k_ui_button_click )
+   {
+      _board_maker.pop_panel[1] = rect[1];
+      _board_maker.ui_target_part = part;
+      _board_maker.ui_state = k_board_maker_ui_state_edit_decal;
+   }
+
+   struct board_maker_decal *decal = &_board_maker.decals[ part ];
+   if( decal->loaded )
+      ui_outline( ctx, rect, -1, ui_colour( ctx, k_ui_orange ), 0 );
+}
+
+static void clear_decal( struct board_maker_decal *decal )
+{
+   if( decal->texture )
+   {
+      glDeleteTextures( 1, &decal->texture );
+      decal->texture = 0;
+   }
+
+   decal->wrap = 0;
+   decal->wrap_prev = 0;
+   decal->loaded = 0;
+   v2_zero( decal->offset );
+   v2_fill( decal->scale, 1.0f );
+   v4_fill( decal->colour, 1.0f );
+}
+
+struct ui_enum_opt _board_maker_wrap_opts[] = 
+{
+   { 0, "Normal" },
+   { 1, "Repeat" }
+};
+
 void _board_maker_ui( ui_context *ctx )
 {
    if( _world.event != k_world_event_board_maker )
@@ -251,42 +398,56 @@ void _board_maker_ui( ui_context *ctx )
 
    ui_capture_mouse( ctx, 1 );
 
-   ui_rect panel = { 8, 8, 200, 600 };
+   ui_rect root = { 8, 8, 200, 600 }, panel;
+   ui_panel( ctx, root, panel );
 
    if( !ui_click_up( ctx, UI_MOUSE_LEFT ) )
       _board_maker.ui_dropper_colour = NULL;
 
+   bool clickable = 1;
+   if( _board_maker.state != k_board_maker_state_none )
+      clickable = 0;
+
+   bool main_clickable = clickable;
+
+#if 0
+   if( _board_maker.ui_state != k_board_maker_ui_state_default )
+      main_clickable = 0;
+#endif
+
    ui_info( ctx, panel, "Wheels" );
    ui_rect w1, w2, w3, w4, wheel_row;
    ui_standard_widget( ctx, panel, wheel_row, 1 );
    ui_split_ratio( wheel_row, k_ui_axis_v, 0.5f, 2, w1, w3 );
    ui_split_ratio( w1, k_ui_axis_v, 0.5f, 2, w1, w2 );
    ui_split_ratio( w3, k_ui_axis_v, 0.5f, 2, w3, w4 );
-   _board_maker_colour_button( ctx, w1, _board_maker.colours[ k_workshop_shader_part_wheel1 ] );
-   _board_maker_colour_button( ctx, w2, _board_maker.colours[ k_workshop_shader_part_wheel2 ] );
-   _board_maker_colour_button( ctx, w3, _board_maker.colours[ k_workshop_shader_part_wheel3 ] );
-   _board_maker_colour_button( ctx, w4, _board_maker.colours[ k_workshop_shader_part_wheel4 ] );
+   _board_maker_colour_button( ctx, w1, _board_maker.colours[ k_workshop_shader_part_wheel1 ], main_clickable );
+   _board_maker_colour_button( ctx, w2, _board_maker.colours[ k_workshop_shader_part_wheel2 ], main_clickable );
+   _board_maker_colour_button( ctx, w3, _board_maker.colours[ k_workshop_shader_part_wheel3 ], main_clickable );
+   _board_maker_colour_button( ctx, w4, _board_maker.colours[ k_workshop_shader_part_wheel4 ], main_clickable );
 
    ui_info( ctx, panel, "Trucks" );
    ui_rect truck_row, t1, t2;
    ui_standard_widget( ctx, panel, truck_row, 1 );
    ui_split_ratio( truck_row, k_ui_axis_v, 0.5f, 2, t1, t2 );
-   _board_maker_colour_button( ctx, t1, _board_maker.colours[ k_workshop_shader_part_truck1 ] );
-   _board_maker_colour_button( ctx, t2, _board_maker.colours[ k_workshop_shader_part_truck2 ] );
+   _board_maker_colour_button( ctx, t1, _board_maker.colours[ k_workshop_shader_part_truck1 ], main_clickable );
+   _board_maker_colour_button( ctx, t2, _board_maker.colours[ k_workshop_shader_part_truck2 ], main_clickable );
 
    ui_info( ctx, panel, "Deck" );
    ui_rect deck_row, deckc, decki, decke;
    ui_standard_widget( ctx, panel, deck_row, 1 );
    ui_split_ratio( deck_row, k_ui_axis_v, 0.66666f, 2, deckc, decke );
    ui_split_ratio( deckc, k_ui_axis_v, 0.5f, 2, deckc, decki );
-   _board_maker_colour_button( ctx, deckc, _board_maker.colours[ k_workshop_shader_part_deck ] );
-   _board_maker_colour_button( ctx, decke, _board_maker.colours[ k_workshop_shader_part_edge ] );
+   _board_maker_colour_button( ctx, deckc, _board_maker.colours[ k_workshop_shader_part_deck ], main_clickable );
+   _board_maker_colour_button( ctx, decki, _board_maker.colours[ k_workshop_shader_part_edge ], main_clickable );
+   _board_maker_decal_button( ctx, decke, k_workshop_shader_part_deck, main_clickable );
 
    ui_info( ctx, panel, "Griptape" );
    ui_rect griptape_row, gtc, gti;
    ui_standard_widget( ctx, panel, griptape_row, 1 );
    ui_split_ratio( griptape_row, k_ui_axis_v, 0.5f, 2, gtc, gti );
-   _board_maker_colour_button( ctx, gtc, _board_maker.colours[ k_workshop_shader_part_griptape ] );
+   _board_maker_colour_button( ctx, gtc, _board_maker.colours[ k_workshop_shader_part_griptape ], main_clickable );
+   _board_maker_decal_button( ctx, gti, k_workshop_shader_part_griptape, main_clickable );
 
    if( _board_maker.ui_dropper_colour )
    {
@@ -297,18 +458,110 @@ void _board_maker_ui( ui_context *ctx )
 
    if( _board_maker.ui_state == k_board_maker_ui_state_pick_colour )
    {
-      _board_maker.picker_panel[0] = panel[0] + panel[2] + 8;
-      _board_maker.picker_panel[2] = 300;
-      _board_maker.picker_panel[3] = 160;
+      _board_maker.pop_panel[0] = root[0] + root[2] + 8;
+      _board_maker.pop_panel[2] = 300;
+      _board_maker.pop_panel[3] = 176;
 
       ui_rect picker_panel, ok_box;
-      rect_copy( _board_maker.picker_panel, picker_panel );
-      ui_outline( ctx, picker_panel, 1, ui_colour( ctx, k_ui_fg ), 0 );
+      ui_panel( ctx, _board_maker.pop_panel, picker_panel );
 
       if( ui_colourpicker( ctx, picker_panel, NULL, _board_maker.ui_target_colour, k_ui_colour_type_rgb ) )
          _board_maker.compositor_state = k_board_maker_compositor_dirty;
 
       ui_split( picker_panel, k_ui_axis_h, -24, 0, picker_panel, ok_box );
+      ctx->font = &vgf_default_large;
+      if( ui_button_text( ctx, ok_box, "\xb3", 1 ) == k_ui_button_click )
+      {
+         _board_maker.ui_state = k_board_maker_ui_state_default;
+      }
+      ctx->font = &vgf_default_small;
+   }
+
+   if( _board_maker.ui_state == k_board_maker_ui_state_edit_decal )
+   {
+      struct board_maker_decal *decal = &_board_maker.decals[ _board_maker.ui_target_part ];
+      ui_rect decal_panel;
+
+      _board_maker.pop_panel[0] = root[0] + root[2] + 8;
+      _board_maker.pop_panel[2] = 400;
+      _board_maker.pop_panel[3] = 127;
+      ui_panel( ctx, _board_maker.pop_panel, decal_panel );
+
+      /* top bar */
+      ui_rect top_bar, l, r;
+      ui_standard_widget( ctx, decal_panel, top_bar, 1 );
+      ui_split( top_bar, k_ui_axis_v, -130, 8, l, r );
+      ui_fill( ctx, top_bar, ui_colour(ctx, k_ui_bg+2) );
+
+      ui_rect remove_button;
+      ui_split( l, k_ui_axis_v, -80, 8, l, remove_button );
+      ui_text( ctx, l, decal->loaded? "Loaded": "Not Loaded", 1, k_ui_align_middle_center, 0 );
+
+      bool delete_me = 0;
+      if( decal->loaded )
+      {
+         if( ui_button_text( ctx, remove_button, "Remove", 1 ) == k_ui_button_click )
+            delete_me = 1;
+      }
+      else
+         _board_maker_image_button( ctx, remove_button, _board_maker.ui_target_part, clickable );
+
+      _board_maker_colour_button( ctx, deckc, _board_maker.colours[ k_workshop_shader_part_deck ], main_clickable );
+      
+      /* options */
+      ui_rect opt_row;
+      ui_standard_widget( ctx, decal_panel, opt_row, 2 );
+
+      ui_rect pos, scale, rot;
+      ui_split_ratio( opt_row, k_ui_axis_v, 0.6666f, 8, pos, rot );
+      ui_split_ratio( pos, k_ui_axis_v, 0.5f, 8, pos, scale );
+
+      ui_rect posx, posy;
+      ui_split_ratio( pos, k_ui_axis_h, 0.5f, 0, posx, posy );
+
+      ui_rect scalex, scaley;
+      ui_split_ratio( scale, k_ui_axis_h, 0.5f, 0, scalex, scaley );
+
+      bool mod = 0;
+
+      mod |= ui_slider_standard( ctx, posx, -1.0f, 1.0f, &decal->offset[0], "x %.2f" );
+      mod |= ui_slider_standard( ctx, posy, -1.0f, 1.0f, &decal->offset[1], "y %.2f" );
+      mod |= ui_slider_standard( ctx, scalex, -4.0f, 4.0f, &decal->scale[0], "w %.2f" );
+      mod |= ui_slider_standard( ctx, scaley, -4.0f, 4.0f, &decal->scale[1], "h %.2f" );
+
+      if( ui_slider_standard( ctx, rot, -180.0f, 180.0f, &decal->rotation, "%.2f\xb0" ) )
+      {
+         mod = 1;
+         if( fabsf(decal->rotation-0.0f) < 2.0f )
+            decal->rotation = 0.0f;
+
+         if( fabsf(decal->rotation-90.0f) < 2.0f )
+            decal->rotation = 90.0f;
+
+         if( fabsf(decal->rotation+90.0f) < 2.0f )
+            decal->rotation = -90.0f;
+
+         if( fabsf(decal->rotation-180.0f) < 2.0f )
+            decal->rotation = 180.0f;
+
+         if( fabsf(decal->rotation+180.0f) < 2.0f )
+            decal->rotation = -180.0f;
+      }
+
+      ui_enum( ctx, r, NULL, _board_maker_wrap_opts, 2, &decal->wrap );
+
+      // warning: next frame
+      if( decal->wrap != decal->wrap_prev )
+      {
+         decal->wrap_prev = decal->wrap;
+         mod = 1;
+      }
+
+      if( mod )
+         _board_maker.compositor_state = k_board_maker_compositor_dirty;
+
+      ui_rect ok_box;
+      ui_split( decal_panel, k_ui_axis_h, -28, 8, decal_panel, ok_box );
 
       ctx->font = &vgf_default_large;
       if( ui_button_text( ctx, ok_box, "\xb3", 1 ) == k_ui_button_click )
@@ -316,7 +569,35 @@ void _board_maker_ui( ui_context *ctx )
          _board_maker.ui_state = k_board_maker_ui_state_default;
       }
       ctx->font = &vgf_default_small;
+
+      if( delete_me )
+      {
+         clear_decal( decal );
+         _board_maker.compositor_state = k_board_maker_compositor_dirty;
+      }
+   }
+
+   if( _board_maker.ui_state == k_board_maker_ui_state_pick_image )
+   {
+      ui_rect fb_rect = { root[0]+root[2]+8, root[1], 800, 600 };
+      ui_panel( ctx, fb_rect, fb_rect );
+
+      enum filebrowser_action action = vg_filebrowser_ui( ctx, fb_rect, _board_maker.browser );
+
+      if( action != k_filebrowser_action_none )
+      {
+         if( action == k_filebrowser_action_accept )
+         {
+            _board_maker.state = k_board_maker_state_load_image;
+         }
+
+         _board_maker.ui_state = _board_maker.ui_image_picker_return_state;
+      }
    }
+
+   ui_rect box = { vg.window_x-(400+8),8, 400,400 };
+   ui_image( ctx, box, &_board_maker.compositor_fb.attachments[0].id );
+   ui_outline( ctx, box, 1, ui_colour( ctx, k_ui_fg ), 0 );
 }
 
 void _board_maker_open(void)
@@ -332,5 +613,15 @@ void _board_maker_open(void)
    for( u32 i=0; i<k_workshop_shader_part_max; i ++ )
    {
       v4_copy( _board_maker_default_colours[i], _board_maker.colours[i] ); 
+
+      struct board_maker_decal *decal = &_board_maker.decals[i];
+      clear_decal( decal );
    }
+
+   struct vg_filebrowser *browser = malloc( sizeof(struct vg_filebrowser) );
+   _board_maker.browser = browser;
+   vg_filebrowser_init( browser );
+   vg_filebrowser_set_path_to_home( browser );
+   browser->mode = k_filebrowser_mode_pick_file;
+   browser->filter = 0x1 << k_media_type_image;
 }
index 4a6a7549be6ad374340b3d5666f072047696e622..0f9ad871dec8bfe3a5cb0602cb4d8dea9b29dcdf 100644 (file)
@@ -35,8 +35,9 @@ struct _board_maker
       k_board_maker_state_not_ready,
       k_board_maker_state_initializing,
       k_board_maker_state_none,
-      k_board_maker_state_load_template,
       k_board_maker_state_loading_template,
+      k_board_maker_state_load_image,
+      k_board_maker_state_loading_image
    }
    state;
 
@@ -55,15 +56,36 @@ struct _board_maker
    v4f colours[ k_workshop_shader_part_max ];
    struct shader_props_workshop *base_shader;
 
-   ui_rect picker_panel;
+   struct board_maker_decal
+   {
+      bool loaded;
+      GLuint texture;
+
+      i32 wrap, wrap_prev;
+
+      v4f colour;
+      v2f offset;
+      v2f scale;
+      f32 rotation;
+   }
+   decals[ k_workshop_shader_part_max ];
+
+   ui_rect pop_panel;
    f32 *ui_target_colour;
    f32 *ui_dropper_colour;
+   enum workshop_shader_part ui_target_part;
+
    enum board_maker_ui_state
    {
       k_board_maker_ui_state_default,
-      k_board_maker_ui_state_pick_colour
+      k_board_maker_ui_state_pick_colour,
+      k_board_maker_ui_state_edit_decal,
+      k_board_maker_ui_state_pick_image
    }
    ui_state;
+   enum board_maker_ui_state ui_image_picker_return_state;
+
+   struct vg_filebrowser *browser;
 }
 extern _board_maker;
 
index 7ba0762b5e7271ac6867a8ab2e316608a83994d5..74308ce9396b1ccb1a3fafb22ae373646ba431af 100644 (file)
@@ -85,8 +85,7 @@ static void skateshop_update_preview_image_thread(void *_args)
    }
 
    vg_strcat( &folder, "/preview.jpg" );
-   vg_async_item *call = 
-      vg_async_alloc( sizeof(struct async_preview_load_thread_data) );
+   vg_async_item *call = vg_async_alloc( sizeof(struct async_preview_load_thread_data) );
    struct async_preview_load_thread_data *inf = call->payload;
 
    inf->reg = reg_preview;
@@ -101,8 +100,7 @@ static void skateshop_update_preview_image_thread(void *_args)
       {
          if( (x != WORKSHOP_PREVIEW_WIDTH) || (y != WORKSHOP_PREVIEW_HEIGHT) )
          {
-            vg_error( "Resolution does not match framebuffer, so we can't"
-                      " show it\n" );
+            vg_error( "Resolution does not match framebuffer, so we can't show it\n" );
             stbi_image_free( inf->data );
             inf->data = NULL;
          }
index 3aab6acaa8175595c2af319700f3bc16c5898860..9ca042097d624514114050427944ac8161052ab0 100644 (file)
@@ -556,6 +556,7 @@ void vg_framebuffer_resize( int w, int h )
 {
 }
 
+#include "vg/vg_ui/filebrowser.c"
 #include "addon.c"
 #include "addon_types.c"
 #include "audio.c"