From f8b92ce3aa08b1445c9f956d8166830946562d12 Mon Sep 17 00:00:00 2001 From: navewindre Date: Wed, 3 Sep 2025 20:10:09 +0200 Subject: a --- src/editor/view2d.cpp | 938 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 938 insertions(+) create mode 100644 src/editor/view2d.cpp (limited to 'src/editor/view2d.cpp') 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 +#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 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 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; +} -- cgit v1.2.3