#include #include "editor.h" #include "../render/gl_2d.h" #include "../game/objlist.h" #include "../game/world/bsp.h" const I32 EDITORVIEW_TITLE_OFFSET = 15; 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; 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; F32 scale = gui_editor_2dview_calc_scale( view ); if( !isfinite( scale ) || fabsf( scale ) < 0.000001f ) { return { 0.f, 0.f }; } F32 xoff = m->mins.x + view->posx; F32 yoff = m->mins.y + view->posy; F32 vx = (F32)gui_relx( view ) + EDITORVIEW_GUTTERS_OFFSETX; F32 vy = (F32)gui_rely( view ) + EDITORVIEW_TITLE_OFFSET + EDITORVIEW_GUTTERS_OFFSETY; 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 ) { 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 ); } 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; 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.type != EDITOR_TOOL_POLY && editor->tool.type != 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.type != EDITOR_TOOL_WALL && editor->tool.type != 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 - EDITORVIEW_TOOLBAR_OFFSET ); 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_editor_2dview_draw_poly_preview( view ); gui_draw_pop_clip(); gui_draw_push_clip( x, y + 16, view->w, view->h ); view->children.each( fn( GUI_BASE** childptr ) { GUI_BASE* child = *childptr; if( !child->enabled ) return; if( child->draw_fn ) child->draw_fn( child ); else dlog( "gui_view_draw_fn(): child %p no draw_fn\n", child ); } ); 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(); F32 zoom_factor = 1.f; if( scroll == (U8)-1 && view->scale > 0.5f ) { zoom_factor = 0.75f; } else if( scroll == 1 && view->scale < 16.f ) { zoom_factor = 1.25f; } if( zoom_factor == 1.f ) return; I32 mx, my; gui_cursor_pos( &mx, &my ); VEC2 world_before = gui_editor_2dview_screen_to_world( view, mx, my ); view->scale *= zoom_factor; 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; view->dragheld = 0; } 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; } I32 mx, my; gui_cursor_pos( &mx, &my ); 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 ), basez }; neww.end.z = gui_editor_2dview_wall_height(); 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; } 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 ); 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_poly_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; } 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.type ) { 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_view2d_delete_obj( GUI_EDITOR_2DVIEW* view ) { void* it; U8 type; if( view->curdrag && view->dragtype ) { it = view->curdrag; type = view->dragtype; view->curdrag = 0; view->dragtype = EDITOR_SELECT_NONE; } else if( editor->gui.props->curselect ) { it = editor->gui.props->curselect; type = editor->gui.props->seltype; view->curselect = 0; view->seltype = EDITOR_SELECT_NONE; } else return; U8 cleared = 1; switch( type ) { case EDITOR_SELECT_POLY: { I32 idx = editor->map->polygons.idx_of( (MAP_POLYGON*)it ); if( idx != -1 ) editor->map->polygons.erase( idx ); } break; case EDITOR_SELECT_WALL: { I32 idx = editor->map->walls.idx_of( (MAP_WALL*)it ); if( idx != -1 ) editor->map->walls.erase( idx ); }; break; case EDITOR_SELECT_SPRITE: { I32 idx = editor->map->sprites.idx_of( (MAP_SPRITE*)it ); if( idx != -1 ) editor->map->sprites.erase( idx ); }; break; case EDITOR_SELECT_PVERTEX: { I32 vidx = -1, idx = editor->map->polygons.idx_where( fn( MAP_POLYGON* p ) { vidx = p->vertices.idx_where( fn( MAP_VERTEX* v ) { return v == it; } ); return vidx != -1; } ); if( idx != -1 && vidx != -1 ) { MAP_POLYGON* p = &editor->map->polygons[idx]; if( p->vertices.size <= 3 ) { editor->map->polygons.erase( idx ); break; } editor->map->polygons[idx].vertices.erase( vidx ); } }; break; case EDITOR_SELECT_WVERTEX: { I32 idx = editor->map->walls.idx_where( fn( MAP_WALL* w ) { return &w->end == it || &w->start == it; } ); if( idx != -1 ) editor->map->walls.erase( idx ); }; break; default: cleared = 0; break; } if( cleared && it == editor->gui.props->curselect ) { gui_editor_propview_select( editor->gui.props, 0, 0 ); } } void gui_editor_2dview_key_input( GUI_EDITOR_2DVIEW* view ) { static U8 del_held = 0; if( kb_down( SDLK_DELETE ) && !input.mouselock ) { if( !del_held ) gui_editor_view2d_delete_obj( view ); del_held = 1; } else del_held = 0; } void gui_editor_2dview_input_fn( void* ptr ) { GUI_EDITOR_2DVIEW* view = (GUI_EDITOR_2DVIEW*)ptr; if( !editor->map ) return; gui_editor_2dview_key_input( view ); 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; gui_base_input_fn( view ); 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: 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 ); } } void update_view_settings( GAME_EDITOR* e ) { GAME_EDITOR::EDITOR_GUI* egui = &e->gui; sprintf( egui->gridlabel->name, "grid: %1.2f", e->grid ); } void grid_increment_cb( void* ) { if( editor->grid < 16.f ) editor->grid *= 2.f; update_view_settings( editor ); if( editor->propgrid ) editor_update_properties_column( editor ); } void grid_decrement_cb( void* ) { if( editor->grid > 0.25f ) editor->grid *= 0.5f; update_view_settings( editor ); if( editor->propgrid ) editor_update_properties_column( editor ); } void grid_propgrid_cb( void* ) { editor_update_properties_column( editor ); } void gui_editor_2dview_create_toolbar( GUI_EDITOR_2DVIEW* view ) { GAME_EDITOR* e = editor; I32 x = 150, y = view->h - 4; editor->gui.gridlabel = gui_label( x, y + 1, "grid: %1.2f", e->grid ); x += 70; gui_button( x, y, 18, 18, "+", grid_increment_cb ); gui_button( x + 23, y, 18, 18, "-", grid_decrement_cb ); x += 50; GUI_CHECKBOX* check = gui_checkbox( x, y, "properties grid", &e->propgrid ); check->cb = grid_propgrid_cb; x += 120; gui_checkbox( x, y, "wireframe", &e->wireframe ); } 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; 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 ) parent = gui_get_window(); parent->children.push( view ); view->parent = parent; gui_set_view( view ); gui_editor_2dview_create_toolbar( view ); gui_set_view( (GUI_VIEW*)parent ); return view; }