From b6e8ca9225fabc291dc928ad1b2f451399e5af21 Mon Sep 17 00:00:00 2001 From: Sara Date: Mon, 8 Dec 2025 15:33:02 +0100 Subject: [PATCH] feat: implemented adding primitive with UI --- .../terrain_editor/add_primitive_button.cpp | 31 ++++++++ modules/terrain_editor/add_primitive_button.h | 21 +++++ modules/terrain_editor/register_types.cpp | 3 +- .../terrain_editor/terrain_mesh_generator.cpp | 15 ++++ .../terrain_editor/terrain_mesh_generator.h | 2 + .../primitive_nodes/point_primitive_node.tscn | 1 + project/objects/terrain_chunk.tscn | 2 +- project/scenes/editor.tscn | 76 ++++++++++++------- 8 files changed, 120 insertions(+), 31 deletions(-) create mode 100644 modules/terrain_editor/add_primitive_button.cpp create mode 100644 modules/terrain_editor/add_primitive_button.h diff --git a/modules/terrain_editor/add_primitive_button.cpp b/modules/terrain_editor/add_primitive_button.cpp new file mode 100644 index 00000000..358d8dab --- /dev/null +++ b/modules/terrain_editor/add_primitive_button.cpp @@ -0,0 +1,31 @@ +#include "add_primitive_button.h" +#include "terrain_editor/edit_history.h" +#include "terrain_editor/terrain_mesh_generator.h" + +void AddPrimitiveButton::_bind_methods() { + BIND_HPROPERTY(Variant::OBJECT, primitive_blueprint, PROPERTY_HINT_RESOURCE_TYPE, "TerrainPrimitive"); + BIND_HPROPERTY(Variant::OBJECT, terrain, PROPERTY_HINT_NODE_TYPE, "TerrainMeshGenerator"); +} + +void AddPrimitiveButton::pressed() { + if (this->terrain) { + Ref copy{ this->primitive_blueprint->duplicate(true) }; + EditHistory::get_singleton()->push_action(callable_mp(this->terrain, &TerrainMeshGenerator::add_primitive).bind(copy), callable_mp(this->terrain, &TerrainMeshGenerator::remove_primitive).bind(copy)); + } +} + +void AddPrimitiveButton::set_primitive_blueprint(Ref blueprint) { + this->primitive_blueprint = blueprint; +} + +Ref AddPrimitiveButton::get_primitive_blueprint() const { + return this->primitive_blueprint; +} + +void AddPrimitiveButton::set_terrain(TerrainMeshGenerator *terrain) { + this->terrain = terrain; +} + +TerrainMeshGenerator *AddPrimitiveButton::get_terrain() const { + return this->terrain; +} diff --git a/modules/terrain_editor/add_primitive_button.h b/modules/terrain_editor/add_primitive_button.h new file mode 100644 index 00000000..02aa9316 --- /dev/null +++ b/modules/terrain_editor/add_primitive_button.h @@ -0,0 +1,21 @@ +#pragma once + +#include "scene/gui/button.h" +#include "terrain_editor/terrain_mesh_generator.h" +#include "terrain_editor/terrain_primitive.h" + +class AddPrimitiveButton : public Button { + GDCLASS(AddPrimitiveButton, Button); + static void _bind_methods(); + void pressed() override; + +public: + void set_primitive_blueprint(Ref primitive); + Ref get_primitive_blueprint() const; + void set_terrain(TerrainMeshGenerator *terrain); + TerrainMeshGenerator *get_terrain() const; + +private: + Ref primitive_blueprint{}; + TerrainMeshGenerator *terrain{ nullptr }; +}; diff --git a/modules/terrain_editor/register_types.cpp b/modules/terrain_editor/register_types.cpp index 712975b5..e6f65dae 100644 --- a/modules/terrain_editor/register_types.cpp +++ b/modules/terrain_editor/register_types.cpp @@ -2,7 +2,7 @@ #include "core/config/engine.h" #include "core/object/class_db.h" -#include "scene/main/scene_tree.h" +#include "terrain_editor/add_primitive_button.h" #include "terrain_editor/edit_history.h" #include "terrain_editor/point_primitive_node.h" #include "terrain_editor/primitive_layer_list.h" @@ -28,6 +28,7 @@ void initialize_terrain_editor_module(ModuleInitializationLevel p_level) { ClassDB::register_class(); Engine::get_singleton()->add_singleton(Engine::Singleton("EditHistory", (EditHistory::singleton_instance = memnew(EditHistory)), "EditHistory")); ClassDB::register_class(); + ClassDB::register_class(); } void uninitialize_terrain_editor_module(ModuleInitializationLevel p_level) { diff --git a/modules/terrain_editor/terrain_mesh_generator.cpp b/modules/terrain_editor/terrain_mesh_generator.cpp index bf540467..f5da7124 100644 --- a/modules/terrain_editor/terrain_mesh_generator.cpp +++ b/modules/terrain_editor/terrain_mesh_generator.cpp @@ -258,6 +258,21 @@ void TerrainMeshGenerator::push_task(Rect2 area, Ref mesh, size_t poi this->input_lock.unlock(); } +void TerrainMeshGenerator::add_primitive(Ref primitive) { + Array list = get_primitives(); + list.push_front(primitive); + set_primitives(list); +} + +void TerrainMeshGenerator::remove_primitive(Ref primitive) { + Array list = get_primitives(); + int idx{ list.find(primitive) }; + if (idx >= 0 && idx < list.size()) { + list.remove_at(idx); + } + set_primitives(list); +} + void TerrainMeshGenerator::set_primitives(Array primitives) { // synchronise primitives for (Variant var : this->primitives_buffer) { diff --git a/modules/terrain_editor/terrain_mesh_generator.h b/modules/terrain_editor/terrain_mesh_generator.h index 283396f6..9cb9141e 100644 --- a/modules/terrain_editor/terrain_mesh_generator.h +++ b/modules/terrain_editor/terrain_mesh_generator.h @@ -37,6 +37,8 @@ protected: public: void push_task(Rect2 area, Ref mesh, size_t side_points, Callable callback = Callable()); + void add_primitive(Ref primitive); + void remove_primitive(Ref primitive); void set_primitives(Array array); Array get_primitives() const; void set_vertex_color_gradient(Ref gradient); diff --git a/project/objects/primitive_nodes/point_primitive_node.tscn b/project/objects/primitive_nodes/point_primitive_node.tscn index 72e1c9f5..e5a2c3c0 100644 --- a/project/objects/primitive_nodes/point_primitive_node.tscn +++ b/project/objects/primitive_nodes/point_primitive_node.tscn @@ -99,6 +99,7 @@ bottom_radius = 0.4 height = 10000.0 [node name="PointPrimitiveNode" type="PointPrimitiveNode" unique_id=1472259146] +transform = Transform3D(0.2, 0, 0, 0, 0.2, 0, 0, 0, 0.2, 0, 0, 0) [node name="point_handle" parent="." unique_id=1807952123 instance=ExtResource("1_njtj3")] transform = Transform3D(10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0) diff --git a/project/objects/terrain_chunk.tscn b/project/objects/terrain_chunk.tscn index ceff0796..cda80a83 100644 --- a/project/objects/terrain_chunk.tscn +++ b/project/objects/terrain_chunk.tscn @@ -4,5 +4,5 @@ [node name="TerrainChunk" type="TerrainChunk" unique_id=1453572398] material_override = ExtResource("1_6vjd7") -size = 25.0 +size = 50.0 lod0_detail = 50 diff --git a/project/scenes/editor.tscn b/project/scenes/editor.tscn index 0950e885..3e10aea7 100644 --- a/project/scenes/editor.tscn +++ b/project/scenes/editor.tscn @@ -8,14 +8,7 @@ [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"] -[sub_resource type="PointPrimitive" id="PointPrimitive_pxqd5"] -slope = -0.7 -height = 0.0 - -[sub_resource type="PointPrimitive" id="PointPrimitive_ba0ut"] -blend_range = 10.0 -center = Vector2(50, 0) -slope = -0.7 +[sub_resource type="PointPrimitive" id="PointPrimitive_5tm2q"] [sub_resource type="FastNoiseLite" id="FastNoiseLite_pxqd5"] frequency = 0.015 @@ -87,7 +80,7 @@ var rotate_speed := .0015 var zoom_speed := 5.0 var rotating := false var panning := false -var distance := 50 +var distance := 200 var pivot := Vector3.ZERO func _ready(): @@ -106,14 +99,6 @@ func _unhandled_input(event: InputEvent) -> void: global_position += (global_basis.y * motion.screen_relative.y - global_basis.x * motion.screen_relative.x) * rotate_speed * distance look_at(pivot) global_position = (global_position - pivot).normalized() * distance + pivot - elif event is InputEventMouseMotion and panning: - get_viewport().set_input_as_handled() - var backward := Vector3(global_basis.z.x, 0.0, global_basis.z.z).normalized() - var right := Vector3(global_basis.x.x, 0.0, global_basis.x.z).normalized() - var motion := event as InputEventMouseMotion - var translation = -(backward * motion.screen_relative.y + right * motion.screen_relative.x) * pan_speed * distance - pivot += translation - global_position += translation elif event is InputEventMouseButton and (event as InputEventMouseButton).button_index == MOUSE_BUTTON_WHEEL_UP: get_viewport().set_input_as_handled() distance = max(1., distance - zoom_speed) @@ -122,17 +107,32 @@ func _unhandled_input(event: InputEvent) -> void: get_viewport().set_input_as_handled() distance = min(1000., distance + zoom_speed) global_position = (global_position - pivot).normalized() * distance + pivot + elif event is InputEventMouseMotion and panning: + get_viewport().set_input_as_handled() + var backward := Vector3(global_basis.z.x, 0.0, global_basis.z.z).normalized() + var right := Vector3(global_basis.x.x, 0.0, global_basis.x.z).normalized() + var motion := event as InputEventMouseMotion + var translation = -(backward * motion.screen_relative.y + right * motion.screen_relative.x) * pan_speed * distance + pivot += translation + global_position += translation " [sub_resource type="CompressedTexture2D" id="CompressedTexture2D_xg7d5"] load_path = "res://.godot/imported/point.svg-e68fd7c1e788d2c48d769cc58eba6e98.ctex" +[sub_resource type="PointPrimitive" id="PointPrimitive_5lcyj"] + +[sub_resource type="PlanePrimitive" id="PlanePrimitive_5lcyj"] + +[sub_resource type="NoisePrimitive" id="NoisePrimitive_5lcyj"] + [node name="Editor" type="Node3D" unique_id=1027707839] [node name="TerrainMeshEditor" type="TerrainMeshEditor" parent="." unique_id=1382595562] -primitives = [SubResource("PointPrimitive_pxqd5"), SubResource("PointPrimitive_ba0ut"), SubResource("NoisePrimitive_ba0ut"), SubResource("NoisePrimitive_pxqd5"), SubResource("NoisePrimitive_q68jb"), SubResource("PlanePrimitive_ba0ut")] +primitives = [SubResource("PointPrimitive_5tm2q"), SubResource("NoisePrimitive_ba0ut"), SubResource("NoisePrimitive_pxqd5"), SubResource("NoisePrimitive_q68jb"), SubResource("PlanePrimitive_ba0ut")] vertex_color_gradient = SubResource("Gradient_b1cmn") color_gradient_end_height = 100.0 +chunk_count = 4 chunk_scene = ExtResource("1_pxqd5") point_primitive_object = ExtResource("1_b1cmn") @@ -159,58 +159,76 @@ offset_right = 258.0 grow_vertical = 2 theme = ExtResource("3_ba0ut") -[node name="TabContainer" type="TabContainer" parent="LeftPanel" unique_id=2095156888] +[node name="VBoxContainer" type="VBoxContainer" parent="LeftPanel" unique_id=1723457232] layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 + +[node name="Layers" type="TabContainer" parent="LeftPanel/VBoxContainer" unique_id=2095156888] +layout_mode = 2 +size_flags_vertical = 3 current_tab = 0 -[node name="Layers" type="VBoxContainer" parent="LeftPanel/TabContainer" unique_id=138865385] +[node name="Layers" type="VBoxContainer" parent="LeftPanel/VBoxContainer/Layers" unique_id=138865385] layout_mode = 2 metadata/_tab_index = 0 -[node name="Tree" type="PrimitiveLayerList" parent="LeftPanel/TabContainer/Layers" unique_id=797700186 node_paths=PackedStringArray("terrain")] +[node name="Tree" type="PrimitiveLayerList" parent="LeftPanel/VBoxContainer/Layers/Layers" unique_id=797700186 node_paths=PackedStringArray("terrain")] layout_mode = 2 size_flags_vertical = 3 -columns = 4 -terrain = NodePath("../../../../TerrainMeshEditor") +scroll_horizontal_enabled = false +terrain = NodePath("../../../../../TerrainMeshEditor") icons = { &"NoisePrimitive": ExtResource("5_eqbpn"), &"PlanePrimitive": ExtResource("4_xg7d5"), &"PointPrimitive": ExtResource("4_5lcyj") } -[node name="HBoxContainer" type="HBoxContainer" parent="LeftPanel/TabContainer/Layers" unique_id=702489990] +[node name="HBoxContainer" type="HBoxContainer" parent="LeftPanel/VBoxContainer/Layers/Layers" unique_id=702489990] layout_mode = 2 size_flags_vertical = 8 -alignment = 1 -[node name="TextureRect" type="TextureRect" parent="LeftPanel/TabContainer/Layers/HBoxContainer" unique_id=1669840346] +[node name="TextureRect" type="TextureRect" parent="LeftPanel/VBoxContainer/Layers/Layers/HBoxContainer" unique_id=1669840346] custom_minimum_size = Vector2(32, 32) layout_mode = 2 texture = ExtResource("4_q68jb") expand_mode = 2 stretch_mode = 4 -[node name="AddPointPrimitive" type="Button" parent="LeftPanel/TabContainer/Layers/HBoxContainer" unique_id=535482641] +[node name="AddPointPrimitive" type="AddPrimitiveButton" parent="LeftPanel/VBoxContainer/Layers/Layers/HBoxContainer" unique_id=255935272 node_paths=PackedStringArray("terrain")] custom_minimum_size = Vector2(32, 32) layout_mode = 2 icon = SubResource("CompressedTexture2D_xg7d5") expand_icon = true +primitive_blueprint = SubResource("PointPrimitive_5lcyj") +terrain = NodePath("../../../../../../TerrainMeshEditor") -[node name="AddPlanePrimitive" type="Button" parent="LeftPanel/TabContainer/Layers/HBoxContainer" unique_id=287076599] +[node name="AddPlanePrimitive" type="AddPrimitiveButton" parent="LeftPanel/VBoxContainer/Layers/Layers/HBoxContainer" unique_id=850669113 node_paths=PackedStringArray("terrain")] custom_minimum_size = Vector2(32, 32) layout_mode = 2 icon = ExtResource("4_xg7d5") expand_icon = true +primitive_blueprint = SubResource("PlanePrimitive_5lcyj") +terrain = NodePath("../../../../../../TerrainMeshEditor") -[node name="AddNoisePrimitive" type="Button" parent="LeftPanel/TabContainer/Layers/HBoxContainer" unique_id=769590598] +[node name="AddNoisePrimitive" type="AddPrimitiveButton" parent="LeftPanel/VBoxContainer/Layers/Layers/HBoxContainer" unique_id=761062556 node_paths=PackedStringArray("terrain")] custom_minimum_size = Vector2(32, 32) layout_mode = 2 icon = ExtResource("5_eqbpn") expand_icon = true +primitive_blueprint = SubResource("NoisePrimitive_5lcyj") +terrain = NodePath("../../../../../../TerrainMeshEditor") + +[node name="Inspector" type="TabContainer" parent="LeftPanel/VBoxContainer" unique_id=240272030] +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 [connection signal="primitives_changed" from="TerrainMeshEditor" to="TerrainMeshEditor" method="_on_primitives_changed"]