summaryrefslogtreecommitdiff
path: root/src/editor
diff options
context:
space:
mode:
authorkasull <qsullian@gmail.com>2026-02-26 04:31:08 -0500
committerkasull <qsullian@gmail.com>2026-02-26 04:31:08 -0500
commit94e0df832c83bc7b9ead0824cfbc41f166869c68 (patch)
treec90d78ea332d66a09020a431b5031aa42686bd9f /src/editor
parentb384930de5044934207d1b2ceb4fa55705094f8b (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.cpp4
-rw-r--r--src/editor/editor.h20
-rw-r--r--src/editor/gui.cpp12
-rw-r--r--src/editor/toolview.cpp208
-rw-r--r--src/editor/view2d.cpp469
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 )