1354 lines
41 KiB
C++
1354 lines
41 KiB
C++
#undef NDEBUG
|
|
#include <cassert>
|
|
#include <scene/main/viewport.h>
|
|
#include <scene/gui/item_list.h>
|
|
#include <scene/gui/button.h>
|
|
#include <scene/gui/line_edit.h>
|
|
#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>
|
|
#include <core/os/file_access.h>
|
|
#include <core/os/dir_access.h>
|
|
#include <core/os/input.h>
|
|
#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"
|
|
#include "road_lines_data.h"
|
|
#include "road_processing.h"
|
|
#include "buildings_data.h"
|
|
#include "road_lines_editor.h"
|
|
|
|
static ImmediateGeometry *line_im = nullptr;
|
|
// static ImmediateGeometry *debug_im = nullptr;
|
|
static Ref<Material> debug_material;
|
|
|
|
#define __evhandler(vname, mtype) \
|
|
template <class T> 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 Vector<Variant> &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<mtype>
|
|
|
|
__evhandler(editor_event, RoadLinesEditor);
|
|
static __evhandler_type(editor_event, RoadLinesEditor) * gd_editor_event;
|
|
|
|
static String current_line = "";
|
|
|
|
class HandlePointSelection : public Object {
|
|
GDCLASS(HandlePointSelection, Object)
|
|
RoadLinesEditor *editor;
|
|
|
|
public:
|
|
HandlePointSelection(RoadLinesEditor *editor)
|
|
: Object()
|
|
, editor(editor)
|
|
{
|
|
#if 0
|
|
SpinBox *sp_line_point =
|
|
editor->get_as_node<SpinBox>("%line_index");
|
|
sp_line_point->connect("value_changed", this,
|
|
"handle_value_change");
|
|
#endif
|
|
}
|
|
virtual ~HandlePointSelection()
|
|
{
|
|
#if 0
|
|
SpinBox *sp_line_point =
|
|
editor->get_as_node<SpinBox>("%line_index");
|
|
sp_line_point->disconnect("value_changed", this,
|
|
"handle_value_change");
|
|
#endif
|
|
}
|
|
|
|
protected:
|
|
void handle_value_change(float value)
|
|
{
|
|
int index = (int)value;
|
|
editor->set_line_index(index);
|
|
}
|
|
void handle_set_point()
|
|
{
|
|
editor->set_point_to_cursor();
|
|
}
|
|
void handle_move_cursor()
|
|
{
|
|
editor->move_cursor_to_point();
|
|
}
|
|
static void _bind_methods()
|
|
{
|
|
ClassDB::bind_method(
|
|
D_METHOD("handle_value_change", "value"),
|
|
&HandlePointSelection::handle_value_change);
|
|
ClassDB::bind_method(D_METHOD("handle_set_point"),
|
|
&HandlePointSelection::handle_set_point);
|
|
ClassDB::bind_method(D_METHOD("handle_move_cursor"),
|
|
&HandlePointSelection::handle_move_cursor);
|
|
}
|
|
};
|
|
|
|
class HandleCreateNewLine : public Object {
|
|
GDCLASS(HandleCreateNewLine, Object)
|
|
RoadLinesEditor *editor;
|
|
|
|
public:
|
|
HandleCreateNewLine(RoadLinesEditor *editor)
|
|
: Object()
|
|
, editor(editor)
|
|
{
|
|
Button *cancel_metadata_button = editor->get_as_node<Button>(
|
|
"%road_lines_metadata_cancel");
|
|
Button *update_metadata_button = editor->get_as_node<Button>(
|
|
"%road_lines_metadata_update");
|
|
TextEdit *metadata_edit = editor->get_as_node<TextEdit>(
|
|
"%road_lines_metadata_edit");
|
|
Button *line_buildings_close =
|
|
editor->get_as_node<Button>("%line_buildings_close");
|
|
cancel_metadata_button->connect("pressed", this,
|
|
"cancel_metadata_handler");
|
|
update_metadata_button->connect("pressed", this,
|
|
"update_metadata_handler");
|
|
metadata_edit->connect("text_changed", this,
|
|
"metadata_changed_handler");
|
|
line_buildings_close->connect("pressed", this,
|
|
"line_buildings_close_handler");
|
|
}
|
|
virtual ~HandleCreateNewLine()
|
|
{
|
|
Button *line_buildings_close =
|
|
editor->get_as_node<Button>("%line_buildings_close");
|
|
TextEdit *metadata_edit = editor->get_as_node<TextEdit>(
|
|
"%road_lines_metadata_edit");
|
|
Button *update_metadata_button = editor->get_as_node<Button>(
|
|
"%road_lines_metadata_update");
|
|
Button *cancel_metadata_button = editor->get_as_node<Button>(
|
|
"%road_lines_metadata_cancel");
|
|
line_buildings_close->disconnect(
|
|
"pressed", this, "line_buildings_close_handler");
|
|
metadata_edit->disconnect("text_changed", this,
|
|
"metadata_changed_handler");
|
|
update_metadata_button->disconnect("pressed", this,
|
|
"update_metadata_handler");
|
|
cancel_metadata_button->disconnect("pressed", this,
|
|
"cancel_metadata_handler");
|
|
}
|
|
|
|
protected:
|
|
void update_metadata_editor()
|
|
{
|
|
if (current_line == "")
|
|
return;
|
|
TextEdit *metadata_edit = editor->get_as_node<TextEdit>(
|
|
"%road_lines_metadata_edit");
|
|
String text = editor->get_current_line_metadata();
|
|
metadata_edit->set_text(text);
|
|
}
|
|
void cancel_metadata_handler()
|
|
{
|
|
TextEdit *metadata_text = editor->get_as_node<TextEdit>(
|
|
"%road_lines_metadata_edit");
|
|
editor->get_as_node<Control>("%road_lines_base")->show();
|
|
editor->get_as_node<Control>("%road_lines_edit_metadata_dlg")
|
|
->hide();
|
|
metadata_text->set_text("");
|
|
}
|
|
void update_metadata_handler()
|
|
{
|
|
TextEdit *metadata_text = editor->get_as_node<TextEdit>(
|
|
"%road_lines_metadata_edit");
|
|
editor->get_as_node<Control>("%road_lines_base")->show();
|
|
editor->get_as_node<Control>("%road_lines_edit_metadata_dlg")
|
|
->hide();
|
|
editor->update_current_line_metadata(metadata_text->get_text());
|
|
metadata_text->set_text("");
|
|
}
|
|
bool check_metadata(const String &text)
|
|
{
|
|
if (!text.begins_with("{"))
|
|
return false;
|
|
Variant p;
|
|
String err_s;
|
|
int line;
|
|
// TODO: create status string for errors
|
|
Error err = JSON::parse(text, p, err_s, line);
|
|
return (err == OK);
|
|
}
|
|
/* Unlike LineEdit, no arguments */
|
|
void metadata_changed_handler()
|
|
{
|
|
TextEdit *metadata_text = editor->get_as_node<TextEdit>(
|
|
"%road_lines_metadata_edit");
|
|
String new_text = metadata_text->get_text();
|
|
String text = new_text.strip_edges();
|
|
if (!check_metadata(text)) {
|
|
metadata_text->add_color_override(
|
|
"font_color", Color(1.0f, 0.0f, 0.0f, 1.0f));
|
|
return;
|
|
} else
|
|
metadata_text->remove_color_override("font_color");
|
|
}
|
|
void line_buildings_close_handler()
|
|
{
|
|
editor->get_as_node<Control>("%road_lines_base")->show();
|
|
editor->get_as_node<Control>("%road_lines_buildings")->hide();
|
|
}
|
|
static void _bind_methods()
|
|
{
|
|
ClassDB::bind_method(
|
|
D_METHOD("cancel_metadata_handler"),
|
|
&HandleCreateNewLine::cancel_metadata_handler);
|
|
ClassDB::bind_method(
|
|
D_METHOD("update_metadata_handler"),
|
|
&HandleCreateNewLine::update_metadata_handler);
|
|
ClassDB::bind_method(
|
|
D_METHOD("metadata_changed_handler"),
|
|
&HandleCreateNewLine::metadata_changed_handler);
|
|
}
|
|
};
|
|
|
|
static HandleCreateNewLine *new_line_handler = nullptr;
|
|
static HandlePointSelection *point_selection_handler = nullptr;
|
|
|
|
RoadLinesEditor::RoadLinesEditor(WorldEditor *editor)
|
|
: active(false)
|
|
, editor(editor)
|
|
, cursor_enabled(false)
|
|
, filter_text("")
|
|
, camera_moved(false)
|
|
, update_roads(false)
|
|
#if 0
|
|
, cursor_pos{ 0, 0, 0 }
|
|
, cursor_set(nullptr)
|
|
, point_pos{ 0, 0, 0 }
|
|
, point_set(nullptr)
|
|
#endif
|
|
, debug_road_nodes(false)
|
|
, debug_road_edges(false)
|
|
, debug_road_wedges(false)
|
|
{
|
|
}
|
|
|
|
RoadLinesEditor::~RoadLinesEditor()
|
|
{
|
|
if (active && editor->is_inside_tree())
|
|
deactivate();
|
|
if (re.is_valid())
|
|
re.unref();
|
|
}
|
|
|
|
Node *RoadLinesEditor::scene()
|
|
{
|
|
return editor->get_tree()->get_current_scene();
|
|
}
|
|
|
|
void RoadLinesEditor::update_line_geometry()
|
|
{
|
|
RoadLinesData *rld = RoadLinesData::get_singleton();
|
|
if (!rld->has_line(current_line)) {
|
|
if (line_im)
|
|
line_im->clear();
|
|
return;
|
|
}
|
|
if (line_im) {
|
|
int i;
|
|
line_im->clear();
|
|
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->set_color(Color(1.0f, 0.0f, 0.0f, 1.0f));
|
|
line_im->add_vertex(Vector3(0.0f, 100.0f, 0.0f));
|
|
line_im->end();
|
|
if (rld->lines(current_line).points.size() > 1) {
|
|
line_im->begin(Mesh::PRIMITIVE_LINES);
|
|
for (i = 0;
|
|
i <
|
|
(int)rld->lines(current_line).points.size() - 1;
|
|
i++) {
|
|
Vector3 pt1 = rld->lines(current_line)
|
|
.points[i]
|
|
.origin;
|
|
Vector3 pt2 = rld->lines(current_line)
|
|
.points[i + 1]
|
|
.origin;
|
|
line_im->set_color(
|
|
Color(0.0f, 0.0f, 0.5f, 1.0f));
|
|
line_im->add_vertex(pt1);
|
|
line_im->set_color(
|
|
Color(0.0f, 0.0f, 0.5f, 1.0f));
|
|
line_im->add_vertex(pt2);
|
|
line_im->set_color(
|
|
Color(0.0f, 0.0f, 0.5f, 1.0f));
|
|
line_im->add_vertex(pt2);
|
|
line_im->set_color(
|
|
Color(0.0f, 0.0f, 0.5f, 1.0f));
|
|
line_im->add_vertex(pt2 +
|
|
Vector3(0.0f, 1.0f, 0.0f));
|
|
}
|
|
line_im->end();
|
|
}
|
|
// FIXME: update line segments on load and when line is changed
|
|
rld->update_line_segments(current_line);
|
|
}
|
|
}
|
|
void RoadLinesEditor::update_line_index_ui()
|
|
{
|
|
EditorEvent::get_singleton()->event.emit(
|
|
"road_lines_editor_update_line_index_ui", varray(current_line));
|
|
}
|
|
void RoadLinesEditor::select_line(const String &line_name)
|
|
{
|
|
print_line("selected line: " + line_name);
|
|
RoadLinesData *rld = RoadLinesData::get_singleton();
|
|
assert(rld->has_line(line_name));
|
|
if (current_line != line_name) {
|
|
current_line = line_name;
|
|
update_line_index_ui();
|
|
EditorEvent::get_singleton()->event.emit(
|
|
"road_lines_editor_clear_selected_line_ui", varray());
|
|
/* as actual index of SpinBox might not change
|
|
call point selection explicitly */
|
|
set_line_index(0);
|
|
update_line_geometry();
|
|
}
|
|
EditorEvent::get_singleton()->event.emit("lines_select_line",
|
|
varray(current_line));
|
|
update_ui();
|
|
}
|
|
|
|
bool RoadLinesEditor::line_exists(const String &line_name)
|
|
{
|
|
RoadLinesData *rld = RoadLinesData::get_singleton();
|
|
return rld->has_line(line_name);
|
|
}
|
|
|
|
void RoadLinesEditor::line_create_point()
|
|
{
|
|
RoadLinesData *rld = RoadLinesData::get_singleton();
|
|
/* Create point in line */
|
|
print_line("line_create_point");
|
|
Vector3 position = get_cursor_position();
|
|
Transform xform(Basis(), position);
|
|
int index = get_line_index();
|
|
rld->insert_line_point(current_line, index + 1, xform);
|
|
EditorEvent::get_singleton()->event.emit("lines_changed_line",
|
|
varray(current_line));
|
|
update_line_geometry();
|
|
update_line_index_ui();
|
|
EditorEvent::get_singleton()->event.emit(
|
|
"road_lines_editor_set_line_index_ui", varray(line_index + 1));
|
|
}
|
|
|
|
void RoadLinesEditor::line_delete_point()
|
|
{
|
|
/* Delete point from line */
|
|
print_line("line_delete_point");
|
|
int index = get_line_index();
|
|
RoadLinesData *rld = RoadLinesData::get_singleton();
|
|
if (rld->lines(current_line).points.size() < 2)
|
|
return;
|
|
rld->erase_line_point(current_line, index);
|
|
EditorEvent::get_singleton()->event.emit("lines_changed_line",
|
|
varray(current_line));
|
|
update_line_geometry();
|
|
update_line_index_ui();
|
|
EditorEvent::get_singleton()->event.emit(
|
|
"road_lines_editor_update_line_index_ui", varray(current_line));
|
|
index = MAX(0, index - 1);
|
|
EditorEvent::get_singleton()->event.emit(
|
|
"road_lines_editor_set_line_index_ui", varray(index));
|
|
}
|
|
|
|
static const String cursor_name = "%line_cursor";
|
|
|
|
void RoadLinesEditor::set_point_to_cursor()
|
|
{
|
|
RoadLinesData *rld = RoadLinesData::get_singleton();
|
|
print_line("set_point_to_cursor");
|
|
Spatial *cursor = get_as_node<Spatial>(cursor_name);
|
|
Transform xform = cursor->get_global_transform();
|
|
int index = get_line_index();
|
|
rld->set_line_point_position(current_line, index, xform.origin);
|
|
EditorEvent::get_singleton()->event.emit("lines_changed_line",
|
|
varray(current_line));
|
|
update_line_geometry();
|
|
set_ui_point_position(rld->lines(current_line).points[index].origin);
|
|
}
|
|
|
|
int RoadLinesEditor::get_line_index()
|
|
{
|
|
return line_index;
|
|
}
|
|
|
|
void RoadLinesEditor::move_cursor_to_point()
|
|
{
|
|
print_line("move_cursor_to_point");
|
|
RoadLinesData *rld = RoadLinesData::get_singleton();
|
|
int index = get_line_index();
|
|
Transform xform(Basis(), rld->lines(current_line).points[index].origin);
|
|
set_cursor_position(xform.origin);
|
|
set_ui_cursor_position(xform.origin);
|
|
}
|
|
|
|
void RoadLinesEditor::move_cursor_to_closest_building()
|
|
{
|
|
print_line("move_cursor_to_closest_building");
|
|
Vector3 pt = get_cursor_position();
|
|
const String &key =
|
|
BuildingsData::get_singleton()->get_closest_building(
|
|
Transform(Basis(), pt));
|
|
const Transform &xform =
|
|
BuildingsData::get_singleton()->get_building(key).xform;
|
|
set_cursor_position(xform.origin);
|
|
Spatial *cursor = get_as_node<Spatial>(cursor_name);
|
|
if (!cursor->is_visible())
|
|
cursor->show();
|
|
}
|
|
|
|
void RoadLinesEditor::update(float delta)
|
|
{
|
|
if (!active)
|
|
activate();
|
|
// print_line("road_lines_editor");
|
|
if (!cursor_enabled && get_camera_mode() == 3) {
|
|
cursor_enabled = true;
|
|
get_as_node<Spatial>(cursor_name)->show();
|
|
} else if (cursor_enabled && get_camera_mode() != 3) {
|
|
cursor_enabled = false;
|
|
get_as_node<Spatial>(cursor_name)->hide();
|
|
}
|
|
if (camera_moved) {
|
|
Camera *cam = editor->get_viewport()->get_camera();
|
|
assert(cam);
|
|
camera_moved = false;
|
|
Transform cam_xform = cam->get_global_transform();
|
|
float h = cam_xform.origin.y;
|
|
cam_xform.origin.z += Math::abs(h) * camera_motion.z * delta;
|
|
cam_xform.origin.x += Math::abs(h) * camera_motion.x * delta;
|
|
cam_xform.origin.y += camera_motion.y * delta;
|
|
camera_motion = Vector3();
|
|
cam->set_global_transform(cam_xform);
|
|
Array move_args;
|
|
move_args.push_back(cam_xform);
|
|
editor->emit_signal("editor_event", "editor_camera_moved",
|
|
move_args);
|
|
}
|
|
}
|
|
|
|
void RoadLinesEditor::exit()
|
|
{
|
|
if (active)
|
|
deactivate();
|
|
}
|
|
|
|
void RoadLinesEditor::editor_command(const String &command,
|
|
const Vector<Variant> &args)
|
|
{
|
|
print_line("command: " + command);
|
|
}
|
|
|
|
void RoadLinesEditor::editor_event(const String &event,
|
|
const Vector<Variant> &args)
|
|
{
|
|
print_line("RoadLinesEditor::event: " + event);
|
|
if (event == "mouse_press" || event == "mouse_drag") {
|
|
if (cursor_enabled) {
|
|
/* Raycasting outside physics process */
|
|
Vector2 position = args[0];
|
|
Camera *cam = editor->get_viewport()->get_camera();
|
|
Vector3 start = cam->project_ray_origin(position);
|
|
Vector3 normal = cam->project_ray_normal(position);
|
|
Vector3 end = start + normal * cam->get_zfar();
|
|
PhysicsDirectSpaceState *space_state =
|
|
editor->get_world()->get_direct_space_state();
|
|
PhysicsDirectSpaceState::RayResult result;
|
|
Set<RID> exclude;
|
|
space_state->intersect_ray(start, end, result, exclude,
|
|
(1 << 15) | (1 << 0), true,
|
|
true);
|
|
Vector3 result_pre;
|
|
if (result.rid == RID())
|
|
goto end;
|
|
result_pre = result.position;
|
|
result_pre.x = Math::stepify(result_pre.x, 2.0f);
|
|
result_pre.z = Math::stepify(result_pre.z, 2.0f);
|
|
start = result_pre + Vector3(0.0f, 200.0f, 0.0f);
|
|
end = result_pre - Vector3(0.0f, 200.0f, 0.0f);
|
|
space_state->intersect_ray(start, end, result, exclude,
|
|
(1 << 15) | (1 << 0), true,
|
|
true);
|
|
if (result.rid != RID()) {
|
|
set_cursor_position(result.position);
|
|
set_ui_cursor_position(result.position);
|
|
}
|
|
end:;
|
|
}
|
|
}
|
|
}
|
|
int RoadLinesEditor::get_camera_mode() const
|
|
{
|
|
return editor->get_camera_mode();
|
|
}
|
|
|
|
void RoadLinesEditor::update_ui()
|
|
{
|
|
RoadLinesData *rld = RoadLinesData::get_singleton();
|
|
get_as_node<Control>("%road_lines_base")->show();
|
|
get_as_node<Control>("%road_lines_edit_metadata_dlg")->hide();
|
|
get_as_node<Control>("%road_lines_buildings")->hide();
|
|
PoolVector<String> elements;
|
|
List<String> line_keys;
|
|
rld->get_lines_key_list(&line_keys);
|
|
List<String>::Element *e = line_keys.front();
|
|
if (!re.is_valid())
|
|
re.instance();
|
|
re->compile(filter_text);
|
|
if (filter_text.length() > 0 && !re->is_valid())
|
|
return;
|
|
int selected_index = -1;
|
|
int index = 0;
|
|
while (e) {
|
|
String key = e->get();
|
|
Array matches = re->search_all(key);
|
|
if (filter_text.length() > 0 && matches.size() == 0) {
|
|
e = e->next();
|
|
continue;
|
|
}
|
|
if (key == current_line)
|
|
selected_index = index;
|
|
elements.push_back(key);
|
|
e = e->next();
|
|
index++;
|
|
}
|
|
#if 0
|
|
if (selected_index >= 0)
|
|
lines_list->set_current(selected_index);
|
|
else
|
|
lines_list->set_current(0);
|
|
#endif
|
|
EditorEvent::get_singleton()->event.emit(
|
|
"road_lines_editor_update_lines_list",
|
|
varray(elements, selected_index));
|
|
}
|
|
|
|
void RoadLinesEditor::create_new_line_at_cursor(const String &line_name)
|
|
{
|
|
print_line("creating new line called: " + line_name);
|
|
RoadLinesData *rld = RoadLinesData::get_singleton();
|
|
struct RoadLinesData::road_line rline;
|
|
rline.flags = 0;
|
|
rline.indices.resize(0);
|
|
rline.lanes = -1;
|
|
if (line_name.ends_with("_road"))
|
|
rline.lanes = 4;
|
|
rline.metadata = Dictionary();
|
|
rline.pattern = 0;
|
|
Transform cursor_position(Basis(), get_cursor_position());
|
|
rline.points.push_back(cursor_position);
|
|
rld->set_line(line_name, rline);
|
|
assert(rld->has_line(line_name));
|
|
update_line_index_ui();
|
|
update_ui();
|
|
}
|
|
|
|
void RoadLinesEditor::delete_current_line()
|
|
{
|
|
RoadLinesData *rld = RoadLinesData::get_singleton();
|
|
String delete_line = current_line;
|
|
current_line = rld->get_next_line(delete_line);
|
|
rld->erase_line(delete_line);
|
|
update_line_index_ui();
|
|
update_ui();
|
|
}
|
|
|
|
Vector3 RoadLinesEditor::get_cursor_position()
|
|
{
|
|
Spatial *cursor = get_as_node<Spatial>(cursor_name);
|
|
return cursor->get_global_transform().origin;
|
|
}
|
|
|
|
void RoadLinesEditor::set_cursor_position(const Vector3 &cursor_position)
|
|
{
|
|
Spatial *cursor = get_as_node<Spatial>(cursor_name);
|
|
cursor->set_global_transform(Transform(Basis(), cursor_position));
|
|
Array pargs;
|
|
pargs.push_back(cursor_position);
|
|
editor->emit_signal("editor_event", "line_cursor_motion", pargs);
|
|
}
|
|
void RoadLinesEditor::set_point_position(const Vector3 &position)
|
|
{
|
|
RoadLinesData *rld = RoadLinesData::get_singleton();
|
|
int index = get_line_index();
|
|
rld->set_line_point_position(current_line, index, position);
|
|
EditorEvent::get_singleton()->event.emit("lines_changed_line",
|
|
varray(current_line));
|
|
update_line_geometry();
|
|
}
|
|
void RoadLinesEditor::set_ui_cursor_position(const Vector3 &cursor_position)
|
|
{
|
|
EditorEvent::get_singleton()->event.emit(
|
|
"road_lines_editor_set_ui_cursor_position",
|
|
varray(cursor_position));
|
|
#if 0
|
|
LineEdit *cursor_x = cursor_pos[0];
|
|
LineEdit *cursor_y = cursor_pos[1];
|
|
LineEdit *cursor_z = cursor_pos[2];
|
|
cursor_x->set_text(String::num(cursor_position.x));
|
|
cursor_y->set_text(String::num(cursor_position.y));
|
|
cursor_z->set_text(String::num(cursor_position.z));
|
|
#endif
|
|
}
|
|
void RoadLinesEditor::set_ui_point_position(const Vector3 &point_position)
|
|
{
|
|
EditorEvent::get_singleton()->event.emit(
|
|
"road_lines_editor_set_ui_point_position",
|
|
varray(point_position));
|
|
#if 0
|
|
LineEdit *point_x = point_pos[0];
|
|
LineEdit *point_y = point_pos[1];
|
|
LineEdit *point_z = point_pos[2];
|
|
point_x->set_text(String::num(point_position.x));
|
|
point_y->set_text(String::num(point_position.y));
|
|
point_z->set_text(String::num(point_position.z));
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
Vector3 cursor_position = point_position;
|
|
line_index = index;
|
|
set_cursor_position(cursor_position);
|
|
set_ui_cursor_position(cursor_position);
|
|
set_ui_point_position(point_position);
|
|
}
|
|
|
|
void RoadLinesEditor::line_list_filter_changed(const String &text)
|
|
{
|
|
print_line("lines filter update: " + text);
|
|
filter_text = text.strip_edges();
|
|
update_ui();
|
|
}
|
|
|
|
void RoadLinesEditor::activate()
|
|
{
|
|
assert(!active);
|
|
print_line("activate::update UI");
|
|
update_ui();
|
|
if (!line_im) {
|
|
line_im = memnew(ImmediateGeometry);
|
|
editor->get_viewport()->add_child(line_im);
|
|
}
|
|
Ref<SpatialMaterial> 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->set_material_override(debug_material);
|
|
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");
|
|
if (!new_line_handler)
|
|
new_line_handler = memnew(HandleCreateNewLine(this));
|
|
if (!point_selection_handler)
|
|
point_selection_handler = memnew(HandlePointSelection(this));
|
|
EditorEvent::get_singleton()->event.add_listener(
|
|
this, &RoadLinesEditor::event_handler);
|
|
|
|
active = true;
|
|
}
|
|
|
|
void RoadLinesEditor::deactivate()
|
|
{
|
|
EditorEvent::get_singleton()->event.remove_listener(
|
|
this, &RoadLinesEditor::event_handler);
|
|
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 (new_line_handler) {
|
|
memdelete(new_line_handler);
|
|
new_line_handler = nullptr;
|
|
}
|
|
if (gd_editor_event) {
|
|
memdelete(gd_editor_event);
|
|
gd_editor_event = nullptr;
|
|
}
|
|
if (point_selection_handler) {
|
|
memdelete(point_selection_handler);
|
|
point_selection_handler = nullptr;
|
|
}
|
|
active = false;
|
|
}
|
|
|
|
void RoadLinesEditor::place_generated_objects()
|
|
{
|
|
place_zebras();
|
|
}
|
|
void RoadLinesEditor::rebuild_roads()
|
|
{
|
|
int debug_flags = 0;
|
|
if (debug_road_nodes)
|
|
debug_flags |= (1 << 0);
|
|
if (debug_road_edges)
|
|
debug_flags |= (1 << 1);
|
|
if (debug_road_wedges)
|
|
debug_flags |= (1 << 2);
|
|
print_line("rebuild_roads: debug: " + itos(debug_flags));
|
|
editor->editor_command("rebuild_roads", varray(debug_flags));
|
|
}
|
|
void RoadLinesEditor::remove_road_meshes()
|
|
{
|
|
editor->editor_command("remove_road_meshes", varray());
|
|
}
|
|
void RoadLinesEditor::handle_input()
|
|
{
|
|
if (editor->get_camera_mode() != 3)
|
|
return;
|
|
}
|
|
void RoadLinesEditor::set_update_roads(bool checked)
|
|
{
|
|
update_roads = checked;
|
|
}
|
|
void RoadLinesEditor::set_debug_road_nodes(bool checked)
|
|
{
|
|
debug_road_nodes = checked;
|
|
}
|
|
void RoadLinesEditor::set_debug_road_edges(bool checked)
|
|
{
|
|
debug_road_edges = checked;
|
|
}
|
|
void RoadLinesEditor::set_debug_road_wedges(bool checked)
|
|
{
|
|
debug_road_wedges = checked;
|
|
}
|
|
const String &RoadLinesEditor::get_current_line() const
|
|
{
|
|
return current_line;
|
|
}
|
|
const String &RoadLinesEditor::get_filter_text() const
|
|
{
|
|
return filter_text;
|
|
}
|
|
void RoadLinesEditor::get_matching_lines(List<String> *lines)
|
|
{
|
|
if (!re.is_valid())
|
|
re.instance();
|
|
re->compile(filter_text);
|
|
if (filter_text.length() > 0 && !re->is_valid())
|
|
return;
|
|
List<String> line_keys;
|
|
RoadLinesData *rld = RoadLinesData::get_singleton();
|
|
rld->get_lines_key_list(&line_keys);
|
|
assert(!line_keys.empty());
|
|
lines->clear();
|
|
List<String>::Element *e = line_keys.front();
|
|
while (e) {
|
|
Array matches = re->search_all(e->get());
|
|
if (filter_text.length() > 0 && matches.size() == 0) {
|
|
e = e->next();
|
|
continue;
|
|
}
|
|
lines->push_back(e->get());
|
|
e = e->next();
|
|
}
|
|
assert(!lines->empty());
|
|
}
|
|
void RoadLinesEditor::place_zebras()
|
|
{
|
|
editor->editor_command("remove_buildings_by_prefix", varray("zebra"));
|
|
/*
|
|
func place_zebras():
|
|
var road_nodes = SceneComps.get_component("road_nodes2")
|
|
var next_index = 0
|
|
var positions = []
|
|
var d = buildings.keys().duplicate()
|
|
for k in d:
|
|
if buildings[k].id == "zebra":
|
|
buildings.erase(k)
|
|
for k in buildings.keys():
|
|
if buildings[k].index >= next_index:
|
|
next_index = buildings[k].index + 1
|
|
|
|
for k in range(road_nodes.roads_data.nodes.size()):
|
|
if !road_nodes.roads_data.edges.has(str(k)):
|
|
continue
|
|
var e = road_nodes.roads_data.edges[str(k)]
|
|
if e.neighbors.size() <= 2:
|
|
continue
|
|
var pos = str2var(road_nodes.roads_data.nodes[k])
|
|
for n in e.neighbors:
|
|
var pne = str2var(road_nodes.roads_data.nodes[n])
|
|
if pos.distance_squared_to(pne) < 50 * 50:
|
|
continue
|
|
# var ne = road_nodes.roads_data.edges[str(n)]
|
|
# var pne = str2var(ne)
|
|
# var pt = pos.linear_interpolate(pne, 0.5)
|
|
var dir: Vector3 = (pne - pos).normalized()
|
|
var nml = dir.rotated(Vector3.UP, PI / 2.0)
|
|
var points = []
|
|
var bad = false
|
|
for x in range(2):
|
|
points.push_back(pos + dir * 30.0 + nml * 4.0 * x)
|
|
for x in range(2):
|
|
points.push_back(pos + dir * 30.0 - nml * 4.0 * (x + 1))
|
|
for xp in positions:
|
|
for lp in points:
|
|
if xp.distance_squared_to(lp) < 30 * 30:
|
|
bad = true
|
|
break
|
|
if bad:
|
|
points.clear()
|
|
for pt in points:
|
|
var xform: Transform = Transform(Basis(), pt)
|
|
xform = xform.looking_at(pt + nml * 30, Vector3.UP)
|
|
positions.push_back(xform.origin)
|
|
var key = var2str(xform)
|
|
assert(!buildings.has(key))
|
|
buildings[key] = {
|
|
"id": "zebra",
|
|
"index": next_index,
|
|
"residents": []
|
|
}
|
|
next_index += 1
|
|
for b in spawned_buildings.keys():
|
|
var sp = spawned_buildings[b]
|
|
building_pool.unparent_building(sp[0], sp[1])
|
|
spawned_buildings.clear()
|
|
|
|
# for k in road_nodes.roads_data.edges.keys():
|
|
# if road_nodes.roads_data.edges[k].neighbors.size() <= 2:
|
|
# continue
|
|
# for wk in road_nodes.current_wedges.keys():
|
|
# var wdata = road_nodes.current_wedges[wk]
|
|
# var sz = wdata.size()
|
|
# if sz <= 2:
|
|
# continue
|
|
# for w in wdata:
|
|
# var pos = w[4].linear_interpolate(w[3], 0.5)
|
|
# var xform: Transform = Transform(Basis(), pos)
|
|
# xform = xform.looking_at(w[1], Vector3.UP)
|
|
# var key = var2str(xform)
|
|
# buildings[key] = {
|
|
# "id": "zebra",
|
|
# "index": next_index,
|
|
# "residents": []
|
|
# }
|
|
*/
|
|
}
|
|
void RoadLinesEditor::save_data()
|
|
{
|
|
RoadLinesData::get_singleton()->save_data();
|
|
}
|
|
void RoadLinesEditor::update_current_line_metadata(const String &text)
|
|
{
|
|
RoadLinesData *rld = RoadLinesData::get_singleton();
|
|
assert(rld->has_line(current_line));
|
|
if (!text.begins_with("{"))
|
|
return;
|
|
String err_s;
|
|
int err_line;
|
|
Variant v;
|
|
Error err = JSON::parse(text, v, err_s, err_line);
|
|
if (err != OK) {
|
|
print_line("metadata parse error " + err_s + " at line " +
|
|
itos(err_line));
|
|
return;
|
|
}
|
|
if (v.get_type() != Variant::DICTIONARY) {
|
|
print_line("Invalid metadata type, should be Dictionary");
|
|
return;
|
|
}
|
|
rld->set_line_metadata(current_line, v);
|
|
}
|
|
String RoadLinesEditor::get_current_line_metadata() const
|
|
{
|
|
RoadLinesData *rld = RoadLinesData::get_singleton();
|
|
assert(rld->has_line(current_line));
|
|
return JSON::print(rld->lines(current_line).metadata, "\t", true);
|
|
}
|
|
void RoadLinesEditor::remove_generated_stuff()
|
|
{
|
|
editor->editor_command("remove_generated_stuff", varray());
|
|
}
|
|
template <class T> T *RoadLinesEditor::get_as_node(const String &path)
|
|
{
|
|
Node *node = scene()->get_node(NodePath(path));
|
|
assert(node);
|
|
T *ret = Object::cast_to<T>(node);
|
|
assert(ret);
|
|
return ret;
|
|
}
|
|
|
|
void RoadLinesEditor::event_handler(const String &event,
|
|
const Vector<Variant> &args)
|
|
{
|
|
if (event == "lines_get_current_line" && current_line != "") {
|
|
EditorEvent::get_singleton()->event.emit("lines_select_line",
|
|
varray(current_line));
|
|
}
|
|
if (event == "lines_changed_line") {
|
|
if (!update_roads)
|
|
print_line("road update disabled");
|
|
if (current_line.ends_with("_road"))
|
|
print_line("the line is a road");
|
|
if (current_line.ends_with("_road") && update_roads)
|
|
rebuild_roads();
|
|
} else if (event == "result:get_building_types") {
|
|
/* TODO:: implement */
|
|
#if 0
|
|
} else if (event == "road_lines_setup_cursor_ui") {
|
|
cursor_pos[0] = Object::cast_to<LineEdit>(args[0]);
|
|
cursor_pos[1] = Object::cast_to<LineEdit>(args[1]);
|
|
cursor_pos[2] = Object::cast_to<LineEdit>(args[2]);
|
|
cursor_set = Object::cast_to<Button>(args[3]);
|
|
assert(cursor_set);
|
|
} else if (event == "road_lines_setup_point_ui") {
|
|
point_pos[0] = Object::cast_to<LineEdit>(args[0]);
|
|
point_pos[1] = Object::cast_to<LineEdit>(args[1]);
|
|
point_pos[2] = Object::cast_to<LineEdit>(args[2]);
|
|
point_set = Object::cast_to<Button>(args[3]);
|
|
assert(cursor_set);
|
|
#endif
|
|
} else if (event == "road_lines_editor_set_cursor_position") {
|
|
Vector3 position = args[0];
|
|
set_cursor_position(position);
|
|
} else if (event == "road_lines_editor_set_point_position") {
|
|
Vector3 position = args[0];
|
|
set_point_position(position);
|
|
} else if (event == "road_lines_filter") {
|
|
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();
|
|
LineEdit *le = Object::cast_to<LineEdit>(args[0]);
|
|
if (line_exists(text) || text.length() < 5 ||
|
|
text.find("_") < 0 || text.ends_with("_")) {
|
|
le->add_color_override("font_color",
|
|
Color(1.0f, 0.0f, 0.0f, 1.0f));
|
|
return;
|
|
} else {
|
|
le->remove_color_override("font_color");
|
|
create_new_line_at_cursor(text);
|
|
}
|
|
} else if (event == "road_lines_create_new_line_name:changed") {
|
|
String new_text = args[1];
|
|
String text = new_text.strip_edges();
|
|
LineEdit *le = Object::cast_to<LineEdit>(args[0]);
|
|
if (line_exists(text) || text.length() < 5 ||
|
|
text.find("_") < 0 || text.ends_with("_")) {
|
|
le->add_color_override("font_color",
|
|
Color(1.0f, 0.0f, 0.0f, 1.0f));
|
|
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 ¶m)
|
|
{
|
|
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));
|
|
} |