From bbb13546b17598efe8137f6a31bdd0afaf0d7416 Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Wed, 28 Aug 2024 16:41:02 +0300 Subject: [PATCH] Fixed reactivation problems --- godot/main/editor.gd | 4 +- godot/main/editor.tscn | 10 +- src/godot | 2 +- src/modules/stream/road_lines_editor.cpp | 215 +++++++++++++++++++++++ src/modules/stream/road_lines_editor.h | 24 +++ src/modules/stream/stream.cpp | 31 +++- src/modules/stream/stream.h | 13 ++ src/modules/stream/world_editor.cpp | 20 ++- src/modules/stream/world_editor.h | 6 +- 9 files changed, 310 insertions(+), 15 deletions(-) create mode 100644 src/modules/stream/road_lines_editor.cpp create mode 100644 src/modules/stream/road_lines_editor.h diff --git a/godot/main/editor.gd b/godot/main/editor.gd index d68e053..4849a32 100644 --- a/godot/main/editor.gd +++ b/godot/main/editor.gd @@ -126,14 +126,16 @@ func mouse_press(position): if mode == 0: print("get closest building") $WorldEditor.editor_command("get_closest_building", [Transform(Basis(), proj)]) + elif mode == 1: + $WorldEditor.editor_command("checkpoint", []) elif mode == 2: + $WorldEditor.editor_command("checkpoint", []) var m = proj m.y = selected_building_xform.origin.y var xform = selected_building_xform.looking_at(m, Vector3.UP) $WorldEditor.editor_command("update_building_transform", [selected_building, xform]) $building_rot_cursor.global_transform = xform selected_building_xform = xform - $WorldEditor.editor_command("buildings_checkpoint", []) var selected_building var selected_building_xform diff --git a/godot/main/editor.tscn b/godot/main/editor.tscn index 850f985..5773fc1 100644 --- a/godot/main/editor.tscn +++ b/godot/main/editor.tscn @@ -195,7 +195,7 @@ text = "POI mode" unique_name_in_owner = true margin_top = 330.0 margin_right = 160.0 -margin_bottom = 356.0 +margin_bottom = 456.0 [node name="HSeparator" type="HSeparator" parent="VBoxContainer/v_road_lines"] margin_right = 160.0 @@ -211,13 +211,15 @@ text = "Road Lines mode" unique_name_in_owner = true margin_top = 26.0 margin_right = 160.0 -margin_bottom = 26.0 +margin_bottom = 126.0 +rect_min_size = Vector2( 0, 100 ) +size_flags_horizontal = 3 [node name="v_npc" type="VBoxContainer" parent="VBoxContainer"] unique_name_in_owner = true -margin_top = 360.0 +margin_top = 460.0 margin_right = 160.0 -margin_bottom = 382.0 +margin_bottom = 482.0 [node name="HSeparator" type="HSeparator" parent="VBoxContainer/v_npc"] margin_right = 160.0 diff --git a/src/godot b/src/godot index b58d16f..01c78d8 160000 --- a/src/godot +++ b/src/godot @@ -1 +1 @@ -Subproject commit b58d16f0b83c049e4a5d185c30486da1beb3e437 +Subproject commit 01c78d87fea331c978d7ba8a2d5edb379564bdde diff --git a/src/modules/stream/road_lines_editor.cpp b/src/modules/stream/road_lines_editor.cpp new file mode 100644 index 0000000..585554c --- /dev/null +++ b/src/modules/stream/road_lines_editor.cpp @@ -0,0 +1,215 @@ +#undef NDEBUG +#include +#include +#include +#include +#include +#include +#include +#include "world_editor.h" +#include "from_string.h" +#include "road_lines_editor.h" + +struct road_line { + std::vector points; + std::vector indices; + int lanes; + int pattern; + int flags; +}; +static HashMap lines; +static ImmediateGeometry *line_im = nullptr; +static Ref debug_material; + +#define __evhandler(vname, mtype) \ + template class GDEventHandler_##vname : public Object { \ + GDCLASS(GDEventHandler_##vname, Object) \ + T *obj; \ + \ + public: \ + GDEventHandler_##vname(T *obj) \ + : Object() \ + , obj(obj) \ + { \ + } \ + virtual ~GDEventHandler_##vname() \ + { \ + } \ + bool connect(Object *obj, const String &signal) \ + { \ + return obj->connect(signal, this, "handler"); \ + } \ + void disconnect(Object *obj, const String &signal) \ + { \ + obj->disconnect(signal, this, "handler"); \ + } \ + \ + protected: \ + void handler(const String &event, const Array &args) \ + { \ + obj->vname(event, args); \ + } \ + static void _bind_methods() \ + { \ + ClassDB::bind_method( \ + D_METHOD("handler", "args"), \ + &GDEventHandler_##vname::handler); \ + } \ + } + +#define __evhandler_type(vname, mtype) GDEventHandler_##vname + +__evhandler(editor_event, RoadLinesEditor); +static __evhandler_type(editor_event, RoadLinesEditor) * gd_editor_event; + +RoadLinesEditor::RoadLinesEditor(WorldEditor *editor) + : active(false) + , editor(editor) +{ +} + +RoadLinesEditor::~RoadLinesEditor() +{ + if (active && editor->is_inside_tree()) + deactivate(); +} + +void RoadLinesEditor::update(float delta) +{ + if (!active) + activate(); + print_line("road_lines_editor"); +} + +void RoadLinesEditor::exit() +{ + if (active) + deactivate(); +} + +void RoadLinesEditor::editor_command(const String &command, const Array &args) +{ + print_line("command: " + command); +} + +void RoadLinesEditor::editor_event(const String &event, const Array &args) +{ + print_line("event: " + event); +} + +void RoadLinesEditor::update_ui() +{ + Node *lines_list_node = + editor->get_tree()->get_current_scene()->get_node( + NodePath("%lines_list")); + ItemList *lines_list = Object::cast_to(lines_list_node); + assert(lines_list); + List line_keys; + lines.get_key_list(&line_keys); + List::Element *e = line_keys.front(); + lines_list->clear(); + while (e) { + String key = e->get(); + lines_list->add_item(key); + e = e->next(); + } +} + +void RoadLinesEditor::activate() +{ + assert(!active); + load_data(); + print_line("activate::update UI"); + update_ui(); + if (!line_im) { + line_im = memnew(ImmediateGeometry); + editor->get_viewport()->add_child(line_im); + } + Ref tmpmat; + tmpmat.instance(); + tmpmat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + tmpmat->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, true); + tmpmat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); + debug_material = tmpmat; + line_im->begin(Mesh::PRIMITIVE_LINES); + line_im->set_color(Color(1.0f, 0.0f, 0.0f, 1.0f)); + line_im->add_vertex(Vector3(0.0f, -100.0f, 0.0f)); + line_im->add_vertex(Vector3(0.0f, 100.0f, 0.0f)); + line_im->end(); + if (!gd_editor_event) + gd_editor_event = memnew( + __evhandler_type(editor_event, RoadLinesEditor)(this)); + gd_editor_event->connect(editor, "editor_event"); + + active = true; +} + +void RoadLinesEditor::deactivate() +{ + Node *lines_list_node = + editor->get_tree()->get_current_scene()->get_node( + NodePath("%lines_list")); + ItemList *lines_list = Object::cast_to(lines_list_node); + lines_list->clear(); + lines.clear(); + if (line_im) { + line_im->queue_delete(); + line_im = nullptr; + } + gd_editor_event->disconnect(editor, "editor_event"); + if (debug_material.is_valid()) + debug_material.unref(); + if (gd_editor_event) { + memdelete(gd_editor_event); + gd_editor_event = nullptr; + } + active = false; +} + +void RoadLinesEditor::load_data() +{ + int i; + ConfigFile config; + Error result = config.load("res://config/stream.conf"); + ERR_FAIL_COND_MSG(result != OK, "Failed to load config"); + String road_lines_path = config.get_value("road", "road_lines_path"); + String road_lines_json = + FileAccess::get_file_as_string(road_lines_path); + Variant json_v; + String es; + int eline; + Error status = JSON::parse(road_lines_json, json_v, es, eline); + ERR_FAIL_COND_MSG(status != OK, "Can't parse json: " + es + + " at line: " + itos(eline)); + + Dictionary json = json_v; + List keys; + json.get_key_list(&keys); + List::Element *e = keys.front(); + while (e) { + String key = e->get(); + struct road_line rline; + Dictionary entry = json.get(key, Dictionary()); + Array points = entry.get("points", Array()); + Array indices; + if (entry.has("indices")) + indices = entry.get("indices", Array()); + int lanes = entry.get("lanes", -1); + int pattern = entry.get("pattern", -1); + rline.pattern = pattern; + rline.points.resize(points.size()); + rline.indices.resize(indices.size()); + for (i = 0; i < (int)points.size(); i++) { + String point_s = points[i]; + rline.points[i] = from_string(point_s); + } + for (i = 0; i < (int)indices.size(); i++) { + int index = indices[i]; + rline.indices[i] = index; + } + // TODO: wtf is flags? + rline.lanes = lanes; + lines[key] = rline; + e = e->next(); + } +} diff --git a/src/modules/stream/road_lines_editor.h b/src/modules/stream/road_lines_editor.h new file mode 100644 index 0000000..76be1b9 --- /dev/null +++ b/src/modules/stream/road_lines_editor.h @@ -0,0 +1,24 @@ +#ifndef ROAD_LINES_EDITOR_H +#define ROAD_LINES_EDITOR_H +#include "world_editor.h" +class ItemList; +class RoadLinesEditor { + bool active; + WorldEditor *editor; + +public: + RoadLinesEditor(WorldEditor *editor); + virtual ~RoadLinesEditor(); + void update(float delta); + void exit(); + void editor_command(const String &command, const Array &args); + void editor_event(const String &event, const Array &args); + + void update_ui(); + +protected: + void activate(); + void deactivate(); + void load_data(); +}; +#endif \ No newline at end of file diff --git a/src/modules/stream/stream.cpp b/src/modules/stream/stream.cpp index a13fc64..061cca7 100644 --- a/src/modules/stream/stream.cpp +++ b/src/modules/stream/stream.cpp @@ -443,11 +443,11 @@ void StreamWorld::run_command(const String &command, const Array &args) } VariantWriter::write_to_string(buildings[id].xform, key); buildings[id].key = key; - } else if (command == "buildings_checkpoint") { - /* TODO */ - } else if (command == "buildings_undo") { - /* TODO */ - } else if (command == "buildings_save") { + } else if (command == "checkpoint") + checkpoint(); + else if (command == "undo") + undo(); + else if (command == "buildings_save") { String buildings_path = config.get_value("buildings", "buildings_path"); save_buildings_json(buildings_path); @@ -553,6 +553,7 @@ StreamWorld::StreamWorld() , viewer(nullptr) , terrain(nullptr) , current_scene(nullptr) + , undo_log_size(64) , world_extent(0) , tile_size(0) , view_distance(0) @@ -597,6 +598,26 @@ StreamWorld::StreamWorld() view_distance = config.get_value("world", "view_distance"); initialized = true; } +void StreamWorld::checkpoint() +{ + struct checkpoint_data cp; + cp.building_data = building_data; + cp.buildings = buildings; + undo_log.push_back(cp); + while ((int)undo_log.size() > undo_log_size) + undo_log.erase(undo_log.begin()); +} +void StreamWorld::undo() +{ + struct checkpoint_data cp = *undo_log.end(); + int id; + for (id = 0; id < (int)buildings.size(); id++) + unload_building(id); + building_data = cp.building_data; + buildings = cp.buildings; + update_view(); + update_items(); +} void StreamWorld::cleanup() { RoadProcessing::cleanup(); diff --git a/src/modules/stream/stream.h b/src/modules/stream/stream.h index bd28b3c..eb11f77 100644 --- a/src/modules/stream/stream.h +++ b/src/modules/stream/stream.h @@ -15,6 +15,7 @@ private: VoxelLodTerrain *terrain; Node *current_scene; ConfigFile config; + /* Per-building information */ struct building { String id; int pattern_id; @@ -29,6 +30,7 @@ private: const String &key); Dictionary to_dict() const; }; + /* Scene objects data */ struct scene_data { Ref packed_scene; String path; @@ -46,11 +48,20 @@ private: }; using tile_map_t = std::unordered_map, tile_hash>; + /* Data for each building type */ HashMap building_data; + /* Data for each building in a world */ std::vector buildings; Vector3 eye; tile_map_t tiles; tile_map_t loaded_tiles; + struct checkpoint_data { + HashMap building_data; + std::vector buildings; + }; + std::vector undo_log; + int undo_log_size; + int world_extent; int tile_size; int view_distance; @@ -69,6 +80,8 @@ private: void unload_building(int id); void request_item(int type, int item); void update_items(); + void checkpoint(); + void undo(); static void _bind_methods(); diff --git a/src/modules/stream/world_editor.cpp b/src/modules/stream/world_editor.cpp index 9329a01..c3211c9 100644 --- a/src/modules/stream/world_editor.cpp +++ b/src/modules/stream/world_editor.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "road_lines_editor.h" #include "world_editor.h" WorldEditor::WorldEditor() @@ -20,6 +21,7 @@ WorldEditor::WorldEditor() , old_mouse_pos(Vector2(-1, -1)) , dragging(false) , drag_delay(0.2f) + , road_lines_editor(memnew(RoadLinesEditor(this))) { if (!InputMap::get_singleton()->has_action("left")) InputMap::get_singleton()->add_action("left"); @@ -45,6 +47,7 @@ WorldEditor::WorldEditor() WorldEditor::~WorldEditor() { + memdelete(road_lines_editor); } void WorldEditor::set_camera_mode(int mode) @@ -158,6 +161,11 @@ void WorldEditor::tools_button(const String &button) change[0] = current_mode; change[1] = modes[button]; emit_signal("editor_event", "mode_change_pre", change); + switch (current_mode) { + case MODE_ROAD_LINES: + road_lines_editor->exit(); + break; + } switch (modes[button]) { case MODE_BUILDINGS: mode_buildings(); @@ -195,11 +203,11 @@ void WorldEditor::editor_command(const String &command, const Array &args) if (stream_world) { stream_world->run_command(command, args); } - } else if (command == "buildings_checkpoint") { + } else if (command == "checkpoint") { if (stream_world) { stream_world->run_command(command, args); } - } else if (command == "buildings_undo") { + } else if (command == "undo") { if (stream_world) { stream_world->run_command(command, args); } @@ -215,6 +223,8 @@ void WorldEditor::editor_command(const String &command, const Array &args) if (stream_world) { stream_world->run_command(command, args); } + } else if (road_lines_editor) { + road_lines_editor->editor_command(command, args); } } @@ -333,6 +343,12 @@ void WorldEditor::_notification(int which) } if (!dragging && drag_delay >= 0.0f) drag_delay -= delta; + switch (current_mode) { + case MODE_ROAD_LINES: + print_line("current_mode: " + itos(current_mode)); + road_lines_editor->update(delta); + break; + } } break; } } diff --git a/src/modules/stream/world_editor.h b/src/modules/stream/world_editor.h index 61d89d8..9a9ce37 100644 --- a/src/modules/stream/world_editor.h +++ b/src/modules/stream/world_editor.h @@ -2,6 +2,7 @@ #define WORLD_EDITOR_H #include #include "stream.h" +class RoadLinesEditor; class WorldEditor : public Spatial { GDCLASS(WorldEditor, Spatial) protected: @@ -16,7 +17,6 @@ protected: void mode_road_lines(); void mode_npc(); void tools_button(const String &button); - void editor_command(const String &command, const Array &args); int get_current_mode() const; StreamWorld *get_stream_world(); void world_command_result(const String &what, const Array &data); @@ -32,9 +32,11 @@ private: Vector2 old_mouse_pos; bool dragging; float drag_delay; + RoadLinesEditor *road_lines_editor; public: WorldEditor(); - ~WorldEditor(); + virtual ~WorldEditor(); + void editor_command(const String &command, const Array &args); }; #endif