594 lines
16 KiB
C++
594 lines
16 KiB
C++
#undef NDEBUG
|
|
#include <cassert>
|
|
#include <core/object.h>
|
|
#include <core/engine.h>
|
|
#include <core/os/input.h>
|
|
#include <core/input_map.h>
|
|
#include <scene/gui/control.h>
|
|
#include <scene/gui/box_container.h>
|
|
#include <scene/gui/button.h>
|
|
#include <scene/gui/option_button.h>
|
|
#include <scene/main/viewport.h>
|
|
#include <scene/3d/camera.h>
|
|
#include <scene/scene_string_names.h>
|
|
#include "road_lines_editor.h"
|
|
#include "world_editor.h"
|
|
#include "editor_event.h"
|
|
#include "buildings_editor.h"
|
|
|
|
class HandleCommandButton : public Object {
|
|
GDCLASS(HandleCommandButton, Object)
|
|
WorldEditor *editor;
|
|
String button_path;
|
|
String command;
|
|
Array command_args;
|
|
Button *get_button()
|
|
{
|
|
Button *button = Object::cast_to<Button>(
|
|
editor->get_node(NodePath(button_path)));
|
|
assert(button);
|
|
return button;
|
|
}
|
|
void button_handler()
|
|
{
|
|
editor->editor_command(command, command_args);
|
|
}
|
|
|
|
public:
|
|
HandleCommandButton(WorldEditor *editor, const String &button_path,
|
|
const String &command,
|
|
const Array &command_args = Array())
|
|
: Object()
|
|
, editor(editor)
|
|
, button_path(button_path)
|
|
, command(command)
|
|
, command_args(command_args)
|
|
{
|
|
if (!get_button()->is_connected("pressed", this,
|
|
"button_handler"))
|
|
get_button()->connect("pressed", this,
|
|
"button_handler");
|
|
}
|
|
virtual ~HandleCommandButton()
|
|
{
|
|
if (get_button()->is_connected("pressed", this,
|
|
"button_handler"))
|
|
get_button()->disconnect("pressed", this,
|
|
"button_handler");
|
|
}
|
|
static void _bind_methods()
|
|
{
|
|
ClassDB::bind_method(D_METHOD("button_handler"),
|
|
&HandleCommandButton::button_handler);
|
|
}
|
|
};
|
|
|
|
WorldEditor::WorldEditor()
|
|
: Spatial()
|
|
, stream_world(nullptr)
|
|
, editor_menu(nullptr)
|
|
, current_mode(-1)
|
|
, current_camera_mode(-1)
|
|
, motion(Vector2())
|
|
, old_mouse_pos(Vector2(-1, -1))
|
|
, dragging(false)
|
|
, drag_delay(0.2f)
|
|
, road_lines_editor(memnew(RoadLinesEditor(this)))
|
|
, buildings_editor(memnew(BuildingsEditor(this)))
|
|
{
|
|
if (!InputMap::get_singleton()->has_action("left"))
|
|
InputMap::get_singleton()->add_action("left");
|
|
if (!InputMap::get_singleton()->has_action("right"))
|
|
InputMap::get_singleton()->add_action("right");
|
|
if (!InputMap::get_singleton()->has_action("backward"))
|
|
InputMap::get_singleton()->add_action("backward");
|
|
if (!InputMap::get_singleton()->has_action("forward"))
|
|
InputMap::get_singleton()->add_action("forward");
|
|
if (!InputMap::get_singleton()->has_action("action"))
|
|
InputMap::get_singleton()->add_action("action");
|
|
if (!InputMap::get_singleton()->has_action("action2"))
|
|
InputMap::get_singleton()->add_action("action2");
|
|
if (!InputMap::get_singleton()->has_action("editor_cam1"))
|
|
InputMap::get_singleton()->add_action("editor_cam1");
|
|
if (!InputMap::get_singleton()->has_action("editor_cam2"))
|
|
InputMap::get_singleton()->add_action("editor_cam2");
|
|
if (!InputMap::get_singleton()->has_action("editor_cam3"))
|
|
InputMap::get_singleton()->add_action("editor_cam3");
|
|
if (!InputMap::get_singleton()->has_action("mouse1"))
|
|
InputMap::get_singleton()->add_action("mouse1");
|
|
EditorEvent::get_singleton()->event.add_listener(
|
|
this, &WorldEditor::event_signal_handler);
|
|
print_line("constructed");
|
|
}
|
|
|
|
WorldEditor::~WorldEditor()
|
|
{
|
|
EditorEvent::get_singleton()->event.remove_listener(
|
|
this, &WorldEditor::event_signal_handler);
|
|
if (road_lines_editor) {
|
|
memdelete(road_lines_editor);
|
|
road_lines_editor = nullptr;
|
|
}
|
|
if (buildings_editor) {
|
|
memdelete(buildings_editor);
|
|
buildings_editor = nullptr;
|
|
}
|
|
print_line("destructed");
|
|
}
|
|
|
|
void WorldEditor::set_camera_mode(int mode)
|
|
{
|
|
print_line("set_camera_mode: " + itos(mode));
|
|
bool update_transform = false;
|
|
Camera *cam = get_viewport()->get_camera();
|
|
if (!cam)
|
|
return;
|
|
Transform cam_xform = cam->get_global_transform();
|
|
if (current_camera_mode == -1) {
|
|
cam_xform.origin.y = 80.0f;
|
|
update_transform = true;
|
|
}
|
|
current_camera_mode = mode;
|
|
switch (mode) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
Input::get_singleton()->set_mouse_mode(
|
|
Input::MOUSE_MODE_CAPTURED);
|
|
cam_xform.basis =
|
|
Basis().rotated(Vector3(1, 0, 0), -Math_PI / 2.0);
|
|
update_transform = true;
|
|
break;
|
|
case 2:
|
|
Input::get_singleton()->set_mouse_mode(
|
|
Input::MOUSE_MODE_CAPTURED);
|
|
cam_xform.basis =
|
|
Basis().rotated(Vector3(1, 0, 0), -Math_PI / 4.0);
|
|
update_transform = true;
|
|
break;
|
|
case 3:
|
|
Input::get_singleton()->set_mouse_mode(
|
|
Input::MOUSE_MODE_VISIBLE);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (update_transform)
|
|
cam->set_global_transform(cam_xform);
|
|
}
|
|
|
|
int WorldEditor::get_camera_mode() const
|
|
{
|
|
return current_camera_mode;
|
|
}
|
|
|
|
void WorldEditor::disable_all()
|
|
{
|
|
}
|
|
|
|
void WorldEditor::mode_buildings()
|
|
{
|
|
disable_all();
|
|
print_line("BUILDINGS");
|
|
}
|
|
|
|
void WorldEditor::mode_navigation()
|
|
{
|
|
disable_all();
|
|
print_line("NAVIGATION");
|
|
}
|
|
|
|
void WorldEditor::mode_poi()
|
|
{
|
|
disable_all();
|
|
print_line("POI");
|
|
}
|
|
|
|
void WorldEditor::mode_road_lines()
|
|
{
|
|
disable_all();
|
|
print_line("ROAD_LINES");
|
|
}
|
|
|
|
void WorldEditor::mode_npc()
|
|
{
|
|
disable_all();
|
|
print_line("NPC");
|
|
}
|
|
struct StringHasher {
|
|
std::size_t operator()(const String &s) const
|
|
{
|
|
return (std::size_t)s.hash64();
|
|
}
|
|
};
|
|
static std::unordered_map<String, int, StringHasher> modes = {
|
|
{ "select_buildings", WorldEditor::MODE_BUILDINGS },
|
|
{ "select_navigation", WorldEditor::MODE_NAVIGATION },
|
|
{ "select_poi", WorldEditor::MODE_POI },
|
|
{ "select_road_lines", WorldEditor::MODE_ROAD_LINES },
|
|
{ "select_npc", WorldEditor::MODE_NPC }
|
|
};
|
|
|
|
static std::unordered_map<int, String> vmode = { { 2, "%v_buildings" },
|
|
{ 3, "%v_navigation" },
|
|
{ 5, "%v_poi" },
|
|
{ 6, "%v_road_lines" },
|
|
{ 7, "%v_npc" } };
|
|
|
|
void WorldEditor::tools_button(const String &button)
|
|
{
|
|
Array change;
|
|
change.resize(2);
|
|
print_line("tools_button: " + button);
|
|
if (modes.find(button) == modes.end())
|
|
goto end;
|
|
assert(modes[button] >= 0);
|
|
if (current_mode == modes[button])
|
|
goto end;
|
|
print_line("mode change: " + itos(current_mode) + " " +
|
|
itos(modes[button]));
|
|
change[0] = current_mode;
|
|
change[1] = modes[button];
|
|
EditorEvent::get_singleton()->event.emit("mode_change_pre", change);
|
|
switch (current_mode) {
|
|
case MODE_ROAD_LINES:
|
|
road_lines_editor->exit();
|
|
break;
|
|
case MODE_BUILDINGS:
|
|
buildings_editor->exit();
|
|
break;
|
|
}
|
|
switch (modes[button]) {
|
|
case MODE_BUILDINGS:
|
|
mode_buildings();
|
|
break;
|
|
case MODE_NAVIGATION:
|
|
mode_navigation();
|
|
break;
|
|
case MODE_POI:
|
|
mode_poi();
|
|
break;
|
|
case MODE_ROAD_LINES:
|
|
mode_road_lines();
|
|
break;
|
|
case MODE_NPC:
|
|
mode_npc();
|
|
break;
|
|
}
|
|
EditorEvent::get_singleton()->event.emit("mode_change_post", change);
|
|
assert(modes[button] >= 0);
|
|
current_mode = modes[button];
|
|
assert(current_mode >= 0);
|
|
end:;
|
|
}
|
|
|
|
void WorldEditor::editor_command(const String &command, const Array &args)
|
|
{
|
|
print_line("running command: " + command);
|
|
if (command.begins_with("select_") &&
|
|
modes.find(command) != modes.end()) {
|
|
tools_button(command);
|
|
return;
|
|
} else if (command == "get_closest_building") {
|
|
if (stream_world) {
|
|
stream_world->run_command(command, args);
|
|
}
|
|
} else if (command == "update_building_transform") {
|
|
if (stream_world) {
|
|
stream_world->run_command(command, args);
|
|
}
|
|
} else if (command == "checkpoint") {
|
|
if (stream_world) {
|
|
stream_world->run_command(command, args);
|
|
}
|
|
} else if (command == "undo") {
|
|
if (stream_world) {
|
|
stream_world->run_command(command, args);
|
|
}
|
|
} else if (command == "buildings_save") {
|
|
if (stream_world) {
|
|
stream_world->run_command(command, args);
|
|
}
|
|
} else if (command == "get_building_types") {
|
|
if (stream_world) {
|
|
stream_world->run_command(command, args);
|
|
}
|
|
} else if (command == "change_building_type") {
|
|
if (stream_world) {
|
|
stream_world->run_command(command, args);
|
|
}
|
|
} else if (command == "remove_generated_stuff") {
|
|
if (stream_world)
|
|
stream_world->run_command(command, args);
|
|
} else if (command == "rebuild_roads") {
|
|
if (stream_world)
|
|
stream_world->run_command(command, args);
|
|
} else if (command == "remove_road_meshes") {
|
|
if (stream_world)
|
|
stream_world->run_command(command, args);
|
|
} else if (command == "remove_building") {
|
|
if (stream_world)
|
|
stream_world->run_command(command, args);
|
|
} else if (command == "remove_buildings_by_prefix") {
|
|
if (stream_world)
|
|
stream_world->run_command(command, args);
|
|
} else if (command == "create_building") {
|
|
if (stream_world)
|
|
stream_world->run_command(command, args);
|
|
} else {
|
|
if (road_lines_editor)
|
|
road_lines_editor->editor_command(command, args);
|
|
if (buildings_editor)
|
|
buildings_editor->editor_command(command, args);
|
|
}
|
|
}
|
|
|
|
int WorldEditor::get_current_mode() const
|
|
{
|
|
return current_mode;
|
|
}
|
|
|
|
void WorldEditor::event_signal_handler(const String &event, const Array &args)
|
|
{
|
|
if (event == "mode_change_pre") {
|
|
int prev_mode = args[0];
|
|
if (prev_mode == -1) {
|
|
auto pt = vmode.begin();
|
|
while (pt != vmode.end()) {
|
|
auto data = *pt;
|
|
Control *p = Object::cast_to<Control>(
|
|
get_node(NodePath(data.second)));
|
|
assert(p);
|
|
p->hide();
|
|
pt++;
|
|
}
|
|
} else {
|
|
Control *p = Object::cast_to<Control>(
|
|
get_node(NodePath(vmode[prev_mode])));
|
|
assert(p);
|
|
p->hide();
|
|
}
|
|
} else if (event == "mode_change_post") {
|
|
int mode_next = args[1];
|
|
Control *p = Object::cast_to<Control>(
|
|
get_node(NodePath(vmode[mode_next])));
|
|
assert(p);
|
|
p->show();
|
|
switch (mode_next) {
|
|
case 2:
|
|
editor_command("get_building_types", Array());
|
|
break;
|
|
case 6:
|
|
editor_command("get_lines_list", Array());
|
|
break;
|
|
}
|
|
} else if (event == "editor_camera_moved") {
|
|
Camera *cam = get_viewport()->get_camera();
|
|
Spatial *area = Object::cast_to<Spatial>(
|
|
get_node(NodePath("%selection_area")));
|
|
assert(cam);
|
|
assert(area);
|
|
Transform area_transform = area->get_global_transform();
|
|
const Transform &cam_transform = cam->get_global_transform();
|
|
area_transform.origin.x = cam_transform.origin.x;
|
|
area_transform.origin.z = cam_transform.origin.z;
|
|
area->set_global_transform(area_transform);
|
|
}
|
|
emit_signal("editor_event", event, args);
|
|
}
|
|
|
|
StreamWorld *WorldEditor::get_stream_world()
|
|
{
|
|
return stream_world;
|
|
}
|
|
|
|
void WorldEditor::world_command_result(const String &what, const Array &data)
|
|
{
|
|
print_line("what: " + what);
|
|
EditorEvent::get_singleton()->event.emit("result:" + what, data);
|
|
}
|
|
|
|
static std::vector<String> tool_buttons = {
|
|
"%select_buildings", "%select_navigation", "%select_poi",
|
|
"%select_road_lines", "%select_npc", "%buildings_save",
|
|
};
|
|
std::vector<HandleCommandButton *> tool_handlers;
|
|
void WorldEditor::_notification(int which)
|
|
{
|
|
switch (which) {
|
|
case NOTIFICATION_ENTER_TREE: {
|
|
if (Engine::get_singleton()->is_editor_hint()) {
|
|
return;
|
|
}
|
|
Node *base = get_parent();
|
|
int count = base->get_child_count();
|
|
int i;
|
|
for (i = 0; i < (int)tool_buttons.size(); i++) {
|
|
String bname =
|
|
get_node(NodePath(tool_buttons[i]))->get_name();
|
|
tool_handlers.push_back(memnew(HandleCommandButton(
|
|
this, tool_buttons[i], bname)));
|
|
}
|
|
for (i = 0; i < count; i++) {
|
|
Node *node = base->get_child(i);
|
|
StreamWorld *sw = Object::cast_to<StreamWorld>(node);
|
|
if (sw && !stream_world &&
|
|
!Engine::get_singleton()->is_editor_hint()) {
|
|
stream_world = sw;
|
|
sw->connect(SceneStringNames::get_singleton()
|
|
->tree_exiting,
|
|
this, "world_exited");
|
|
sw->connect("command_result", this,
|
|
"world_command_result");
|
|
}
|
|
}
|
|
auto pt = vmode.begin();
|
|
while (pt != vmode.end()) {
|
|
auto data = *pt;
|
|
Control *p = Object::cast_to<Control>(
|
|
get_node(NodePath(data.second)));
|
|
assert(p);
|
|
p->hide();
|
|
pt++;
|
|
}
|
|
Spatial *building_cursor = Object::cast_to<Spatial>(
|
|
get_node(NodePath("%building_cursor")));
|
|
assert(building_cursor);
|
|
building_cursor->hide();
|
|
Spatial *line_cursor = Object::cast_to<Spatial>(
|
|
get_node(NodePath("%line_cursor")));
|
|
assert(line_cursor);
|
|
line_cursor->hide();
|
|
|
|
set_process_unhandled_input(true);
|
|
set_physics_process(true);
|
|
Spatial *cursor = Object::cast_to<Spatial>(
|
|
get_node(NodePath("%building_cursor")));
|
|
assert(cursor);
|
|
cursor->hide();
|
|
Spatial *rot_cursor = Object::cast_to<Spatial>(
|
|
get_node(NodePath("%building_rot_cursor")));
|
|
assert(rot_cursor);
|
|
rot_cursor->hide();
|
|
} break;
|
|
case NOTIFICATION_EXIT_TREE:
|
|
set_physics_process(false);
|
|
set_process_unhandled_input(false);
|
|
break;
|
|
case NOTIFICATION_PHYSICS_PROCESS: {
|
|
if (Engine::get_singleton()->is_editor_hint()) {
|
|
return;
|
|
}
|
|
if (!is_inside_tree())
|
|
return;
|
|
Camera *cam = get_viewport()->get_camera();
|
|
if (!cam)
|
|
return;
|
|
float delta = get_physics_process_delta_time();
|
|
if (dragging) {
|
|
/* Stop drag mode is mouse1 no longer pressed */
|
|
if (!Input::get_singleton()->is_action_pressed(
|
|
"mouse1")) {
|
|
dragging = false;
|
|
drag_delay = 0.2f;
|
|
Array args;
|
|
Vector2 position =
|
|
get_viewport()->get_mouse_position();
|
|
args.push_back(position);
|
|
EditorEvent::get_singleton()->event.emit(
|
|
"mouse_drag_off", args);
|
|
}
|
|
}
|
|
#if 0
|
|
if (current_camera_mode == 1) {
|
|
Vector2 mouse_pos =
|
|
get_viewport()->get_mouse_position();
|
|
if (old_mouse_pos.x < 0) {
|
|
old_mouse_pos = mouse_pos;
|
|
motion = Vector2();
|
|
} else {
|
|
motion = mouse_pos - old_mouse_pos;
|
|
old_mouse_pos = mouse_pos;
|
|
}
|
|
}
|
|
#endif
|
|
if (!dragging && drag_delay >= 0.0f)
|
|
drag_delay -= delta;
|
|
switch (current_mode) {
|
|
case MODE_BUILDINGS:
|
|
buildings_editor->update(delta);
|
|
break;
|
|
case MODE_ROAD_LINES:
|
|
road_lines_editor->update(delta);
|
|
break;
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
void WorldEditor::_unhandled_input(const Ref<InputEvent> &event)
|
|
{
|
|
Ref<InputEventMouseMotion> mm = event;
|
|
Ref<InputEventMouseButton> mb = event;
|
|
Ref<InputEventKey> kb = event;
|
|
bool input_handled = false;
|
|
if (event->is_action_pressed("editor_cam1")) {
|
|
set_camera_mode(1);
|
|
input_handled = true;
|
|
}
|
|
if (event->is_action_pressed("editor_cam2")) {
|
|
set_camera_mode(2);
|
|
input_handled = true;
|
|
}
|
|
if (event->is_action_pressed("editor_cam3")) {
|
|
set_camera_mode(3);
|
|
input_handled = true;
|
|
}
|
|
if (event->is_action_pressed("mouse1")) {
|
|
// if (Input::get_singleton()->is_action_just_pressed("mouse1")) {
|
|
Array args;
|
|
Vector2 position = get_viewport()->get_mouse_position();
|
|
args.push_back(position);
|
|
EditorEvent::get_singleton()->event.emit("mouse_press", args);
|
|
// }
|
|
input_handled = true;
|
|
}
|
|
if (mm.is_valid()) {
|
|
motion += mm->get_relative();
|
|
if (Input::get_singleton()->is_action_pressed("mouse1")) {
|
|
if (dragging) {
|
|
Array args;
|
|
Vector2 position =
|
|
get_viewport()->get_mouse_position();
|
|
args.push_back(position);
|
|
EditorEvent::get_singleton()->event.emit(
|
|
"mouse_drag", args);
|
|
} else {
|
|
if (drag_delay < 0.0f && !dragging) {
|
|
dragging = true;
|
|
Array args;
|
|
Vector2 position =
|
|
get_viewport()
|
|
->get_mouse_position();
|
|
args.push_back(position);
|
|
EditorEvent::get_singleton()->event.emit(
|
|
"mouse_drag_on", args);
|
|
}
|
|
}
|
|
input_handled = true;
|
|
}
|
|
}
|
|
if (input_handled)
|
|
get_viewport()->set_input_as_handled();
|
|
if (current_mode == MODE_ROAD_LINES)
|
|
road_lines_editor->handle_input();
|
|
}
|
|
|
|
void WorldEditor::world_exited()
|
|
{
|
|
stream_world = nullptr;
|
|
}
|
|
|
|
void WorldEditor::_bind_methods()
|
|
{
|
|
ClassDB::bind_method(D_METHOD("editor_command", "command", "args"),
|
|
&WorldEditor::editor_command);
|
|
ClassDB::bind_method(D_METHOD("get_current_mode"),
|
|
&WorldEditor::get_current_mode);
|
|
ClassDB::bind_method(D_METHOD("get_stream_world"),
|
|
&WorldEditor::get_stream_world);
|
|
ClassDB::bind_method(D_METHOD("world_exited"),
|
|
&WorldEditor::world_exited);
|
|
ClassDB::bind_method(D_METHOD("world_command_result", "what", "data"),
|
|
&WorldEditor::world_command_result);
|
|
ClassDB::bind_method(D_METHOD("set_camera_mode", "mode"),
|
|
&WorldEditor::set_camera_mode);
|
|
ClassDB::bind_method(D_METHOD("get_camera_mode"),
|
|
&WorldEditor::get_camera_mode);
|
|
ClassDB::bind_method(D_METHOD("_unhandled_input", "event"),
|
|
&WorldEditor::_unhandled_input);
|
|
ADD_SIGNAL(MethodInfo("editor_event",
|
|
PropertyInfo(Variant::STRING, "event_name"),
|
|
PropertyInfo(Variant::ARRAY, "args")));
|
|
}
|