feat: implemented saving & loading

This commit is contained in:
Sara Gerretsen 2025-11-18 18:00:32 +01:00
parent 6724c38968
commit 61ba2a54f2
3 changed files with 120 additions and 0 deletions

View file

@ -18,6 +18,7 @@ void initialize_terrain_editor_module(ModuleInitializationLevel p_level) {
ClassDB::register_class<ExpressionPrimitive>(); ClassDB::register_class<ExpressionPrimitive>();
ClassDB::register_class<PointPrimitiveNode>(); ClassDB::register_class<PointPrimitiveNode>();
ClassDB::register_class<TerrainMeshEditor>(); ClassDB::register_class<TerrainMeshEditor>();
ClassDB::register_class<SaveData>();
} }
void uninitialize_terrain_editor_module(ModuleInitializationLevel p_level) { void uninitialize_terrain_editor_module(ModuleInitializationLevel p_level) {

View file

@ -1,20 +1,59 @@
#include "terrain_mesh_editor.h" #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"
#include "scene/3d/node_3d.h" #include "scene/3d/node_3d.h"
#include "scene/gui/file_dialog.h"
#include "scene/resources/packed_scene.h" #include "scene/resources/packed_scene.h"
#include "terrain_editor/macros.h" #include "terrain_editor/macros.h"
#include "terrain_editor/point_primitive_node.h" #include "terrain_editor/point_primitive_node.h"
#include "terrain_editor/terrain_primitive.h" #include "terrain_editor/terrain_primitive.h"
void SaveData::_bind_methods() {
BIND_HPROPERTY(Variant::ARRAY, primitives, PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:TerrainPrimitive", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE));
}
void SaveData::write_to_file() {
ResourceSaver::save(this, this->save_file_path, ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES | ResourceSaver::FLAG_BUNDLE_RESOURCES);
}
void SaveData::set_save_path(String value) {
this->save_file_path = value;
}
String SaveData::get_save_path() const {
return this->save_file_path;
}
void SaveData::set_primitives(Array array) {
this->primitives = array.duplicate(true); // do a full deep copy of the data, to avoid modification before saving
}
Array SaveData::get_primitives() const {
return this->primitives;
}
void TerrainMeshEditor::_bind_methods() { void TerrainMeshEditor::_bind_methods() {
BIND_HPROPERTY(Variant::OBJECT, point_primitive_object, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"); BIND_HPROPERTY(Variant::OBJECT, point_primitive_object, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene");
ClassDB::bind_method(D_METHOD("save_data"), &self_type::save_data);
} }
void TerrainMeshEditor::ready() { void TerrainMeshEditor::ready() {
connect(sig_primitive_list_changed, callable_mp(this, &self_type::on_primitive_list_changed)); connect(sig_primitive_list_changed, callable_mp(this, &self_type::on_primitive_list_changed));
on_primitive_list_changed(get_primitives()); on_primitive_list_changed(get_primitives());
if (FileDialog * dialog{ memnew(FileDialog) }) {
this->file_dialog = dialog;
add_child(dialog);
dialog->set_filters({ "*.terrain.tres;World Resource Files;", "*.terrain.res;Binary World Resource Files;" });
dialog->set_access(FileDialog::ACCESS_FILESYSTEM);
dialog->connect("file_selected", callable_mp(this, &self_type::on_save_file_selected));
}
} }
void TerrainMeshEditor::on_primitive_list_changed(Array primitives) { void TerrainMeshEditor::on_primitive_list_changed(Array primitives) {
this->out_of_date = true;
for (Node3D *existing : this->primitive_nodes) { for (Node3D *existing : this->primitive_nodes) {
existing->queue_free(); existing->queue_free();
} }
@ -26,11 +65,34 @@ void TerrainMeshEditor::on_primitive_list_changed(Array primitives) {
PointPrimitiveNode *primitive_node{ cast_to<PointPrimitiveNode>(this->point_primitive_object->instantiate()) }; PointPrimitiveNode *primitive_node{ cast_to<PointPrimitiveNode>(this->point_primitive_object->instantiate()) };
primitive_node->set_primitive(point); primitive_node->set_primitive(point);
this->add_child(primitive_node); this->add_child(primitive_node);
this->primitive_nodes.push_back(primitive_node);
} }
} }
} }
} }
void TerrainMeshEditor::on_save_file_selected(String path) {
switch (this->file_dialog->get_file_mode()) {
default:
print_error("Attempt to open or save with invalid file dialog mode");
break;
case FileDialog::FILE_MODE_SAVE_FILE:
this->data->set_save_path(path);
this->data->set_primitives(get_primitives());
this->data->write_to_file();
break;
case FileDialog::FILE_MODE_OPEN_FILE:
Ref<Resource> resource{ ResourceLoader::load(path, "SaveData", ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP) };
Ref<SaveData> loaded_data{ resource };
if (loaded_data.is_valid()) {
this->data = loaded_data;
this->set_primitives(this->data->get_primitives().duplicate(true));
this->data->set_save_path(path);
}
break;
}
}
void TerrainMeshEditor::_notification(int what) { void TerrainMeshEditor::_notification(int what) {
if (Engine::get_singleton()->is_editor_hint()) { if (Engine::get_singleton()->is_editor_hint()) {
return; return;
@ -39,11 +101,42 @@ void TerrainMeshEditor::_notification(int what) {
default: default:
return; return;
case NOTIFICATION_READY: case NOTIFICATION_READY:
set_process_unhandled_input(true);
ready(); ready();
return; return;
} }
} }
void TerrainMeshEditor::unhandled_input(Ref<InputEvent> const &event) {
Ref<InputEventKey> key{ event };
if (key.is_valid() && key->get_key_label() == Key::S && key->get_modifiers_mask() == (KeyModifierMask::SHIFT | KeyModifierMask::CTRL)) {
this->file_dialog->set_file_mode(FileDialog::FILE_MODE_SAVE_FILE);
this->file_dialog->set_ok_button_text("Save");
this->file_dialog->popup_file_dialog();
} else if (key.is_valid() && key->get_key_label() == Key::S && key->get_modifiers_mask() == KeyModifierMask::CTRL) {
save_data();
} else if (key.is_valid() && key->get_key_label() == Key::O && key->get_modifiers_mask() == KeyModifierMask::CTRL) {
this->file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE);
this->file_dialog->set_ok_button_text("Open");
this->file_dialog->popup_file_dialog();
}
}
void TerrainMeshEditor::save_data() {
// TODO: figure out if this is the correct way to see if the popup is popped up
if (this->file_dialog->is_visible()) {
return;
}
if (this->data->get_save_path().is_empty()) {
this->file_dialog->set_file_mode(FileDialog::FILE_MODE_SAVE_FILE);
this->file_dialog->set_ok_button_text("Save");
this->file_dialog->popup_file_dialog();
} else {
this->data->set_primitives(get_primitives());
this->data->write_to_file();
}
}
void TerrainMeshEditor::set_point_primitive_object(Ref<PackedScene> scene) { void TerrainMeshEditor::set_point_primitive_object(Ref<PackedScene> scene) {
this->point_primitive_object = scene; this->point_primitive_object = scene;
} }

