Almost separated the buildings editor

This commit is contained in:
2024-09-15 23:57:40 +03:00
parent e505c6df0d
commit d5ad5bac8c
11 changed files with 817 additions and 221 deletions

View File

@@ -10,9 +10,9 @@ onready var vmode = {
7: $"%v_npc",
}
func change_building_type(index):
var item = $"%building_type".get_item_text(index)
$WorldEditor.editor_command("change_building_type", [selected_building, item])
#func change_building_type(index):
# var item = $"%building_type".get_item_text(index)
# $WorldEditor.editor_command("change_building_type", [selected_building, item])
func _ready():
for b in [
@@ -27,9 +27,9 @@ func _ready():
$WorldEditor.connect("editor_event", self, "editor_event")
for k in vmode.keys():
vmode[k].hide()
$building_cursor.hide()
$"%building_cursor".hide()
$"%line_cursor".hide()
$"%building_type".connect("item_selected", self, "change_building_type")
# $"%building_type".connect("item_selected", self, "change_building_type")
func editor_event(evname: String, args: Array):
print(evname, args)
@@ -49,8 +49,9 @@ func editor_event(evname: String, args: Array):
elif mode_next == 6:
$WorldEditor.editor_command("get_lines_list", [])
elif evname == "result:get_closest_building":
print(evname, args)
select_building(args[0], args[3], args[4])
pass
# print(evname, args)
# select_building(args[0], args[3], args[4])
elif evname == "result:get_building_types":
print(evname, args)
var btypes = args[0]
@@ -63,9 +64,11 @@ func editor_event(evname: String, args: Array):
# elif evname == "edit_update_building":
# check_edit_building()
elif evname == "mouse_press":
mouse_press(args[0])
pass
# mouse_press(args[0])
elif evname == "mouse_drag":
mouse_drag(args[0])
pass
# mouse_drag(args[0])
elif evname == "mouse_drag_on":
pass
elif evname == "mouse_drag_off":
@@ -74,87 +77,87 @@ func editor_event(evname: String, args: Array):
pass
else:
breakpoint
func mouse_drag(position):
if $WorldEditor.get_current_mode() != 2:
return
if $WorldEditor.get_camera_mode() != 3:
return
# breakpoint
var camera = get_viewport().get_camera()
var start = camera.project_ray_origin(position)
var normal = camera.project_ray_normal(position)
var end = start + normal * camera.get_zfar()
var space_state = get_world().direct_space_state
var result = space_state.intersect_ray(start, end, [], 1 << 15, false, true)
if result.has("collider"):
var proj = result.position
var mode = $"%buildings_edit_mode".selected
if mode == 1:
# move
print(proj)
var newpos = proj
newpos.x = stepify(newpos.x, 2.0)
newpos.z = stepify(newpos.z, 2.0)
var gen: VoxelGeneratorImgMapper = $VoxelLodTerrain.generator
var hpos = gen.get_height_full(newpos)
newpos.y = hpos
$WorldEditor.editor_command("update_building_transform", [selected_building, Transform(selected_building_xform.basis, newpos)])
selected_building_xform = Transform(selected_building_xform.basis, newpos)
$building_cursor.global_transform.origin = newpos
elif mode == 2:
# rotate
var m = proj
m.y = selected_building_xform.origin.y
var xform = selected_building_xform.looking_at(m, Vector3.UP)
$WorldEditor.editor_command("update_building_transform", [selected_building, xform])
$building_rot_cursor.global_transform = xform
selected_building_xform = xform
func mouse_press(position):
if $WorldEditor.get_current_mode() != 2:
return
if $WorldEditor.get_camera_mode() != 3:
return
# breakpoint
# dragging = true
var camera = get_viewport().get_camera()
var start = camera.project_ray_origin(position)
var normal = camera.project_ray_normal(position)
var end = start + normal * camera.get_zfar()
var space_state = get_world().direct_space_state
var result = space_state.intersect_ray(start, end, [], 1 << 15, false, true)
if result.has("collider"):
var proj = result.position
drag_start = proj
var mode = $"%buildings_edit_mode".selected
if mode == 0:
print("get closest building")
$WorldEditor.editor_command("get_closest_building", [Transform(Basis(), proj)])
elif mode == 1:
$WorldEditor.editor_command("checkpoint", [])
elif mode == 2:
$WorldEditor.editor_command("checkpoint", [])
var m = proj
m.y = selected_building_xform.origin.y
var xform = selected_building_xform.looking_at(m, Vector3.UP)
$WorldEditor.editor_command("update_building_transform", [selected_building, xform])
$building_rot_cursor.global_transform = xform
selected_building_xform = xform
#func mouse_drag(position):
# if $WorldEditor.get_current_mode() != 2:
# return
# if $WorldEditor.get_camera_mode() != 3:
# return
## breakpoint
# var camera = get_viewport().get_camera()
# var start = camera.project_ray_origin(position)
# var normal = camera.project_ray_normal(position)
# var end = start + normal * camera.get_zfar()
# var space_state = get_world().direct_space_state
# var result = space_state.intersect_ray(start, end, [], 1 << 15, false, true)
# if result.has("collider"):
# var proj = result.position
# var mode = $"%buildings_edit_mode".selected
# if mode == 1:
# # move
# print(proj)
# var newpos = proj
# newpos.x = stepify(newpos.x, 2.0)
# newpos.z = stepify(newpos.z, 2.0)
# var gen: VoxelGeneratorImgMapper = $"%VoxelLodTerrain".generator
# var hpos = gen.get_height_full(newpos)
# newpos.y = hpos
# $WorldEditor.editor_command("update_building_transform", [selected_building, Transform(selected_building_xform.basis, newpos)])
# selected_building_xform = Transform(selected_building_xform.basis, newpos)
# $"%building_cursor".global_transform.origin = newpos
# elif mode == 2:
# # rotate
# var m = proj
# m.y = selected_building_xform.origin.y
# var xform = selected_building_xform.looking_at(m, Vector3.UP)
# $WorldEditor.editor_command("update_building_transform", [selected_building, xform])
# $"%building_rot_cursor".global_transform = xform
# selected_building_xform = xform
#func mouse_press(position):
# if $WorldEditor.get_current_mode() != 2:
# return
# if $WorldEditor.get_camera_mode() != 3:
# return
## breakpoint
## dragging = true
# var camera = get_viewport().get_camera()
# var start = camera.project_ray_origin(position)
# var normal = camera.project_ray_normal(position)
# var end = start + normal * camera.get_zfar()
# var space_state = get_world().direct_space_state
# var result = space_state.intersect_ray(start, end, [], 1 << 15, false, true)
# if result.has("collider"):
# var proj = result.position
# drag_start = proj
# var mode = $"%buildings_edit_mode".selected
# if mode == 0:
# print("get closest building")
# $WorldEditor.editor_command("get_closest_building", [Transform(Basis(), proj)])
# elif mode == 1:
# $WorldEditor.editor_command("checkpoint", [])
# elif mode == 2:
# $WorldEditor.editor_command("checkpoint", [])
# var m = proj
# m.y = selected_building_xform.origin.y
# var xform = selected_building_xform.looking_at(m, Vector3.UP)
# $WorldEditor.editor_command("update_building_transform", [selected_building, xform])
# $"%building_rot_cursor".global_transform = xform
# selected_building_xform = xform
var selected_building
#var selected_building
var selected_building_xform
func select_building(xform, id, mid):
selected_building = id
selected_building_xform = xform
print("selected id: ", id)
$WorldEditor.select_building(xform, id, mid)
#func select_building(xform, id, mid):
# selected_building = id
# selected_building_xform = xform
# print("selected id: ", id)
# $WorldEditor.select_building(xform, id, mid)
# for h in range($"%building_type".get_item_count()):
# var item = $"%building_type".get_item_text(h)
# if item == mid:
# $"%building_type".select(h)
# break
if !$building_cursor.visible:
$building_cursor.show()
$building_cursor.global_transform.origin = xform.origin
# if !$"%building_cursor".visible:
# $"%building_cursor".show()
# $"%building_cursor".global_transform.origin = xform.origin
func _process(delta):
# if Input.is_action_just_pressed("editor_cam1"):
# setup_cam1()
@@ -163,13 +166,13 @@ func _process(delta):
# if Input.is_action_just_pressed("editor_cam3"):
# setup_cam3()
var mode = $"%buildings_edit_mode".selected
if mode == 2:
$building_rot_cursor.global_transform = selected_building_xform
if !$building_rot_cursor.visible:
$building_rot_cursor.show()
else:
if $building_rot_cursor.visible:
$building_rot_cursor.hide()
# if mode == 2:
# $"%building_rot_cursor".global_transform = selected_building_xform
# if !$"%building_rot_cursor".visible:
# $"%building_rot_cursor".show()
# else:
# if $"%building_rot_cursor".visible:
# $"%building_rot_cursor".hide()
# if dragging:
# if !Input.is_action_pressed("mouse1"):
# dragging = false

View File

@@ -116,7 +116,7 @@ text = "NPC Mode"
unique_name_in_owner = true
margin_top = 154.0
margin_right = 232.0
margin_bottom = 298.0
margin_bottom = 322.0
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/v_buildings"]
margin_right = 232.0
@@ -134,7 +134,7 @@ margin_top = 26.0
margin_right = 232.0
margin_bottom = 46.0
text = "Select"
items = [ "Select", null, false, 0, null, "Move", null, false, 1, null, "Rotate", null, false, 2, null ]
items = [ "Select", null, false, 0, null, "Move", null, false, 1, null, "Rotate", null, false, 2, null, "Create", null, false, 3, null ]
selected = 0
[node name="Label2" type="Label" parent="VBoxContainer/v_buildings"]
@@ -162,18 +162,25 @@ margin_right = 232.0
margin_bottom = 120.0
text = "Delete building"
[node name="buildings_save" type="Button" parent="VBoxContainer/v_buildings"]
[node name="buildings_create_building" type="Button" parent="VBoxContainer/v_buildings"]
unique_name_in_owner = true
margin_top = 124.0
margin_right = 232.0
margin_bottom = 144.0
text = "Create building"
[node name="buildings_save" type="Button" parent="VBoxContainer/v_buildings"]
unique_name_in_owner = true
margin_top = 148.0
margin_right = 232.0
margin_bottom = 168.0
text = "Save Buildings"
[node name="v_navigation" type="VBoxContainer" parent="VBoxContainer"]
unique_name_in_owner = true
margin_top = 302.0
margin_top = 326.0
margin_right = 232.0
margin_bottom = 324.0
margin_bottom = 348.0
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/v_navigation"]
margin_right = 232.0
@@ -187,9 +194,9 @@ text = "Navigation mode"
[node name="v_poi" type="VBoxContainer" parent="VBoxContainer"]
unique_name_in_owner = true
margin_top = 328.0
margin_top = 352.0
margin_right = 232.0
margin_bottom = 350.0
margin_bottom = 374.0
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/v_poi"]
margin_right = 232.0
@@ -203,9 +210,9 @@ text = "POI mode"
[node name="v_road_lines" type="VBoxContainer" parent="VBoxContainer"]
unique_name_in_owner = true
margin_top = 354.0
margin_top = 378.0
margin_right = 232.0
margin_bottom = 962.0
margin_bottom = 986.0
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/v_road_lines"]
margin_right = 232.0
@@ -462,9 +469,9 @@ text = "Cancel"
[node name="v_npc" type="VBoxContainer" parent="VBoxContainer"]
unique_name_in_owner = true
margin_top = 966.0
margin_top = 990.0
margin_right = 232.0
margin_bottom = 988.0
margin_bottom = 1012.0
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/v_npc"]
margin_right = 232.0
@@ -479,6 +486,7 @@ text = "NPC mode"
[node name="StreamWorld" type="StreamWorld" parent="."]
[node name="VoxelLodTerrain" type="VoxelLodTerrain" parent="."]
unique_name_in_owner = true
generator = SubResource( 5 )
mesher = SubResource( 6 )
voxel_bounds = AABB( -5.36871e+08, -2048, -5.36871e+08, 1.07374e+09, 4096, 1.07374e+09 )
@@ -505,6 +513,8 @@ transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.1, 0 )
shape = SubResource( 9 )
[node name="building_cursor" type="MeshInstance" parent="."]
unique_name_in_owner = true
visible = false
mesh = SubResource( 10 )
material/0 = SubResource( 11 )
@@ -518,6 +528,8 @@ transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2, 0 )
shape = SubResource( 12 )
[node name="building_rot_cursor" type="Spatial" parent="."]
unique_name_in_owner = true
visible = false
[node name="building_rot_cursor" type="MeshInstance" parent="building_rot_cursor"]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -60 )

