#include "board_maker.h"
#include "shaders/workshop_compositor.h"
+#include "vg/vg_ui/filebrowser.h"
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
* ----------------------------------------- */
shader_workshop_compositor_use();
shader_workshop_compositor_uTexMain( 0 );
+ shader_workshop_compositor_uTexDecal( 1 );
- glActiveTexture( GL_TEXTURE0 );
if( _board_maker.base_shader )
{
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();
}
}
{
_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;
}
}
}
}
}
-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 );
{
_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 )
}
}
+/* 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 )
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 )
{
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 )
_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)
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;
}