summaryrefslogtreecommitdiff
path: root/src/editor/view2d.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/editor/view2d.cpp')
-rw-r--r--src/editor/view2d.cpp471
1 files changed, 397 insertions, 74 deletions
diff --git a/src/editor/view2d.cpp b/src/editor/view2d.cpp
index 4498248..92988df 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,246 @@ 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 = editor->tool.type == EDITOR_TOOL_POLY
+ ? CLR::MAGENTA( 0.9f )
+ : 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 +658,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 +1067,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 +1109,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 +1131,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 +1329,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 +1386,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 +1398,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 )