diff --git a/modules/terrain_editor/layer_editor.cpp b/modules/terrain_editor/layer_editor.cpp new file mode 100644 index 00000000..c151bc1e --- /dev/null +++ b/modules/terrain_editor/layer_editor.cpp @@ -0,0 +1,51 @@ +#include "layer_editor.h" +#include "scene/resources/packed_scene.h" + +void LayerEditor::_bind_methods() { + String hint_string{ vformat("StringName;%s/%s:PackedScene", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE) }; + BIND_HPROPERTY(Variant::DICTIONARY, inspectors, PROPERTY_HINT_DICTIONARY_TYPE, hint_string); + BIND_HPROPERTY(Variant::OBJECT, terrain, PROPERTY_HINT_NODE_TYPE, "TerrainMeshEditor"); +} + +void LayerEditor::deselect_current() { + if (this->current_inspector) { + this->current_inspector->queue_free(); + } + this->current_inspector = nullptr; +} + +void LayerEditor::select(Ref primitive) { + deselect_current(); + if (primitive.is_valid() && this->inspectors.has(primitive->get_class())) { + this->current_inspector = this->inspectors.get(primitive->get_class())->instantiate(); + add_child(this->current_inspector); + } +} + +void LayerEditor::set_inspectors(Dictionary dict) { + this->inspectors.clear(); + for (KeyValue kvp : dict) { + StringName name{ kvp.key }; + Ref value{ kvp.value }; + this->inspectors.insert(name, value); + } +} + +Dictionary LayerEditor::get_inspectors() const { + Dictionary r; + for (KeyValue> const &kvp : this->inspectors) { + r[kvp.key] = kvp.value; + } + return r; +} + +void LayerEditor::set_terrain(TerrainMeshEditor *editor) { + this->terrain = editor; + if (editor && !Engine::get_singleton()->is_editor_hint()) { + editor->connect(TerrainMeshEditor::sig_selection_changed, callable_mp(this, &self_type::select)); + } +} + +TerrainMeshEditor *LayerEditor::get_terrain() const { + return this->terrain; +} diff --git a/modules/terrain_editor/layer_editor.h b/modules/terrain_editor/layer_editor.h new file mode 100644 index 00000000..860e0649 --- /dev/null +++ b/modules/terrain_editor/layer_editor.h @@ -0,0 +1,24 @@ +#pragma once + +#include "scene/gui/tab_container.h" +#include "terrain_editor/terrain_mesh_editor.h" +#include "terrain_primitive.h" + +class LayerEditor : public TabContainer { + GDCLASS(LayerEditor, TabContainer); + static void _bind_methods(); + +protected: +public: + void deselect_current(); + void select(Ref primitive); + void set_inspectors(Dictionary dict); + Dictionary get_inspectors() const; + void set_terrain(TerrainMeshEditor *terrain); + TerrainMeshEditor *get_terrain() const; + +private: + HashMap> inspectors{}; + Node *current_inspector{ nullptr }; + TerrainMeshEditor *terrain{ nullptr }; +}; diff --git a/modules/terrain_editor/primitive_layer_list.cpp b/modules/terrain_editor/primitive_layer_list.cpp index 1d02d916..97ae5db7 100644 --- a/modules/terrain_editor/primitive_layer_list.cpp +++ b/modules/terrain_editor/primitive_layer_list.cpp @@ -9,7 +9,7 @@ #include void PrimitiveLayerList::_bind_methods() { - BIND_HPROPERTY(Variant::OBJECT, terrain, PROPERTY_HINT_NODE_TYPE, "TerrainMeshGenerator"); + BIND_HPROPERTY(Variant::OBJECT, terrain, PROPERTY_HINT_NODE_TYPE, "TerrainMeshEditor"); BIND_HPROPERTY(Variant::DICTIONARY, icons, PROPERTY_HINT_DICTIONARY_TYPE, "StringName;Texture2D"); } @@ -27,15 +27,15 @@ void PrimitiveLayerList::generate_subtree(size_t idx, Ref prim base->set_text(NAME_COLUMN, prim->get_name().is_empty() ? prim->get_class() : prim->get_name()); base->set_editable(NAME_COLUMN, true); base->set_expand_right(NAME_COLUMN, true); - this->subtrees.insert(prim, base); } void PrimitiveLayerList::regenerate_tree(Array array) { get_root()->clear_children(); - this->subtrees.clear(); + this->primitives.clear(); size_t i{ 0 }; for (Variant var : array) { Ref prim{ var }; + this->primitives.push_back(prim); if (prim.is_valid()) { generate_subtree(i, prim, get_root()); ++i; @@ -88,6 +88,16 @@ void PrimitiveLayerList::item_edited() { } } +void PrimitiveLayerList::item_selected() { + TreeItem *item{ get_selected() }; + int64_t const idx{ item->get_text(IDX_COLUMN).to_int() }; + if (idx < this->primitives.size() && idx >= 0) { + this->terrain->set_current_selected(this->primitives[idx]); + } else { + this->terrain->set_current_selected(nullptr); + } +} + void PrimitiveLayerList::_notification(int what) { if (Engine::get_singleton()->is_editor_hint()) { return; @@ -97,6 +107,7 @@ void PrimitiveLayerList::_notification(int what) { return; case NOTIFICATION_READY: connect("item_edited", callable_mp(this, &self_type::item_edited)); + connect("item_selected", callable_mp(this, &self_type::item_selected)); create_item(); set_columns(COLUMN_MAX); set_hide_root(true); @@ -126,10 +137,10 @@ Dictionary PrimitiveLayerList::get_icons() const { return dict; } -void PrimitiveLayerList::set_terrain(TerrainMeshGenerator *terrain) { +void PrimitiveLayerList::set_terrain(TerrainMeshEditor *terrain) { this->terrain = terrain; } -TerrainMeshGenerator *PrimitiveLayerList::get_terrain() const { +TerrainMeshEditor *PrimitiveLayerList::get_terrain() const { return this->terrain; } diff --git a/modules/terrain_editor/primitive_layer_list.h b/modules/terrain_editor/primitive_layer_list.h index 159c079e..7c0c756f 100644 --- a/modules/terrain_editor/primitive_layer_list.h +++ b/modules/terrain_editor/primitive_layer_list.h @@ -2,7 +2,7 @@ #include "core/templates/hash_map.h" #include "scene/gui/tree.h" -#include "terrain_editor/terrain_mesh_generator.h" +#include "terrain_editor/terrain_mesh_editor.h" #include "terrain_editor/terrain_primitive.h" class PrimitiveLayerList : public Tree { @@ -19,6 +19,7 @@ class PrimitiveLayerList : public Tree { void switch_index(size_t from, size_t to); void layer_renamed(TreeItem *item); void item_edited(); + void item_selected(); protected: void _notification(int what); @@ -26,12 +27,12 @@ protected: public: void set_icons(Dictionary dict); Dictionary get_icons() const; - void set_terrain(TerrainMeshGenerator *generator); - TerrainMeshGenerator *get_terrain() const; + void set_terrain(TerrainMeshEditor *generator); + TerrainMeshEditor *get_terrain() const; private: HashMap> icons{}; - TerrainMeshGenerator *terrain{}; + TerrainMeshEditor *terrain{}; - HashMap, TreeItem *> subtrees{}; + Vector> primitives{}; }; diff --git a/modules/terrain_editor/register_types.cpp b/modules/terrain_editor/register_types.cpp index e6f65dae..cc1f0e9b 100644 --- a/modules/terrain_editor/register_types.cpp +++ b/modules/terrain_editor/register_types.cpp @@ -4,6 +4,7 @@ #include "core/object/class_db.h" #include "terrain_editor/add_primitive_button.h" #include "terrain_editor/edit_history.h" +#include "terrain_editor/layer_editor.h" #include "terrain_editor/point_primitive_node.h" #include "terrain_editor/primitive_layer_list.h" #include "terrain_editor/terrain_chunk.h" @@ -29,6 +30,7 @@ void initialize_terrain_editor_module(ModuleInitializationLevel p_level) { Engine::get_singleton()->add_singleton(Engine::Singleton("EditHistory", (EditHistory::singleton_instance = memnew(EditHistory)), "EditHistory")); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); } void uninitialize_terrain_editor_module(ModuleInitializationLevel p_level) { diff --git a/modules/terrain_editor/terrain_mesh_editor.cpp b/modules/terrain_editor/terrain_mesh_editor.cpp index 9e06762a..cb743ac5 100644 --- a/modules/terrain_editor/terrain_mesh_editor.cpp +++ b/modules/terrain_editor/terrain_mesh_editor.cpp @@ -1,6 +1,5 @@ #include "terrain_mesh_editor.h" #include "core/input/input_event.h" -#include "core/io/file_access.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/os/keyboard.h" @@ -36,9 +35,13 @@ Array SaveData::get_primitives() const { return this->primitives; } +String const TerrainMeshEditor::sig_selection_changed{ "selection_changed" }; + void TerrainMeshEditor::_bind_methods() { BIND_HPROPERTY(Variant::OBJECT, point_primitive_object, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"); ClassDB::bind_method(D_METHOD("save_data"), &self_type::save_data); + BIND_HPROPERTY(Variant::OBJECT, current_selected, PROPERTY_HINT_RESOURCE_TYPE, "TerrainPrimitive"); + ADD_SIGNAL(MethodInfo(sig_selection_changed, PropertyInfo(Variant::OBJECT, "new_selection", PROPERTY_HINT_RESOURCE_TYPE, "TerrainPrimitive"))); } void TerrainMeshEditor::ready() { @@ -54,7 +57,6 @@ void TerrainMeshEditor::ready() { } void TerrainMeshEditor::on_primitive_list_changed(Array primitives) { - this->out_of_date = true; for (Node3D *existing : this->primitive_nodes) { existing->queue_free(); } @@ -162,6 +164,15 @@ void TerrainMeshEditor::save_data() { } } +void TerrainMeshEditor::set_current_selected(Ref primitive) { + this->current_selected = primitive; + emit_signal(sig_selection_changed, primitive); +} + +Ref TerrainMeshEditor::get_current_selected() const { + return this->current_selected; +} + void TerrainMeshEditor::set_point_primitive_object(Ref scene) { this->point_primitive_object = scene; } diff --git a/modules/terrain_editor/terrain_mesh_editor.h b/modules/terrain_editor/terrain_mesh_editor.h index 34cb3e29..19f6b200 100644 --- a/modules/terrain_editor/terrain_mesh_editor.h +++ b/modules/terrain_editor/terrain_mesh_editor.h @@ -3,6 +3,7 @@ #include "core/io/resource.h" #include "scene/gui/file_dialog.h" #include "terrain_editor/terrain_mesh_generator.h" +#include "terrain_editor/terrain_primitive.h" class SaveData : public Resource { GDCLASS(SaveData, Resource); @@ -34,13 +35,18 @@ protected: public: void save_data(); + void set_current_selected(Ref); + Ref get_current_selected() const; void set_point_primitive_object(Ref scene); Ref get_point_primitive_object() const; private: Ref data{ memnew(SaveData) }; + Ref current_selected{}; FileDialog *file_dialog{}; - bool out_of_date{ false }; Vector primitive_nodes{}; Ref point_primitive_object{}; + +public: + static String const sig_selection_changed; }; diff --git a/project/scenes/editor.tscn b/project/scenes/editor.tscn index a6d218df..bf52863b 100644 --- a/project/scenes/editor.tscn +++ b/project/scenes/editor.tscn @@ -7,6 +7,7 @@ [ext_resource type="Texture2D" uid="uid://bb0mnjwx58nt3" path="res://assets/icons/plus.svg" id="4_q68jb"] [ext_resource type="Texture2D" uid="uid://bl3gn6qruuy8w" path="res://assets/icons/plane.svg" id="4_xg7d5"] [ext_resource type="Texture2D" uid="uid://d1te42w7wpkrx" path="res://assets/icons/noise.svg" id="5_eqbpn"] +[ext_resource type="PackedScene" uid="uid://bsvvhue5x4rb" path="res://ui/primitive_inspectors/point_primitive_inspector.tscn" id="8_5tm2q"] [sub_resource type="PointPrimitive" id="PointPrimitive_5tm2q"] @@ -78,7 +79,7 @@ script/source = "extends Camera3D var pan_speed = .001 var rotate_speed := .0015 -var zoom_speed := 1.0 +var zoom_speed := 2. var rotating := false var panning := false var distance := 200 @@ -223,13 +224,12 @@ icons = { &"PointPrimitive": ExtResource("4_5lcyj") } -[node name="Inspector" type="TabContainer" parent="LeftPanel/VBoxContainer" unique_id=240272030] +[node name="Inspector" type="LayerEditor" parent="LeftPanel/VBoxContainer" unique_id=833878161 node_paths=PackedStringArray("terrain")] layout_mode = 2 size_flags_vertical = 3 -current_tab = 0 - -[node name="Inspector" type="VBoxContainer" parent="LeftPanel/VBoxContainer/Inspector" unique_id=1225013744] -layout_mode = 2 -metadata/_tab_index = 0 +inspectors = { +&"PointPrimitive": ExtResource("8_5tm2q") +} +terrain = NodePath("../../../TerrainMeshEditor") [connection signal="primitives_changed" from="TerrainMeshEditor" to="TerrainMeshEditor" method="_on_primitives_changed"] diff --git a/project/ui/editor_elements/float_editor.tscn b/project/ui/editor_elements/float_editor.tscn new file mode 100644 index 00000000..462e77fc --- /dev/null +++ b/project/ui/editor_elements/float_editor.tscn @@ -0,0 +1,21 @@ +[gd_scene format=3 uid="uid://cwby0in0f2wi2"] + +[node name="FloatEditor" type="HBoxContainer" unique_id=1730998858] +offset_right = 318.0 +offset_bottom = 31.0 + +[node name="HSlider" type="HSlider" parent="." unique_id=163542019] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +max_value = 300.0 +step = 0.01 +value = 200.0 + +[node name="SpinBox" type="SpinBox" parent="." unique_id=580598896] +layout_mode = 2 +min_value = -10000.0 +max_value = 100000.0 +step = 0.01 +value = 10.0 +allow_greater = true diff --git a/project/ui/primitive_inspectors/point_primitive_inspector.tscn b/project/ui/primitive_inspectors/point_primitive_inspector.tscn new file mode 100644 index 00000000..3136791c --- /dev/null +++ b/project/ui/primitive_inspectors/point_primitive_inspector.tscn @@ -0,0 +1,23 @@ +[gd_scene format=3 uid="uid://bsvvhue5x4rb"] + +[ext_resource type="PackedScene" uid="uid://cwby0in0f2wi2" path="res://ui/editor_elements/float_editor.tscn" id="1_qb00w"] + +[node name="PointPrimitive" type="MarginContainer" unique_id=905749607] +offset_right = 302.0 +offset_bottom = 230.0 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 +metadata/_tab_index = 0 + +[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=236606520] +layout_mode = 2 + +[node name="HeightLabel" type="Label" parent="VBoxContainer" unique_id=1478707845] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Height" + +[node name="FloatEditor" parent="VBoxContainer" unique_id=1730998858 instance=ExtResource("1_qb00w")] +layout_mode = 2 diff --git a/test-terrains/highland.terrain.res b/test-terrains/highland.terrain.res new file mode 100644 index 00000000..ba3c005a Binary files /dev/null and b/test-terrains/highland.terrain.res differ diff --git a/test-terrains/my_terrain.terrain.res b/test-terrains/my_terrain.terrain.res new file mode 100644 index 00000000..385bd333 Binary files /dev/null and b/test-terrains/my_terrain.terrain.res differ diff --git a/test-terrains/my_terrain.terrain.tres b/test-terrains/my_terrain.terrain.tres new file mode 100644 index 00000000..3b9b9aef --- /dev/null +++ b/test-terrains/my_terrain.terrain.tres @@ -0,0 +1,47 @@ +[gd_resource type="SaveData" load_steps=10 format=3] + +[sub_resource type="PointPrimitive" id="PointPrimitive_pxqd5"] +center = Vector2(5.4302, 139.65) +slope = -0.7 +height = 67.0 + +[sub_resource type="PointPrimitive" id="PointPrimitive_ba0ut"] +center = Vector2(-81.1489, 92.763) +slope = -0.7 +height = 35.0 + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_pxqd5"] +frequency = 0.02 +fractal_octaves = 3 + +[sub_resource type="NoisePrimitive" id="NoisePrimitive_ba0ut"] +blend_range = 5.0 +noise = SubResource("FastNoiseLite_pxqd5") +noise_amplitude = 20.0 + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_b1cmn"] +noise_type = 0 +frequency = 0.03 +fractal_type = 2 +metadata/_preview_in_3d_space_ = true + +[sub_resource type="NoisePrimitive" id="NoisePrimitive_pxqd5"] +blend_range = 10.0 +noise = SubResource("FastNoiseLite_b1cmn") +noise_amplitude = 5.0 + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_ba0ut"] +fractal_type = 2 +fractal_gain = 1.0 +fractal_weighted_strength = 0.58 + +[sub_resource type="NoisePrimitive" id="NoisePrimitive_q68jb"] +blend_mode = 1 +noise = SubResource("FastNoiseLite_ba0ut") + +[sub_resource type="PlanePrimitive" id="PlanePrimitive_pxqd5"] +blend_range = 10.0 +baseline = -1.0 + +[resource] +primitives = [SubResource("PointPrimitive_pxqd5"), SubResource("PointPrimitive_ba0ut"), SubResource("NoisePrimitive_ba0ut"), SubResource("NoisePrimitive_pxqd5"), SubResource("NoisePrimitive_q68jb"), SubResource("PlanePrimitive_pxqd5")] diff --git a/test-terrains/test.terrain.res b/test-terrains/test.terrain.res new file mode 100644 index 00000000..6ee7ceef Binary files /dev/null and b/test-terrains/test.terrain.res differ diff --git a/test-terrains/valley.terrain.res b/test-terrains/valley.terrain.res new file mode 100644 index 00000000..ba6e7afb Binary files /dev/null and b/test-terrains/valley.terrain.res differ