422 lines
12 KiB
C++
422 lines
12 KiB
C++
#undef NDEBUG
|
|
#include <cassert>
|
|
#include <core/variant.h>
|
|
#include <core/engine.h>
|
|
#include <core/os/time.h>
|
|
#include <scene/gui/option_button.h>
|
|
#include <scene/gui/line_edit.h>
|
|
#include <scene/main/viewport.h>
|
|
#include <scene/3d/camera.h>
|
|
#include <modules/voxel/terrain/voxel_lod_terrain.h>
|
|
#include <modules/imgmapper/voxel_generator_imgmapper.h>
|
|
#include "from_string.h"
|
|
#include "editor_event.h"
|
|
#include "world_editor.h"
|
|
#include "buildings_data.h"
|
|
#include "base_data.h"
|
|
#include "ui/main_tabs.h"
|
|
#include "buildings_editor.h"
|
|
|
|
BuildingsEditor::BuildingsEditor(WorldEditor *editor)
|
|
: editor(editor)
|
|
, selected_building("")
|
|
, active(false)
|
|
{
|
|
}
|
|
|
|
void BuildingsEditor::exit()
|
|
{
|
|
if (active)
|
|
deactivate();
|
|
}
|
|
|
|
void BuildingsEditor::activate()
|
|
{
|
|
assert(scene());
|
|
assert(!active);
|
|
ConfigFile config;
|
|
Error result = config.load("res://config/stream.conf");
|
|
ERR_FAIL_COND_MSG(result != OK, "Failed to load config");
|
|
print_line("BuildingsEditor ACTIVE");
|
|
int i;
|
|
/* TODO: make separate function for this */
|
|
EditorEvent::get_singleton()->event.add_listener(
|
|
this, &BuildingsEditor::event_handler);
|
|
active = true;
|
|
}
|
|
|
|
void BuildingsEditor::deactivate()
|
|
{
|
|
assert(active);
|
|
EditorEvent::get_singleton()->event.remove_listener(
|
|
this, &BuildingsEditor::event_handler);
|
|
print_line("BuildingsEditor DEACTIVE");
|
|
active = false;
|
|
}
|
|
|
|
void BuildingsEditor::event_handler(const String &event,
|
|
const Vector<Variant> &args)
|
|
{
|
|
assert(scene());
|
|
assert(active);
|
|
print_line("E::" + event);
|
|
if (event == "mouse_drag")
|
|
mouse_drag(args[0]);
|
|
else if (event == "mouse_press")
|
|
mouse_press(args[0]);
|
|
else if (event == "button:create_building")
|
|
handle_create_building();
|
|
else if (event == "button:delete_building")
|
|
delete_building_handler();
|
|
else if (event == "result:get_closest_building") {
|
|
select_building(args[0], args[3], args[4]);
|
|
if (get_buildings_editor_mode() == 2 /* rotate */) {
|
|
/* move rot cursor too */
|
|
get_as_node<Spatial>("%building_rot_cursor")
|
|
->set_global_transform(selected_building_xform);
|
|
}
|
|
} else if (event == "result:get_building_types") {
|
|
/* TODO: replace with direct data access */
|
|
} else if (event == "run_update_building_transform") {
|
|
selected_building_xform = args[1];
|
|
editor->editor_command("update_building_transform", args);
|
|
if (get_buildings_editor_mode() == 2 /* rotate */) {
|
|
/* move rot cursor too */
|
|
get_as_node<Spatial>("%building_rot_cursor")
|
|
->set_global_transform(selected_building_xform);
|
|
}
|
|
}
|
|
print_line("buildings::" + event);
|
|
}
|
|
|
|
void BuildingsEditor::handle_create_building()
|
|
{
|
|
assert(scene());
|
|
print_line("create_building");
|
|
int bmode = get_buildings_editor_mode();
|
|
assert(bmode == 3);
|
|
flecs::world ecs = BaseData::get_singleton()->get();
|
|
flecs::entity e = ecs.lookup("world_editor");
|
|
assert(e.is_valid());
|
|
const String &item =
|
|
e.get<WorldEditor::components::buildings_editor_mode>()
|
|
->current_type;
|
|
Transform xform;
|
|
xform.origin = get_cursor_position();
|
|
Dictionary building_data;
|
|
/* FIXME: calculate AABBs as in previous editor */
|
|
String building_xform = to_string<Transform>(xform);
|
|
building_data["id"] = item;
|
|
building_data["key"] = String::num_uint64(
|
|
String(item + building_xform +
|
|
itos(Time::get_singleton()->get_ticks_usec()))
|
|
.hash64(),
|
|
16);
|
|
building_data["xform"] = building_xform;
|
|
editor->editor_command("create_building", varray(building_data));
|
|
}
|
|
|
|
template <class T>
|
|
inline void BuildingsEditor::mode_visibility(int mode, const String &path)
|
|
{
|
|
assert(scene());
|
|
T *obj = get_as_node<T>(path);
|
|
if (get_buildings_editor_mode() == mode) {
|
|
if (!obj->is_visible())
|
|
obj->show();
|
|
} else {
|
|
if (obj->is_visible())
|
|
obj->hide();
|
|
}
|
|
}
|
|
|
|
void BuildingsEditor::update(float delta)
|
|
{
|
|
assert(scene());
|
|
#ifdef _VERBOSE_DEBUG
|
|
if (active)
|
|
print_line("update: " + String::num(delta) + " " +
|
|
itos(editor->get_current_mode()) + " " +
|
|
itos(editor->get_camera_mode()));
|
|
#endif
|
|
if (!active)
|
|
activate();
|
|
int mode = get_buildings_editor_mode();
|
|
if (mode == 2) {
|
|
Spatial *rot_cursor =
|
|
get_as_node<Spatial>("%building_rot_cursor");
|
|
if (!rot_cursor->is_visible())
|
|
rot_cursor->set_global_transform(
|
|
selected_building_xform);
|
|
}
|
|
}
|
|
|
|
void BuildingsEditor::mouse_drag(const Vector2 &position)
|
|
{
|
|
assert(scene());
|
|
if (editor->get_current_mode() != WorldEditor::MODE_BUILDINGS) {
|
|
print_verbose("bad editor mode: " +
|
|
itos(editor->get_current_mode()));
|
|
return;
|
|
}
|
|
print_line("in mouse_drag");
|
|
Camera *camera = editor->get_viewport()->get_camera();
|
|
Vector3 start = camera->project_ray_origin(position);
|
|
Vector3 normal = camera->project_ray_normal(position);
|
|
Vector3 end = start + normal * camera->get_zfar();
|
|
PhysicsDirectSpaceState *space_state =
|
|
editor->get_world()->get_direct_space_state();
|
|
PhysicsDirectSpaceState::RayResult result;
|
|
Set<RID> excludes;
|
|
bool r = space_state->intersect_ray(start, end, result, excludes,
|
|
1 << 15, false, true, false);
|
|
if (r && result.rid != RID()) {
|
|
Vector3 proj = result.position;
|
|
int mode = get_buildings_editor_mode();
|
|
switch (mode) {
|
|
case 1: {
|
|
/* move */
|
|
print_verbose("move: " + (proj.operator String()));
|
|
Vector3 newpos(Math::stepify(proj.x, 2.0f), proj.y,
|
|
Math::stepify(proj.z, 2.0f));
|
|
Ref<VoxelGeneratorImgMapper> gen =
|
|
get_as_node<VoxelLodTerrain>("%VoxelLodTerrain")
|
|
->get_generator();
|
|
float h = gen->get_height_full(newpos);
|
|
newpos.y = h;
|
|
editor->editor_command(
|
|
"update_building_transform",
|
|
varray(selected_building,
|
|
Transform(selected_building_xform.basis,
|
|
newpos)));
|
|
set_cursor_position(newpos);
|
|
selected_building_xform = Transform(
|
|
selected_building_xform.basis, newpos);
|
|
emit("buildings_building_moved", varray());
|
|
} break;
|
|
case 2: {
|
|
/* rotate */
|
|
print_verbose("rotate: " + (proj.operator String()));
|
|
Vector3 m = proj;
|
|
m.y = selected_building_xform.origin.y;
|
|
Transform xform = selected_building_xform.looking_at(
|
|
m, Vector3(0.0f, 1.0f, 0.0f));
|
|
editor->editor_command("update_building_transform",
|
|
varray(selected_building,
|
|
xform));
|
|
selected_building_xform = xform;
|
|
get_as_node<Spatial>("%building_rot_cursor")
|
|
->set_global_transform(xform);
|
|
emit("buildings_building_rotated", varray());
|
|
} break;
|
|
case 3: { /* create */
|
|
print_verbose("create: " + (proj.operator String()));
|
|
Vector3 newpos(Math::stepify(proj.x, 2.0f), proj.y,
|
|
Math::stepify(proj.z, 2.0f));
|
|
Ref<VoxelGeneratorImgMapper> gen =
|
|
get_as_node<VoxelLodTerrain>("%VoxelLodTerrain")
|
|
->get_generator();
|
|
float h = gen->get_height_full(newpos);
|
|
newpos.y = h;
|
|
set_cursor_position(newpos);
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BuildingsEditor::mouse_press(const Vector2 &position)
|
|
{
|
|
assert(scene());
|
|
if (editor->get_current_mode() != WorldEditor::MODE_BUILDINGS) {
|
|
print_verbose("bad editor mode: " +
|
|
itos(editor->get_current_mode()));
|
|
return;
|
|
}
|
|
print_line("in mouse_press");
|
|
Camera *camera = editor->get_viewport()->get_camera();
|
|
Vector3 start = camera->project_ray_origin(position);
|
|
Vector3 normal = camera->project_ray_normal(position);
|
|
Vector3 end = start + normal * camera->get_zfar();
|
|
PhysicsDirectSpaceState *space_state =
|
|
editor->get_world()->get_direct_space_state();
|
|
PhysicsDirectSpaceState::RayResult result;
|
|
Set<RID> excludes;
|
|
bool r = space_state->intersect_ray(start, end, result, excludes,
|
|
1 << 15, false, true, false);
|
|
if (r && result.rid != RID()) {
|
|
Vector3 proj = result.position;
|
|
int mode = get_buildings_editor_mode();
|
|
switch (mode) {
|
|
case 0: {
|
|
/* select */
|
|
Transform xform(Basis(), proj);
|
|
editor->editor_command("get_closest_building",
|
|
varray(xform));
|
|
} break;
|
|
case 1: {
|
|
/* move */
|
|
editor->editor_command("checkpoint", varray());
|
|
} break;
|
|
case 2: {
|
|
/* TODO: deduplicate */
|
|
/* rotate */
|
|
editor->editor_command("checkpoint", varray());
|
|
print_verbose("rotate: " + (proj.operator String()));
|
|
Vector3 m = proj;
|
|
m.y = selected_building_xform.origin.y;
|
|
Transform xform = selected_building_xform.looking_at(
|
|
m, Vector3(0.0f, 1.0f, 0.0f));
|
|
editor->editor_command("update_building_transform",
|
|
varray(selected_building,
|
|
xform));
|
|
selected_building_xform = xform;
|
|
get_as_node<Spatial>("%building_rot_cursor")
|
|
->set_global_transform(xform);
|
|
emit("buildings_building_rotated", varray());
|
|
} break;
|
|
/* TODO: deduplicate */
|
|
case 3: { /* create */
|
|
print_verbose("create: " + (proj.operator String()));
|
|
Vector3 newpos(Math::stepify(proj.x, 2.0f), proj.y,
|
|
Math::stepify(proj.z, 2.0f));
|
|
Ref<VoxelGeneratorImgMapper> gen =
|
|
get_as_node<VoxelLodTerrain>("%VoxelLodTerrain")
|
|
->get_generator();
|
|
float h = gen->get_height_full(newpos);
|
|
newpos.y = h;
|
|
set_cursor_position(newpos);
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BuildingsEditor::set_cursor_position(const Vector3 &position)
|
|
{
|
|
assert(scene());
|
|
Spatial *cursor = get_as_node<Spatial>("%building_cursor");
|
|
if (!cursor->is_visible())
|
|
cursor->show();
|
|
Transform xform = cursor->get_global_transform();
|
|
if (!xform.origin.is_equal_approx(position)) {
|
|
xform.origin = position;
|
|
cursor->set_global_transform(xform);
|
|
emit("buildings_building_cursor_moved", varray(position));
|
|
}
|
|
}
|
|
|
|
Vector3 BuildingsEditor::get_cursor_position() const
|
|
{
|
|
assert(scene());
|
|
const Spatial *cursor = get_as_node<Spatial>("%building_cursor");
|
|
Transform xform = cursor->get_global_transform();
|
|
return xform.origin;
|
|
}
|
|
|
|
int BuildingsEditor::get_buildings_editor_mode() const
|
|
{
|
|
flecs::world ecs = BaseData::get_singleton()->get();
|
|
flecs::entity e = ecs.lookup("world_editor");
|
|
assert(e.is_valid());
|
|
int mode =
|
|
e.get<WorldEditor::components::buildings_editor_mode>()->mode;
|
|
return mode;
|
|
}
|
|
|
|
BuildingsEditor::~BuildingsEditor()
|
|
{
|
|
if (active)
|
|
deactivate();
|
|
}
|
|
|
|
String BuildingsEditor::get_selected_building() const
|
|
{
|
|
return selected_building;
|
|
}
|
|
|
|
Transform BuildingsEditor::get_selected_building_xform() const
|
|
{
|
|
return selected_building_xform;
|
|
}
|
|
|
|
void BuildingsEditor::delete_building_handler()
|
|
{
|
|
editor->editor_command("remove_building", varray(selected_building));
|
|
editor->editor_command("get_closest_building",
|
|
varray(selected_building_xform));
|
|
}
|
|
|
|
void BuildingsEditor::change_building_type(const String &type_name)
|
|
{
|
|
int bmode = get_buildings_editor_mode();
|
|
assert(bmode == 0 || bmode == 1 ||
|
|
bmode == 2); /* select, move, rotate */
|
|
editor->editor_command("change_building_type",
|
|
varray(selected_building, type_name));
|
|
}
|
|
|
|
void BuildingsEditor::emit(const String &event_string,
|
|
const Vector<Variant> &event_args)
|
|
{
|
|
EditorEvent::get_singleton()->event.emit(event_string, event_args);
|
|
}
|
|
|
|
void BuildingsEditor::remove_buildings_by_prefix(const String &prefix)
|
|
{
|
|
editor->editor_command("remove_buildings_by_prefix", varray(prefix));
|
|
}
|
|
|
|
void BuildingsEditor::select_building(const Transform &xform, const String &key,
|
|
const String &mid)
|
|
{
|
|
int i;
|
|
assert(scene());
|
|
selected_building_xform = xform;
|
|
selected_building = key;
|
|
print_line("selected key: " + key);
|
|
emit("set_selected_building_type", varray(mid));
|
|
set_cursor_position(xform.origin);
|
|
}
|
|
|
|
void BuildingsEditor::editor_command(const String &command,
|
|
const Vector<Variant> &args)
|
|
{
|
|
if (command == "select_building") {
|
|
select_building(args[0], args[1], args[2]);
|
|
}
|
|
}
|
|
|
|
template <class T> T *BuildingsEditor::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;
|
|
}
|
|
|
|
template <class T>
|
|
const T *BuildingsEditor::get_as_node(const String &path) const
|
|
{
|
|
assert(scene());
|
|
const Node *node = scene()->get_node(NodePath(path));
|
|
assert(node);
|
|
const T *ret = Object::cast_to<T>(node);
|
|
assert(ret);
|
|
return ret;
|
|
}
|
|
|
|
Node *BuildingsEditor::scene()
|
|
{
|
|
assert(editor);
|
|
assert(editor->get_tree());
|
|
return editor->get_tree()->get_current_scene();
|
|
}
|
|
|
|
const Node *BuildingsEditor::scene() const
|
|
{
|
|
assert(editor);
|
|
assert(editor->get_tree());
|
|
const Node *ret = editor->get_tree()->get_current_scene();
|
|
return ret;
|
|
}
|