Files
streaming_world/src/modules/stream/world_editor.cpp

476 lines
13 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"
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)))
, selected_building(-1)
{
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");
}
WorldEditor::~WorldEditor()
{
memdelete(road_lines_editor);
}
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;
}
int WorldEditor::get_selected_building() const
{
return selected_building;
}
Transform WorldEditor::get_selected_building_xform() const
{
return selected_building_xform;
}
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");
}
enum {
MODE_BUILDINGS = 2,
MODE_NAVIGATION = 3,
MODE_POI = 5,
MODE_ROAD_LINES = 6,
MODE_NPC = 7,
};
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", MODE_BUILDINGS },
{ "select_navigation", MODE_NAVIGATION },
{ "select_poi", MODE_POI },
{ "select_road_lines", MODE_ROAD_LINES },
{ "select_npc", MODE_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;
if (current_mode == modes[button])
goto end;
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();
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;
}
emit_signal("editor_event", "mode_change_post", change);
current_mode = modes[button];
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 == "select_building") {
select_building(args[0], args[1], args[2]);
} else if (command == "remove_generated_stuff") {
stream_world->run_command(command, args);
} else if (road_lines_editor) {
road_lines_editor->editor_command(command, args);
}
}
void WorldEditor::select_building(const Transform &xform, int id,
const String &mid)
{
int i;
selected_building_xform = xform;
selected_building = id;
print_line("selected id: " + itos(id));
OptionButton *building_type = Object::cast_to<OptionButton>(
get_node(NodePath("%building_type")));
assert(building_type);
for (i = 0; i < building_type->get_item_count(); i++) {
const String &item = building_type->get_item_text(i);
if (item == mid) {
building_type->select(i);
break;
}
/* TODO: set building cursor position to xform */
Button *delete_button = Object::cast_to<Button>(
get_node(NodePath("%buildings_delete_building")));
assert(delete_button);
delete_button->show();
/* FIXME */
if (!delete_button->is_connected("pressed", this,
"delete_building_handler"))
delete_button->connect("pressed", this,
"delete_building_handler");
}
}
int WorldEditor::get_current_mode() const
{
return current_mode;
}
StreamWorld *WorldEditor::get_stream_world()
{
return stream_world;
}
void WorldEditor::world_command_result(const String &what, const Array &data)
{
print_line("what: " + what);
emit_signal("editor_event", "result:" + what, data);
}
void WorldEditor::_notification(int which)
{
switch (which) {
case NOTIFICATION_ENTER_TREE: {
Node *base = get_parent();
int count = base->get_child_count();
int i;
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");
}
}
set_process_unhandled_input(true);
set_physics_process(true);
} break;
case NOTIFICATION_EXIT_TREE:
set_physics_process(false);
set_process_unhandled_input(false);
break;
case NOTIFICATION_PHYSICS_PROCESS: {
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);
emit_signal("editor_event", "mouse_drag_off",
args);
}
}
Transform cam_xform = cam->get_global_transform();
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;
}
bool moved = false;
float h = cam_xform.origin.y;
float xx = Input::get_singleton()->get_axis("left",
"right");
float zz = Input::get_singleton()->get_axis("backward",
"forward");
float hh = Input::get_singleton()->get_axis("action2",
"action");
if (Math::abs(zz) > 0.1f) {
cam_xform.origin.z -= Math::abs(h) * zz * delta;
moved = true;
}
if (Math::abs(xx) > 0.1f) {
cam_xform.origin.x += Math::abs(h) * xx * delta;
moved = true;
}
if (Math::abs(hh) > 0.1f && Math::abs(xx) < 0.1f &&
Math::abs(zz) < 0.1f) {
cam_xform.origin.y += 10.0f * hh * delta;
moved = true;
}
if (moved) {
cam->set_global_transform(cam_xform);
Array move_args;
move_args.push_back(cam_xform);
emit_signal("editor_event",
"editor_camera_moved", move_args);
}
}
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;
}
}
void WorldEditor::_unhandled_input(const Ref<InputEvent> &event)
{
if (Input::get_singleton()->is_action_just_pressed("editor_cam1"))
set_camera_mode(1);
if (Input::get_singleton()->is_action_just_pressed("editor_cam2"))
set_camera_mode(2);
if (Input::get_singleton()->is_action_just_pressed("editor_cam3"))
set_camera_mode(3);
if (current_camera_mode != 1) {
Ref<InputEventMouseMotion> motionevt = event;
if (motionevt.is_valid())
motion = motionevt->get_relative();
}
if (Input::get_singleton()->is_action_just_pressed("mouse1")) {
Array args;
Vector2 position = get_viewport()->get_mouse_position();
args.push_back(position);
emit_signal("editor_event", "mouse_press", args);
} else if (Input::get_singleton()->is_action_pressed("mouse1")) {
if (dragging) {
Array args;
Vector2 position = get_viewport()->get_mouse_position();
args.push_back(position);
emit_signal("editor_event", "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);
emit_signal("editor_event", "mouse_drag_on",
args);
}
}
}
}
void WorldEditor::world_exited()
{
stream_world = nullptr;
}
void WorldEditor::delete_building_handler()
{
Array args, args2;
args.push_back(selected_building);
stream_world->run_command("remove_building", args);
args2.push_back(selected_building_xform);
stream_world->run_command("get_closest_building", args2);
}
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("select_building", "xform", "id", "mid"),
&WorldEditor::select_building);
ClassDB::bind_method(D_METHOD("get_selected_building"),
&WorldEditor::get_selected_building);
ClassDB::bind_method(D_METHOD("get_selected_building_xform"),
&WorldEditor::get_selected_building_xform);
ClassDB::bind_method(D_METHOD("_unhandled_input", "event"),
&WorldEditor::_unhandled_input);
ClassDB::bind_method(D_METHOD("delete_building_handler"),
&WorldEditor::delete_building_handler);
ADD_SIGNAL(MethodInfo("editor_event",
PropertyInfo(Variant::STRING, "event_name"),
PropertyInfo(Variant::ARRAY, "args")));
}