summaryrefslogtreecommitdiff
path: root/src/editor/view2d.cpp
diff options
context:
space:
mode:
authornavewindre <boneyaard@gmail.com>2025-09-03 20:10:09 +0200
committernavewindre <boneyaard@gmail.com>2025-09-03 20:10:09 +0200
commitf8b92ce3aa08b1445c9f956d8166830946562d12 (patch)
tree94e63a5aec9f8f52b577f56799e0c9201fd976a5 /src/editor/view2d.cpp
a
Diffstat (limited to 'src/editor/view2d.cpp')
-rw-r--r--src/editor/view2d.cpp938
1 files changed, 938 insertions, 0 deletions
diff --git a/src/editor/view2d.cpp b/src/editor/view2d.cpp
new file mode 100644
index 0000000..35fd208
--- /dev/null
+++ b/src/editor/view2d.cpp
@@ -0,0 +1,938 @@
+#include <math.h>
+#include "editor.h"
+#include "../render/gl_2d.h"
+
+#include "../game/objlist.h"
+
+const I32 EDITORVIEW_TITLE_OFFSET = 15;
+const I32 EDITORVIEW_GUTTERS_OFFSETX = 22;
+const I32 EDITORVIEW_GUTTERS_OFFSETY = 16;
+
+F32 gui_editor_2dview_calc_scale( GUI_EDITOR_2DVIEW* view ) {
+ WORLD_MAP* m = editor->map;
+
+ I32 w = view->w - EDITORVIEW_GUTTERS_OFFSETX - 1;
+ I32 h = view->h - EDITORVIEW_GUTTERS_OFFSETY - 1;
+
+ F32 mapw = m->maxs.x - m->mins.x;
+ F32 maph = m->maxs.y - m->mins.y;
+
+ F32 scale = min( w / mapw, h / maph );
+ return scale * view->scale;
+}
+
+VEC2 gui_editor_2dview_screen_to_world( GUI_EDITOR_2DVIEW* view, I32 x, I32 y ) {
+ WORLD_MAP* m = editor->map;
+
+ 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;
+
+ scaledx /= view->scale;
+ scaledx += view->posx;
+ scaledy /= view->scale;
+ scaledy += view->posy;
+
+ return { scaledx, scaledy };
+}
+
+VEC2 gui_editor_2dview_world_to_screen( GUI_EDITOR_2DVIEW* view, VEC2 world ) {
+ WORLD_MAP* m = editor->map;
+ F32 scale = gui_editor_2dview_calc_scale( view );
+
+ F32 xoff = m->mins.x + view->posx;
+ F32 yoff = m->mins.y + view->posy;
+
+ F32 vx = (F32)gui_relx( view );
+ F32 vy = (F32)gui_rely( view );
+ vy += EDITORVIEW_TITLE_OFFSET + EDITORVIEW_GUTTERS_OFFSETY;
+ vx += EDITORVIEW_GUTTERS_OFFSETX;
+
+ F32 x = vx + (world.x - xoff) * scale;
+ F32 y = vy + (world.y - yoff) * scale;
+
+ return { x, y };
+}
+
+void gui_editor_2dview_select( GUI_EDITOR_2DVIEW* view, void* what, U8 seltype ) {
+ view->curselect = what;
+ view->seltype = seltype;
+}
+
+U8 gui_editor_2dview_is_gizmo_active( GUI_EDITOR_2DVIEW* view, void* what, U8 seltype ) {
+ GUI_EDITOR_PROPVIEW* props = editor->gui.props;
+ if( props->seltype == EDITOR_SELECT_WALL && (seltype == EDITOR_SELECT_WVERTEX) ) {
+ MAP_WALL* s = (MAP_WALL*)props->curselect;
+ if( what == &s->start || what == &s->end )
+ return 1;
+ }
+ else if( props->seltype == seltype && props->curselect == what ) {
+ return 1;
+ }
+
+ if( view->curdrag ) {
+ return what == view->curdrag && seltype == view->dragtype;
+ }
+ else {
+ return what == view->curselect && seltype == view->seltype;
+ }
+}
+
+void gui_editor_2dview_draw_gizmo( I32 x, I32 y, CLR clr, U8 selected = 0 ) {
+ const I32 GIZMO_SIZE = selected ? 10 : 6;
+ const I32 ihalf = GIZMO_SIZE / 2;
+
+ gui_draw_frect( x - ihalf - 1, y - ihalf - 1, GIZMO_SIZE, GIZMO_SIZE, { .7f, .7f, .7f, 1.f } );
+ gui_draw_frect( x - ihalf + 1, y - ihalf + 1, GIZMO_SIZE, GIZMO_SIZE, { 0.f, 0.f, 0.f, 1.f } );
+ gui_draw_frect( x - ihalf, y - ihalf, GIZMO_SIZE, GIZMO_SIZE, clr );
+}
+
+void gui_editor_2dview_draw_gutters( GUI_EDITOR_2DVIEW* view, I32 x, I32 y ) {
+ WORLD_MAP* m = editor->map;
+ F32 w = view->w;
+ F32 h = view->h;
+
+ gui_draw_str( x + 2, y, ALIGN_L, FNT_JPN12, ui_clr.txt, "%1.f,%1.f", view->posx, view->posy );
+ for( U32 i = 1; i <= 5; ++i ) {
+ F32 tx = x + i * (w / 5);
+ F32 ty = y;
+
+ F32 tp = (tx - x) / w;
+
+ F32 scaledx = m->mins.x + (m->maxs.x - m->mins.x) * tp;
+ if( w > h )
+ scaledx *= ( w / h );
+ scaledx /= view->scale;
+ scaledx += view->posx;
+ gui_draw_str( tx - 2, ty, ALIGN_R, FNT_JPN12, ui_clr.txt, "%1.f", scaledx );
+ gui_draw_line( tx, ty, tx + 1, ty + 16, ui_clr.txt );
+ }
+
+ for( U32 i = 1; i <= 5; ++i ) {
+ F32 tx = x + 2;
+ F32 ty = y + i * (h / 5);
+
+ F32 tp = (ty - y) / h;
+
+ F32 scaledy = m->mins.y + (m->maxs.y - m->mins.y) * tp;
+ if( w < h )
+ scaledy *= h / w;
+ scaledy /= view->scale;
+ scaledy += view->posy;
+ gui_draw_str( tx, ty - 16, ALIGN_L, FNT_JPN12, ui_clr.txt, "%1.f", scaledy );
+ gui_draw_line( tx - 2, ty, tx + 20, ty, ui_clr.txt );
+ }
+}
+
+void gui_editor_2dview_draw_polygons( GUI_EDITOR_2DVIEW* view, I32 x, I32 y ) {
+ WORLD_MAP* m = editor->map;
+
+ F32 scale = gui_editor_2dview_calc_scale( view );
+ F32 xoff = m->mins.x + view->posx;
+ F32 yoff = m->mins.y + view->posy;
+
+ m->polygons.each( fn( MAP_POLYGON* p ) {
+ SURF_PROPS* props = polygon_get_props( m, p );
+ if( !editor->wireframe ) {
+ LIST<VERTEX> vertices;
+ p->vertices.each( fn( MAP_VERTEX* v ) {
+ VERTEX v2;
+ v2.uv = v->uv;
+ v2.pos.x = x + (v->pos.x - xoff) * scale;
+ v2.pos.y = y + (v->pos.y - yoff) * scale;
+ v2.clr = props->clr;
+ vertices.push( v2 );
+ } );
+
+ if( props->tex )
+ gl_textured_polygon( _gui.gl2d_font, vertices.data, vertices.size, props->tex );
+ else
+ gl_polygon( _gui.gl2d, vertices.data, vertices.size );
+ }
+ else {
+ for( U32 i = 0; i < p->vertices.size; ++i ) {
+ MAP_VERTEX* v1 = &p->vertices[i];
+ MAP_VERTEX* v2 = &p->vertices[(i+1) % p->vertices.size];
+
+ I32 x0 = x + (I32)( (v1->pos.x - xoff) * scale );
+ I32 y0 = y + (I32)( (v1->pos.y - yoff) * scale );
+ I32 x1 = x + (I32)( (v2->pos.x - xoff) * scale );
+ I32 y1 = y + (I32)( (v2->pos.y - yoff) * scale );
+
+ gui_draw_line( x0, y0, x1, y1, props->clr );
+ }
+ }
+
+ if( editor->tool != EDITOR_TOOL_POLY && editor->tool != EDITOR_TOOL_SELECT )
+ return;
+ // draw gizmos
+ p->vertices.each( fn( MAP_VERTEX* v ) {
+ I32 vx = (I32)( x + (v->pos.x - xoff) * scale );
+ I32 vy = (I32)( y + (v->pos.y - yoff) * scale );
+ U8 selected = gui_editor_2dview_is_gizmo_active( view, v, EDITOR_SELECT_PVERTEX )
+ || gui_editor_2dview_is_gizmo_active( view, p, EDITOR_SELECT_POLY );
+
+ gui_editor_2dview_draw_gizmo( vx, vy, CLR::CYAN(), selected );
+ } );
+ } );
+}
+
+void gui_editor_2dview_draw_walls( GUI_EDITOR_2DVIEW* view, I32 x, I32 y ) {
+ WORLD_MAP* m = editor->map;
+
+ F32 scale = gui_editor_2dview_calc_scale( view );
+ F32 xoff = m->mins.x + view->posx;
+ F32 yoff = m->mins.y + view->posy;
+
+ m->walls.each( fn( MAP_WALL* s ) {
+ SURF_PROPS* props = wall_get_props( m, s );
+
+ I32 x0 = (I32)( x + (s->start.x - xoff) * scale );
+ I32 y0 = (I32)( y + (s->start.y - yoff) * scale );
+ I32 x1 = (I32)( x + (s->end.x - xoff) * scale );
+ I32 y1 = (I32)( y + (s->end.y - yoff) * scale );
+
+ gui_draw_line( x0, y0, x1, y1, props->clr );
+ if( gui_editor_2dview_is_gizmo_active( view, s, EDITOR_SELECT_WALL ) ) {
+ gui_draw_line( x0, y0 - 1, x1, y1 - 1, CLR::WHITE( .5f ) );
+ gui_draw_line( x0, y0 + 1, x1, y1 + 1, CLR::WHITE( .5f ) );
+ gui_draw_line( x0 - 1, y0, x1 - 1, y1, CLR::WHITE( .5f ) );
+ gui_draw_line( x0 + 1, y0, x1 + 1, y1, CLR::WHITE( .5f ) );
+ }
+ } );
+
+ if( editor->tool != EDITOR_TOOL_WALL && editor->tool != EDITOR_TOOL_SELECT )
+ return;
+
+ // gizmos
+ m->walls.each( fn( MAP_WALL* s ) {
+ I32 x0 = (I32)( x + (s->start.x - xoff) * scale );
+ I32 y0 = (I32)( y + (s->start.y - yoff) * scale );
+ I32 x1 = (I32)( x + (s->end.x - xoff) * scale );
+ I32 y1 = (I32)( y + (s->end.y - yoff) * scale );
+
+ U8 sel = gui_editor_2dview_is_gizmo_active( view, s, EDITOR_SELECT_WALL );
+ U8 sel1 = gui_editor_2dview_is_gizmo_active( view, &s->start, EDITOR_SELECT_WVERTEX );
+ U8 sel2 = gui_editor_2dview_is_gizmo_active( view, &s->end, EDITOR_SELECT_WVERTEX );
+
+ gui_editor_2dview_draw_gizmo( x0, y0, CLR::GREEN(), sel || sel1 );
+ gui_editor_2dview_draw_gizmo( x1, y1, CLR::GREEN(), sel || sel2 );
+ } );
+}
+
+void gui_editor_2dview_draw_sprites( GUI_EDITOR_2DVIEW* view, I32 x, I32 y ) {
+ WORLD_MAP* m = editor->map;
+
+ F32 scale = gui_editor_2dview_calc_scale( view );
+ F32 xoff = m->mins.x + view->posx;
+ F32 yoff = m->mins.y + view->posy;
+
+ m->sprites.each( fn( MAP_SPRITE* s ) {
+ F32 wantedsize = editor->spritesize;
+ F32 aspect = s->size.x / s->size.y;
+ F32 w = (aspect > 1.0f)? wantedsize : wantedsize * aspect;
+ F32 h = (aspect < 1.0f)? wantedsize : wantedsize / aspect;
+ VEC2 pos = {
+ (F32)x + (s->pos.x - xoff) * scale,
+ (F32)y + (s->pos.y - yoff) * scale
+ };
+
+ U8 sel= gui_editor_2dview_is_gizmo_active( view, s, EDITOR_SELECT_SPRITE );
+ if( sel ) {
+ gui_draw_rect( pos.x - w/2 - 1, pos.y - h/2 - 1, w + 2, h + 2, CLR::WHITE( 0.5f ) );
+ }
+
+ gl_2d_textured_frect(
+ _gui.gl2d_font,
+ { (F32)((I32)pos.x - w/2), (F32)((I32)pos.y - h/2) },
+ { (F32)w, (F32)h },
+ s->tex,
+ s->clr
+ );
+ } );
+}
+
+void gui_editor_2dview_draw_player( GUI_EDITOR_2DVIEW* view, I32 x, I32 y ) {
+ WORLD_MAP* m = editor->map;
+
+ if( !objl->pl )
+ return;
+
+ F32 scale = gui_editor_2dview_calc_scale( view );
+ F32 xoff = m->mins.x + view->posx;
+ F32 yoff = m->mins.y + view->posy;
+
+ VEC3 pos = objl->pl->pos;
+
+ VEC2 pos2d = {
+ (F32)x + (pos.x - xoff) * scale,
+ (F32)y + (pos.y - yoff) * scale
+ };
+
+ F32 yaw = objl->pl->rot.y;
+ VEC2 dir = m_radial_offset( yaw, 20.f );
+ VEC2 ray = pos2d + dir;
+
+ gui_draw_line( (I32)pos2d.x, (I32)pos2d.y, (I32)ray.x, (I32)ray.y, CLR::GREEN() );
+ gui_draw_frect( (I32)pos2d.x-4, (I32)pos2d.y-4, 8, 8, CLR::WHITE() );
+ gui_draw_frect( (I32)pos2d.x-3, (I32)pos2d.y-3, 6, 6, CLR::RED() );
+}
+
+void gui_editor_2dview_draw_origin( GUI_EDITOR_2DVIEW* view, I32 x, I32 y ) {
+ WORLD_MAP* m = editor->map;
+
+ F32 scale = gui_editor_2dview_calc_scale( view );
+ F32 xoff = m->mins.x + view->posx;
+ F32 yoff = m->mins.y + view->posy;
+
+ VEC3 pos = m->startpos;
+ I32 screenx = (I32)( x + (pos.x - xoff) * scale );
+ I32 screeny = (I32)( y + (pos.y - yoff) * scale );
+
+ if( gui_editor_2dview_is_gizmo_active( view, m, EDITOR_SELECT_ORIGIN ) ) {
+ I32 w, h;
+ gui_draw_get_str_bounds( &w, &h, FNT_JPN12, "(origin)" );
+ gui_draw_frect( screenx - w / 2 - 1, screeny + h - 1, w, 1, CLR::BLACK() );
+ gui_draw_frect( screenx - w / 2, screeny + h, w, 1, ui_clr.txt );
+ gui_draw_str( screenx - 1, screeny - 1, ALIGN_C, FNT_JPN12, CLR::WHITE( .5f ), "(origin)" );
+ }
+
+ gui_draw_str( screenx + 1, screeny + 1, ALIGN_C, FNT_JPN12, CLR::BLACK(), "(origin)" );
+ gui_draw_str( screenx, screeny, ALIGN_C, FNT_JPN12, ui_clr.txt, "(origin)" );
+}
+
+void gui_editor_2dview_draw_fn( void* ptr ) {
+ GUI_EDITOR_2DVIEW* view = (GUI_EDITOR_2DVIEW*)ptr;
+ if( !editor->map )
+ return;
+
+ I32 x = gui_relx( view );
+ I32 y = gui_rely( view );
+ I32 w = view->w;
+ I32 h = view->h;
+
+ gui_draw_str( x, y, ALIGN_L, FNT_JPN12, ui_clr.txt, "2d view" );
+ y += EDITORVIEW_TITLE_OFFSET;
+
+ CLR col = gui_is_fg_window( view )? ui_clr.border : ui_clr.border_inactive;
+ gui_draw_frect( x-1, y-1, view->w+2, view->h+2, col );
+ gui_draw_frect( x, y, view->w, view->h, CLR::BLACK() );
+
+ gui_editor_2dview_draw_gutters( view, x, y );
+
+ U32 offx = EDITORVIEW_GUTTERS_OFFSETX;
+ U32 offy = EDITORVIEW_GUTTERS_OFFSETY;
+
+ gui_draw_push_clip( x + offx, y + offy, w - offx, h - offy );
+ gui_editor_2dview_draw_polygons( view, x + offx, y + offy );
+ gui_editor_2dview_draw_walls( view, x + offx, y + offy );
+ 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_draw_pop_clip();
+}
+
+U8 gui_editor_2dview_input_drag( GUI_EDITOR_2DVIEW* view, U8 mouse ) {
+ if( !mouse ) {
+ view->dragheld = 0;
+ return 0;
+ }
+
+ I32 mx, my;
+ gui_cursor_pos( &mx, &my );
+
+ I32 x = gui_relx( view );
+ I32 y = gui_rely( view ) + EDITORVIEW_TITLE_OFFSET;
+ I32 w = view->w - EDITORVIEW_GUTTERS_OFFSETX;
+ I32 h = view->h - EDITORVIEW_GUTTERS_OFFSETY;
+ x += EDITORVIEW_GUTTERS_OFFSETX;
+ y += EDITORVIEW_GUTTERS_OFFSETY;
+
+ U8 inbound = mx >= x && mx <= x + w && my >= y && my <= y + h;
+ if( !inbound && !view->dragheld )
+ return 0;
+
+ I32 moffx = mx - x;
+ I32 moffy = my - y;
+
+ if( !view->dragheld ) {
+ view->moffx = moffx;
+ view->moffy = moffy;
+ view->voffx = view->posx;
+ view->voffy = view->posy;
+ view->dragheld = 1;
+ return 1;
+ }
+
+ F32 iscale = 1.f / gui_editor_2dview_calc_scale( view );
+
+ view->posx = view->voffx - (moffx - view->moffx) * iscale;
+ view->posy = view->voffy - (moffy - view->moffy) * iscale;
+ return 1;
+}
+
+void gui_editor_2dview_input_tool_none( GUI_EDITOR_2DVIEW* view ) {
+ U8 mouse = gui_mbutton_down( 0 ) || gui_mbutton_down( 1 );
+
+ gui_editor_2dview_input_drag( view, mouse );
+}
+
+VEC2 gui_editor_2dview_input_get_drag_vec( GUI_EDITOR_2DVIEW* view ) {
+ I32 mx, my;
+ gui_cursor_pos( &mx, &my );
+
+ F32 iscale = 1.f / gui_editor_2dview_calc_scale( view );
+ F32 dx = (mx - view->oldmx) * iscale + view->mremainx;
+ F32 dy = (my - view->oldmy) * iscale + view->mremainy;
+
+ F32 igridx = copysignf( floorf( fabsf( dx / editor->grid ) ), dx );
+ F32 igridy = copysignf( floorf( fabsf( dy / editor->grid ) ), dy );
+
+ return { editor->grid * igridx, editor->grid * igridy };
+}
+
+void gui_editor_2dview_input_select_onmove( GUI_EDITOR_2DVIEW* view ) {
+ I32 mx, my;
+ gui_cursor_pos( &mx, &my );
+
+ F32 iscale = 1.f / gui_editor_2dview_calc_scale( view );
+ F32 dx = (mx - view->oldmx) * iscale + view->mremainx;
+ F32 dy = (my - view->oldmy) * iscale + view->mremainy;
+
+ F32 rmx = copysignf( fmodf( fabsf( dx ), editor->grid ), dx );
+ F32 rmy = copysignf( fmodf( fabsf( dy ), editor->grid ), dy );
+
+ view->oldmx = mx;
+ view->oldmy = my;
+ view->mremainx = rmx;
+ view->mremainy = rmy;
+ view->dragmoved = 1;
+
+ GUI_EDITOR_PROPVIEW* props = editor->gui.props;
+ // special case for dragging wall vertices, just always update
+ U8 iswallv = props->seltype == EDITOR_SELECT_WALL && view->dragtype == EDITOR_SELECT_WVERTEX;
+ if( props->curselect == view->curdrag || iswallv )
+ gui_editor_propview_update( editor->gui.props );
+}
+
+void gui_editor_2dview_input_select_drag_wall( GUI_EDITOR_2DVIEW* view ) {
+ MAP_WALL* s = (MAP_WALL*)view->curdrag;
+
+ s->start.x = m_snap_to_grid( s->start.x, editor->grid );
+ s->start.y = m_snap_to_grid( s->start.y, editor->grid );
+ s->end.x = m_snap_to_grid( s->end.x, editor->grid );
+ s->end.y = m_snap_to_grid( s->end.y, editor->grid );
+
+ VEC2 mv = gui_editor_2dview_input_get_drag_vec( view );
+ if( !is_zero( mv ) ) {
+ s->start += mv;
+ s->end += mv;
+ map_check_bounds( editor->map );
+
+ gui_editor_2dview_input_select_onmove( view );
+ }
+}
+
+void gui_editor_2dview_input_select_drag_vertex( GUI_EDITOR_2DVIEW* view ) {
+ VEC2* v = (VEC2*)view->curdrag;
+ v->x = m_snap_to_grid( v->x, editor->grid );
+ v->y = m_snap_to_grid( v->y, editor->grid );
+
+ VEC2 mv = gui_editor_2dview_input_get_drag_vec( view );
+ if( !is_zero( mv ) ) {
+ *v += mv;
+ map_check_bounds( editor->map );
+
+ gui_editor_2dview_input_select_onmove( view );
+ }
+}
+
+void gui_editor_2dview_input_select_drag_polygon( GUI_EDITOR_2DVIEW* view ) {
+ MAP_POLYGON* p = (MAP_POLYGON*)view->curdrag;
+
+ VEC2 mv = gui_editor_2dview_input_get_drag_vec( view );
+ if( !is_zero( mv ) ) {
+ p->vertices.each( fn( MAP_VERTEX* v ) {
+ v->pos.x = m_snap_to_grid( v->pos.x, editor->grid );
+ v->pos.y = m_snap_to_grid( v->pos.y, editor->grid );
+
+ v->pos.x += mv.x;
+ v->pos.y += mv.y;
+ } );
+
+ map_polygon_calc_bounds( p );
+ map_check_bounds( editor->map );
+ gui_editor_2dview_input_select_onmove( view );
+ }
+}
+
+void gui_editor_2dview_input_select_drag_sprite( GUI_EDITOR_2DVIEW* view ) {
+ MAP_SPRITE* s = (MAP_SPRITE*)view->curdrag;
+
+ s->pos.x = m_snap_to_grid( s->pos.x, editor->grid );
+ s->pos.y = m_snap_to_grid( s->pos.y, editor->grid );
+
+ VEC2 mv = gui_editor_2dview_input_get_drag_vec( view );
+ if( !is_zero( mv ) ) {
+ s->pos += mv;
+ gui_editor_2dview_input_select_onmove( view );
+ }
+}
+
+void gui_editor_2dview_input_select_drag_origin( GUI_EDITOR_2DVIEW* view ) {
+ WORLD_MAP* m = editor->map;
+
+ m->startpos.x = m_snap_to_grid( m->startpos.x, editor->grid );
+ m->startpos.y = m_snap_to_grid( m->startpos.y, editor->grid );
+
+ VEC2 mv = gui_editor_2dview_input_get_drag_vec( view );
+ if( !is_zero( mv ) ) {
+ m->startpos += mv;
+ gui_editor_2dview_input_select_onmove( view );
+ }
+}
+
+void gui_editor_2dview_input_select_ondrop( GUI_EDITOR_2DVIEW* view ) {
+ if( !view->dragmoved )
+ gui_editor_propview_select( editor->gui.props, view->curdrag, view->dragtype );
+
+ view->curdrag = 0;
+}
+
+void gui_editor_2dview_input_select_drag( GUI_EDITOR_2DVIEW* view ) {
+ U8 m1 = gui_mbutton_down( 0 );
+ I32 mx, my;
+ gui_cursor_pos( &mx, &my );
+
+ if( !m1 ) {
+ if( view->held ) {
+ gui_editor_2dview_input_select_ondrop( view );
+ view->held = 0;
+ }
+ return;
+ }
+
+ if( !view->held ) {
+ view->curdrag = view->curselect;
+ view->dragtype = view->seltype;
+ view->oldmx = mx;
+ view->oldmy = my;
+ view->mremainx = view->mremainy = 0;
+ view->dragmoved = 0;
+ view->held = 1;
+ return;
+ }
+
+ switch( view->dragtype ) {
+ case EDITOR_SELECT_WALL: gui_editor_2dview_input_select_drag_wall( view ); break;
+ case EDITOR_SELECT_POLY: gui_editor_2dview_input_select_drag_polygon( view ); break;
+ case EDITOR_SELECT_SPRITE: gui_editor_2dview_input_select_drag_sprite( view ); break;
+ case EDITOR_SELECT_ORIGIN: gui_editor_2dview_input_select_drag_origin( view ); break;
+ case EDITOR_SELECT_WVERTEX:
+ case EDITOR_SELECT_PVERTEX:
+ gui_editor_2dview_input_select_drag_vertex( view ); break;
+ default: {
+ view->oldmx = mx;
+ view->oldmy = my;
+ } break;
+ }
+}
+
+void gui_editor_2dview_input_select_walls( GUI_EDITOR_2DVIEW* view ) {
+ WORLD_MAP* m = editor->map;
+
+ I32 mx, my;
+ gui_cursor_pos( &mx, &my );
+
+ VEC2 mpos = { (F32)mx, (F32)my };
+
+ F32 mindist = 5.0001f;
+ for( U32 i = 0; i < m->walls.size; ++i ) {
+ MAP_WALL* w = &m->walls[i];
+
+ VEC2 w1 = { w->start.x, w->start.y };
+ VEC2 w2 = { w->end.x, w->end.y };
+ w1 = gui_editor_2dview_world_to_screen( view, w1 );
+ w2 = gui_editor_2dview_world_to_screen( view, w2 );
+
+ F32 d1 = vec_dist( mpos, w1 );
+ F32 d2 = vec_dist( mpos, w2 );
+ if( d1 < mindist && d1 < d2 ) {
+ gui_editor_2dview_select( view, &w->start, EDITOR_SELECT_WVERTEX );
+ mindist = d1;
+ continue;
+ }
+ else if( d2 < mindist ) {
+ gui_editor_2dview_select( view, &w->end, EDITOR_SELECT_WVERTEX );
+ mindist = d2;
+ continue;
+ }
+ // give priority to vertices
+ if( view->seltype == EDITOR_SELECT_WVERTEX )
+ continue;
+
+ F32 dist = m_dist_line_to_point( w1, w2, mpos );
+ if( dist < 3.f && dist < mindist ) {
+ gui_editor_2dview_select( view, w, EDITOR_SELECT_WALL );
+ mindist = dist;
+ }
+ }
+
+ if( !view->curselect && !view->held )
+ return;
+
+ gui_editor_2dview_input_select_drag( view );
+}
+
+void gui_editor_2dview_input_select_polygons( GUI_EDITOR_2DVIEW* view ) {
+ WORLD_MAP* m = editor->map;
+
+ I32 mx, my;
+ gui_cursor_pos( &mx, &my );
+
+ VEC2 mpos = { (F32)mx, (F32)my };
+
+ F32 mindist = 5.0001f;
+ for( U32 i = 0; i < m->polygons.size; ++i ) {
+ MAP_POLYGON* p = &m->polygons[i];
+ LIST<VEC2> plist;
+
+ p->vertices.each( fn( MAP_VERTEX* v ) {
+ VEC2 world = { v->pos.x, v->pos.y };
+ VEC2 screen = gui_editor_2dview_world_to_screen( view, world );
+ F32 d = vec_dist( mpos, screen );
+ if( d < mindist ) {
+ gui_editor_2dview_select( view, v, EDITOR_SELECT_PVERTEX );
+ mindist = d;
+ }
+
+ plist.push( screen );
+ } );
+
+ if( view->seltype == EDITOR_SELECT_PVERTEX )
+ continue;
+
+ if( m_point_in_polygon( mpos, plist.data, plist.size ) )
+ gui_editor_2dview_select( view, p, EDITOR_SELECT_POLY );
+ }
+
+ if( !view->curselect && !view->held )
+ return;
+
+ gui_editor_2dview_input_select_drag( view );
+}
+
+void gui_editor_2dview_input_select_sprites( GUI_EDITOR_2DVIEW* view ) {
+ WORLD_MAP* m = editor->map;
+
+ I32 mx, my;
+ gui_cursor_pos( &mx, &my );
+
+ VEC2 mpos = { (F32)mx, (F32)my };
+
+ F32 hsize = editor->spritesize * .5f;
+ F32 mindist = editor->spritesize;
+ for( U32 i = 0; i < m->sprites.size; ++i ) {
+ MAP_SPRITE* s = &m->sprites[i];
+
+ VEC2 world = { s->pos.x, s->pos.y };
+ VEC2 screen = gui_editor_2dview_world_to_screen( view, world );
+
+ if( mpos.x >= screen.x - hsize && mpos.x <= screen.x + hsize
+ && mpos.y >= screen.y - hsize && mpos.y <= screen.y + hsize )
+ {
+ F32 dist = vec_dist( mpos, screen );
+ if( dist < mindist ) {
+ mindist = dist;
+ gui_editor_2dview_select( view, s, EDITOR_SELECT_SPRITE );
+ }
+ }
+ }
+
+ if( !view->curselect && !view->held )
+ return;
+
+ gui_editor_2dview_input_select_drag( view );
+}
+
+void gui_editor_2dview_input_select_origin( GUI_EDITOR_2DVIEW* view ) {
+ WORLD_MAP* m = editor->map;
+
+ I32 mx, my;
+ gui_cursor_pos( &mx, &my );
+
+ VEC2 mpos = { (F32)mx, (F32)my };
+ VEC2 world = { m->startpos.x, m->startpos.y };
+ VEC2 screen = gui_editor_2dview_world_to_screen( view, world );
+
+ I32 w, h;
+ gui_draw_get_str_bounds( &w, &h, FNT_JPN12, "(origin)" );
+
+ if( fabsf( (F32)mx - screen.x ) < (w * .5f + 2.f)
+ && fabsf( (F32)my - h * .5f - screen.y ) < (h * .5f + 2.f) ) {
+ gui_editor_2dview_select( view, m, EDITOR_SELECT_ORIGIN );
+ }
+
+ if( !view->curselect && !view->held )
+ return;
+
+ gui_editor_2dview_input_select_drag( view );
+}
+
+void gui_editor_2dview_input_tool_select( GUI_EDITOR_2DVIEW* view ) {
+ U8 m1 = gui_mbutton_down( 0 );
+ U8 m2 = !m1 && gui_mbutton_down( 1 );
+
+ if( gui_editor_2dview_input_drag( view, m2 ) ) {
+ return;
+ }
+
+ view->curselect = 0;
+ view->seltype = EDITOR_SELECT_NONE;
+ gui_editor_2dview_input_select_origin( view );
+ if( view->curselect ) return;
+ gui_editor_2dview_input_select_sprites( view );
+ if( view->curselect ) return;
+ gui_editor_2dview_input_select_walls( view );
+ if( view->curselect ) return;
+ gui_editor_2dview_input_select_polygons( view );
+}
+
+void gui_editor_2dview_input_scroll( GUI_EDITOR_2DVIEW* view ) {
+ U8 scroll = gui_mbutton_down( GUI_MBTNSCROLL );
+ gui_capture_scroll();
+
+ if( scroll == (U8)-1 && view->scale > 0.5f ) {
+ view->scale *= 0.75f;
+ }
+ else if( scroll == 1 && view->scale < 16.f ) {
+ view->scale *= 1.25f;
+ }
+}
+
+void gui_editor_2dview_input_tool_wall( 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_WALL neww;
+ neww.start = neww.end = {
+ m_snap_to_grid( world.x, editor->grid ),
+ m_snap_to_grid( world.y, editor->grid ),
+ 0
+ };
+ neww.end.z = 80.f;
+ neww.propid = 0;
+
+ I32 idx = editor->map->walls.size;
+ editor->map->walls.push( neww );
+ view->curdrag = &editor->map->walls[idx].end;
+ view->held = 1;
+
+ view->oldmx = mx;
+ view->oldmy = my;
+ view->mremainx = 0.0;
+ view->mremainy = 0.0;
+ return;
+ }
+
+ gui_editor_2dview_input_select_drag_vertex( view );
+}
+
+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;
+ 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 );
+ }
+
+ 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 );
+ }
+}
+
+void gui_editor_2dview_input_tool_ent( GUI_EDITOR_2DVIEW* view ) {
+
+}
+
+void gui_editor_2dview_input_tool_sprite( 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_SPRITE news;
+ news.pos = { world.x, world.y, 20.f };
+ news.size = { 20.f, 20.f };
+ news.clr = { 1.f, 1.f, 1.f, 1.f };
+ news.tex = 0;
+
+ I32 idx = editor->map->walls.size;
+ editor->map->sprites.push( news );
+ view->curdrag = &editor->map->sprites[idx];
+ view->held = 1;
+
+ view->oldmx = mx;
+ view->oldmy = my;
+ view->mremainx = 0.0;
+ view->mremainy = 0.0;
+ return;
+ }
+
+ gui_editor_2dview_input_select_drag_sprite( view );
+}
+
+
+void gui_editor_2dview_input_tool_draw( GUI_EDITOR_2DVIEW* view ) {
+ U8 m1 = gui_mbutton_down( 0 );
+ U8 m2 = !m1 && gui_mbutton_down( 1 );
+
+ if( gui_editor_2dview_input_drag( view, m2 ) ) {
+ return;
+ }
+
+ switch( editor->tool ) {
+ case EDITOR_TOOL_WALL: return gui_editor_2dview_input_tool_wall( view );
+ case EDITOR_TOOL_POLY: return gui_editor_2dview_input_tool_poly( view );
+ case EDITOR_TOOL_ENT: return gui_editor_2dview_input_tool_ent( view );
+ case EDITOR_TOOL_SPRITE: return gui_editor_2dview_input_tool_sprite( view );
+ default: return;
+ }
+}
+
+void gui_editor_2dview_input_fn( void* ptr ) {
+ GUI_EDITOR_2DVIEW* view = (GUI_EDITOR_2DVIEW*)ptr;
+ if( !editor->map )
+ return;
+
+ I32 x = gui_relx( view );
+ I32 y = gui_rely( view ) + EDITORVIEW_TITLE_OFFSET;
+ I32 w = view->w;
+ I32 h = view->h;
+
+ U8 mouse = gui_mbutton_down( 0 ) || gui_mbutton_down( 1 );
+ I32 mx, my;
+ gui_cursor_pos( &mx, &my );
+
+ U8 inbounds = mx >= x && mx <= x + w && my >= y && my <= y + h;
+ if( inbounds )
+ gui_editor_2dview_input_scroll( view );
+
+ if( !mouse )
+ view->heldoutbounds = 0;
+ else if( mouse && !inbounds )
+ view->heldoutbounds = 1;
+
+ if( view->heldoutbounds )
+ return;
+
+ switch( editor->tool ) {
+ case EDITOR_TOOL_SELECT: return gui_editor_2dview_input_tool_select( view );
+ case EDITOR_TOOL_WALL:
+ case EDITOR_TOOL_POLY:
+ case EDITOR_TOOL_SPRITE:
+ case EDITOR_TOOL_ENT:
+ return gui_editor_2dview_input_tool_draw( view );
+ default: return gui_editor_2dview_input_tool_none( view );
+ }
+}
+
+GUI_EDITOR_2DVIEW* gui_editor_2dview( I32 x, I32 y, I32 w, I32 h ) {
+ GUI_EDITOR_2DVIEW* view = new GUI_EDITOR_2DVIEW;
+ view->x = x;
+ view->y = y;
+ view->xbound = view->w = w;
+ view->h = h;
+ view->ybound = h + EDITORVIEW_TITLE_OFFSET;
+
+ view->draw_fn = gui_editor_2dview_draw_fn;
+ view->input_fn = gui_editor_2dview_input_fn;
+ strcpy( view->name, "EDITOR_2D_VIEW" );
+
+ view->scale = 1.f;
+
+ GUI_BASE* parent = gui_get_view();
+ if( !parent )
+ parent = gui_get_window();
+
+ parent->children.push( view );
+ view->parent = parent;
+
+ return view;
+}