View file

@ -1,20 +1,46 @@
#pragma once #pragma once
#include "core/io/resource.h"
#include "scene/gui/file_dialog.h"
#include "terrain_editor/terrain_mesh_generator.h" #include "terrain_editor/terrain_mesh_generator.h"
class SaveData : public Resource {
GDCLASS(SaveData, Resource);
static void _bind_methods();
public:
void write_to_file();
void set_save_path(String value);
String get_save_path() const;
void set_primitives(Array primitives);
Array get_primitives() const;
private:
String save_file_path{};
Array primitives{};
};
class TerrainMeshEditor : public TerrainMeshGenerator { class TerrainMeshEditor : public TerrainMeshGenerator {
GDCLASS(TerrainMeshEditor, TerrainMeshGenerator); GDCLASS(TerrainMeshEditor, TerrainMeshGenerator);
static void _bind_methods(); static void _bind_methods();
void ready(); void ready();
void on_primitive_list_changed(Array primitives); void on_primitive_list_changed(Array primitives);
void on_primitive_node_removed(); void on_primitive_node_removed();
void on_save_file_selected(String path);
protected: protected:
void _notification(int what); void _notification(int what);
virtual void unhandled_input(Ref<InputEvent> const &event) override;
public:
void save_data();
void set_point_primitive_object(Ref<PackedScene> scene); void set_point_primitive_object(Ref<PackedScene> scene);
Ref<PackedScene> get_point_primitive_object() const; Ref<PackedScene> get_point_primitive_object() const;
private: private:
Ref<SaveData> data{ memnew(SaveData) };
FileDialog *file_dialog{};
bool out_of_date{ false };
Vector<Node3D *> primitive_nodes{}; Vector<Node3D *> primitive_nodes{};
Ref<PackedScene> point_primitive_object{}; Ref<PackedScene> point_primitive_object{};
}; };