Almost separated the buildings editor
This commit is contained in:
450
src/modules/stream/buildings_editor.cpp
Normal file
450
src/modules/stream/buildings_editor.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
#undef NDEBUG
|
||||
#include <cassert>
|
||||
#include <core/variant.h>
|
||||
#include <scene/gui/option_button.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 "world_editor.h"
|
||||
#include "buildings_editor.h"
|
||||
|
||||
class HandleChangeBuildingType : public Object {
|
||||
GDCLASS(HandleChangeBuildingType, Object)
|
||||
BuildingsEditor *editor;
|
||||
|
||||
void change_building_type(int index)
|
||||
{
|
||||
OptionButton *building_type =
|
||||
editor->get_as_node<OptionButton>("%building_type");
|
||||
const String &item = building_type->get_item_text(index);
|
||||
int bmode = editor->get_buildings_editor_mode();
|
||||
if (bmode == 0 || bmode == 1 ||
|
||||
bmode == 2) /* select, move, rotate */
|
||||
editor->change_building_type(item);
|
||||
/* do not change building types in create mode (3) */
|
||||
}
|
||||
|
||||
public:
|
||||
HandleChangeBuildingType(BuildingsEditor *editor)
|
||||
: Object()
|
||||
, editor(editor)
|
||||
{
|
||||
OptionButton *building_type =
|
||||
editor->get_as_node<OptionButton>("%building_type");
|
||||
building_type->connect("item_selected", this,
|
||||
"change_building_type");
|
||||
}
|
||||
virtual ~HandleChangeBuildingType()
|
||||
{
|
||||
OptionButton *building_type =
|
||||
editor->get_as_node<OptionButton>("%building_type");
|
||||
if (building_type->is_connected("item_selected", this,
|
||||
"change_building_type"))
|
||||
building_type->disconnect("item_selected", this,
|
||||
"change_building_type");
|
||||
}
|
||||
|
||||
protected:
|
||||
static void _bind_methods()
|
||||
{
|
||||
ClassDB::bind_method(
|
||||
D_METHOD("change_building_type", "index"),
|
||||
&HandleChangeBuildingType::change_building_type);
|
||||
}
|
||||
};
|
||||
|
||||
class HandleDeleteButton : public Object {
|
||||
GDCLASS(HandleDeleteButton, Object)
|
||||
BuildingsEditor *editor;
|
||||
|
||||
public:
|
||||
HandleDeleteButton(BuildingsEditor *editor)
|
||||
: Object()
|
||||
, editor(editor)
|
||||
{
|
||||
Button *delete_button = editor->get_as_node<Button>(
|
||||
"%buildings_delete_building");
|
||||
assert(delete_button);
|
||||
/* FIXME */
|
||||
if (!delete_button->is_connected("pressed", this,
|
||||
"delete_building_handler"))
|
||||
delete_button->connect("pressed", this,
|
||||
"delete_building_handler");
|
||||
}
|
||||
~HandleDeleteButton()
|
||||
{
|
||||
Button *delete_button = editor->get_as_node<Button>(
|
||||
"%buildings_delete_building");
|
||||
if (delete_button->is_connected("pressed", this,
|
||||
"delete_building_handler"))
|
||||
delete_button->disconnect("pressed", this,
|
||||
"delete_building_handler");
|
||||
}
|
||||
void delete_building_handler()
|
||||
{
|
||||
editor->delete_building_handler();
|
||||
}
|
||||
static void _bind_methods()
|
||||
{
|
||||
ClassDB::bind_method(
|
||||
D_METHOD("delete_building_handler"),
|
||||
&HandleDeleteButton::delete_building_handler);
|
||||
}
|
||||
};
|
||||
static HandleChangeBuildingType *change_building_type_handler = nullptr;
|
||||
static HandleDeleteButton *delete_button_handler = nullptr;
|
||||
|
||||
BuildingsEditor::BuildingsEditor(WorldEditor *editor)
|
||||
: editor(editor)
|
||||
, selected_building(-1)
|
||||
{
|
||||
}
|
||||
|
||||
void BuildingsEditor::exit()
|
||||
{
|
||||
if (active)
|
||||
deactivate();
|
||||
}
|
||||
|
||||
void BuildingsEditor::activate()
|
||||
{
|
||||
assert(!active);
|
||||
editor->event.add_listener(this, &BuildingsEditor::event_handler);
|
||||
if (!change_building_type_handler)
|
||||
change_building_type_handler =
|
||||
memnew(HandleChangeBuildingType(this));
|
||||
if (!delete_button_handler)
|
||||
delete_button_handler = memnew(HandleDeleteButton(this));
|
||||
assert(change_building_type_handler && delete_button_handler);
|
||||
active = true;
|
||||
}
|
||||
|
||||
void BuildingsEditor::deactivate()
|
||||
{
|
||||
assert(active);
|
||||
if (change_building_type_handler) {
|
||||
memdelete(change_building_type_handler);
|
||||
change_building_type_handler = nullptr;
|
||||
}
|
||||
if (delete_button_handler) {
|
||||
memdelete(delete_button_handler);
|
||||
delete_button_handler = nullptr;
|
||||
}
|
||||
editor->event.remove_listener(this, &BuildingsEditor::event_handler);
|
||||
active = false;
|
||||
}
|
||||
|
||||
void BuildingsEditor::event_handler(const String &event, const Array &args)
|
||||
{
|
||||
if (event == "mouse_drag")
|
||||
mouse_drag(args[0]);
|
||||
else if (event == "mouse_press")
|
||||
mouse_press(args[0]);
|
||||
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);
|
||||
}
|
||||
}
|
||||
print_line("buildings::" + event);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void BuildingsEditor::mode_visibility(int mode, const String &path)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
mode_visibility<Button>(0, "%buildings_delete_building");
|
||||
mode_visibility<Spatial>(2, "%building_rot_cursor");
|
||||
mode_visibility<Button>(3, "%buildings_create_building");
|
||||
}
|
||||
|
||||
void BuildingsEditor::mouse_drag(const Vector2 &position)
|
||||
{
|
||||
if (editor->get_current_mode() != WorldEditor::MODE_BUILDINGS)
|
||||
return;
|
||||
if (editor->get_camera_mode() != 3)
|
||||
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;
|
||||
Array args;
|
||||
args.push_back(selected_building);
|
||||
args.push_back(Transform(selected_building_xform.basis,
|
||||
newpos));
|
||||
editor->editor_command("update_building_transform",
|
||||
args);
|
||||
Spatial *cursor =
|
||||
get_as_node<Spatial>("%building_cursor");
|
||||
Transform orig_pos = cursor->get_global_transform();
|
||||
orig_pos.origin = newpos;
|
||||
cursor->set_global_transform(orig_pos);
|
||||
selected_building_xform = Transform(
|
||||
selected_building_xform.basis, newpos);
|
||||
} 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));
|
||||
Array args;
|
||||
args.push_back(selected_building);
|
||||
args.push_back(xform);
|
||||
editor->editor_command("update_building_transform",
|
||||
args);
|
||||
selected_building_xform = xform;
|
||||
get_as_node<Spatial>("%building_rot_cursor")
|
||||
->set_global_transform(xform);
|
||||
} 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;
|
||||
Transform xform(Basis(), newpos);
|
||||
get_as_node<Spatial>("%building_cursor")
|
||||
->set_global_transform(xform);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BuildingsEditor::mouse_press(const Vector2 &position)
|
||||
{
|
||||
if (editor->get_current_mode() != WorldEditor::MODE_BUILDINGS)
|
||||
return;
|
||||
if (editor->get_camera_mode() != 3)
|
||||
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 */
|
||||
Array args;
|
||||
Transform xform(Basis(), proj);
|
||||
args.push_back(xform);
|
||||
editor->editor_command("get_closest_building", args);
|
||||
} break;
|
||||
case 1: {
|
||||
/* move */
|
||||
editor->editor_command("checkpoint", Array());
|
||||
} break;
|
||||
case 2: {
|
||||
/* TODO: deduplicate */
|
||||
/* rotate */
|
||||
editor->editor_command("checkpoint", Array());
|
||||
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));
|
||||
Array args;
|
||||
args.push_back(selected_building);
|
||||
args.push_back(xform);
|
||||
editor->editor_command("update_building_transform",
|
||||
args);
|
||||
selected_building_xform = xform;
|
||||
get_as_node<Spatial>("%building_rot_cursor")
|
||||
->set_global_transform(xform);
|
||||
} 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;
|
||||
Transform xform(Basis(), newpos);
|
||||
get_as_node<Spatial>("%building_cursor")
|
||||
->set_global_transform(xform);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int BuildingsEditor::get_buildings_editor_mode() const
|
||||
{
|
||||
const OptionButton *buildings_edit_mode =
|
||||
get_as_node<OptionButton>("%buildings_edit_mode");
|
||||
int selected = buildings_edit_mode->get_selected();
|
||||
return selected;
|
||||
}
|
||||
|
||||
BuildingsEditor::~BuildingsEditor()
|
||||
{
|
||||
if (active)
|
||||
deactivate();
|
||||
}
|
||||
|
||||
int BuildingsEditor::get_selected_building() const
|
||||
{
|
||||
return selected_building;
|
||||
}
|
||||
|
||||
Transform BuildingsEditor::get_selected_building_xform() const
|
||||
{
|
||||
return selected_building_xform;
|
||||
}
|
||||
|
||||
void BuildingsEditor::delete_building_handler()
|
||||
{
|
||||
Array args, args2;
|
||||
args.push_back(selected_building);
|
||||
editor->editor_command("remove_building", args);
|
||||
args2.push_back(selected_building_xform);
|
||||
editor->editor_command("get_closest_building", args2);
|
||||
}
|
||||
|
||||
void BuildingsEditor::change_building_type(const String &type_name)
|
||||
{
|
||||
Array args;
|
||||
int bmode = get_buildings_editor_mode();
|
||||
assert(bmode == 0 || bmode == 1 ||
|
||||
bmode == 2); /* select, move, rotate */
|
||||
args.push_back(selected_building);
|
||||
args.push_back(type_name);
|
||||
editor->editor_command("change_building_type", args);
|
||||
}
|
||||
|
||||
void BuildingsEditor::remove_buildings_by_prefix(const String &prefix)
|
||||
{
|
||||
Array args;
|
||||
args.push_back(prefix);
|
||||
editor->editor_command("remove_buildings_by_prefix", args);
|
||||
}
|
||||
|
||||
void BuildingsEditor::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>(
|
||||
editor->get_node(NodePath("%building_type")));
|
||||
assert(building_type);
|
||||
bool ok = false;
|
||||
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);
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
Button *delete_button =
|
||||
get_as_node<Button>("%buildings_delete_building");
|
||||
delete_button->show();
|
||||
}
|
||||
Spatial *cursor = get_as_node<Spatial>("%building_cursor");
|
||||
/* Show cursor and change cursor position */
|
||||
if (!cursor->is_visible())
|
||||
cursor->show();
|
||||
Transform orig_xform = cursor->get_global_transform();
|
||||
orig_xform.origin = xform.origin;
|
||||
cursor->set_global_transform(orig_xform);
|
||||
}
|
||||
|
||||
void BuildingsEditor::editor_command(const String &command, const Array &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
|
||||
{
|
||||
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()
|
||||
{
|
||||
return editor->get_tree()->get_current_scene();
|
||||
}
|
||||
|
||||
const Node *BuildingsEditor::scene() const
|
||||
{
|
||||
const Node *ret = editor->get_tree()->get_current_scene();
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user