#include "properties.h" #include "editor.h" #include "../render/gl.h" #include "../game/assets.h" #include "../game/object.h" const I32 PROPVIEW_TITLE_OFFSET = 15; const I32 PROPVIEW_PAD = 10; const I32 PROPVIEW_ACTION_BTN_W = 20; const I32 PROPVIEW_ACTION_BTN_GAP = 5; I32 propview_col_left() { return PROPVIEW_PAD; } I32 propview_col_right( GUI_EDITOR_PROPVIEW* view ) { if( !view ) return PROPVIEW_PAD; return max( PROPVIEW_PAD, view->w - PROPVIEW_PAD ); } I32 propview_input_width( GUI_EDITOR_PROPVIEW* view ) { return max( 120, propview_col_right( view ) - propview_col_left() ); } I32 propview_sub_input_x( I32 x ) { return x + 10; } // I32 propview_sub_input_width( GUI_EDITOR_PROPVIEW* view, I32 x ) { // I32 right = propview_col_right( view ); // return max( 100, right - propview_sub_input_x( x ) ); // } I32 propview_action_x( GUI_EDITOR_PROPVIEW* view ) { I32 right = propview_col_right( view ); return max( propview_col_left(), right - PROPVIEW_ACTION_BTN_W ); } I32 propview_action2_x( GUI_EDITOR_PROPVIEW* view ) { return max( propview_col_left(), propview_action_x( view ) - PROPVIEW_ACTION_BTN_W - PROPVIEW_ACTION_BTN_GAP ); } void gui_editor_propview_select( GUI_EDITOR_PROPVIEW* view, void* what, U8 seltype, U8 clearall ) { using PROPSELECT = GUI_EDITOR_PROPVIEW::PROPVIEW_SELECT; if( !editor->map ) return; view->curprops.clear(); if( clearall ) view->curselect.clear(); PROPSELECT sel{ .obj = what, .seltype = seltype }; if( seltype == EDITOR_SELECT_WVERTEX ) { MAP_WALL* s = editor->map->walls.where( fn( MAP_WALL* s ) { return ( &s->start == what || &s->end == what ); } ); if( !s ) return gui_editor_propview_select( view, editor->map, EDITOR_SELECT_ORIGIN, 1 ); sel.obj = s; sel.seltype = EDITOR_SELECT_WALL; } if( view->curselect.idx_where( fn( PROPSELECT* p ) { return p->obj == sel.obj; } ) != -1 ) return; view->curselect.push( sel ); gui_editor_propview_update( view ); } U8 gui_editor_propview_is_selected( GUI_EDITOR_PROPVIEW *e, void *what, U8 seltype ) { for( auto& it : e->curselect ) { if( it.obj == what && it.seltype == seltype ) return 1; } return 0; } void gui_editor_propview_sync_props_value( GUI_EDITOR_PROPVIEW* view, EDITOR_PROP* prop ) { void* valuep = eprop_ptr( prop ); for( auto& it : view->curselect ) { EOBJECT* eobj = (EOBJECT*)it.obj; EDITOR_PROP* otherp = eprop_from_name( eobj, prop->displayname ); if( otherp == prop ) continue; if( otherp && otherp->type == prop->type ) { void* targetp = eprop_ptr( otherp ); if( targetp != valuep ) memcpy( targetp, valuep, prop->size ); } } } void gui_editor_propview_sync_props_value( GUI_EDITOR_PROPVIEW* view, const char* propname ) { if( view->curselect.size < 2 ) return; EDITOR_PROP** pptr = view->curprops.where( fn( EDITOR_PROP** pptr ) { return (*pptr)->displayname == propname; } ); if( !pptr ) return; gui_editor_propview_sync_props_value( view, *pptr ); } void gui_editor_propview_sync_props_fdiff( GUI_EDITOR_PROPVIEW* view, EDITOR_PROP* prop, F32* fdiff, U32 valc ) { for( auto& it : view->curselect ) { EOBJECT* eobj = (EOBJECT*)it.obj; EDITOR_PROP* otherp = eprop_from_name( eobj, prop->displayname ); if( otherp == prop ) continue; if( otherp && otherp->type == prop->type ) { F32* targetp = (F32*)eprop_ptr( otherp ); if( targetp == eprop_ptr( prop ) ) continue; for( U32 i = 0; i < valc; ++i ) targetp[i] += fdiff[i]; } } } void gui_editor_propview_sync_props_fdiff( GUI_EDITOR_PROPVIEW* view, const char* propname, F32* fdiff, U32 valc ) { if( view->curselect.size < 2 ) return; EDITOR_PROP** pptr = view->curprops.where( fn( EDITOR_PROP** pptr ) { return (*pptr)->displayname == propname; } ); if( !pptr ) return; gui_editor_propview_sync_props_fdiff( view, *pptr, fdiff, valc ); } // todo : int diff struct TEXBTN_CB { EDITOR_PROP* prop; GL_TEX2D** texp; }; void gui_editor_propview_create_eobj_prop_ro( GUI_EDITOR_PROPVIEW* view, EDITOR_PROP* prop, I32* x, I32* y, I32 w, I32 space ); void gui_editor_propview_create_eobj_props( GUI_EDITOR_PROPVIEW* view, EOBJECT* e, I32* x, I32* y, I32 w ); void gui_editor_propview_create_eobj_prop( GUI_EDITOR_PROPVIEW* view, EDITOR_PROP* prop, I32* x, I32* y, I32 w, I32 space ) { F32 action_x = propview_action_x( view ); I32 action2_x = propview_action2_x( view ); const char* n = prop->displayname; F32 min = prop->min; F32 max = prop->max; F32 step; if( prop->step ) step = prop->step; else step = editor->propgrid ? editor->grid : 0.25f; switch( prop->type ) { case EPROP_F32: { GUI_FLOATINPUT* in = gui_floatinput( *x, *y, w, n, (F32*)eprop_ptr( prop ), min, max, step ); in->cb = pfn( void* ptr ) { GUI_FLOATINPUT* fin = (GUI_FLOATINPUT*)ptr; gui_editor_propview_sync_props_fdiff( editor->gui.props, (EDITOR_PROP*)fin->cbextra, &fin->lastchange, 1 ); }; in->cbextra = prop; *y += (space+18); } break; case EPROP_VEC2: case EPROP_VEC3: case EPROP_VEC4: { U32 c = prop->type - EPROP_VEC2 + 2; GUI_VECTORINPUT* in = gui_vectorinput( *x, *y, w, n, (F32*)eprop_ptr( prop ), c, min, max, step, "xyzw", c == 2 ? "%.03f" : "%0.02f" ); in->cb = pfn( void* ptr ) { GUI_VECTORINPUT* vin = (GUI_VECTORINPUT*)ptr; gui_editor_propview_sync_props_fdiff( editor->gui.props, (EDITOR_PROP*)vin->cbextra, vin->lastchange.data, vin->inputs.size ); }; in->cbextra = prop; *y += (space+18); } break; case EPROP_CLR: { GUI_COLORINPUT* cin = gui_colorinput( *x, *y, w, n, (CLR*)eprop_ptr( prop ) ); cin->cb = pfn( void* ptr ) { GUI_COLORINPUT* cin = (GUI_COLORINPUT*)ptr; gui_editor_propview_sync_props_fdiff( editor->gui.props, (EDITOR_PROP*)cin->cbextra, cin->lastchange.data, cin->inputs.size ); }; cin->cbextra = prop; *y += (space+18); }break; case EPROP_MAPPROP: { MAP_PROPREF* ref = (MAP_PROPREF*)eprop_ptr( prop ); SURF_PROPS* props = map_get_props( editor->map, *ref ); gui_label( *x, *y, "prop id: %d", *ref ); GUI_BUTTON* newprop = gui_button( action2_x, *y, 20, 20, "+", pfn( void* ptr ) { GUI_BUTTON* btn = (GUI_BUTTON*)ptr; SURF_PROPS props{ .tex = 0, .clr = CLR::WHITE() }; editor->map->props.push( props ); EDITOR_PROP* eprop = (EDITOR_PROP*)btn->extra; MAP_PROPREF* ref = (MAP_PROPREF*)eprop_ptr( eprop ); (ref)->id = editor->map->props.size - 1; editor->gui.assets_scroll = ( editor->map->props.size + 1 ) * 28; gui_editor_propview_sync_props_value( editor->gui.props, eprop ); gui_editor_propview_update( editor->gui.props ); } ); newprop->extra = prop; GUI_BUTTON* goprop = gui_button( action_x, *y, 20, 20, "\x1A", pfn( void* ptr ) { GUI_BUTTON* btn = (GUI_BUTTON*)ptr; gui_editor_propview_select( editor->gui.props, btn->extra, EDITOR_SELECT_SURFPROPS ); } ); goprop->extra = props; *y += space; *x += 10; gui_editor_propview_create_eobj_props( view, props, x, y, w - 10 ); *x -= 10; } break; case EPROP_TEXTURE: { GL_TEX2D** tex = (GL_TEX2D**)eprop_ptr( prop ); if( *tex ) gui_label( *x, *y, "%s: %s", n, (*tex)->name ); else gui_label( *x, *y, "%s: none", n ); GUI_BUTTON* btn = gui_button( action_x, *y, 20, 20, "\x1A", cfn( void* ptr ) { GUI_BUTTON* btn = (GUI_BUTTON*)ptr; GL_TEX2D** ptex = (GL_TEX2D**)btn->extra; GUI_EDITOR_TEXTUREPICKER* picker = gui_editor_texturepicker( 200, 100, 400, 400, ptex ); picker->cb = pfn( void* p ) { GUI_EDITOR_TEXTUREPICKER* picker = (GUI_EDITOR_TEXTUREPICKER*)p; gui_editor_propview_sync_props_value( editor->gui.props, (EDITOR_PROP*)picker->cbextra ); gui_editor_propview_update( editor->gui.props ); }; picker->cbextra = prop; } ); btn->extra = tex; *y += space; } break; case EPROP_VERTEX_LIST: { LIST* list = ( LIST* )eprop_ptr( prop ); gui_label( *x, *y, "%s : %d", n, list->size ); *y += space; for( U32 i = 0; i < list->size; ++i ) { MAP_VERTEX* v = &list->data[i]; gui_label( (*x) + 10, *y, "[%d] -> [%s]", i, to_str( v->pos ).data ); GUI_BUTTON* btn = gui_button( action_x, *y, 20, 20, "\x1A", pfn( void* ptr ) { GUI_BUTTON* btn = (GUI_BUTTON*)ptr; gui_editor_propview_select( editor->gui.props, btn->extra, EDITOR_SELECT_PVERTEX ); } ); btn->extra = v; *y += space; } } break; default: return gui_editor_propview_create_eobj_prop_ro( view, prop, x, y, w, space ); break; } } void gui_editor_propview_create_eobj_prop_ro( GUI_EDITOR_PROPVIEW* view, EDITOR_PROP* prop, I32* x, I32* y, I32 w, I32 space ) { STR str = prop->displayname; str += " : ["; void* ptr = eprop_ptr( prop ); switch( prop->type ) { case EPROP_F32: str += to_str( *(F32*)ptr ); break; case EPROP_VEC2: str += to_str( *(VEC2*)ptr ); break; case EPROP_VEC3: str += to_str( *(VEC3*)ptr ); break; case EPROP_VEC4: str += to_str( *(VEC4*)ptr ); break; case EPROP_CLR: str += to_str( *(CLR*)ptr ); break; case EPROP_U32: str += to_str( *(U32*)ptr ); break; case EPROP_U64: str += to_str( *(U64*)ptr ); break; case EPROP_I32: str += to_str( *(I32*)ptr ); break; case EPROP_I64: str += to_str( *(I64*)ptr ); break; case EPROP_STRING: str += to_str( *(CLR*)ptr ); break; default: str += STR("UNIMPL_TYPE : ") + to_str( prop->type ); return; } str += "]"; gui_label( *x, *y, str.data ); *y += space; } void gui_editor_propview_create_eobj_props( GUI_EDITOR_PROPVIEW* view, EOBJECT* e, I32* x, I32* y, I32 w ) { I32 space = 20; for( auto& it : e->eprops ) { EDITOR_PROP* eprop = eprop_from_ref( e, it ); if( eprop->readonly ) gui_editor_propview_create_eobj_prop_ro( view, eprop, x, y, w, space ); else gui_editor_propview_create_eobj_prop( view, eprop, x, y, w, space ); } } void gui_editor_propview_create_wallprops( GUI_EDITOR_PROPVIEW* view ) { MAP_WALL* s = (MAP_WALL*)view->curselect.data[0].obj; WORLD_MAP* m = editor->map; I32 x = 10, y = 10; I32 space = 20; I32 wall_idx = m->walls.idx_where( fn( MAP_WALL* ms ) { return s == ms; } ); gui_label( x, y, "idx: %d", wall_idx ); y += space; return gui_editor_propview_create_eobj_props( view, s, &x, &y, propview_input_width( view ) ); } void gui_editor_propview_create_polyprops( GUI_EDITOR_PROPVIEW* view ) { MAP_POLYGON* p = (MAP_POLYGON*)view->curselect.data[0].obj; WORLD_MAP* m = editor->map; I32 x = 10, y = 10; I32 space = 20; I32 poly_idx = m->polygons.idx_where( fn( MAP_POLYGON* mp ) { return p == mp; } ); gui_label( x, y, "idx: %d", poly_idx ); y += space; return gui_editor_propview_create_eobj_props( view, p, &x, &y, propview_input_width( view ) ); } void gui_editor_propview_create_mapprops( GUI_EDITOR_PROPVIEW* view ) { WORLD_MAP* m = (WORLD_MAP*)view->curselect.data[0].obj; I32 x = 10, y = 10; I32 space = 20; F32 step = editor->propgrid? editor->grid : 0.25f; gui_label( x, y, "name: %s", m->name.data ); y += space; gui_label( x, y, "walls: %d", m->walls.size ); y += space; gui_label( x, y, "polygons: %d", m->polygons.size ); y += space; gui_label( x, y, "props: %d", m->props.size ); y += space; I32 action_x = propview_action_x( view ); I32 i = 0; m->props.each( fn( SURF_PROPS* p ) { if( p->tex ) gui_label( x + 10, y, "[%d] -> %s", i++, assets_abspath( p->tex->name ) ); else gui_label( x + 10, y, "[%d] -> { %.02f, %.02f, %.02f, %.02f }", i++, p->clr.r, p->clr.g, p->clr.b, p->clr.a ); GUI_BUTTON* btn = gui_button( action_x, y, 20, 20, "\x1A", pfn( void* ptr ) { GUI_BUTTON* btn = (GUI_BUTTON*)ptr; gui_editor_propview_select( editor->gui.props, btn->extra, EDITOR_SELECT_SURFPROPS ); } ); btn->extra = p; y += space; } ); gui_label( x, y, "sprites: %d", m->sprites.size ); y += space; gui_label( x, y, "entities: %d", m->entities.size ); y += space; gui_label( x, y, "loaded textures: %d", m->textures.size ); y += space; gui_vectorinput( x, y, propview_input_width( view ), "spawn position", (F32*)&m->startpos, 3, -INFINITY, INFINITY, step ); y += (space+18); GUI_FLOATINPUT* ang = gui_floatinput( x, y, propview_input_width( view ), "spawn angle", &m->startang, -180.f, 180.f, 1.f ); y += (space+18); ang->wraparound = 1; } void gui_editor_propview_create_pvertexprops( GUI_EDITOR_PROPVIEW* view ) { MAP_VERTEX* v = (MAP_VERTEX*)view->curselect.data[0].obj; WORLD_MAP* m = editor->map; I32 x = 10, y = 10; I32 space = 20; I32 vert_idx = -1; I32 poly_idx = m->polygons.idx_where( fn( MAP_POLYGON* p ) { vert_idx = p->vertices.idx_where( fn( MAP_VERTEX* pv ) { return (pv == v); } ); return vert_idx != -1; } ); I32 action_x = propview_action_x( view ); gui_label( x, y, "idx: %d", vert_idx ); y += space; gui_label( x, y, "polygon idx: %d", poly_idx ); gui_button( action_x, y - 2, 20, 20, "\x1A", pfn( void* ) { GUI_EDITOR_PROPVIEW* view = editor->gui.props; MAP_VERTEX* v = (MAP_VERTEX*)view->curselect.data[0].obj; WORLD_MAP* m = editor->map; MAP_POLYGON* polygon = m->polygons.where( fn( MAP_POLYGON* p ) { I32 vert_idx = p->vertices.idx_where( fn( MAP_VERTEX* pv ) { return (pv == v); } ); return vert_idx != -1; } ); if( polygon ) gui_editor_propview_select( view, polygon, EDITOR_SELECT_POLY ); } ); y += space; return gui_editor_propview_create_eobj_props( view, v, &x, &y, propview_input_width( view ) ); } void gui_editor_propview_create_spriteprops( GUI_EDITOR_PROPVIEW* view ) { MAP_SPRITE* s = (MAP_SPRITE*)view->curselect.data[0].obj; WORLD_MAP* m = editor->map; I32 x = 10, y = 10; I32 space = 20; I32 sprite_idx = m->sprites.idx_where( fn( MAP_SPRITE* ms ) { return ms == s; } ); gui_label( x, y, "idx: %d", sprite_idx ); y += space; return gui_editor_propview_create_eobj_props( view, s, &x, &y, propview_input_width( view ) ); } void gui_editor_propview_create_surfprops( GUI_EDITOR_PROPVIEW* view ) { SURF_PROPS* p = (SURF_PROPS*)view->curselect.data[0].obj; WORLD_MAP* m = editor->map; I32 x = 10, y = 10; I32 space = 20; I32 i = m->props.idx_where( fn( SURF_PROPS* mp ) { return mp == p; } ); if( i == -1 ) return; gui_label( x, y, "prop id: %d", i ); y += space; return gui_editor_propview_create_eobj_props( view, p, &x, &y, propview_input_width( view ) ); } void gui_editor_propview_create_entprops( GUI_EDITOR_PROPVIEW* view ) { MAP_ENTITY* e = (MAP_ENTITY*)view->curselect.data[0].obj; WORLD_MAP* m = editor->map; I32 x = 10, y = 10; I32 space = 20; F32 step = editor->propgrid ? editor->grid : 0.25f; I32 ent_idx = m->entities.idx_where( fn( MAP_ENTITY* me ) { return me == e; } ); gui_label( x, y, "idx: %d", ent_idx ); y += space; gui_label( x, y, "class: %s", obj_classid_to_name( e->classid ) ); y += space; gui_label( x, y, "class id: %u", e->classid ); y += space; gui_vectorinput( x, y, propview_input_width( view ), "position", (F32*)&e->pos, 3, -INFINITY, INFINITY, step ); y += (space+18); } void gui_editor_propview_filter_props( GUI_EDITOR_PROPVIEW* view ) { if( view->curselect.size < 2 ) return view->curprops.clear(); EOBJECT* first = (EOBJECT*)view->curselect.data[0].obj; for( auto& it : first->eprops ) { U8 add = 1; EDITOR_PROP* prop = eprop_from_ref( first, it ); for( U32 i = 1; i < view->curselect.size; ++i ) { EOBJECT* obj = (EOBJECT*)view->curselect.data[i].obj; if( !eprop_from_name( obj, prop->displayname ) ) { add = 0; break; } } if( add ) view->curprops.push( prop ); } } void gui_editor_propview_create_groupprops( GUI_EDITOR_PROPVIEW* view ) { if( !view->curprops.size ) gui_editor_propview_filter_props( view ); I32 x = 10, y = 10, w = propview_input_width( view ); U32 space = 20; for( auto& it : view->curprops ) { if( it->readonly ) gui_editor_propview_create_eobj_prop_ro( view, it, &x, &y, w, space ); else gui_editor_propview_create_eobj_prop( view, it, &x, &y, w, space ); } } void gui_editor_propview_update( GUI_EDITOR_PROPVIEW* view ) { if( !editor->map ) return; GUI_VIEW* target = gui_get_view(); defer({ if( target ) gui_set_view( target ); }); gui_empty_children( view->itemview ); gui_set_view( view->itemview ); view->itemview->initheld = 1; if( !view->curselect.size ) return; if( view->curselect.size > 1 ) { gui_editor_propview_create_groupprops( view ); } else { switch( view->curselect.data->seltype ) { case EDITOR_SELECT_WALL: gui_editor_propview_create_wallprops( view ); break; case EDITOR_SELECT_POLY: gui_editor_propview_create_polyprops( view ); break; case EDITOR_SELECT_ORIGIN: gui_editor_propview_create_mapprops( view ); break; case EDITOR_SELECT_ENT: gui_editor_propview_create_entprops( view ); break; case EDITOR_SELECT_PVERTEX: gui_editor_propview_create_pvertexprops( view ); break; case EDITOR_SELECT_SPRITE: gui_editor_propview_create_spriteprops( view ); break; case EDITOR_SELECT_SURFPROPS: gui_editor_propview_create_surfprops( view ); break; default: break; } } } void gui_editor_propview_get_title( GUI_EDITOR_PROPVIEW* view, char* buf ) { if( view->curselect.size > 1 ) { sprintf( buf, "group properties" ); return; } switch( view->curselect.data->seltype ) { case EDITOR_SELECT_NONE: sprintf( buf, "properties" ); break; case EDITOR_SELECT_POLY: sprintf( buf, "polygon properties" ); break; case EDITOR_SELECT_ORIGIN: sprintf( buf, "properties" ); break; case EDITOR_SELECT_ENT: sprintf( buf, "entity properties" ); break; case EDITOR_SELECT_SPRITE: sprintf( buf, "sprite properties" ); break; case EDITOR_SELECT_PVERTEX: sprintf( buf, "vertex properties" ); break; case EDITOR_SELECT_SURFPROPS: sprintf( buf, "surface properties" ); break; case EDITOR_SELECT_WVERTEX: case EDITOR_SELECT_WALL: sprintf( buf, "wall properties" ); break; } } void gui_editor_propview_draw_fn( void* ptr ) { if( !editor->map ) return; GUI_EDITOR_PROPVIEW* view = (GUI_EDITOR_PROPVIEW*)ptr; I32 x = gui_relx( view ); I32 y = gui_rely( view ); I32 w = view->w; I32 h = view->h; char title[64]; gui_editor_propview_get_title( view, title ); gui_draw_str( x, y, ALIGN_L, FNT_JPN12, ui_clr.txt, title ); y += PROPVIEW_TITLE_OFFSET; CLR col = gui_is_fg_window( view )? ui_clr.border : ui_clr.border_inactive; gui_draw_frect( x, y, w, h, col ); gui_draw_frect( x+1, y+1, w-2, h-2, ui_clr.bg_sec ); view->itemview->draw_fn( view->itemview ); } GUI_EDITOR_PROPVIEW* gui_editor_propview( I32 x, I32 y, I32 w, I32 h ) { if( !gui_check_target() ) return 0; GUI_EDITOR_PROPVIEW* view = new GUI_EDITOR_PROPVIEW; view->x = x; view->y = y; view->w = w; view->h = h; view->xbound = view->w; view->ybound = view->h + PROPVIEW_TITLE_OFFSET; strcpy( view->name, "EDITOR_PROP_VIEW" ); view->draw_fn = gui_editor_propview_draw_fn; view->input_fn = gui_base_input_fn; view->curselect = {}; GUI_VIEW* parent = gui_get_view(); parent->children.push( view ); view->parent = parent; gui_set_view( view ); view->itemview = gui_view( 0, PROPVIEW_TITLE_OFFSET, w, h ); gui_set_view( parent ); return view; }