diff options
| author | aura <nw@moneybot.cc> | 2026-07-27 11:55:57 +0200 |
|---|---|---|
| committer | aura <nw@moneybot.cc> | 2026-07-27 11:55:57 +0200 |
| commit | 372f23024163e0fab57a847ddda41f06857efd7e (patch) | |
| tree | ca57dd38a583387a7bbcc970b7d359731df0f4bf | |
| parent | 452e3619450ab9e7d22886445f61cc90272e4d60 (diff) | |
undo/redo props - WIP
| -rw-r--r-- | src/editor/editor.cpp | 161 | ||||
| -rw-r--r-- | src/editor/editor.h | 65 | ||||
| -rw-r--r-- | src/editor/properties.cpp | 31 | ||||
| -rw-r--r-- | src/editor/properties.h | 35 | ||||
| -rw-r--r-- | src/editor/view2d.cpp | 180 | ||||
| -rw-r--r-- | src/game/physics/movement.cpp | 1 | ||||
| -rw-r--r-- | src/game/raycast.cpp | 1 | ||||
| -rw-r--r-- | src/util/allocator.h | 1 | ||||
| -rw-r--r-- | src/util/profiler.h | 2 | ||||
| -rw-r--r-- | src/util/vector.h | 9 |
10 files changed, 377 insertions, 109 deletions
diff --git a/src/editor/editor.cpp b/src/editor/editor.cpp index c96f908..72ca4d2 100644 --- a/src/editor/editor.cpp +++ b/src/editor/editor.cpp @@ -2,6 +2,7 @@ #include "../game.h" #include "../game/object.h" +#include "properties.h" GAME_EDITOR* editor = 0; @@ -31,7 +32,7 @@ void editor_clear_gui_state_refs( GAME_EDITOR* e ) { e->gui.header_viewtype = 0; } -void editor_push_undo_action( GAME_EDITOR* e, const GAME_EDITOR::EDITOR_UNDO_ACTION& action ) { +void editor_push_undo_action( GAME_EDITOR* e, const EDITOR_UNDO_ACTION& action ) { if( !e ) return; @@ -265,11 +266,11 @@ void editor_undo_record_create_walls( GAME_EDITOR* e, I32 start_idx, I32 count ) if( count <= 0 ) return; - GAME_EDITOR::EDITOR_UNDO_ACTION action{}; + EDITOR_UNDO_ACTION action{}; action.type = EDITOR_UNDO_CREATE_WALLS; - action.start_idx = start_idx; + action.create.start_idx = start_idx; for( I32 i = 0; i < count; ++i ) - action.walls.push( map->walls[start_idx + i] ); + action.create.walls.push( map->walls[start_idx + i] ); editor_push_undo_action( e, action ); } @@ -282,10 +283,11 @@ void editor_undo_record_create_poly( GAME_EDITOR* e, I32 start_idx ) { if( start_idx < 0 || start_idx >= (I32)map->polygons.size ) return; - GAME_EDITOR::EDITOR_UNDO_ACTION action{}; - action.type = EDITOR_UNDO_CREATE_POLY; - action.start_idx = start_idx; - action.polys.push( map->polygons[start_idx] ); + EDITOR_UNDO_ACTION action{ + .type = EDITOR_UNDO_CREATE_POLY, + .create = { .start_idx = start_idx } + }; + action.create.polys.push( map->polygons[start_idx] ); editor_push_undo_action( e, action ); } @@ -298,10 +300,12 @@ void editor_undo_record_create_sprite( GAME_EDITOR* e, I32 start_idx ) { if( start_idx < 0 || start_idx >= (I32)map->sprites.size ) return; - GAME_EDITOR::EDITOR_UNDO_ACTION action{}; - action.type = EDITOR_UNDO_CREATE_SPRITES; - action.start_idx = start_idx; - action.sprites.push( map->sprites[start_idx] ); + EDITOR_UNDO_ACTION action = { + .type = EDITOR_UNDO_CREATE_SPRITES, + .create = { .start_idx = start_idx } + }; + + action.create.sprites.push( map->sprites[start_idx] ); editor_push_undo_action( e, action ); } @@ -314,18 +318,36 @@ void editor_undo_record_create_entity( GAME_EDITOR* e, I32 start_idx ) { if( start_idx < 0 || start_idx >= (I32)map->entities.size ) return; - GAME_EDITOR::EDITOR_UNDO_ACTION action{}; - action.type = EDITOR_UNDO_CREATE_ENTITIES; - action.start_idx = start_idx; - action.entities.push( map->entities[start_idx] ); + EDITOR_UNDO_ACTION action{ + .type = EDITOR_UNDO_CREATE_ENTITIES, + .create = { .start_idx = start_idx } + }; + action.create.entities.push( map->entities[start_idx] ); editor_push_undo_action( e, action ); } -static U8 editor_apply_undo_action_reverse( GAME_EDITOR* e, GAME_EDITOR::EDITOR_UNDO_ACTION& action ) { +// NOTE: +// this is a util func. +// individual components create records manually +void editor_undo_record_change_prop( GAME_EDITOR* e, FNV1A hash, void* oldv, void* newv, U32 size ) { if( !e || !e->map ) - return 0; + return; + + EDITOR_UNDO_ACTION rec{ .type = EDITOR_UNDO_EDITPROPS }; + for( auto e : e->gui.props->curselect ) { + EDITOR_UNDO_PROPS::CHANGES changes{}; + + changes.props.push( { hash, size, oldv, newv } ); + changes.obj = (EOBJECT*)e.obj; + rec.props.changes.push( changes ); + } + + e->undo_actions.push( rec ); +} + +U8 editor_apply_undo_action_reverse_create( GAME_EDITOR* e, EDITOR_UNDO_ACTION& action ) { WORLD_MAP* map = e->map; U8 clear_props_select = 0; U8 clear_view_select = 0; @@ -333,13 +355,13 @@ static U8 editor_apply_undo_action_reverse( GAME_EDITOR* e, GAME_EDITOR::EDITOR_ switch( action.type ) { case EDITOR_UNDO_CREATE_WALLS: { - I32 count = (I32)action.walls.size; + I32 count = (I32)action.create.walls.size; for( I32 i = count - 1; i >= 0; --i ) { if( !map->walls.size ) break; - I32 expected = action.start_idx + i; - I32 idx = editor_find_wall_idx( map, action.walls[i], expected ); + I32 expected = action.create.start_idx + i; + I32 idx = editor_find_wall_idx( map, action.create.walls[i], expected ); if( idx < 0 ) idx = editor_clamp_valid_idx( expected, (I32)map->walls.size ); @@ -350,15 +372,15 @@ static U8 editor_apply_undo_action_reverse( GAME_EDITOR* e, GAME_EDITOR::EDITOR_ } } break; case EDITOR_UNDO_CREATE_POLY: { - I32 count = (I32)action.polys.size; + I32 count = (I32)action.create.polys.size; if( count <= 0 ) return 0; for( I32 i = count - 1; i >= 0; --i ) { if( !map->polygons.size ) break; - I32 expected = action.start_idx + i; - I32 idx = i < (I32)action.polys.size ? editor_find_poly_idx( map, action.polys[i], expected ) : -1; + I32 expected = action.create.start_idx + i; + I32 idx = i < (I32)action.create.polys.size ? editor_find_poly_idx( map, action.create.polys[i], expected ) : -1; if( idx < 0 ) idx = editor_clamp_valid_idx( expected, (I32)map->polygons.size ); @@ -369,13 +391,13 @@ static U8 editor_apply_undo_action_reverse( GAME_EDITOR* e, GAME_EDITOR::EDITOR_ } } break; case EDITOR_UNDO_CREATE_SPRITES: { - I32 count = (I32)action.sprites.size; + I32 count = (I32)action.create.sprites.size; for( I32 i = count - 1; i >= 0; --i ) { if( !map->sprites.size ) break; - I32 expected = action.start_idx + i; - I32 idx = editor_find_sprite_idx( map, action.sprites[i], expected ); + I32 expected = action.create.start_idx + i; + I32 idx = editor_find_sprite_idx( map, action.create.sprites[i], expected ); if( idx < 0 ) idx = editor_clamp_valid_idx( expected, (I32)map->sprites.size ); @@ -385,13 +407,13 @@ static U8 editor_apply_undo_action_reverse( GAME_EDITOR* e, GAME_EDITOR::EDITOR_ } } break; case EDITOR_UNDO_CREATE_ENTITIES: { - I32 count = (I32)action.entities.size; + I32 count = (I32)action.create.entities.size; for( I32 i = count - 1; i >= 0; --i ) { if( !map->entities.size ) break; - I32 expected = action.start_idx + i; - I32 idx = editor_find_entity_idx( map, action.entities[i], expected ); + I32 expected = action.create.start_idx + i; + I32 idx = editor_find_entity_idx( map, action.create.entities[i], expected ); if( idx < 0 ) idx = editor_clamp_valid_idx( expected, (I32)map->entities.size ); @@ -422,41 +444,88 @@ static U8 editor_apply_undo_action_reverse( GAME_EDITOR* e, GAME_EDITOR::EDITOR_ return 1; } -static U8 editor_apply_undo_action_forward( GAME_EDITOR* e, GAME_EDITOR::EDITOR_UNDO_ACTION& action ) { +U8 editor_apply_undo_action_reverse_props( GAME_EDITOR* e, EDITOR_UNDO_ACTION& action ) { + EDITOR_UNDO_PROPS* c = &action.props; + + for( auto it : c->changes ) { + EOBJECT* obj = it.obj; + for( auto it2 : it.props ) { + EDITOR_PROP* p = eprop_from_hash( obj, it2.hash ); + if( !p ) { + dlog( "editor_apply_undo_action_reverse_props() : warn: prop not found %x\n", it2.hash ); + return 0; + } + + void* data = eprop_ptr( p ); + memcpy( data, it2.oldval.data, p->size ); + } + } + + return 1; +} + +U8 editor_apply_undo_action_reverse( GAME_EDITOR* e, EDITOR_UNDO_ACTION& action ) { if( !e || !e->map ) return 0; + if( action.type == EDITOR_UNDO_EDITPROPS ) + return editor_apply_undo_action_reverse_props( e, action ); + + return editor_apply_undo_action_reverse_create( e, action ); +} + +U8 editor_apply_undo_action_forward_props( GAME_EDITOR* e, EDITOR_UNDO_ACTION& action ) { + EDITOR_UNDO_PROPS* c = &action.props; + + for( auto it : c->changes ) { + EOBJECT* obj = it.obj; + for( auto it2 : it.props ) { + EDITOR_PROP* p = eprop_from_hash( obj, it2.hash ); + if( !p ) { + dlog( "editor_apply_undo_action_forward_props() : warn: prop not found %x\n", it2.hash ); + return 0; + } + + void* data = eprop_ptr( p ); + memcpy( data, it2.newval.data, p->size ); + } + } + + return 1; +} + +U8 editor_apply_undo_action_forward_create( GAME_EDITOR* e, EDITOR_UNDO_ACTION& action ) { WORLD_MAP* map = e->map; switch( action.type ) { case EDITOR_UNDO_CREATE_WALLS: { - if( !action.walls.size ) + if( !action.create.walls.size ) return 0; - action.walls.each( fn( MAP_WALL* w ) { + action.create.walls.each( fn( MAP_WALL* w ) { map->walls.push( *w ); } ); } break; case EDITOR_UNDO_CREATE_POLY: { - if( !action.polys.size ) + if( !action.create.polys.size ) return 0; - action.polys.each( fn( MAP_POLYGON* p ) { + action.create.polys.each( fn( MAP_POLYGON* p ) { map->polygons.push( *p ); } ); } break; case EDITOR_UNDO_CREATE_SPRITES: { - if( !action.sprites.size ) + if( !action.create.sprites.size ) return 0; - action.sprites.each( fn( MAP_SPRITE* s ) { + action.create.sprites.each( fn( MAP_SPRITE* s ) { map->sprites.push( *s ); } ); } break; case EDITOR_UNDO_CREATE_ENTITIES: { - if( !action.entities.size ) + if( !action.create.entities.size ) return 0; - action.entities.each( fn( MAP_ENTITY* ent ) { + action.create.entities.each( fn( MAP_ENTITY* ent ) { map->entities.push( *ent ); } ); } break; @@ -469,11 +538,21 @@ static U8 editor_apply_undo_action_forward( GAME_EDITOR* e, GAME_EDITOR::EDITOR_ return 1; } +U8 editor_apply_undo_action_forward( GAME_EDITOR* e, EDITOR_UNDO_ACTION& action ) { + if( !e || !e->map ) + return 0; + + if( action.type == EDITOR_UNDO_EDITPROPS ) + return editor_apply_undo_action_forward_props( e, action ); + + return editor_apply_undo_action_forward_create( e, action ); +} + U8 editor_undo( GAME_EDITOR* e ) { if( !e || !e->map || !e->undo_actions.size ) return 0; - GAME_EDITOR::EDITOR_UNDO_ACTION action = e->undo_actions.pop(); + EDITOR_UNDO_ACTION action = e->undo_actions.pop(); if( !editor_apply_undo_action_reverse( e, action ) ) { e->undo_actions.push( action ); return 0; @@ -487,7 +566,7 @@ U8 editor_redo( GAME_EDITOR* e ) { if( !e || !e->map || !e->redo_actions.size ) return 0; - GAME_EDITOR::EDITOR_UNDO_ACTION action = e->redo_actions.pop(); + EDITOR_UNDO_ACTION action = e->redo_actions.pop(); if( !editor_apply_undo_action_forward( e, action ) ) { e->redo_actions.push( action ); return 0; diff --git a/src/editor/editor.h b/src/editor/editor.h index 4050029..b506d9c 100644 --- a/src/editor/editor.h +++ b/src/editor/editor.h @@ -34,12 +34,13 @@ enum EditorSelectType_t { EDITOR_SELECT_SURFPROPS }; -enum EditorUndoType_t { +enum EditorActionType_t { EDITOR_UNDO_NONE = 0, EDITOR_UNDO_CREATE_WALLS = 1, EDITOR_UNDO_CREATE_POLY = 2, EDITOR_UNDO_CREATE_SPRITES = 3, - EDITOR_UNDO_CREATE_ENTITIES = 4 + EDITOR_UNDO_CREATE_ENTITIES = 4, + EDITOR_UNDO_EDITPROPS = 5 }; enum EditorInfoBoxType_t { @@ -96,6 +97,55 @@ struct GAME_EDITOR_TOOL { I32 objecttype; }; +struct EDITOR_UNDO_PROPS { + struct PROP_VALUE { + LIST<U8> oldval{}; + LIST<U8> newval{}; + FNV1A hash; + U32 size; + + PROP_VALUE() = default; + PROP_VALUE( FNV1A _hash, U32 _size ) { + this->hash = _hash; + this->size = _size; + + this->oldval.resize( _size ); + this->newval.resize( _size ); + } + PROP_VALUE( FNV1A _hash, U32 _size, void* oldv, void* newv ) { + this->hash = _hash; + this->size = _size; + + this->oldval.resize( _size ); + this->newval.resize( _size ); + memcpy( this->oldval.data, oldv, _size ); + memcpy( this->newval.data, newv, _size ); + } + }; + + struct CHANGES { + EOBJECT* obj; + LIST<PROP_VALUE> props; + }; + LIST<CHANGES> changes; +}; + +struct EDITOR_UNDO_CREATE { + U8 objtype{}; + I32 start_idx{}; + + LIST<MAP_WALL> walls{}; + LIST<MAP_POLYGON> polys{}; + LIST<MAP_SPRITE> sprites{}; + LIST<MAP_ENTITY> entities{}; +}; + +struct EDITOR_UNDO_ACTION { + U8 type; + EDITOR_UNDO_PROPS props; + EDITOR_UNDO_CREATE create; +}; + struct GAME_EDITOR { GAME_DATA* game; @@ -150,14 +200,6 @@ struct GAME_EDITOR { LIST<GUI_LIST_ENTRY> map_list{}; - struct EDITOR_UNDO_ACTION { - U8 type{}; - I32 start_idx{}; - LIST<MAP_WALL> walls{}; - LIST<MAP_POLYGON> polys{}; - LIST<MAP_SPRITE> sprites{}; - LIST<MAP_ENTITY> entities{}; - }; LIST<EDITOR_UNDO_ACTION> undo_actions{}; LIST<EDITOR_UNDO_ACTION> redo_actions{}; }; @@ -214,6 +256,7 @@ extern void editor_undo_record_create_walls( GAME_EDITOR* e, I32 start_idx, I32 extern void editor_undo_record_create_poly( GAME_EDITOR* e, I32 start_idx ); extern void editor_undo_record_create_sprite( GAME_EDITOR* e, I32 start_idx ); extern void editor_undo_record_create_entity( GAME_EDITOR* e, I32 start_idx ); +extern void editor_undo_record_change_prop( GAME_EDITOR* e, FNV1A hash, void* oldv, void* newv, U32 size ); extern U8 editor_undo( GAME_EDITOR* e ); extern U8 editor_redo( GAME_EDITOR* e ); @@ -261,6 +304,8 @@ struct GUI_EDITOR_2DVIEW : GUI_VIEW { I32 pending_wall_undo_idx; I32 pending_object_undo_idx; U8 pending_object_undo_type; + + EDITOR_UNDO_PROPS pending_undo_props{}; }; struct GUI_EDITOR_PROPVIEW : GUI_VIEW { diff --git a/src/editor/properties.cpp b/src/editor/properties.cpp index a9835e4..109e622 100644 --- a/src/editor/properties.cpp +++ b/src/editor/properties.cpp @@ -111,6 +111,8 @@ U8 gui_editor_propview_is_selected( GUI_EDITOR_PROPVIEW *e, void *what, U8 selty void gui_editor_propview_sync_props_value( GUI_EDITOR_PROPVIEW* view, EDITOR_PROP* prop ) { void* valuep = eprop_ptr( prop ); + + EDITOR_UNDO_ACTION rec{ .type = EDITOR_UNDO_EDITPROPS }; for( auto& it : view->curselect ) { EOBJECT* eobj = (EOBJECT*)it.obj; EDITOR_PROP* otherp = eprop_from_name( eobj, prop->displayname ); @@ -118,10 +120,22 @@ void gui_editor_propview_sync_props_value( GUI_EDITOR_PROPVIEW* view, EDITOR_PRO if( otherp && otherp->type == prop->type ) { void* targetp = eprop_ptr( otherp ); - if( targetp != valuep ) + if( targetp != valuep ) { + EDITOR_UNDO_PROPS::CHANGES changes; + + void* oldval = alloca( prop->size ); + memcpy( oldval, targetp, prop->size ); memcpy( targetp, valuep, prop->size ); + + changes.props.push( { prop->hash, prop->size, oldval, targetp } ); + changes.obj = eobj; + rec.props.changes.push( changes ); + } } } + + if( rec.props.changes.size ) + editor->undo_actions.push( rec ); } void gui_editor_propview_sync_props_value( GUI_EDITOR_PROPVIEW* view, const char* propname ) { @@ -134,6 +148,10 @@ void gui_editor_propview_sync_props_value( GUI_EDITOR_PROPVIEW* view, const char } void gui_editor_propview_sync_props_fdiff( GUI_EDITOR_PROPVIEW* view, EDITOR_PROP* prop, F32* fdiff, U32 valc ) { + if( !fdiff ) + return; + + EDITOR_UNDO_ACTION rec{ .type = EDITOR_UNDO_EDITPROPS }; for( auto& it : view->curselect ) { EOBJECT* eobj = (EOBJECT*)it.obj; EDITOR_PROP* otherp = eprop_from_name( eobj, prop->displayname ); @@ -144,10 +162,21 @@ void gui_editor_propview_sync_props_fdiff( GUI_EDITOR_PROPVIEW* view, EDITOR_PRO if( targetp == eprop_ptr( prop ) ) continue; + EDITOR_UNDO_PROPS::CHANGES changes; + F32* oldval = (F32*)alloca( prop->size ); + memcpy( oldval, targetp, prop->size ); + for( U32 i = 0; i < valc; ++i ) targetp[i] += fdiff[i]; + + changes.props.push( { prop->hash, prop->size, oldval, targetp } ); + changes.obj = eobj; + rec.props.changes.push( changes ); } } + + if( rec.props.changes.size ) + editor->undo_actions.push( rec ); } void gui_editor_propview_sync_props_fdiff( GUI_EDITOR_PROPVIEW* view, const char* propname, F32* fdiff, U32 valc ) { diff --git a/src/editor/properties.h b/src/editor/properties.h index 810c075..d4fc3bb 100644 --- a/src/editor/properties.h +++ b/src/editor/properties.h @@ -6,6 +6,7 @@ #include <math.h> #include "../util/string.h" #include "../util/fnv.h" +#include "../util/profiler.h" #define EPROP( _type, name, value, display ) \ _type name = value; \ @@ -90,6 +91,7 @@ struct EOBJECT { LIST<EPROP_ENTRY> eprops{}; }; + template <typename T> concept __eobject_base = __is_base_of(EOBJECT, T); @@ -112,6 +114,7 @@ struct EDITOR_PROP { _parent->eprops.push( { offset } ); + parentoff = pdata - parent; offset = _this - pdata; dataoff = offset; } @@ -133,6 +136,7 @@ struct EDITOR_PROP { _parent->eprops.push( { offset } ); + parentoff = pdata - parent; offset = _this - pdata; dataoff = offset; } @@ -154,6 +158,7 @@ struct EDITOR_PROP { _parent->eprops.push( { offset } ); + parentoff = pdata - parent; offset = _this - pdata; dataoff = offset; } @@ -175,11 +180,13 @@ struct EDITOR_PROP { _parent->eprops.push( { offset } ); + parentoff = pdata - parent; offset = _this - pdata; dataoff = offset; } U64 dataoff; + U64 parentoff; U8 type; STR displayname; F64 min{ -INFINITY }; @@ -220,22 +227,40 @@ inline EDITOR_PROP* eprop_from_ref( EOBJECT* obj, EPROP_ENTRY e ) { return (EDITOR_PROP*)( (U64)obj + e.offset ); } +inline EDITOR_PROP* eprop_from_ptr( EOBJECT* obj, void* ptr ) { + if( (U64)ptr < (U64)obj ) { + trace( "eprop_from_ptr() : error : invalid pointer access, obj = %llx, ptr = %llx\n", (U64)obj, (U64)ptr ); + return 0; + } + + U64 off = ( (U64)ptr - (U64)obj ); + for( auto& it : obj->eprops ) { + EDITOR_PROP* p = eprop_from_ref( obj, it ); + if( off == p->parentoff ) + return p; + } + + return 0; +} + inline void* eprop_ptr( EDITOR_PROP* prop ) { return (void*)( (U64)prop - prop->dataoff); } -inline EDITOR_PROP* eprop_from_name( EOBJECT* obj, STR name ) { - FNV1A fnv = fnv1a( name ); - +inline EDITOR_PROP* eprop_from_hash( EOBJECT* obj, FNV1A hash ) { for( auto& it : obj->eprops ) { EDITOR_PROP* prop = eprop_from_ref( obj, it ); - if( fnv == prop->hash ) + if( hash == prop->hash ) return prop; } - return 0; } +inline EDITOR_PROP* eprop_from_name( EOBJECT* obj, STR name ) { + FNV1A fnv = fnv1a( name ); + return eprop_from_hash( obj, fnv ); +} + template <typename T> const U8 eprop_type() { return EDITOR_PROP::__eprop_type<T>::type; diff --git a/src/editor/view2d.cpp b/src/editor/view2d.cpp index c4738dd..cca83d1 100644 --- a/src/editor/view2d.cpp +++ b/src/editor/view2d.cpp @@ -4,7 +4,6 @@ #include "../render/gl_2d.h" #include "../game/object.h" #include "../game/objlist.h" -#include "../game/world/bsp.h" const I32 EDITORVIEW_TITLE_OFFSET = 15; const I32 EDITORVIEW_GUTTERS_OFFSETX = 22; @@ -839,36 +838,9 @@ VEC2 gui_editor_2dview_input_get_drag_vec( GUI_EDITOR_2DVIEW* view ) { } U8 gui_editor_2dview_input_select_drag_obj( GUI_EDITOR_2DVIEW* view, void* obj, U8 seltype ); -U8 gui_editor_2dview_move_group( GUI_EDITOR_2DVIEW* view ) { - U8 update = 0; - if( view->dragtype == EDITOR_SELECT_WVERTEX || view->dragtype == EDITOR_SELECT_PVERTEX ) - return update; - - GUI_EDITOR_PROPVIEW* props = editor->gui.props; - // update all selected objects - for( auto& it : props->curselect ) { - if( it.obj == view->curdrag ) { - update = 1; - continue; - } +U8 gui_editor_2dview_drag_group( GUI_EDITOR_2DVIEW* view ); - // special case where a wall is selected but we are dragging one of its vertices - if( props->curselect.size == 1 && it.seltype == EDITOR_SELECT_WALL ) { - MAP_WALL* w = (MAP_WALL*)it.obj; - if( &w->start == view->curdrag || &w->end == view->curdrag ) { - update = 1; - continue; - } - } - - if( gui_editor_2dview_input_select_drag_obj( view, it.obj, it.seltype ) ) - update = 1; - } - - return update; -} - -void gui_editor_2dview_input_select_onmove( GUI_EDITOR_2DVIEW* view ) { +void gui_editor_2dview_input_select_onmove( GUI_EDITOR_2DVIEW* view, EDITOR_UNDO_PROPS* undo ) { I32 mx, my; gui_cursor_pos( &mx, &my ); @@ -883,7 +855,7 @@ void gui_editor_2dview_input_select_onmove( GUI_EDITOR_2DVIEW* view ) { GUI_EDITOR_PROPVIEW* props = editor->gui.props; U8 update = !is_zero( gui_editor_2dview_input_get_drag_vec( view ) ); if( update && gui_editor_propview_is_selected( props, view->curdrag, view->dragtype ) ) { - if( gui_editor_2dview_move_group( view ) ) + if( gui_editor_2dview_drag_group( view ) ) gui_editor_propview_update( props ); } @@ -894,45 +866,109 @@ void gui_editor_2dview_input_select_onmove( GUI_EDITOR_2DVIEW* view ) { view->dragmoved = 1; } -void gui_editor_2dview_input_select_drag_wall( GUI_EDITOR_2DVIEW* view, MAP_WALL* s ) { +void gui_editor_2dview_set_undo_prop( GUI_EDITOR_2DVIEW* view, EOBJECT* target, FNV1A hash, U32 size, void* oldv, void* newv = 0 ) { + if( !target ) + return; + + EDITOR_UNDO_PROPS::CHANGES* target_change = view->pending_undo_props.changes.where( + [&]( EDITOR_UNDO_PROPS::CHANGES* c ) { return c->obj == target; } + ); + + if( !target_change ) { + target_change = view->pending_undo_props.changes.push( { .obj = target } ); + } + + EDITOR_UNDO_PROPS::PROP_VALUE* target_prop = target_change->props.where( + [&]( EDITOR_UNDO_PROPS::PROP_VALUE* p ) { return p->hash == hash; } + ); + + if( !target_prop ) { + target_prop = target_change->props.push( { hash, size } ); + memcpy( target_prop->oldval.data, oldv, size ); + } + + if( target_prop->size != size ) { + return dlog( "gui_editor_2dview_set_undo_prop() : prop size mismatch" ); + } + + if( newv ) + memcpy( target_prop->newval.data, newv, size ); +} + +void gui_editor_2dview_input_select_drag_wall( + GUI_EDITOR_2DVIEW* view, + MAP_WALL* s +) { gui_editor_2dview_snap_plane( &s->start ); gui_editor_2dview_snap_plane( &s->end ); VEC2 mv = gui_editor_2dview_input_get_drag_vec( view ); if( !is_zero( mv ) ) { + VEC3 olds = s->start; + VEC3 olde = s->end; gui_editor_2dview_add_plane_delta( &s->start, mv ); gui_editor_2dview_add_plane_delta( &s->end, mv ); + + EDITOR_PROP* sprop = eprop_from_ptr( s, &s->start ); + EDITOR_PROP* eprop = eprop_from_ptr( s, &s->end ); + + gui_editor_2dview_set_undo_prop( view, s, sprop->hash, sprop->size, &olds, &s->start ); + gui_editor_2dview_set_undo_prop( view, s, eprop->hash, eprop->size, &olde, &s->end ); + map_check_bounds( editor->map ); } } -void gui_editor_2dview_input_select_drag_pos( GUI_EDITOR_2DVIEW* view, VEC3* v ) { +void gui_editor_2dview_input_select_drag_wallpos( + GUI_EDITOR_2DVIEW* view, + VEC3* v +) { gui_editor_2dview_snap_plane( v ); VEC2 mv = gui_editor_2dview_input_get_drag_vec( view ); if( !is_zero( mv ) ) { + VEC3 oldv = *v; gui_editor_2dview_add_plane_delta( v, mv ); map_check_bounds( editor->map ); + + // giga hack + EDITOR_PROP* p = (EDITOR_PROP*)( (U64)v + sizeof(VEC3) ); + MAP_WALL* s = (MAP_WALL*)( (U64)v - p->parentoff ); + gui_editor_2dview_set_undo_prop( view, s, p->hash, p->size, &oldv, v ); } } -void gui_editor_2dview_input_select_drag_vertex( GUI_EDITOR_2DVIEW* view, MAP_VERTEX* v ) { +void gui_editor_2dview_input_select_drag_vertex( + GUI_EDITOR_2DVIEW* view, + MAP_VERTEX* v +) { gui_editor_2dview_snap_plane( &v->pos ); VEC2 mv = gui_editor_2dview_input_get_drag_vec( view ); if( !is_zero( mv ) ) { + VEC3 oldv = v->pos; gui_editor_2dview_add_plane_delta( &v->pos, mv ); map_check_bounds( editor->map ); + + EDITOR_PROP* p = eprop_from_ptr( v, &v->pos ); + gui_editor_2dview_set_undo_prop( view, v, p->hash, p->size, &oldv, &v->pos ); } } -void gui_editor_2dview_input_select_drag_polygon( GUI_EDITOR_2DVIEW* view, MAP_POLYGON* p ) { +void gui_editor_2dview_input_select_drag_polygon( + GUI_EDITOR_2DVIEW* view, + MAP_POLYGON* p +) { VEC2 mv = gui_editor_2dview_input_get_drag_vec( view ); if( !is_zero( mv ) ) { p->vertices.each( fn( MAP_VERTEX* v ) { + VEC3 oldv = v->pos; gui_editor_2dview_snap_plane( &v->pos ); gui_editor_2dview_add_plane_delta( &v->pos, mv ); + + EDITOR_PROP* p = eprop_from_ptr( v, &v->pos ); + gui_editor_2dview_set_undo_prop( view, v, p->hash, p->size, &oldv, &v->pos ); } ); map_polygon_calc_bounds( p ); @@ -940,31 +976,51 @@ void gui_editor_2dview_input_select_drag_polygon( GUI_EDITOR_2DVIEW* view, MAP_P } } -void gui_editor_2dview_input_select_drag_sprite( GUI_EDITOR_2DVIEW* view, MAP_SPRITE* s ) { +void gui_editor_2dview_input_select_drag_sprite( + GUI_EDITOR_2DVIEW* view, + MAP_SPRITE* s +) { gui_editor_2dview_snap_plane( &s->pos ); VEC2 mv = gui_editor_2dview_input_get_drag_vec( view ); if( !is_zero( mv ) ) { + VEC3 oldv = s->pos; gui_editor_2dview_add_plane_delta( &s->pos, mv ); + + EDITOR_PROP* p = eprop_from_ptr( s, &s->pos ); + gui_editor_2dview_set_undo_prop( view, s, p->hash, p->size, &oldv, &s->pos ); } } - -void gui_editor_2dview_input_select_drag_entity( GUI_EDITOR_2DVIEW* view, MAP_ENTITY* e ) { +void gui_editor_2dview_input_select_drag_entity( + GUI_EDITOR_2DVIEW* view, + MAP_ENTITY* e +) { gui_editor_2dview_snap_plane( &e->pos ); VEC2 mv = gui_editor_2dview_input_get_drag_vec( view ); if( !is_zero( mv ) ) { + VEC3 oldv = e->pos; gui_editor_2dview_add_plane_delta( &e->pos, mv ); + + EDITOR_PROP* p = eprop_from_ptr( e, &e->pos ); + gui_editor_2dview_set_undo_prop( view, e, p->hash, p->size, &oldv, &e->pos ); } } -void gui_editor_2dview_input_select_drag_origin( GUI_EDITOR_2DVIEW* view, WORLD_MAP* m ) { +void gui_editor_2dview_input_select_drag_origin( + GUI_EDITOR_2DVIEW* view, + WORLD_MAP* m +) { gui_editor_2dview_snap_plane( &m->startpos ); VEC2 mv = gui_editor_2dview_input_get_drag_vec( view ); if( !is_zero( mv ) ) { + VEC3 oldv = m->startpos; gui_editor_2dview_add_plane_delta( &m->startpos, mv ); + + EDITOR_PROP* p = eprop_from_ptr( m, &m->startpos ); + gui_editor_2dview_set_undo_prop( view, m, p->hash, p->size, &oldv, &m->startpos ); } } @@ -980,7 +1036,7 @@ U8 gui_editor_2dview_input_select_drag_obj( GUI_EDITOR_2DVIEW* view, void* obj, case EDITOR_SELECT_SPRITE: gui_editor_2dview_input_select_drag_sprite( view, (MAP_SPRITE*)p ); return 1; case EDITOR_SELECT_ORIGIN: gui_editor_2dview_input_select_drag_origin( view, (WORLD_MAP*)p ); return 1; case EDITOR_SELECT_PVERTEX: gui_editor_2dview_input_select_drag_vertex( view, (MAP_VERTEX*)p ); return 1; - case EDITOR_SELECT_WVERTEX: gui_editor_2dview_input_select_drag_pos( view, (VEC3*)p ); return 1; + case EDITOR_SELECT_WVERTEX: gui_editor_2dview_input_select_drag_wallpos( view, (VEC3*)p ); return 1; } return 0; @@ -990,9 +1046,45 @@ 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, !input.keys[SDL_SCANCODE_LCTRL] ); + if( view->pending_undo_props.changes.size ) { + EDITOR_UNDO_ACTION rec{ .type = EDITOR_UNDO_EDITPROPS, .props = view->pending_undo_props }; + editor->undo_actions.push( rec ); + view->pending_undo_props = {}; + } + view->curdrag = 0; } +U8 gui_editor_2dview_drag_group( GUI_EDITOR_2DVIEW* view ) { + U8 update = 0; + if( view->dragtype == EDITOR_SELECT_WVERTEX || view->dragtype == EDITOR_SELECT_PVERTEX ) + return update; + + GUI_EDITOR_PROPVIEW* props = editor->gui.props; + // update all selected objects + for( auto& it : props->curselect ) { + if( it.obj == view->curdrag ) { + update = 1; + continue; + } + + // special case where a wall is selected but we are dragging one of its vertices + if( props->curselect.size == 1 && it.seltype == EDITOR_SELECT_WALL ) { + MAP_WALL* w = (MAP_WALL*)it.obj; + if( &w->start == view->curdrag || &w->end == view->curdrag ) { + update = 1; + continue; + } + } + + if( gui_editor_2dview_input_select_drag_obj( view, it.obj, it.seltype ) ) { + update = 1; + } + } + + return update; +} + void gui_editor_2dview_input_select_drag( GUI_EDITOR_2DVIEW* view ) { U8 m1 = gui_mbutton_down( 0 ); I32 mx, my; @@ -1017,15 +1109,17 @@ void gui_editor_2dview_input_select_drag( GUI_EDITOR_2DVIEW* view ) { return; } + EDITOR_UNDO_ACTION rec{ .type = EDITOR_UNDO_EDITPROPS }; + EDITOR_UNDO_PROPS::CHANGES changes{ .obj = (EOBJECT*)view->curdrag }; if( !gui_editor_2dview_input_select_drag_obj( view, view->curdrag, view->dragtype ) ) { view->oldmx = mx; view->oldmy = my; return; } - VEC2 mv = gui_editor_2dview_input_get_drag_vec( view ); - if( !is_zero( mv ) ) { - gui_editor_2dview_input_select_onmove( view ); + if( !is_zero( gui_editor_2dview_input_get_drag_vec( view ) ) ) { + gui_editor_2dview_input_select_onmove( view, &rec.props ); + rec.props.changes.push( changes ); } } diff --git a/src/game/physics/movement.cpp b/src/game/physics/movement.cpp index 838c8a9..4cb669f 100644 --- a/src/game/physics/movement.cpp +++ b/src/game/physics/movement.cpp @@ -465,7 +465,6 @@ void gmove_step_move( VEC3 dest, BSP_TRACE* tr ) { up = tr->point; gmove_try_move( tr, &up, &upvel ); - VEC3 upnorm = tr->normal; // move back down onto the ground endpos = up; diff --git a/src/game/raycast.cpp b/src/game/raycast.cpp index ec66b48..e8bcfd9 100644 --- a/src/game/raycast.cpp +++ b/src/game/raycast.cpp @@ -73,7 +73,6 @@ LIST<RAY_HITDATA> ray_trace_list( VEC3 start, F32 ang, U32 max_iter ) { U32 ray_trace( TRACE* tr, F32 ang ) { F32 rayang = m_deg2rad( ang ); - VEC3 start = tr->startpos; VEC3 raydir = { cos( rayang ), sin( rayang ), 0.f }; raydir *= RAY_DEPTH_MAX; diff --git a/src/util/allocator.h b/src/util/allocator.h index d97952d..3cee282 100644 --- a/src/util/allocator.h +++ b/src/util/allocator.h @@ -2,7 +2,6 @@ #include <stdlib.h> #include <string.h> -#include <utility> #include "callback.h" template <typename T> diff --git a/src/util/profiler.h b/src/util/profiler.h index 05e4b0c..c7a0fd1 100644 --- a/src/util/profiler.h +++ b/src/util/profiler.h @@ -142,6 +142,7 @@ extern void __profiler_intern_draw_overlay( struct GL_FONT* font ); #define profiler_init() __profiler_intern_init() #define profiler_draw_tree( font ) __profiler_intern_draw_overlay( font ) #define profiler_trace() __profiler_intern_trace() +#define trace( ... ) do { dlog( __VA_ARGS__ ); profiler_trace(); } while(0) #else #define PROFILE( x ) @@ -149,4 +150,5 @@ extern void __profiler_intern_draw_overlay( struct GL_FONT* font ); #define profiler_trace() #define profiler_init() #define profiler_draw_tree( x ) +#define trace( ... ) #endif diff --git a/src/util/vector.h b/src/util/vector.h index eb386ab..294b627 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -13,10 +13,9 @@ struct VEC2 { VEC2() { x = y = 0.0f; } VEC2( F32 X, F32 Y ) { x = X; y = Y; } - VEC2( const VEC2& v ) { x = v.x; y = v.y; } + VEC2( const VEC2& v ) = default; bool operator==( const VEC2& v ) const { return ( x == v.x && y == v.y ); } - VEC2& operator=( const VEC2& v ) { x = v.x; y = v.y; return *this; } F32& operator[]( I32 i ) { return ( (F32*)this )[i]; } F32 operator[]( I32 i ) const { return ( (const F32*)this )[i]; } @@ -48,13 +47,12 @@ struct VEC3 { VEC3() { x = y = z = 0.0f; } VEC3( F32 X, F32 Y, F32 Z ) { x = X; y = Y; z = Z; } - VEC3( const VEC3& v ) { x = v.x; y = v.y; z = v.z; } + VEC3( const VEC3& v ) = default; VEC3( const VEC2& v ) { x = v.x; y = v.y; z = 0.f; } bool operator==( const VEC3& v ) const { return ( x == v.x && y == v.y && z == v.z ); } bool operator!=( const VEC3& v ) const { return !( x == v.x && y == v.y && z == v.z ); } - VEC3& operator=( const VEC3& v ) { x = v.x; y = v.y; z = v.z; return *this; } F32& operator[]( I32 i ) { return ( (F32*)this )[i]; } F32 operator[]( I32 i ) const { return ( (F32*)this )[i]; } @@ -84,7 +82,7 @@ struct VEC4 { VEC4() { x = y = z = w = 0; } VEC4( F32 X, F32 Y, F32 Z, F32 W ) { x = X; y = Y; z = Z; w = W; } - VEC4( const VEC4& v ) { x = v.x; y = v.y; z = v.z; w = v.w; } + VEC4( const VEC4& v ) = default; VEC4( const VEC3& v ) { x = v.x; y = v.y; z = v.z; w = 1.f; } VEC4( const VEC2& v ) { x = v.x; y = v.y; z = 0.f; w = 1.f; } @@ -93,7 +91,6 @@ struct VEC4 { VEC4( F32* v ) { x = v[0]; y = v[1]; z = v[2]; w = v[3]; } - VEC4 operator=( const VEC4& v ) { x = v.x; y = v.y; z = v.z; w = v.w; return *this; } F32& operator[]( const int i ) { return *(&x + i); } F32 operator[]( const int i ) const { return *(&x + i); } |
