diff --git a/godot/main/editor.gd b/godot/main/editor.gd new file mode 100644 index 0000000..3805e2e --- /dev/null +++ b/godot/main/editor.gd @@ -0,0 +1,203 @@ +extends Spatial + +var camera_mode = -1 + +onready var vmode = { + 2: $"%v_buildings", + 3: $"%v_navigation", + 5: $"%v_poi", + 6: $"%v_road_lines", + 7: $"%v_npc", +} + +func _ready(): + for b in [ + $"%select_buildings", + $"%select_navigation", + $"%select_poi", + $"%select_road_lines", + $"%select_npc", + ]: + b.connect("pressed", $WorldEditor, "editor_command", [b.name, []]) + $WorldEditor.connect("editor_event", self, "editor_event") + for k in vmode.keys(): + vmode[k].hide() + $building_cursor.hide() + +func editor_event(evname: String, args: Array): + print(evname, args) + if evname == "mode_change_pre": + var mode_prev = args[0] + if mode_prev == -1: + for k in vmode.keys(): + vmode[k].hide() + elif vmode.has(mode_prev): + vmode[mode_prev].hide() + elif evname == "mode_change_post": + var mode_next = args[1] + if vmode.has(mode_next): + vmode[mode_next].show() + elif evname == "result:get_closest_building": + print(evname, args) + select_building(args[0], args[3]) + else: + breakpoint +var selected_building +var selected_building_xform +func select_building(xform, id): + selected_building = id + selected_building_xform = xform + print("selected id: ", id) + 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() + if Input.is_action_just_pressed("editor_cam2"): + setup_cam2() + if Input.is_action_just_pressed("editor_cam3"): + setup_cam3() + if dragging: + if !Input.is_action_pressed("mouse1"): + dragging = false + drag_delay = 0.2 + else: + var position = get_viewport().get_mouse_position() + 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 + if $"%buildings_edit_mode".selected == 1: + 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 +var motion = Vector2() +var old_mouse_pos = Vector2(-1, -1) +var rotation_y = 0 +var dragging = false +var drag_delay = 0.2 +var drag_start = Vector3() +func _unhandled_input(event): + var editor_mode = $WorldEditor.get_current_mode() + if camera_mode in [2, 3]: + if event is InputEventMouseMotion: + motion = event.relative + if camera_mode == 3: + match editor_mode: + 2: + check_edit_building() +func check_edit_building(): + if Input.is_action_just_pressed("mouse1"): + var position = get_viewport().get_mouse_position() +# var proj = get_viewport().get_camera().project_position(position, $cam.global_transform.origin.y + position.y * 0.5) +# print(position, proj) +# proj = get_viewport().get_camera().project_ray_origin(position) +# proj.y = 0 +# proj.x = stepify(proj.x, 100.0) +# proj.z = stepify(proj.z, 100.0) +# var mouse_pos = get_viewport().get_mouse_position() + 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 + if $"%buildings_edit_mode".selected == 0: + print("get closest building") + $WorldEditor.editor_command("get_closest_building", [Transform(Basis(), proj)]) + elif Input.is_action_pressed("mouse1"): + if $"%buildings_edit_mode".selected == 1: + if drag_delay < 0.0: + dragging = true + var position = get_viewport().get_mouse_position() + 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 + if $"%buildings_edit_mode".selected == 1: + drag_start = proj + else: + drag_delay -= get_process_delta_time() + else: + dragging = false + drag_delay = 0.2 + else: + dragging = false + drag_delay = 0.2 +func _physics_process(delta): + var editor_mode = $WorldEditor.get_current_mode() + if camera_mode == 1: + var 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 + var moved = false + match camera_mode: + 1: + var xx = Input.get_axis("left", "right") + var zz = Input.get_axis("backward", "forward") + var hh = Input.get_axis("action2", "action") + var h = $Camera.global_transform.origin.y + if abs(zz) > 0.1: + $Camera.global_transform.origin.z -= abs(h) * zz * delta + moved = true + if abs(xx) > 0.1: + $Camera.global_transform.origin.x += abs(h) * xx * delta + moved = true + if abs(hh) > 0.1 && abs(xx) < 0.1 && abs(zz) < 0.1: + $Camera.global_transform.origin.y += 10 * hh * delta + moved = true + 2: + var h = $Camera.global_transform.origin.y + var xx = motion.x + $Camera.rotate_y(-xx * 0.005) + var offset = -$Camera.global_transform.basis.z * 10.0 * delta + offset.y = 0 + var hh = Input.get_axis("action2", "action") + if abs(hh) > 0.1: + $Camera.global_transform.origin.y += 10 * hh * delta + moved = true + var zz = Input.get_axis("backward", "forward") + if abs(zz) > 0.1: + $Camera.global_transform.origin += zz * abs(h) * offset + moved = true + motion = Vector2() + if moved: + $Area.global_transform.origin.x = $Camera.global_transform.origin.x + $Area.global_transform.origin.z = $Camera.global_transform.origin.z +func setup_cam1(): + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + camera_mode = 1 + if camera_mode == -1: + $Camera.global_transform.origin.y = 80.0 + $Camera.global_transform.basis = Basis().rotated(Vector3(1, 0, 0), -PI / 2.0) +func setup_cam2(): + camera_mode = 2 + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + if camera_mode == -1: + $Camera.global_transform.origin.y = 80.0 + $Camera.global_transform.basis = Basis().rotated(Vector3(1, 0, 0), -PI / 4.0) +func setup_cam3(): + camera_mode = 3 + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE diff --git a/godot/main/editor.tscn b/godot/main/editor.tscn index 53e028a..b3d7ebb 100644 --- a/godot/main/editor.tscn +++ b/godot/main/editor.tscn @@ -1,5 +1,244 @@ -[gd_scene format=2] +[gd_scene load_steps=14 format=2] + +[ext_resource path="res://main/editor.gd" type="Script" id=1] +[ext_resource path="res://terrain/terrain_draw.png" type="Image" id=2] +[ext_resource path="res://terrain/terrain.png" type="Image" id=3] +[ext_resource path="res://terrain/terrain_edit.png" type="Image" id=4] + +[sub_resource type="VoxelGeneratorImgMapper" id=5] +height_start = -150.0 +height_range = 300.0 +image_bg = ExtResource( 3 ) +image_overlay = ExtResource( 4 ) +image_draw = ExtResource( 2 ) + +[sub_resource type="VoxelMesherTransvoxel" id=6] + +[sub_resource type="ORMSpatialMaterial" id=7] +albedo_color = Color( 0.254902, 0.886275, 0.101961, 1 ) + +[sub_resource type="ProceduralSky" id=4] + +[sub_resource type="Environment" id=8] +background_mode = 2 +background_sky = SubResource( 4 ) + +[sub_resource type="BoxShape" id=9] +extents = Vector3( 550, 0.1, 550 ) + +[sub_resource type="CubeMesh" id=10] +size = Vector3( 5, 120, 5 ) + +[sub_resource type="SpatialMaterial" id=11] +flags_transparent = true +albedo_color = Color( 0.698039, 0.192157, 0.101961, 0.427451 ) +emission_enabled = true +emission = Color( 0.894118, 0.0980392, 0.0980392, 1 ) +emission_energy = 1.0 +emission_operator = 0 +emission_on_uv2 = false + +[sub_resource type="BoxShape" id=12] +extents = Vector3( 50, 1, 50 ) [node name="editor" type="Spatial"] +script = ExtResource( 1 ) [node name="WorldEditor" type="WorldEditor" parent="."] + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +anchor_left = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = -160.0 + +[node name="ColorRect" type="ColorRect" parent="VBoxContainer"] +margin_right = 160.0 +margin_bottom = 30.0 +rect_min_size = Vector2( 160, 30 ) +size_flags_horizontal = 3 +size_flags_vertical = 5 +color = Color( 0.25098, 0.25098, 0.25098, 1 ) + +[node name="Label" type="Label" parent="VBoxContainer/ColorRect"] +margin_left = 39.0 +margin_top = 8.0 +margin_right = 118.0 +margin_bottom = 22.0 +size_flags_horizontal = 3 +size_flags_vertical = 7 +text = "Mode select" +align = 1 +valign = 1 + +[node name="select_buildings" type="Button" parent="VBoxContainer"] +unique_name_in_owner = true +margin_top = 34.0 +margin_right = 160.0 +margin_bottom = 54.0 +text = "Buildings Mode" + +[node name="select_navigation" type="Button" parent="VBoxContainer"] +unique_name_in_owner = true +margin_top = 58.0 +margin_right = 160.0 +margin_bottom = 78.0 +text = "Navigation Mode" + +[node name="select_poi" type="Button" parent="VBoxContainer"] +unique_name_in_owner = true +margin_top = 82.0 +margin_right = 160.0 +margin_bottom = 102.0 +text = "POI Mode" + +[node name="select_road_lines" type="Button" parent="VBoxContainer"] +unique_name_in_owner = true +margin_top = 106.0 +margin_right = 160.0 +margin_bottom = 126.0 +text = "Road Lines Mode" + +[node name="select_npc" type="Button" parent="VBoxContainer"] +unique_name_in_owner = true +margin_top = 130.0 +margin_right = 160.0 +margin_bottom = 150.0 +text = "NPC Mode" + +[node name="v_buildings" type="VBoxContainer" parent="VBoxContainer"] +unique_name_in_owner = true +margin_top = 154.0 +margin_right = 160.0 +margin_bottom = 224.0 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer/v_buildings"] +margin_right = 160.0 +margin_bottom = 4.0 + +[node name="Label" type="Label" parent="VBoxContainer/v_buildings"] +margin_top = 8.0 +margin_right = 160.0 +margin_bottom = 22.0 +text = "Buildings mode" + +[node name="buildings_edit_mode" type="OptionButton" parent="VBoxContainer/v_buildings"] +unique_name_in_owner = true +margin_top = 26.0 +margin_right = 160.0 +margin_bottom = 46.0 +text = "Select" +items = [ "Select", null, false, 0, null, "Move", null, false, 1, null, "Rotate", null, false, 2, null ] +selected = 0 + +[node name="building_type" type="OptionButton" parent="VBoxContainer/v_buildings"] +unique_name_in_owner = true +margin_top = 50.0 +margin_right = 160.0 +margin_bottom = 70.0 +text = "Building Type" + +[node name="v_navigation" type="VBoxContainer" parent="VBoxContainer"] +unique_name_in_owner = true +margin_top = 228.0 +margin_right = 160.0 +margin_bottom = 250.0 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer/v_navigation"] +margin_right = 160.0 +margin_bottom = 4.0 + +[node name="Label" type="Label" parent="VBoxContainer/v_navigation"] +margin_top = 8.0 +margin_right = 160.0 +margin_bottom = 22.0 +text = "Navigation mode" + +[node name="v_poi" type="VBoxContainer" parent="VBoxContainer"] +unique_name_in_owner = true +margin_top = 254.0 +margin_right = 160.0 +margin_bottom = 276.0 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer/v_poi"] +margin_right = 160.0 +margin_bottom = 4.0 + +[node name="Label" type="Label" parent="VBoxContainer/v_poi"] +margin_top = 8.0 +margin_right = 160.0 +margin_bottom = 22.0 +text = "POI mode" + +[node name="v_road_lines" type="VBoxContainer" parent="VBoxContainer"] +unique_name_in_owner = true +margin_top = 280.0 +margin_right = 160.0 +margin_bottom = 302.0 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer/v_road_lines"] +margin_right = 160.0 +margin_bottom = 4.0 + +[node name="Label" type="Label" parent="VBoxContainer/v_road_lines"] +margin_top = 8.0 +margin_right = 160.0 +margin_bottom = 22.0 +text = "Road Lines mode" + +[node name="v_npc" type="VBoxContainer" parent="VBoxContainer"] +unique_name_in_owner = true +margin_top = 306.0 +margin_right = 160.0 +margin_bottom = 328.0 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer/v_npc"] +margin_right = 160.0 +margin_bottom = 4.0 + +[node name="Label" type="Label" parent="VBoxContainer/v_npc"] +margin_top = 8.0 +margin_right = 160.0 +margin_bottom = 22.0 +text = "NPC mode" + +[node name="StreamWorld" type="StreamWorld" parent="."] + +[node name="VoxelLodTerrain" type="VoxelLodTerrain" parent="."] +generator = SubResource( 5 ) +mesher = SubResource( 6 ) +voxel_bounds = AABB( -5.36871e+08, -2048, -5.36871e+08, 1.07374e+09, 4096, 1.07374e+09 ) +lod_count = 5 +material = SubResource( 7 ) + +[node name="Camera" type="Camera" parent="."] +transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0 ) +environment = SubResource( 8 ) +far = 1000.0 + +[node name="VoxelViewer" type="VoxelViewer" parent="Camera"] + +[node name="DirectionalLight" type="DirectionalLight" parent="."] +transform = Transform( 1, 0, 0, 0, 0.707107, 0.707107, 0, -0.707107, 0.707107, 0, 2, 0 ) + +[node name="Area" type="Area" parent="."] +collision_layer = 32768 +collision_mask = 32768 +monitoring = false + +[node name="CollisionShape" type="CollisionShape" parent="Area"] +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="."] +mesh = SubResource( 10 ) +material/0 = SubResource( 11 ) + +[node name="Area" type="Area" parent="building_cursor"] +collision_layer = 32768 +collision_mask = 32768 +monitoring = false + +[node name="CollisionShape" type="CollisionShape" parent="building_cursor/Area"] +transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2, 0 ) +shape = SubResource( 12 ) diff --git a/godot/project.godot b/godot/project.godot index 3e733c8..0344227 100644 --- a/godot/project.godot +++ b/godot/project.godot @@ -35,6 +35,36 @@ right={ "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":68,"unicode":0,"echo":false,"script":null) ] } +editor_cam1={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":49,"physical_scancode":0,"unicode":0,"echo":false,"script":null) + ] +} +action={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":69,"unicode":0,"echo":false,"script":null) + ] +} +action2={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":81,"unicode":0,"echo":false,"script":null) + ] +} +editor_cam2={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":50,"physical_scancode":0,"unicode":0,"echo":false,"script":null) + ] +} +editor_cam3={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":51,"physical_scancode":0,"unicode":0,"echo":false,"script":null) + ] +} +mouse1={ +"deadzone": 0.5, +"events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"doubleclick":false,"script":null) + ] +} [rendering] diff --git a/src/godot b/src/godot index 1bfb788..2cc5ca4 160000 --- a/src/godot +++ b/src/godot @@ -1 +1 @@ -Subproject commit 1bfb788c4f50120d0197c9d84828151e0ac6189a +Subproject commit 2cc5ca43ef68382c94d963bd9336ad8efea6adae diff --git a/src/modules/stream/stream.cpp b/src/modules/stream/stream.cpp index 3589197..4298a55 100644 --- a/src/modules/stream/stream.cpp +++ b/src/modules/stream/stream.cpp @@ -30,6 +30,7 @@ void StreamWorld::read_buildings_json(const String &buildings_path) while (e) { struct building b; String key = e->get(); + b.key = key; b.id = json[key].get("id"); if (b.id == "empty") { e = e->next(); @@ -277,6 +278,67 @@ void StreamWorld::update_items() void StreamWorld::run_command(const String &command, const Array &args) { + if (command == "get_closest_building") { + if (args.size() == 0) { + print_error("bad command: not enough args: " + command); + return; + } + const Transform &xform = args[0]; + int i; + float dst = Math_INF; + Transform ret; + String rkey; + int id = -1; + for (i = 0; i < (int)buildings.size(); i++) { + Vector3 o = xform.origin; + Vector3 m = buildings[i].xform.origin; + float mdst = o.distance_squared_to(m); + if (dst > mdst) { + ret = buildings[i].xform; + dst = mdst; + rkey = buildings[i].key; + id = i; + } + } + Array ret_data; + ret_data.resize(4); + ret_data[0] = ret; + ret_data[1] = dst; + ret_data[2] = rkey; + ret_data[3] = id; + emit_signal("command_result", command, ret_data); + } else if (command == "get_building_id_for_key") { + if (args.size() == 0) { + print_error("bad command: not enough args: " + command); + return; + } + String key = args[1]; + Array ret_data; + ret_data.resize(1); + int id = -1, i; + for (i = 0; i < (int)buildings.size(); i++) + if (buildings[i].key == key) { + id = i; + break; + } + ret_data[0] = id; + emit_signal("command_result", command, ret_data); + } else if (command == "update_building_transform") { + if (args.size() == 0) { + print_error("bad command: not enough args: " + command); + return; + } + int id = args[0]; + if (id < 0 || id >= (int)buildings.size()) + return; + String key = buildings[id].key; + buildings[id].xform == args[1]; + Spatial *bnode = Object::cast_to(item_nodes[id]); + bnode->set_global_transform(args[1]); + VariantWriter::write_to_string(buildings[id].xform, key); + buildings[id].key = key; + } else + print_error("No command " + command); } void StreamWorld::_notification(int which) @@ -287,6 +349,8 @@ void StreamWorld::_notification(int which) case NOTIFICATION_EXIT_WORLD: break; case NOTIFICATION_ENTER_TREE: + if (Engine::get_singleton()->is_editor_hint()) + break; if (initialized) { if (Engine::get_singleton()->is_editor_hint()) current_scene = Object::cast_to(this); @@ -303,6 +367,8 @@ void StreamWorld::_notification(int which) case NOTIFICATION_EXIT_TREE: break; case NOTIFICATION_PROCESS: { + if (Engine::get_singleton()->is_editor_hint()) + break; update_view(); const String *key = scenes.next(nullptr); while (key) { @@ -337,6 +403,9 @@ void StreamWorld::_bind_methods() &StreamWorld::viewer_dead); ClassDB::bind_method(D_METHOD("run_command", "command", "args"), &StreamWorld::run_command); + ADD_SIGNAL(MethodInfo("command_result", + PropertyInfo(Variant::STRING, "result_name"), + PropertyInfo(Variant::ARRAY, "args"))); } StreamWorld::StreamWorld() @@ -396,4 +465,4 @@ void StreamWorld::cleanup() StreamWorld::~StreamWorld() { RoadProcessing::cleanup(); -} \ No newline at end of file +} diff --git a/src/modules/stream/stream.h b/src/modules/stream/stream.h index c0a99eb..098ffab 100644 --- a/src/modules/stream/stream.h +++ b/src/modules/stream/stream.h @@ -15,6 +15,7 @@ private: Node *current_scene; struct building { String id; + String key; Transform xform; AABB aabb; }; @@ -57,10 +58,11 @@ private: void unload_building(int id); void request_item(int type, int item); void update_items(); - void run_command(const String &command, const Array &args); + static void _bind_methods(); public: + void run_command(const String &command, const Array &args); StreamWorld(); ~StreamWorld(); static void cleanup(); diff --git a/src/modules/stream/world_editor.cpp b/src/modules/stream/world_editor.cpp index 9cc14b9..3b5f20f 100644 --- a/src/modules/stream/world_editor.cpp +++ b/src/modules/stream/world_editor.cpp @@ -1,13 +1,16 @@ #include +#include #include #include #include +#include #include "world_editor.h" WorldEditor::WorldEditor() : Spatial() , stream_world(nullptr) , editor_menu(nullptr) + , current_mode(-1) { } @@ -70,9 +73,16 @@ static std::unordered_map modes = { }; 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 (modes[button]) { case MODE_BUILDINGS: mode_buildings(); @@ -90,82 +100,89 @@ void WorldEditor::tools_button(const String &button) mode_npc(); break; } + emit_signal("editor_event", "mode_change_post", change); + current_mode = modes[button]; end:; } -#define CREATE_TOOLS_BUTTON(bname, btext) \ - { \ - Vector binds; \ - binds.push_back(#bname); \ - Button *bname = memnew(Button); \ - bname->set_name(#bname); \ - bname->set_text(btext); \ - bname->set_custom_minimum_size(Vector2(188, 16)); \ - bname->connect("pressed", this, "tools_button", binds); \ - tools->add_child(bname); \ - bname->update(); \ - tools->update(); \ - } -void WorldEditor::create_menu() +void WorldEditor::editor_command(const String &command, const Array &args) { - editor_menu = memnew(Control); - editor_menu->set_name("menu"); - editor_menu->set_anchor(MARGIN_LEFT, 1.0f); - editor_menu->set_anchor(MARGIN_RIGHT, 1.0f); - editor_menu->set_anchor(MARGIN_BOTTOM, 1.0f); - editor_menu->set_anchor(MARGIN_TOP, 0.0f); - editor_menu->update(); - VBoxContainer *tools = memnew(VBoxContainer); - Vector2 min_size = tools->get_minimum_size(); - min_size.x = 200.0f; - min_size.y = 400.0f; - tools->set_custom_minimum_size(min_size); - tools->set_name("tools"); - tools->set_anchor_and_margin(MARGIN_LEFT, 1.0f, -210.0f); - tools->set_anchor_and_margin(MARGIN_RIGHT, 1.0f, 0.0f); - tools->set_anchor_and_margin(MARGIN_BOTTOM, 1.0f, 0.0f); - tools->set_anchor_and_margin(MARGIN_TOP, 0.0f, 0.0f); - tools->set_mouse_filter(Control::MOUSE_FILTER_STOP); - editor_menu->add_child(tools); - - CREATE_TOOLS_BUTTON(select_buildings, "Select Buildings"); - CREATE_TOOLS_BUTTON(select_navigation, "Select Navigation"); - CREATE_TOOLS_BUTTON(select_poi, "Select POI"); - CREATE_TOOLS_BUTTON(select_road_lines, "Select Road Lines"); - CREATE_TOOLS_BUTTON(select_npc, "Select NPC"); - editor_menu->update(); - tools->update(); - editor_menu->update(); - tools->update(); - print_line("Created menu"); + 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); + } + } } -Control *WorldEditor::get_editor_menu() +int WorldEditor::get_current_mode() const { - if (!is_inside_tree()) - return nullptr; - if (!editor_menu) - create_menu(); - return editor_menu; + 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: { - Control *c = get_editor_menu(); - if (c) - add_child(c); + 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(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"); + } + } } break; case NOTIFICATION_EXIT_TREE: - if (editor_menu) - editor_menu->queue_delete(); break; } } +void WorldEditor::world_exited() +{ + stream_world = nullptr; +} + void WorldEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("tools_button", "button"), - &WorldEditor::tools_button); + 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); + ADD_SIGNAL(MethodInfo("editor_event", + PropertyInfo(Variant::STRING, "event_name"), + PropertyInfo(Variant::ARRAY, "args"))); } diff --git a/src/modules/stream/world_editor.h b/src/modules/stream/world_editor.h index 952a52f..123c3db 100644 --- a/src/modules/stream/world_editor.h +++ b/src/modules/stream/world_editor.h @@ -13,12 +13,18 @@ protected: void mode_poi(); void mode_road_lines(); void mode_npc(); - void tools_button(const String &which); - void create_menu(); - Control *get_editor_menu(); + void tools_button(const String &button); + void editor_command(const String &command, const Array &args); + int get_current_mode() const; + StreamWorld *get_stream_world(); + void world_command_result(const String &what, const Array &data); void _notification(int which); static void _bind_methods(); +private: + void world_exited(); + int current_mode; + public: WorldEditor(); ~WorldEditor();