diff options
| author | kasull <qsullian@gmail.com> | 2026-02-26 04:31:08 -0500 |
|---|---|---|
| committer | kasull <qsullian@gmail.com> | 2026-02-26 04:31:08 -0500 |
| commit | 94e0df832c83bc7b9ead0824cfbc41f166869c68 (patch) | |
| tree | c90d78ea332d66a09020a431b5031aa42686bd9f /src/editor | |
| parent | b384930de5044934207d1b2ceb4fa55705094f8b (diff) | |
add wall shape dropdown and drag-based polygon creation with height controls
fix wall Z bounds calculation and key index masking in input handling
preserve 2D view/cursor stability when map bounds update
Diffstat (limited to 'src/editor')
| -rw-r--r-- | src/editor/editor.cpp | 4 | ||||
| -rw-r--r-- | src/editor/editor.h | 20 | ||||
| -rw-r--r-- | src/editor/gui.cpp | 12 | ||||
| -rw-r--r-- | src/editor/toolview.cpp | 208 | ||||
| -rw-r--r-- | src/editor/view2d.cpp | 469 |
5 files changed, 636 insertions, 77 deletions
diff --git a/src/editor/editor.cpp b/src/editor/editor.cpp index d97164e..ad23d1e 100644 --- a/src/editor/editor.cpp +++ b/src/editor/editor.cpp @@ -15,6 +15,10 @@ GAME_EDITOR* editor_create( GAME_DATA* game ) { e->grid = 1.f; e->spritesize = EDITOR_DEFAULT_SPRITE_SIZE; + e->tool.wallshape = EDITOR_WALLSHAPE_LINE; + e->tool.polysides = EDITOR_DEFAULT_POLY_SIDES; + e->tool.wallheight = EDITOR_DEFAULT_WALL_HEIGHT; + e->tool.placementheight = EDITOR_DEFAULT_PLACEMENT_HEIGHT; e->game = game; gui_init( game ); diff --git a/src/editor/editor.h b/src/editor/editor.h index 9063f50..cd42a4e 100644 --- a/src/editor/editor.h +++ b/src/editor/editor.h @@ -4,6 +4,11 @@ #include "../game/world/map.h" const F32 EDITOR_DEFAULT_SPRITE_SIZE = 32.f; +const F32 EDITOR_DEFAULT_POLY_SIDES = 6.f; +const F32 EDITOR_DEFAULT_WALL_HEIGHT = 80.f; +const F32 EDITOR_DEFAULT_PLACEMENT_HEIGHT = 0.f; +const I32 EDITOR_POLY_SIDES_MIN = 3; +const I32 EDITOR_POLY_SIDES_MAX = 32; enum EditorTools_t { EDITOR_TOOL_NONE, @@ -14,6 +19,11 @@ enum EditorTools_t { EDITOR_TOOL_ENT }; +enum EditorWallShape_t { + EDITOR_WALLSHAPE_LINE = 0, + EDITOR_WALLSHAPE_POLYGON = 1 +}; + enum EditorSelectType_t { EDITOR_SELECT_NONE, EDITOR_SELECT_WALL, @@ -30,7 +40,10 @@ struct GAME_EDITOR_TOOL { U8 type; /* shapes */ - U8 walls; + I32 wallshape; + F32 polysides; + F32 wallheight; + F32 placementheight; GL_TEX2D* tex; /* entity */ @@ -107,6 +120,10 @@ struct GUI_EDITOR_2DVIEW : GUI_VIEW { void* curdrag; U8 dragtype; U8 dragmoved; + + U8 poly_drag; + VEC2 poly_start; + VEC2 poly_end; }; struct GUI_EDITOR_PROPVIEW : GUI_VIEW { @@ -118,6 +135,7 @@ struct GUI_EDITOR_PROPVIEW : GUI_VIEW { struct GUI_EDITOR_TOOLVIEW : GUI_VIEW { GUI_VIEW* itemview; + U8 wallshape_dropdown_open; }; struct GUI_EDITOR_TEXTUREPICKER : GUI_WINDOW { diff --git a/src/editor/gui.cpp b/src/editor/gui.cpp index 39914c8..c5e44df 100644 --- a/src/editor/gui.cpp +++ b/src/editor/gui.cpp @@ -69,7 +69,17 @@ void editor_update_toolview( GAME_EDITOR* e ) { gui_editor_toolview_update( egui->tool ); } -void settool( U8 t ) { editor->tool.type = t; } +void settool( U8 t ) { + editor->tool.type = t; + + if( editor->gui.v2d ) { + editor->gui.v2d->poly_drag = 0; + } + + if( editor->gui.tool ) { + editor_update_toolview( editor ); + } +} void editor_create_toolview_column( GAME_EDITOR* e ) { GAME_EDITOR::EDITOR_GUI* egui = &e->gui; diff --git a/src/editor/toolview.cpp b/src/editor/toolview.cpp index 2853c48..df1d79a 100644 --- a/src/editor/toolview.cpp +++ b/src/editor/toolview.cpp @@ -2,6 +2,183 @@ #include "../util/string.h" const U32 TOOLVIEW_TITLE_OFFSET = 15; +const I32 TOOLVIEW_INNER_X = 10; +const I32 TOOLVIEW_INNER_PAD = 20; +const I32 TOOLVIEW_ROW_HEIGHT = 20; +const I32 TOOLVIEW_ROW_GAP = 4; +const I32 TOOLVIEW_WALL_SHAPE_LIST_HEIGHT = 54; + +GUI_EDITOR_TOOLVIEW* gui_editor_toolview_from_item_child( GUI_BASE* child ) { + if( !child || !child->parent || !child->parent->parent ) + return 0; + + return (GUI_EDITOR_TOOLVIEW*)child->parent->parent; +} + +void gui_editor_toolview_sanitize_float_input( void* ptr ) { + GUI_FLOATINPUT* input = (GUI_FLOATINPUT*)ptr; + F32* pval = input->pval; + + if( pval == &editor->tool.polysides ) { + if( !isfinite( *pval ) ) *pval = EDITOR_DEFAULT_POLY_SIDES; + F32 rounded = floorf( *pval + 0.5f ); + if( rounded < EDITOR_POLY_SIDES_MIN ) rounded = EDITOR_POLY_SIDES_MIN; + if( rounded > EDITOR_POLY_SIDES_MAX ) rounded = EDITOR_POLY_SIDES_MAX; + *pval = rounded; + return; + } + + if( pval == &editor->tool.wallheight ) { + if( !isfinite( *pval ) ) *pval = EDITOR_DEFAULT_WALL_HEIGHT; + if( *pval < 1.f ) *pval = 1.f; + return; + } + + if( pval == &editor->tool.placementheight ) { + if( !isfinite( *pval ) ) *pval = EDITOR_DEFAULT_PLACEMENT_HEIGHT; + } +} + +I32 gui_editor_toolview_create_float_input( + GUI_EDITOR_TOOLVIEW* view, + I32 y, + const char* title, + F32* pval, + F32 min, + F32 max, + F32 step, + const char* fmt +) { + GUI_FLOATINPUT* input = gui_floatinput( + TOOLVIEW_INNER_X, + y, + view->w - TOOLVIEW_INNER_PAD, + title, + pval, + min, + max, + step, + fmt + ); + + input->cb = gui_editor_toolview_sanitize_float_input; + if( input->cb ) input->cb( input ); + return y + TOOLVIEW_ROW_HEIGHT + TOOLVIEW_ROW_GAP; +} + +void gui_editor_toolview_queue_refresh( GUI_EDITOR_TOOLVIEW* view ) { + if( !view ) + return; + + gui_push_callback( view, pfn( void* ptr ) { + gui_editor_toolview_update( (GUI_EDITOR_TOOLVIEW*)ptr ); + } ); +} + +void gui_editor_toolview_wallshape_dropdown_toggle_cb( void* ptr ) { + GUI_BUTTON* btn = (GUI_BUTTON*)ptr; + GUI_EDITOR_TOOLVIEW* view = gui_editor_toolview_from_item_child( btn ); + if( !view ) + return; + + view->wallshape_dropdown_open = !view->wallshape_dropdown_open; + gui_editor_toolview_queue_refresh( view ); +} + +void gui_editor_toolview_wallshape_select_cb( void* ptr ) { + GUI_LIST* list = (GUI_LIST*)ptr; + GUI_EDITOR_TOOLVIEW* view = gui_editor_toolview_from_item_child( list ); + if( !view ) + return; + + view->wallshape_dropdown_open = 0; + gui_editor_toolview_queue_refresh( view ); +} + +I32 gui_editor_toolview_create_wallshape_dropdown( GUI_EDITOR_TOOLVIEW* view, I32 y ) { + static LIST<GUI_LIST_ENTRY> shape_entries{}; + if( !shape_entries.size ) { + GUI_LIST_ENTRY line{}; + line.val = EDITOR_WALLSHAPE_LINE; + strcpy( line.title, "line" ); + shape_entries.push( line ); + + GUI_LIST_ENTRY polygon{}; + polygon.val = EDITOR_WALLSHAPE_POLYGON; + strcpy( polygon.title, "polygon" ); + shape_entries.push( polygon ); + } + + const char* shape_name = editor->tool.wallshape == EDITOR_WALLSHAPE_POLYGON ? "polygon" : "line"; + char dropdown_title[64]; + sprintf( dropdown_title, "shape: %s v", shape_name ); + + gui_button( + TOOLVIEW_INNER_X, + y, + view->w - TOOLVIEW_INNER_PAD, + TOOLVIEW_ROW_HEIGHT, + dropdown_title, + gui_editor_toolview_wallshape_dropdown_toggle_cb + ); + + y += TOOLVIEW_ROW_HEIGHT + TOOLVIEW_ROW_GAP; + if( !view->wallshape_dropdown_open ) + return y; + + GUI_LIST* shape = gui_list( + TOOLVIEW_INNER_X, + y, + view->w - TOOLVIEW_INNER_PAD, + TOOLVIEW_WALL_SHAPE_LIST_HEIGHT, + "shape", + &shape_entries, + &editor->tool.wallshape + ); + + shape->cb = gui_editor_toolview_wallshape_select_cb; + y += TOOLVIEW_WALL_SHAPE_LIST_HEIGHT + TOOLVIEW_ROW_GAP; + return y; +} + +I32 gui_editor_toolview_create_poly_sides_input( GUI_EDITOR_TOOLVIEW* view, I32 y ) { + return gui_editor_toolview_create_float_input( + view, + y, + "sides", + &editor->tool.polysides, + (F32)EDITOR_POLY_SIDES_MIN, + (F32)EDITOR_POLY_SIDES_MAX, + 1.f, + "%.0f" + ); +} + +I32 gui_editor_toolview_create_wall_height_input( GUI_EDITOR_TOOLVIEW* view, I32 y ) { + return gui_editor_toolview_create_float_input( + view, + y, + "wall height", + &editor->tool.wallheight, + 1.f, + 1024.f, + 1.f, + "%.0f" + ); +} + +I32 gui_editor_toolview_create_placement_height_input( GUI_EDITOR_TOOLVIEW* view, I32 y ) { + return gui_editor_toolview_create_float_input( + view, + y, + "placement z", + &editor->tool.placementheight, + -4096.f, + 4096.f, + 1.f, + "%.0f" + ); +} void gui_editor_toolview_get_title( GUI_EDITOR_TOOLVIEW* view, char* out ) { switch( editor->tool.type ) { @@ -15,7 +192,35 @@ void gui_editor_toolview_get_title( GUI_EDITOR_TOOLVIEW* view, char* out ) { } void gui_editor_toolview_update( GUI_EDITOR_TOOLVIEW* view ) { - + if( !view ) + return; + + GUI_VIEW* oldview = gui_get_view(); + gui_set_view( view->itemview ); + gui_empty_children( view->itemview ); + view->itemview->initheld = 1; + + I32 y = 10; + if( editor->tool.type == EDITOR_TOOL_WALL ) { + y = gui_editor_toolview_create_wallshape_dropdown( view, y ); + if( view->wallshape_dropdown_open ) { + gui_set_view( oldview ); + return; + } + + if( editor->tool.wallshape == EDITOR_WALLSHAPE_POLYGON ) { + y = gui_editor_toolview_create_poly_sides_input( view, y ); + } + + y = gui_editor_toolview_create_wall_height_input( view, y ); + y = gui_editor_toolview_create_placement_height_input( view, y ); + } + else if( editor->tool.type == EDITOR_TOOL_POLY ) { + y = gui_editor_toolview_create_poly_sides_input( view, y ); + y = gui_editor_toolview_create_placement_height_input( view, y ); + } + + gui_set_view( oldview ); } void gui_editor_toolview_draw_fn( void* ptr ) { @@ -53,6 +258,7 @@ GUI_EDITOR_TOOLVIEW* gui_editor_toolview( I32 x, I32 y, I32 w, I32 h ) { strcpy( view->name, "EDITOR_PROP_VIEW" ); view->draw_fn = gui_editor_toolview_draw_fn; view->input_fn = gui_base_input_fn; + view->wallshape_dropdown_open = 0; GUI_VIEW* parent = gui_get_view(); parent->children.push( view ); diff --git a/src/editor/view2d.cpp b/src/editor/view2d.cpp index 4498248..73b4062 100644 --- a/src/editor/view2d.cpp +++ b/src/editor/view2d.cpp @@ -9,6 +9,91 @@ const I32 EDITORVIEW_GUTTERS_OFFSETX = 22; const I32 EDITORVIEW_GUTTERS_OFFSETY = 16; const I32 EDITORVIEW_TOOLBAR_OFFSET = 20; +VEC2 gui_editor_2dview_screen_to_world( GUI_EDITOR_2DVIEW* view, I32 x, I32 y ); + +F32 gui_editor_2dview_ground_z() { + WORLD_MAP* map = editor->map; + if( !map ) + return 0.f; + + F32 wantedz = map->startpos.z; + F32 bestz = 0.f; + F32 bestdist = FLT_MAX; + U8 found_floor = 0; + map->polygons.each( fn( MAP_POLYGON* p ) { + if( p->type != MPT_FLOOR || !p->vertices.size ) + return; + + F32 floorz = p->vertices[0].pos.z; + F32 dist = fabsf( floorz - wantedz ); + if( !found_floor || dist < bestdist ) { + bestz = floorz; + bestdist = dist; + found_floor = 1; + } + } ); + + if( found_floor ) + return bestz; + + return isfinite( map->mins.z ) ? map->mins.z : 0.f; +} + +F32 gui_editor_2dview_wall_height() { + F32 wallheight = editor->tool.wallheight; + if( !isfinite( wallheight ) || wallheight < 1.f ) + wallheight = EDITOR_DEFAULT_WALL_HEIGHT; + return wallheight; +} + +F32 gui_editor_2dview_placement_z() { + F32 placeheight = editor->tool.placementheight; + if( !isfinite( placeheight ) ) + placeheight = EDITOR_DEFAULT_PLACEMENT_HEIGHT; + + return gui_editor_2dview_ground_z() + placeheight; +} + +F32 gui_editor_2dview_fit_scale_for_bounds( GUI_EDITOR_2DVIEW* view, VEC3 mins, VEC3 maxs ) { + I32 w = view->w - EDITORVIEW_GUTTERS_OFFSETX - 1; + I32 h = view->h - EDITORVIEW_GUTTERS_OFFSETY - 1; + if( w <= 0 || h <= 0 ) + return 1.f; + + F32 mapw = maxs.x - mins.x; + F32 maph = maxs.y - mins.y; + if( !isfinite( mapw ) || !isfinite( maph ) || mapw <= 0.0001f || maph <= 0.0001f ) + return 1.f; + + return min( w / mapw, h / maph ); +} + +void gui_editor_2dview_check_bounds_preserve_view( GUI_EDITOR_2DVIEW* view ) { + if( !view || !editor || !editor->map ) + return; + + I32 mx, my; + gui_cursor_pos( &mx, &my ); + VEC2 world_before = gui_editor_2dview_screen_to_world( view, mx, my ); + + VEC3 oldmins = editor->map->mins; + VEC3 oldmaxs = editor->map->maxs; + F32 oldfit = gui_editor_2dview_fit_scale_for_bounds( view, oldmins, oldmaxs ); + + map_check_bounds( editor->map ); + + VEC3 newmins = editor->map->mins; + VEC3 newmaxs = editor->map->maxs; + F32 newfit = gui_editor_2dview_fit_scale_for_bounds( view, newmins, newmaxs ); + if( newfit > 0.000001f ) { + view->scale *= oldfit / newfit; + } + + VEC2 world_after = gui_editor_2dview_screen_to_world( view, mx, my ); + view->posx += world_before.x - world_after.x; + view->posy += world_before.y - world_after.y; +} + F32 gui_editor_2dview_calc_scale( GUI_EDITOR_2DVIEW* view ) { WORLD_MAP* m = editor->map; @@ -24,31 +109,20 @@ F32 gui_editor_2dview_calc_scale( GUI_EDITOR_2DVIEW* view ) { VEC2 gui_editor_2dview_screen_to_world( GUI_EDITOR_2DVIEW* view, I32 x, I32 y ) { WORLD_MAP* m = editor->map; + F32 scale = gui_editor_2dview_calc_scale( view ); + if( !isfinite( scale ) || fabsf( scale ) < 0.000001f ) { + return { 0.f, 0.f }; + } - I32 _x = gui_relx( view ) + EDITORVIEW_GUTTERS_OFFSETX; - I32 _y = gui_rely( view ) + EDITORVIEW_TITLE_OFFSET + EDITORVIEW_GUTTERS_OFFSETY; - x -= _x; y -= _y; - - F32 w = view->w - EDITORVIEW_GUTTERS_OFFSETX; - F32 h = view->h - EDITORVIEW_GUTTERS_OFFSETY; - - F32 tx = x / w; - F32 ty = y / h; - - F32 scaledx = m->mins.x + (m->maxs.x - m->mins.x) * tx; - F32 scaledy = m->mins.y + (m->maxs.y - m->mins.y) * ty; - - if( w > h ) - scaledx *= ( w / h ); - if( w < h ) - scaledy *= h / w; + F32 xoff = m->mins.x + view->posx; + F32 yoff = m->mins.y + view->posy; - scaledx /= view->scale; - scaledx += view->posx; - scaledy /= view->scale; - scaledy += view->posy; + F32 vx = (F32)gui_relx( view ) + EDITORVIEW_GUTTERS_OFFSETX; + F32 vy = (F32)gui_rely( view ) + EDITORVIEW_TITLE_OFFSET + EDITORVIEW_GUTTERS_OFFSETY; - return { scaledx, scaledy }; + F32 worldx = ((F32)x - vx) / scale + xoff; + F32 worldy = ((F32)y - vy) / scale + yoff; + return { worldx, worldy }; } VEC2 gui_editor_2dview_world_to_screen( GUI_EDITOR_2DVIEW* view, VEC2 world ) { @@ -102,6 +176,244 @@ void gui_editor_2dview_draw_gizmo( I32 x, I32 y, CLR clr, U8 selected = 0 ) { gui_draw_frect( x - ihalf, y - ihalf, GIZMO_SIZE, GIZMO_SIZE, clr ); } +I32 gui_editor_2dview_poly_sides() { + I32 sides = (I32)floorf( editor->tool.polysides + 0.5f ); + if( sides < EDITOR_POLY_SIDES_MIN ) sides = EDITOR_POLY_SIDES_MIN; + if( sides > EDITOR_POLY_SIDES_MAX ) sides = EDITOR_POLY_SIDES_MAX; + return sides; +} + +U8 gui_editor_2dview_poly_keep_regular( I32 sides ) { + if( sides == 4 ) + return 0; + + return kb_down( SDLK_LSHIFT ) || kb_down( SDLK_RSHIFT ); +} + +U8 gui_editor_2dview_is_poly_drag_tool() { + if( editor->tool.type == EDITOR_TOOL_POLY ) + return 1; + + return editor->tool.type == EDITOR_TOOL_WALL + && editor->tool.wallshape == EDITOR_WALLSHAPE_POLYGON; +} + +I32 gui_editor_2dview_poly_points( VEC2 start, VEC2 end, I32 sides, U8 regular, VEC2* out_points ) { + if( !out_points || sides < EDITOR_POLY_SIDES_MIN || sides > EDITOR_POLY_SIDES_MAX ) + return 0; + + F32 minx = start.x < end.x ? start.x : end.x; + F32 maxx = start.x > end.x ? start.x : end.x; + F32 miny = start.y < end.y ? start.y : end.y; + F32 maxy = start.y > end.y ? start.y : end.y; + + if( sides == 4 ) { + out_points[0] = { maxx, maxy }; + out_points[1] = { minx, maxy }; + out_points[2] = { minx, miny }; + out_points[3] = { maxx, miny }; + return 4; + } + + F32 cx = ( minx + maxx ) * 0.5f; + F32 cy = ( miny + maxy ) * 0.5f; + F32 sx = ( maxx - minx ) * 0.5f; + F32 sy = ( maxy - miny ) * 0.5f; + + if( regular ) { + F32 uniform = sx < sy ? sx : sy; + sx = sy = uniform; + } + + F32 step = ( 2.f * PI ) / sides; + F32 rot = PI * 0.5f; + for( I32 i = 0; i < sides; ++i ) { + F32 ang = rot + step * i; + out_points[i] = { + cx + cosf( ang ) * sx, + cy + sinf( ang ) * sy + }; + } + + return sides; +} + +struct EDITORVIEW_DRAG_RECT { + F32 minx, maxx; + F32 miny, maxy; + F32 w, h; +}; + +struct EDITORVIEW_DRAG_SHAPE { + EDITORVIEW_DRAG_RECT rect; + VEC2 points[EDITOR_POLY_SIDES_MAX]; + I32 pointc; +}; + +U8 gui_editor_2dview_build_drag_shape( GUI_EDITOR_2DVIEW* view, EDITORVIEW_DRAG_SHAPE* out_shape ) { + if( !out_shape ) + return 0; + + EDITORVIEW_DRAG_RECT& rect = out_shape->rect; + rect.minx = view->poly_start.x < view->poly_end.x ? view->poly_start.x : view->poly_end.x; + rect.maxx = view->poly_start.x > view->poly_end.x ? view->poly_start.x : view->poly_end.x; + rect.miny = view->poly_start.y < view->poly_end.y ? view->poly_start.y : view->poly_end.y; + rect.maxy = view->poly_start.y > view->poly_end.y ? view->poly_start.y : view->poly_end.y; + rect.w = rect.maxx - rect.minx; + rect.h = rect.maxy - rect.miny; + + if( rect.w <= 0.0001f || rect.h <= 0.0001f ) + return 0; + + I32 sides = gui_editor_2dview_poly_sides(); + U8 regular = gui_editor_2dview_poly_keep_regular( sides ); + out_shape->pointc = gui_editor_2dview_poly_points( + view->poly_start, + view->poly_end, + sides, + regular, + out_shape->points + ); + + return out_shape->pointc >= 3; +} + +U8 gui_editor_2dview_is_color( CLR c, F32 r, F32 g, F32 b ) { + return fabsf( c.r - r ) < 0.01f + && fabsf( c.g - g ) < 0.01f + && fabsf( c.b - b ) < 0.01f; +} + +I32 gui_editor_2dview_find_or_create_wallpoly_propid() { + I32 propid = editor->map->props.idx_where( fn( SURF_PROPS* p ) { + return !p->tex + && ( gui_editor_2dview_is_color( p->clr, 0.f, 1.f, 1.f ) + || gui_editor_2dview_is_color( p->clr, 0.f, 0.f, 1.f ) ); + } ); + + if( propid != -1 ) + return propid; + + editor->map->props.push( { .tex = 0, .clr = CLR::CYAN() } ); + return editor->map->props.size - 1; +} + +void gui_editor_2dview_draw_dashed_hline( I32 x0, I32 x1, I32 y, CLR col ) { + if( x0 > x1 ) { + I32 tmp = x0; + x0 = x1; + x1 = tmp; + } + + const I32 dash = 6; + const I32 gap = 4; + for( I32 x = x0; x <= x1; x += dash + gap ) { + I32 ex = x + dash; + if( ex > x1 ) ex = x1; + gui_draw_line( x, y, ex, y, col ); + } +} + +void gui_editor_2dview_draw_dashed_vline( I32 x, I32 y0, I32 y1, CLR col ) { + if( y0 > y1 ) { + I32 tmp = y0; + y0 = y1; + y1 = tmp; + } + + const I32 dash = 6; + const I32 gap = 4; + for( I32 y = y0; y <= y1; y += dash + gap ) { + I32 ey = y + dash; + if( ey > y1 ) ey = y1; + gui_draw_line( x, y, x, ey, col ); + } +} + +void gui_editor_2dview_draw_poly_preview( GUI_EDITOR_2DVIEW* view ) { + if( !gui_editor_2dview_is_poly_drag_tool() || !view->poly_drag ) + return; + + VEC2 start = gui_editor_2dview_world_to_screen( view, view->poly_start ); + VEC2 end = gui_editor_2dview_world_to_screen( view, view->poly_end ); + + I32 minx = (I32)( start.x < end.x ? start.x : end.x ); + I32 maxx = (I32)( start.x > end.x ? start.x : end.x ); + I32 miny = (I32)( start.y < end.y ? start.y : end.y ); + I32 maxy = (I32)( start.y > end.y ? start.y : end.y ); + + CLR dash_clr = CLR::WHITE( 0.7f ); + gui_editor_2dview_draw_dashed_hline( minx, maxx, miny, dash_clr ); + gui_editor_2dview_draw_dashed_hline( minx, maxx, maxy, dash_clr ); + gui_editor_2dview_draw_dashed_vline( minx, miny, maxy, dash_clr ); + gui_editor_2dview_draw_dashed_vline( maxx, miny, maxy, dash_clr ); + + EDITORVIEW_DRAG_SHAPE shape{}; + if( !gui_editor_2dview_build_drag_shape( view, &shape ) ) + return; + + CLR poly_clr = CLR::CYAN( 0.9f ); + for( I32 i = 0; i < shape.pointc; ++i ) { + I32 next = ( i + 1 ) % shape.pointc; + VEC2 p0 = gui_editor_2dview_world_to_screen( view, shape.points[i] ); + VEC2 p1 = gui_editor_2dview_world_to_screen( view, shape.points[next] ); + gui_draw_line( (I32)p0.x, (I32)p0.y, (I32)p1.x, (I32)p1.y, poly_clr ); + } +} + +void gui_editor_2dview_create_poly_from_drag( GUI_EDITOR_2DVIEW* view ) { + EDITORVIEW_DRAG_SHAPE shape{}; + if( !gui_editor_2dview_build_drag_shape( view, &shape ) ) + return; + + MAP_POLYGON newp{}; + newp.propid = 0; + newp.type = MPT_FLOOR; + F32 placez = gui_editor_2dview_placement_z(); + + F32 invw = 1.f / shape.rect.w; + F32 invh = 1.f / shape.rect.h; + for( I32 i = 0; i < shape.pointc; ++i ) { + VEC2 p = shape.points[i]; + + MAP_VERTEX v{}; + v.pos = { p.x, p.y, placez }; + v.uv = { + ( p.x - shape.rect.minx ) * invw, + ( shape.rect.maxy - p.y ) * invh + }; + v.clr = CLR::WHITE(); + newp.vertices.push( v ); + } + + map_polygon_calc_bounds( &newp ); + editor->map->polygons.push( newp ); + gui_editor_2dview_check_bounds_preserve_view( view ); +} + +void gui_editor_2dview_create_wallpoly_from_drag( GUI_EDITOR_2DVIEW* view ) { + EDITORVIEW_DRAG_SHAPE shape{}; + if( !gui_editor_2dview_build_drag_shape( view, &shape ) ) + return; + + I32 propid = gui_editor_2dview_find_or_create_wallpoly_propid(); + + F32 basez = gui_editor_2dview_placement_z(); + F32 wallheight = gui_editor_2dview_wall_height(); + for( I32 i = 0; i < shape.pointc; ++i ) { + VEC2 p0 = shape.points[i]; + VEC2 p1 = shape.points[(i + 1) % shape.pointc]; + + MAP_WALL wall{}; + wall.start = { p0.x, p0.y, basez }; + wall.end = { p1.x, p1.y, wallheight }; + wall.propid = propid; + editor->map->walls.push( wall ); + } + + gui_editor_2dview_check_bounds_preserve_view( view ); +} + void gui_editor_2dview_draw_gutters( GUI_EDITOR_2DVIEW* view, I32 x, I32 y ) { WORLD_MAP* m = editor->map; F32 w = view->w; @@ -344,6 +656,7 @@ void gui_editor_2dview_draw_fn( void* ptr ) { gui_editor_2dview_draw_sprites( view, x + offx, y + offy ); gui_editor_2dview_draw_player( view, x + offx, y + offy ); gui_editor_2dview_draw_origin( view, x + offx, y + offy ); + gui_editor_2dview_draw_poly_preview( view ); gui_draw_pop_clip(); gui_draw_push_clip( x, y + 16, view->w, view->h ); @@ -752,9 +1065,40 @@ void gui_editor_2dview_input_scroll( GUI_EDITOR_2DVIEW* view ) { } void gui_editor_2dview_input_tool_wall( GUI_EDITOR_2DVIEW* view ) { + if( editor->tool.wallshape == EDITOR_WALLSHAPE_POLYGON ) { + U8 m1 = gui_mbutton_down( 0 ); + I32 mx, my; + gui_cursor_pos( &mx, &my ); + VEC2 world = gui_editor_2dview_screen_to_world( view, mx, my ); + + if( !m1 ) { + if( view->poly_drag ) { + view->poly_end = world; + gui_editor_2dview_create_wallpoly_from_drag( view ); + } + + view->poly_drag = 0; + return; + } + + if( !view->poly_drag ) { + view->poly_drag = 1; + view->poly_start = world; + view->poly_end = world; + return; + } + + view->poly_end = world; + return; + } + U8 m1 = gui_mbutton_down( 0 ); if( !m1 ) { + if( view->held ) { + gui_editor_2dview_check_bounds_preserve_view( view ); + } view->held = 0; + view->curdrag = 0; return; } @@ -763,13 +1107,14 @@ void gui_editor_2dview_input_tool_wall( GUI_EDITOR_2DVIEW* view ) { VEC2 world = gui_editor_2dview_screen_to_world( view, mx, my ); if( !view->held ) { + F32 basez = gui_editor_2dview_placement_z(); MAP_WALL neww; neww.start = neww.end = { m_snap_to_grid( world.x, editor->grid ), m_snap_to_grid( world.y, editor->grid ), - 0 + basez }; - neww.end.z = 80.f; + neww.end.z = gui_editor_2dview_wall_height(); neww.propid = 0; I32 idx = editor->map->walls.size; @@ -784,68 +1129,37 @@ void gui_editor_2dview_input_tool_wall( GUI_EDITOR_2DVIEW* view ) { return; } - gui_editor_2dview_input_select_drag_vertex( view ); + VEC3* end = (VEC3*)view->curdrag; + if( end ) { + end->x = m_snap_to_grid( world.x, editor->grid ); + end->y = m_snap_to_grid( world.y, editor->grid ); + } } void gui_editor_2dview_input_tool_poly( GUI_EDITOR_2DVIEW* view ) { U8 m1 = gui_mbutton_down( 0 ); - if( !m1 ) { - view->held = 0; - return; - } - I32 mx, my; gui_cursor_pos( &mx, &my ); - VEC2 world = gui_editor_2dview_screen_to_world( view, mx, my ); - if( !view->held ) { - MAP_POLYGON newp; - newp.propid = 0; - newp.vertices.push( { .pos = { world.x, world.y, 0.f }, .uv = { 1, 0 } } ); - newp.vertices.push( { .pos = { world.x, world.y, 0.f }, .uv = { 0, 0 } } ); - newp.vertices.push( { .pos = { world.x, world.y, 0.f }, .uv = { 0, 1 } } ); - newp.vertices.push( { .pos = { world.x, world.y, 0.f }, .uv = { 1, 1 } } ); - - map_polygon_calc_bounds( &newp ); - - I32 idx = editor->map->polygons.size; - editor->map->polygons.push( newp ); - view->curdrag = &editor->map->polygons[idx]; - view->held = 1; - view->oldmx = mx; - view->oldmy = my; - view->mremainx = 0.0; - view->mremainy = 0.0; + if( !m1 ) { + if( view->poly_drag ) { + view->poly_end = world; + gui_editor_2dview_create_poly_from_drag( view ); + } + + view->poly_drag = 0; return; } - MAP_POLYGON* p = (MAP_POLYGON*)view->curdrag; - MAP_VERTEX - *tl = &p->vertices[1], - *tr = &p->vertices[0], - *bl = &p->vertices[2], - *br = &p->vertices[3]; - - VEC3 start = tl->pos; - VEC3 end = br->pos; - - for( U32 i = 0; i < 3; ++i ) { - start[i] = m_snap_to_grid( start[i], editor->grid ); - end[i] = m_snap_to_grid( end[i], editor->grid ); + if( !view->poly_drag ) { + view->poly_drag = 1; + view->poly_start = world; + view->poly_end = world; + return; } - VEC2 move = gui_editor_2dview_input_get_drag_vec( view ); - if( !is_zero( move ) ) { - end += move; - tl->pos = { start.x, start.y, tl->pos.z }; - tr->pos = { end.x, start.y, tr->pos.z }; - bl->pos = { start.x, end.y, bl->pos.z }; - br->pos = { end.x, end.y, br->pos.z }; - - map_polygon_calc_bounds( p ); - gui_editor_2dview_input_select_onmove( view ); - } + view->poly_end = world; } void gui_editor_2dview_input_tool_ent( GUI_EDITOR_2DVIEW* view ) { @@ -1013,6 +1327,10 @@ void gui_editor_2dview_input_fn( void* ptr ) { if( my >= y + h - 18 ) return; + if( !gui_editor_2dview_is_poly_drag_tool() ) { + view->poly_drag = 0; + } + switch( editor->tool.type ) { case EDITOR_TOOL_SELECT: return gui_editor_2dview_input_tool_select( view ); case EDITOR_TOOL_WALL: @@ -1066,7 +1384,7 @@ void gui_editor_2dview_create_toolbar( GUI_EDITOR_2DVIEW* view ) { } GUI_EDITOR_2DVIEW* gui_editor_2dview( I32 x, I32 y, I32 w, I32 h ) { - GUI_EDITOR_2DVIEW* view = new GUI_EDITOR_2DVIEW; + GUI_EDITOR_2DVIEW* view = new GUI_EDITOR_2DVIEW(); view->x = x; view->y = y; view->xbound = view->w = w; @@ -1078,6 +1396,9 @@ GUI_EDITOR_2DVIEW* gui_editor_2dview( I32 x, I32 y, I32 w, I32 h ) { strcpy( view->name, "EDITOR_2D_VIEW" ); view->scale = 1.f; + view->poly_drag = 0; + view->poly_start = { 0.f, 0.f }; + view->poly_end = { 0.f, 0.f }; GUI_BASE* parent = gui_get_view(); if( !parent ) |
