Quality of life with road lines edge data (lots, buildings, etc.)

This commit is contained in:
2025-02-12 05:50:12 +03:00
parent 2780fd300a
commit b56103930c
24 changed files with 17399 additions and 2212 deletions

View File

@@ -7,6 +7,11 @@
#include <scene/gui/text_edit.h>
#include <scene/gui/spin_box.h>
#include <scene/gui/menu_button.h>
#include <scene/gui/box_container.h>
#include <scene/gui/label.h>
#include <scene/gui/panel_container.h>
#include <scene/gui/scroll_container.h>
#include <scene/gui/separator.h>
#include <scene/3d/immediate_geometry.h>
#include <scene/3d/camera.h>
#include <core/io/config_file.h>
@@ -16,6 +21,7 @@
#include <core/os/time.h>
#include <core/io/json.h>
#include <modules/regex/regex.h>
#include "signal_handler.h"
#include "editor_event.h"
#include "world_editor.h"
#include "from_string.h"
@@ -69,104 +75,6 @@ static Ref<Material> debug_material;
__evhandler(editor_event, RoadLinesEditor);
static __evhandler_type(editor_event, RoadLinesEditor) * gd_editor_event;
class HandleSelection : public Object {
GDCLASS(HandleSelection, Object)
RoadLinesEditor *editor;
public:
HandleSelection(RoadLinesEditor *editor)
: Object()
, editor(editor)
{
#if 0
LineEdit *filter =
editor->get_as_node<LineEdit>("%road_lines_filter");
filter->connect("text_changed", this, "filter_handler");
filter->connect("text_entered", this, "filter_handler");
#endif
#if 0
ItemList *lines_list =
editor->get_as_node<ItemList>("%lines_list");
lines_list->connect("item_selected", this, "handler");
Button *cursor_set = editor->cursor_set;
assert(cursor_set);
cursor_set->connect("pressed", this, "set_cursor_handler");
Button *point_set = editor->cursor_set;
assert(point_set);
point_set->connect("pressed", this, "set_point_handler");
#endif
}
virtual ~HandleSelection()
{
#if 0
Button *cursor_set = editor->cursor_set;
assert(cursor_set);
cursor_set->disconnect("pressed", this, "set_cursor_handler");
Button *point_set = editor->cursor_set;
assert(point_set);
point_set->disconnect("pressed", this, "set_point_handler");
ItemList *lines_list =
editor->get_as_node<ItemList>("%lines_list");
lines_list->disconnect("item_selected", this, "handler");
LineEdit *filter =
editor->get_as_node<LineEdit>("%road_lines_filter");
filter->disconnect("text_entered", this, "filter_handler");
filter->disconnect("text_changed", this, "filter_handler");
#endif
}
protected:
void handler(int index)
{
if (index < 0)
return;
ItemList *lines_list =
editor->get_as_node<ItemList>("%lines_list");
editor->select_line(lines_list->get_item_text(index));
}
void filter_handler(const String &text)
{
editor->line_list_filter_changed(text);
}
#if 0
void set_cursor_handler()
{
LineEdit *cursor_x = editor->cursor_pos[0];
LineEdit *cursor_y = editor->cursor_pos[1];
LineEdit *cursor_z = editor->cursor_pos[2];
Vector3 position;
position.x = cursor_x->get_text().to_float();
position.y = cursor_y->get_text().to_float();
position.z = cursor_z->get_text().to_float();
editor->set_cursor_position(position);
}
void set_point_handler()
{
LineEdit *point_x = editor->point_pos[0];
LineEdit *point_y = editor->point_pos[1];
LineEdit *point_z = editor->point_pos[2];
Vector3 position;
position.x = point_x->get_text().to_float();
position.y = point_y->get_text().to_float();
position.z = point_z->get_text().to_float();
editor->set_point_position(position);
}
#endif
static void _bind_methods()
{
ClassDB::bind_method(D_METHOD("handler", "index"),
&HandleSelection::handler);
ClassDB::bind_method(D_METHOD("filter_handler", "text"),
&HandleSelection::filter_handler);
#if 0
ClassDB::bind_method(D_METHOD("set_cursor_handler"),
&HandleSelection::set_cursor_handler);
ClassDB::bind_method(D_METHOD("set_point_handler"),
&HandleSelection::set_point_handler);
#endif
}
};
static String current_line = "";
class HandlePointSelection : public Object {
@@ -277,25 +185,6 @@ protected:
String text = editor->get_current_line_metadata();
metadata_edit->set_text(text);
}
void update_line_buildings_editor()
{
int i;
if (current_line == "")
return;
ItemList *items =
editor->get_as_node<ItemList>("%line_buildings_list");
items->clear();
for (i = 0; i < (int)RoadLinesData::get_singleton()
->lines(current_line)
.buildings.size();
i++) {
const String &key = RoadLinesData::get_singleton()
->lines(current_line)
.buildings[i]
.building_key;
items->add_item(key);
}
}
void cancel_metadata_handler()
{
TextEdit *metadata_text = editor->get_as_node<TextEdit>(
@@ -345,14 +234,6 @@ protected:
editor->get_as_node<Control>("%road_lines_base")->show();
editor->get_as_node<Control>("%road_lines_buildings")->hide();
}
void line_buildings_assign_handler()
{
update_line_buildings_editor();
}
void line_buildings_remove_handler()
{
update_line_buildings_editor();
}
static void _bind_methods()
{
ClassDB::bind_method(
@@ -364,19 +245,9 @@ protected:
ClassDB::bind_method(
D_METHOD("metadata_changed_handler"),
&HandleCreateNewLine::metadata_changed_handler);
ClassDB::bind_method(
D_METHOD("line_buildings_close_handler"),
&HandleCreateNewLine::line_buildings_close_handler);
ClassDB::bind_method(
D_METHOD("line_buildings_assign_handler"),
&HandleCreateNewLine::line_buildings_assign_handler);
ClassDB::bind_method(
D_METHOD("line_buildings_remove_handler"),
&HandleCreateNewLine::line_buildings_remove_handler);
}
};
static HandleSelection *selection_handler = nullptr;
static HandleCreateNewLine *new_line_handler = nullptr;
static HandlePointSelection *point_selection_handler = nullptr;
@@ -459,27 +330,6 @@ void RoadLinesEditor::update_line_geometry()
}
// FIXME: update line segments on load and when line is changed
rld->update_line_segments(current_line);
if (rld->lines(current_line).buildings.size() > 1) {
line_im->begin(Mesh::PRIMITIVE_LINES);
for (i = 0;
i < (int)rld->lines(current_line).buildings.size();
i++) {
print_line("idx: " + itos(i));
const RoadLinesData::line_building_data &b =
rld->lines(current_line).buildings[i];
Vector3 pt = rld->get_point_by_offsets(
current_line, b.line_offset,
b.normal_offset);
line_im->set_color(
Color(0.1f, 0.8f, 0.8f, 1.0f));
line_im->add_vertex(pt +
Vector3(0.0f, 5.0f, 0.0f));
line_im->add_vertex(pt +
Vector3(0.0f, 15.0f, 0.0f));
print_line("idx: " + itos(i) + " done");
}
line_im->end();
}
}
}
void RoadLinesEditor::update_line_index_ui()
@@ -807,6 +657,10 @@ void RoadLinesEditor::set_ui_point_position(const Vector3 &point_position)
void RoadLinesEditor::set_line_index(int index)
{
RoadLinesData *rld = RoadLinesData::get_singleton();
if (!rld->has_line(current_line)) {
print_error("bad line: " + current_line);
return;
}
assert(rld->has_line(current_line));
assert(index < (int)rld->lines(current_line).points.size());
Vector3 point_position = rld->lines(current_line).points[index].origin;
@@ -849,8 +703,6 @@ void RoadLinesEditor::activate()
gd_editor_event = memnew(
__evhandler_type(editor_event, RoadLinesEditor)(this));
gd_editor_event->connect(editor, "editor_event");
if (!selection_handler)
selection_handler = memnew(HandleSelection(this));
if (!new_line_handler)
new_line_handler = memnew(HandleCreateNewLine(this));
if (!point_selection_handler)
@@ -865,11 +717,6 @@ void RoadLinesEditor::deactivate()
{
EditorEvent::get_singleton()->event.remove_listener(
this, &RoadLinesEditor::event_handler);
Node *lines_list_node =
editor->get_tree()->get_current_scene()->get_node(
NodePath("%lines_list"));
ItemList *lines_list = Object::cast_to<ItemList>(lines_list_node);
lines_list->clear();
if (line_im) {
line_im->queue_delete();
line_im = nullptr;
@@ -881,10 +728,6 @@ void RoadLinesEditor::deactivate()
memdelete(new_line_handler);
new_line_handler = nullptr;
}
if (selection_handler) {
memdelete(selection_handler);
selection_handler = nullptr;
}
if (gd_editor_event) {
memdelete(gd_editor_event);
gd_editor_event = nullptr;
@@ -1135,8 +978,13 @@ void RoadLinesEditor::event_handler(const String &event,
String text = args[1];
line_list_filter_changed(text);
} else if (event == "road_lines_editor_set_line_index") {
if (current_line.length() == 0)
return;
int index = (int)args[1];
set_line_index(index);
EditorEvent::get_singleton()->event.emit(
"road_lines_editor_set_line_index::post",
varray(index));
} else if (event == "road_lines_create_new_line_name:entered") {
String new_text = args[1];
String text = new_text.strip_edges();
@@ -1161,5 +1009,346 @@ void RoadLinesEditor::event_handler(const String &event,
return;
} else
le->remove_color_override("font_color");
} else if (event == "road_lines_create_edge_editor") {
Control *c = Object::cast_to<Control>(args[0]);
assert(c);
create_edge_editor_ui(c);
} else if (event == "road_lines_edge_editor::update") {
RoadLinesData *rld = RoadLinesData::get_singleton();
int index = args[0];
const Dictionary &data = args[1];
print_line("Update for index: " + itos(index));
print_line("Update: " + to_string(data));
RoadLinesData::road_line rl = rld->get_line(current_line);
if (index < 0 || index >= (int)rl.edges.size()) {
print_error("Invalid index: " + itos(index));
print_error("Edge count: " +
itos((int)rl.edges.size()));
return;
}
RoadLinesData::road_edge::from_dict(rl.edges[index], data);
rld->set_line(current_line, rl);
print_line("Update for index: " + itos(index) + " done");
}
}
class EdgeEditorHandler {
RoadLinesEditor *editor;
String event_prefix;
int index;
Dictionary data;
std::vector<LineEditEnterString *> handlers;
std::vector<PopupMenuSelectHandler *> menu_handlers;
ConfigFile stream_conf;
void side_handler(Control *top, const Dictionary &edge_data,
const String &ident)
{
List<Variant> keys;
List<Variant>::Element *el;
edge_data.get_key_list(&keys);
el = keys.front();
VBoxContainer *v = memnew(VBoxContainer);
top->call_deferred("add_child", v);
while (el) {
HBoxContainer *h = memnew(HBoxContainer);
v->call_deferred("add_child", h);
Label *l = memnew(Label);
String name = el->get();
l->set_text(name);
h->call_deferred("add_child", l);
LineEdit *le = memnew(LineEdit);
h->call_deferred("add_child", le);
le->set_meta("edit_key", name);
le->set_meta("edit_ident", ident);
Variant value = edge_data[el->get()];
le->set_meta("edit_type", value.get_type());
if (value.get_type() == Variant::INT) {
int m = value;
le->set_text(itos(m));
} else if (value.get_type() == Variant::STRING) {
String m = value;
le->set_text(m);
} else if (value.get_type() == Variant::REAL) {
float m = value;
le->set_text(String::num(m));
}
LineEditEnterString *handler = memnew(
LineEditEnterString(le,
event_prefix + "::edit"));
handlers.push_back(handler);
el = el->next();
}
}
template <class T>
T get_edge_conf(const String &pname, const String &side,
const String &param)
{
T result = stream_conf.get_value(
"lines/edges/" + pname + "/" + side, param,
stream_conf.get_value("lines/edges/default/" + side,
param, 0));
if (result == 0 && param.ends_with("dir_offset"))
result = 1;
return result;
}
void event_handler(const String &event, const Vector<Variant> &args)
{
if (event == "road_lines_edge_editor::edit") {
LineEdit *le = Object::cast_to<LineEdit>(args[0]);
assert(le);
String text = args[1];
String key = le->get_meta("edit_key");
String ident = le->get_meta("edit_ident");
int type = le->get_meta("edit_type");
if (type == Variant::INT) {
int m = text.to_int();
Dictionary side = data[ident];
side[key] = m;
data[ident] = side;
} else if (type == Variant::STRING) {
String m = text.strip_edges();
Dictionary side = data[ident];
side[key] = m;
data[ident] = side;
} else if (type == Variant::REAL) {
float m = text.strip_edges().to_float();
Dictionary side = data[ident];
side[key] = m;
data[ident] = side;
}
EditorEvent::get_singleton()->event.emit(
event_prefix + "::update", varray(index, data));
String output = to_string(data);
print_line("index: " + itos(index));
print_line("edited: " + output);
} else if (event == "road_lines_edge_editor::menu::left") {
PopupMenu *menu = Object::cast_to<PopupMenu>(args[0]);
int id = args[1];
int item_index = menu->get_item_index(id);
RoadLinesData *rld = RoadLinesData::get_singleton();
RoadLinesData::road_line rl =
rld->get_line(current_line);
String pname;
switch (id) {
case 200:
pname = "clear";
break;
default:
pname = menu->get_item_metadata(item_index);
break;
}
if (pname.begins_with("lot-")) {
rl.edges[index].left.lot_type =
pname.replace("lot-", "");
if (rl.edges[index].left.lot == 0) {
rl.edges[index].left.lot = 1;
rl.edges[index].left.lot_offset =
get_edge_conf<float>(
pname, "left",
"lot_offset");
rl.edges[index].left.lot_y_rotation =
get_edge_conf<float>(
pname, "left",
"lot_y_rotation");
float dir_offt =
rl.points[index + 1]
.origin.distance_to(
rl.points[index]
.origin) /
2.0f;
rl.edges[index].left.lot_dir_offset =
dir_offt;
}
} else if (pname == "clear") {
rl.edges[index].left.lot_type = "";
rl.edges[index].left.lot = 0;
rl.edges[index].left.buildings.clear();
}
rld->set_line(current_line, rl);
editor->rebuild_roads();
} else if (event == "road_lines_edge_editor::menu::right") {
PopupMenu *menu = Object::cast_to<PopupMenu>(args[0]);
int id = args[1];
int item_index = menu->get_item_index(id);
String pname;
switch (id) {
case 200:
pname = "clear";
break;
default:
pname = menu->get_item_metadata(item_index);
break;
}
RoadLinesData *rld = RoadLinesData::get_singleton();
RoadLinesData::road_line rl =
rld->get_line(current_line);
if (pname.begins_with("lot-")) {
rl.edges[index].right.lot_type =
pname.replace("lot-", "");
if (rl.edges[index].right.lot == 0) {
rl.edges[index].right.lot = 1;
rl.edges[index].right.lot_offset =
get_edge_conf<float>(
pname, "right",
"lot_offset");
rl.edges[index].right.lot_y_rotation =
get_edge_conf<float>(
pname, "right",
"lot_y_rotation");
float dir_offt =
rl.points[index + 1]
.origin.distance_to(
rl.points[index]
.origin) /
2.0f;
rl.edges[index].right.lot_dir_offset =
dir_offt;
}
} else if (pname == "clear") {
rl.edges[index].right.lot_type = "";
rl.edges[index].right.lot = 0;
rl.edges[index].right.buildings.clear();
}
rld->set_line(current_line, rl);
editor->rebuild_roads();
}
}
public:
EdgeEditorHandler(RoadLinesEditor *editor, Control *top, int index)
: editor(editor)
, event_prefix("road_lines_edge_editor")
, index(index)
{
int i;
for (i = 0; i < top->get_child_count(); i++)
top->get_child(i)->queue_delete();
print_line("line index: " + itos(index));
RoadLinesData *rld = RoadLinesData::get_singleton();
if (index < 0 ||
index >= (int)rld->lines(current_line).points.size() - 1)
return;
assert(index >= 0 &&
index < (int)rld->lines(current_line).points.size() - 1);
assert(index >= 0 &&
index < (int)rld->lines(current_line).edges.size());
data = rld->lines(current_line).edges[index].to_dict();
const Dictionary &left = data["left"], &right = data["right"];
PanelContainer *p = memnew(PanelContainer);
top->call_deferred("add_child", p);
ScrollContainer *s = memnew(ScrollContainer);
p->call_deferred("add_child", s);
s->set_custom_minimum_size(Vector2(0, 90));
VBoxContainer *v = memnew(VBoxContainer);
s->call_deferred("add_child", v);
v->set_h_size_flags(Control::SIZE_EXPAND_FILL);
v->set_v_size_flags(Control::SIZE_EXPAND_FILL);
Label *h1 = memnew(Label), *h2 = memnew(Label);
h1->set_text("left");
h2->set_text("right");
v->call_deferred("add_child", h1);
side_handler(v, left, "left");
HSeparator *sep = memnew(HSeparator);
v->call_deferred("add_child", sep);
v->call_deferred("add_child", h2);
side_handler(v, right, "right");
MenuButton *mb = memnew(MenuButton);
v->call_deferred("add_child", mb);
mb->set_text("Action...");
MenuButton *mb_left = memnew(MenuButton);
v->call_deferred("add_child", mb_left);
mb_left->set_text("Select left lot...");
MenuButton *mb_right = memnew(MenuButton);
mb_right->set_text("Select right lot...");
v->call_deferred("add_child", mb_right);
Error err = stream_conf.load("res://config/stream.conf");
assert(err == OK);
std::vector<std::pair<String, int> > menu_items = {
{ "Move lots starting from current edge to next edges",
100 },
};
std::vector<std::pair<String, int> > menu_items_leftright = {
{ "Clear current lot and building(s)", 200 },
};
for (i = 0; i < (int)menu_items.size(); i++)
mb->get_popup()->add_item(menu_items[i].first,
menu_items[i].second);
for (i = 0; i < (int)menu_items_leftright.size(); i++) {
mb_left->get_popup()->add_item(
menu_items_leftright[i].first,
menu_items_leftright[i].second);
mb_right->get_popup()->add_item(
menu_items_leftright[i].first,
menu_items_leftright[i].second);
}
Dictionary bdata =
stream_conf.get_value("buildings", "building_data");
int item_id = 1000;
List<Variant> building_list;
List<Variant>::Element *el;
bdata.get_key_list(&building_list);
el = building_list.front();
while (el) {
String pname = el->get();
String msg;
if (pname.begins_with("lot-"))
msg = "Create lot: " + pname;
else if (pname.begins_with("residental-"))
msg = "Add house: " + pname;
if (msg.length() > 0 && pname.begins_with("lot-")) {
std::vector<PopupMenu *> menus = {
mb_left->get_popup(),
mb_right->get_popup()
};
for (i = 0; i < (int)menus.size(); i++) {
menus[i]->add_item(msg, item_id);
int item_index =
menus[i]->get_item_index(
item_id);
menus[i]->set_item_metadata(item_index,
pname);
item_id++;
}
}
el = el->next();
}
menu_handlers.push_back(memnew(PopupMenuSelectHandler(
mb->get_popup(), event_prefix + "::menu")));
menu_handlers.push_back(memnew(PopupMenuSelectHandler(
mb_left->get_popup(), event_prefix + "::menu::left")));
menu_handlers.push_back(memnew(PopupMenuSelectHandler(
mb_right->get_popup(),
event_prefix + "::menu::right")));
EditorEvent::get_singleton()->event.add_listener(
this, &EdgeEditorHandler::event_handler);
}
virtual ~EdgeEditorHandler()
{
int i;
EditorEvent::get_singleton()->event.remove_listener(
this, &EdgeEditorHandler::event_handler);
for (i = 0; i < (int)handlers.size(); i++)
memdelete(handlers[i]);
for (i = 0; i < (int)menu_handlers.size(); i++)
memdelete(menu_handlers[i]);
handlers.clear();
menu_handlers.clear();
}
};
static EdgeEditorHandler *edge_editor = nullptr;
void RoadLinesEditor::create_edge_editor_ui(Control *top)
{
RoadLinesData *rld = RoadLinesData::get_singleton();
assert(current_line.length() > 0);
int index = get_line_index();
if (index < 0)
return;
if ((int)rld->lines(current_line).edges.size() < index)
return;
print_line("created with index: " + itos(index));
if (edge_editor)
memdelete(edge_editor);
edge_editor = memnew(EdgeEditorHandler(this, top, index));
}