View 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;
}

View File

@@ -0,0 +1,35 @@
#ifndef BUILDINGS_EDITOR_H
#define BUILDINGS_EDITOR_H
#include <core/math/transform.h>
class WorldEditor;
class BuildingsEditor {
WorldEditor *editor;
int selected_building;
Transform selected_building_xform;
bool active;
void activate();
void deactivate();
void event_handler(const String &event, const Array &args);
template <class T> void mode_visibility(int mode, const String &path);
public:
BuildingsEditor(WorldEditor *editor);
void exit();
void update(float delta);
void mouse_drag(const Vector2 &position);
void mouse_press(const Vector2 &position);
int get_buildings_editor_mode() const;
virtual ~BuildingsEditor();
int get_selected_building() const;
Transform get_selected_building_xform() const;
void select_building(const Transform &xform, int id, const String &mid);
void remove_buildings_by_prefix(const String &prefix);
void editor_command(const String &command, const Array &args);
void delete_building_handler();
void change_building_type(const String &type_name);
template <class T> T *get_as_node(const String &path);
template <class T> const T *get_as_node(const String &path) const;
Node *scene();
const Node *scene() const;
};
#endif

View File

@@ -8,8 +8,13 @@
#include <core/os/dir_access.h>
#include <core/os/time.h>
#include <core/math/geometry.h>
#include <scene/3d/immediate_geometry.h>
#include <scene/main/viewport.h>
#include "from_string.h"
#include "road_lines_data.h"
static ImmediateGeometry *debug_im;
static Ref<Material> debug_material;
RoadLinesData::RoadLinesData()
{
int i;
@@ -66,6 +71,14 @@ RoadLinesData *RoadLinesData::get_singleton()
return singleton;
}
RoadLinesData::~RoadLinesData()
{
if (debug_im) {
memdelete(debug_im);
debug_im = nullptr;
}
}
void RoadLinesData::cleanup()
{
memdelete(singleton);
@@ -211,54 +224,72 @@ void RoadLinesData::index_lines(
e = e->next();
}
}
static inline int get_segment_index(const String &road, int pos)
{
RoadLinesData *rld = RoadLinesData::get_singleton();
int idx = rld->lines[road].indices[pos];
return idx;
}
void RoadLinesData::create_segments(const String &road,
std::vector<int> &segments)
{
int i;
RoadLinesData *rld = RoadLinesData::get_singleton();
for (i = 0; i < (int)rld->lines[road].indices.size() - 1; i++) {
int idx1 = rld->lines[road].indices[i];
int idx2 = rld->lines[road].indices[i + 1];
segments.push_back(idx1);
segments.push_back(idx2);
segments.push_back(i);
segments.push_back(i + 1);
}
}
/* add close points on each line to the line */
void RoadLinesData::insert_close_points(std::vector<Vector3> &road_lines_nodes)
void RoadLinesData::insert_close_points(std::vector<Vector3> &road_lines_nodes,
float distance_squared)
{
int i;
List<String> keys;
RoadLinesData *rld = RoadLinesData::get_singleton();
rld->get_road_lines_key_list(&keys);
get_road_lines_key_list(&keys);
List<String>::Element *e = keys.front();
for (i = 0; i < (int)road_lines_nodes.size(); i++) {
int idx3 = i;
Vector3 p3 = road_lines_nodes[idx3];
/* Checking each road point against
all line segments */
while (e) {
int j;
std::vector<int> segments;
String rkey = e->get();
create_segments(rkey, segments);
for (j = 0; j < (int)segments.size(); j += 3) {
int idx1 = segments[j];
int idx2 = segments[j + 1];
int idx = segments[j + 2];
for (j = 0; j < (int)segments.size(); j += 2) {
/* indices in road_lines_nodes */
int idx1 = get_segment_index(rkey, segments[j]);
int idx2 = get_segment_index(rkey,
segments[j + 1]);
/* insertion point in line indices
array to split segment and add point */
int idx = segments[j + 1];
/* Skip segment point */
if (idx3 == idx1 || idx3 == idx2)
continue;
Vector3 p1 = road_lines_nodes[idx1];
Vector3 p2 = road_lines_nodes[idx2];
Vector3 p3 = road_lines_nodes[idx3];
std::vector<Vector3> seg = { p1, p2 };
Vector3 closest =
Geometry::get_closest_point_to_segment(
p3, seg.data());
if (p3.distance_squared_to(closest) < 160) {
road_lines_nodes[idx3] = closest;
rld->lines[rkey].indices.insert(
rld->lines[rkey].indices.begin() +
/* should be no duplicate points
in road_lines_nodes */
if (closest.is_equal_approx(p1))
continue;
if (closest.is_equal_approx(p2))
continue;
if (p3.distance_squared_to(closest) <
distance_squared) {
/* split segment and replace road
point with a point on segment */
lines[rkey].indices.insert(
lines[rkey].indices.begin() +
idx,
idx3);
road_lines_nodes[idx3] = closest;
}
}
e = e->next();
@@ -423,13 +454,42 @@ void RoadLinesData::dump_road_lines(const std::vector<Vector3> &road_lines_nodes
}
}
static inline ImmediateGeometry *get_debug_node()
{
debug_im = memnew(ImmediateGeometry);
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;
debug_im->set_material_override(debug_material);
SceneTree::get_singleton()
->get_current_scene()
->get_viewport()
->add_child(debug_im);
return debug_im;
}
void RoadLinesData::process_lines(
std::unordered_map<uint32_t, std::vector<Vector3> >
&road_lines_nodes_hash,
std::vector<Vector3> &road_lines_nodes)
{
int i;
get_debug_node();
debug_im->clear();
index_lines(road_lines_nodes_hash, road_lines_nodes);
insert_close_points(road_lines_nodes);
debug_im->begin(Mesh::PRIMITIVE_LINES);
for (i = 0; i < (int)road_lines_nodes.size(); i++) {
debug_im->set_color(Color(0.1f, 0.6f, 0.6f, 1.0f));
debug_im->add_vertex(road_lines_nodes[i]);
debug_im->set_color(Color(0.1f, 0.6f, 0.6f, 1.0f));
debug_im->add_vertex(road_lines_nodes[i] +
Vector3(0.0f, 200.0f, 0.0f));
}
debug_im->end();
insert_close_points(road_lines_nodes, 160.0f);
update_road_lines_nodes(road_lines_nodes);
dump_road_lines(road_lines_nodes);
}

View File

@@ -22,6 +22,7 @@ public:
};
HashMap<String, struct road_line> lines;
static RoadLinesData *get_singleton();
virtual ~RoadLinesData();
static void cleanup();
String get_road_lines_path();
void get_road_lines_key_list(List<String> *keys);
@@ -36,8 +37,8 @@ private:
std::vector<Vector3> &road_lines_nodes);
void create_segments(const String &road, std::vector<int> &segments);
void insert_close_points(std::vector<Vector3> &road_lines_nodes);
void insert_close_points(std::vector<Vector3> &road_lines_nodes,
float distance_squared);
void update_road_lines_nodes(std::vector<Vector3> &road_lines_nodes);

View File

@@ -23,6 +23,7 @@
#include "road_lines_editor.h"
static ImmediateGeometry *line_im = nullptr;
// static ImmediateGeometry *debug_im = nullptr;
static Ref<Material> debug_material;
#define __evhandler(vname, mtype) \
@@ -946,7 +947,9 @@ void RoadLinesEditor::handle_input()
}
void RoadLinesEditor::place_zebras()
{
editor->remove_buildings_by_prefix("zebra");
Array args;
args.push_back("zebra");
editor->editor_command("remove_buildings_by_prefix", args);
/*
func place_zebras():
var road_nodes = SceneComps.get_component("road_nodes2")

View File

@@ -8,6 +8,7 @@
#include <core/os/file_access.h>
#include <scene/resources/mesh.h>
#include <scene/3d/mesh_instance.h>
#include <scene/3d/immediate_geometry.h>
#include <core/math/transform.h>
#include <core/math/geometry.h>
#include <core/math/vector2.h>

View File

@@ -1,6 +1,7 @@
#ifndef ROAD_LINES_PROCESSING_H_
#define ROAD_LINES_PROCESSSING_H_
class Node;
class ImmediateGeometry;
class RoadProcessing {
public:
static void road_setup(Node *target);

View File

@@ -13,6 +13,7 @@
#include <scene/scene_string_names.h>
#include "road_lines_editor.h"
#include "world_editor.h"
#include "buildings_editor.h"
WorldEditor::WorldEditor()
: Spatial()
@@ -25,7 +26,7 @@ WorldEditor::WorldEditor()
, dragging(false)
, drag_delay(0.2f)
, road_lines_editor(memnew(RoadLinesEditor(this)))
, selected_building(-1)
, buildings_editor(memnew(BuildingsEditor(this)))
{
if (!InputMap::get_singleton()->has_action("left"))
InputMap::get_singleton()->add_action("left");
@@ -47,11 +48,20 @@ WorldEditor::WorldEditor()
InputMap::get_singleton()->add_action("editor_cam3");
if (!InputMap::get_singleton()->has_action("mouse1"))
InputMap::get_singleton()->add_action("mouse1");
event.add_listener(this, &WorldEditor::event_signal_handler);
}
WorldEditor::~WorldEditor()
{
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;
}
}
void WorldEditor::set_camera_mode(int mode)
@@ -100,16 +110,6 @@ 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()
{
}
@@ -143,13 +143,6 @@ 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
{
@@ -157,11 +150,11 @@ struct StringHasher {
}
};
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 }
{ "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 }
};
void WorldEditor::tools_button(const String &button)
{
@@ -174,7 +167,7 @@ void WorldEditor::tools_button(const String &button)
goto end;
change[0] = current_mode;
change[1] = modes[button];
emit_signal("editor_event", "mode_change_pre", change);
event.emit("mode_change_pre", change);
switch (current_mode) {
case MODE_ROAD_LINES:
road_lines_editor->exit();
@@ -197,7 +190,7 @@ void WorldEditor::tools_button(const String &button)
mode_npc();
break;
}
emit_signal("editor_event", "mode_change_post", change);
event.emit("mode_change_post", change);
current_mode = modes[button];
end:;
}
@@ -237,8 +230,6 @@ void WorldEditor::editor_command(const String &command, const Array &args)
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") {
if (stream_world)
stream_world->run_command(command, args);
@@ -248,37 +239,17 @@ void WorldEditor::editor_command(const String &command, const Array &args)
} else if (command == "remove_road_meshes") {
if (stream_world)
stream_world->run_command(command, args);
} else if (road_lines_editor) {
} 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 (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");
if (buildings_editor)
buildings_editor->editor_command(command, args);
}
}
@@ -287,6 +258,11 @@ int WorldEditor::get_current_mode() const
return current_mode;
}
void WorldEditor::event_signal_handler(const String &event, const Array &args)
{
emit_signal("editor_event", event, args);
}
StreamWorld *WorldEditor::get_stream_world()
{
return stream_world;
@@ -295,7 +271,7 @@ StreamWorld *WorldEditor::get_stream_world()
void WorldEditor::world_command_result(const String &what, const Array &data)
{
print_line("what: " + what);
emit_signal("editor_event", "result:" + what, data);
event.emit("result:" + what, data);
}
void WorldEditor::_notification(int which)
@@ -320,6 +296,14 @@ void WorldEditor::_notification(int which)
}
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);
@@ -342,8 +326,7 @@ void WorldEditor::_notification(int which)
Vector2 position =
get_viewport()->get_mouse_position();
args.push_back(position);
emit_signal("editor_event", "mouse_drag_off",
args);
event.emit("mouse_drag_off", args);
}
}
Transform cam_xform = cam->get_global_transform();
@@ -382,13 +365,15 @@ void WorldEditor::_notification(int which)
cam->set_global_transform(cam_xform);
Array move_args;
move_args.push_back(cam_xform);
emit_signal("editor_event",
"editor_camera_moved", move_args);
event.emit("editor_camera_moved", move_args);
}
}
if (!dragging && drag_delay >= 0.0f)
drag_delay -= delta;
switch (current_mode) {
case MODE_BUILDINGS:
buildings_editor->update(delta);
break;
case MODE_ROAD_LINES:
// print_line("current_mode: " + itos(current_mode));
road_lines_editor->update(delta);
@@ -415,13 +400,13 @@ void WorldEditor::_unhandled_input(const Ref<InputEvent> &event)
Array args;
Vector2 position = get_viewport()->get_mouse_position();
args.push_back(position);
emit_signal("editor_event", "mouse_press", args);
this->event.emit("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);
this->event.emit("mouse_drag", args);
} else {
if (drag_delay < 0.0f && !dragging) {
dragging = true;
@@ -429,8 +414,7 @@ void WorldEditor::_unhandled_input(const Ref<InputEvent> &event)
Vector2 position =
get_viewport()->get_mouse_position();
args.push_back(position);
emit_signal("editor_event", "mouse_drag_on",
args);
this->event.emit("mouse_drag_on", args);
}
}
}
@@ -443,22 +427,6 @@ 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::remove_buildings_by_prefix(const String &prefix)
{
Array args;
args.push_back(prefix);
stream_world->run_command("remove_buildings_by_prefix", args);
}
void WorldEditor::_bind_methods()
{
ClassDB::bind_method(D_METHOD("editor_command", "command", "args"),
@@ -475,16 +443,8 @@ void WorldEditor::_bind_methods()
&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")));

View File

@@ -1,8 +1,10 @@
#ifndef WORLD_EDITOR_H
#define WORLD_EDITOR_H
#include <list>
#include <scene/3d/spatial.h>
#include "stream.h"
class RoadLinesEditor;
class BuildingsEditor;
class WorldEditor : public Spatial {
GDCLASS(WorldEditor, Spatial)
protected:
@@ -16,7 +18,6 @@ protected:
void mode_road_lines();
void mode_npc();
void tools_button(const String &button);
int get_current_mode() const;
StreamWorld *get_stream_world();
void world_command_result(const String &what, const Array &data);
void _notification(int which);
@@ -32,18 +33,87 @@ private:
bool dragging;
float drag_delay;
RoadLinesEditor *road_lines_editor;
int selected_building;
Transform selected_building_xform;
void delete_building_handler();
BuildingsEditor *buildings_editor;
public:
enum {
MODE_BUILDINGS = 2,
MODE_NAVIGATION = 3,
MODE_POI = 5,
MODE_ROAD_LINES = 6,
MODE_NPC = 7,
};
WorldEditor();
virtual ~WorldEditor();
void editor_command(const String &command, const Array &args);
int get_camera_mode() const;
int get_selected_building() const;
Transform get_selected_building_xform() const;
void select_building(const Transform &xform, int id, const String &mid);
void remove_buildings_by_prefix(const String &prefix);
int get_current_mode() const;
class EventHelper {
class event_listener_ptrs {
public:
class H {};
H *obj;
void (H::*method)(const String &event,
const Array &args);
void execute(const String &event,
const Array &args) const
{
(obj->*method)(event, args);
}
};
std::list<event_listener_ptrs> listeners;
typedef event_listener_ptrs::H *obj_t;
typedef void (event_listener_ptrs::H::*method_t)(
const String &event, const Array &args);
public:
template <class T>
void add_listener(T *obj, void (T::*method)(const String &event,
const Array &args))
{
auto evl = listeners.begin();
bool bad = false;
while (evl != listeners.end()) {
const event_listener_ptrs &xev = *evl;
if (xev.obj == reinterpret_cast<obj_t>(obj) &&
xev.method == reinterpret_cast<method_t>(
method)) {
bad = true;
break;
}
evl++;
}
if (bad)
return;
event_listener_ptrs ev;
ev.obj = reinterpret_cast<obj_t>(obj);
ev.method = reinterpret_cast<method_t>(method);
listeners.push_back(ev);
}
template <class T>
void remove_listener(T *obj,
void (T::*method)(const String &event,
const Array &args))
{
listeners.remove_if([obj,
method](const event_listener_ptrs
&e) {
return e.obj == reinterpret_cast<obj_t>(obj) &&
e.method == reinterpret_cast<method_t>(
method);
});
}
void emit(const String &event, const Array &args)
{
auto evl = listeners.begin();
while (evl != listeners.end()) {
const event_listener_ptrs &xev = *evl;
xev.execute(event, args);
evl++;
}
}
};
EventHelper event;
void event_signal_handler(const String &event, const Array &args);
};
#endif