Skip to content

Commit

Permalink
Add terrain_edited signal to Terrain3DEditor, which is now a singleton.
Browse files Browse the repository at this point in the history
  • Loading branch information
tcoxon authored and TokisanGames committed Jan 3, 2024
1 parent 67c86c6 commit 4021de6
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 14 deletions.
18 changes: 18 additions & 0 deletions doc/classes/Terrain3DEditor.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
<description>
</description>
</method>
<method name="get_singleton" qualifiers="static">
<return type="Terrain3DEditor" />
<description>
</description>
</method>
<method name="get_terrain">
<return type="Terrain3D" />
<description>
Expand Down Expand Up @@ -71,6 +76,19 @@
</description>
</method>
</methods>
<signals>
<signal name="terrain_edited">
<param index="0" name="edited_area" type="AABB" />
<description>
This signal is emitted whenever the editor is used to:
- add or remove a region,
- alter a region map with a brush tool,
- undo or redo any of the above operations.

The parameter contains the axis-aligned bounding box of the area edited.
</description>
</signal>
</signals>
<constants>
<constant name="ADD" value="0" enum="Operation">
</constant>
Expand Down
4 changes: 2 additions & 2 deletions project/addons/terrain_3d/editor/editor.gd
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var mouse_global_position: Vector3 = Vector3.ZERO


func _enter_tree() -> void:
editor = Terrain3DEditor.new()
editor = Terrain3DEditor.get_singleton()
ui = UI.new()
ui.plugin = self
add_child(ui)
Expand All @@ -44,7 +44,7 @@ func _exit_tree() -> void:
remove_control_from_container(texture_dock_container, texture_dock)
texture_dock.queue_free()
ui.queue_free()
editor.free()
editor = null


func _handles(p_object: Object) -> bool:
Expand Down
4 changes: 4 additions & 0 deletions src/register_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ void initialize_terrain_3d(ModuleInitializationLevel p_level) {
ClassDB::register_class<Terrain3DTexture>();
ClassDB::register_class<Terrain3DTextureList>();
ClassDB::register_class<Terrain3DEditor>();

Terrain3DEditor::create();
}

void uninitialize_terrain_3d(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}

Terrain3DEditor::free();
}

extern "C" {
Expand Down
86 changes: 77 additions & 9 deletions src/terrain_3d_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "terrain_3d_editor.h"
#include "util.h"

Terrain3DEditor *Terrain3DEditor::_singleton = nullptr;

///////////////////////////
// Subclass Functions
///////////////////////////
Expand Down Expand Up @@ -43,21 +45,49 @@ void Terrain3DEditor::Brush::set_data(Dictionary p_data) {
// Private Functions
///////////////////////////

void Terrain3DEditor::_region_modified(Vector3 p_global_position, Vector2 p_height_range) {
Vector2i region_offset = _terrain->get_storage()->get_region_offset(p_global_position);
Terrain3DStorage::RegionSize region_size = _terrain->get_storage()->get_region_size();

AABB edited_area;
edited_area.position = Vector3(region_offset.x * region_size, p_height_range.x, region_offset.y * region_size);
edited_area.size = Vector3(region_size, p_height_range.y - p_height_range.x, region_size);

if (_modified) {
_modified_area = _modified_area.merge(edited_area);
} else {
_modified_area = edited_area;
}
_modified = true;
emit_signal("terrain_edited", edited_area);
}

void Terrain3DEditor::_operate_region(Vector3 p_global_position) {
bool has_region = _terrain->get_storage()->has_region(p_global_position);
bool modified = false;
Vector2 height_range;

if (_operation == ADD) {
if (!has_region) {
_terrain->get_storage()->add_region(p_global_position);
_modified = true;
modified = true;

}
}
if (_operation == SUBTRACT) {
if (has_region) {
int region_index = _terrain->get_storage()->get_region_index(p_global_position);
Ref<Image> height_map = _terrain->get_storage()->get_map_region(Terrain3DStorage::TYPE_HEIGHT, region_index);
height_range = Util::get_min_max(height_map);

_terrain->get_storage()->remove_region(p_global_position);
_modified = true;
modified = true;
}
}

if (modified) {
_region_modified(p_global_position, height_range);
}
}

void Terrain3DEditor::_operate_map(Vector3 p_global_position, real_t p_camera_direction) {
Expand All @@ -72,13 +102,13 @@ void Terrain3DEditor::_operate_map(Vector3 p_global_position, real_t p_camera_di
} else {
LOG(DEBUG, "No region to operate on, attempting to add");
storage->add_region(p_global_position);
_modified = true;
region_size = storage->get_region_size();
region_index = storage->get_region_index(p_global_position);
if (region_index == -1) {
LOG(ERROR, "Failed to add region, no region to operate on");
return;
}
_region_modified(p_global_position);
}
}
if (_tool < 0 || _tool >= REGION) {
Expand Down Expand Up @@ -123,6 +153,10 @@ void Terrain3DEditor::_operate_map(Vector3 p_global_position, real_t p_camera_di
}
Object::cast_to<Node>(_terrain->get_plugin()->get("ui"))->call("set_decal_rotation", rot);

AABB edited_area;
edited_area.position = p_global_position - Vector3(brush_size, 0.0, brush_size) / 2;
edited_area.size = Vector3(brush_size, 0.0, brush_size);

for (int x = 0; x < brush_size; x++) {
for (int y = 0; y < brush_size; y++) {
Vector2i brush_offset = Vector2i(x, y) - (Vector2i(brush_size, brush_size) / 2);
Expand All @@ -141,6 +175,7 @@ void Terrain3DEditor::_operate_map(Vector3 p_global_position, real_t p_camera_di
continue;
}
new_region_index = storage->get_region_index(brush_global_position);
_region_modified(brush_global_position);
}

if (new_region_index != region_index) {
Expand All @@ -160,6 +195,10 @@ void Terrain3DEditor::_operate_map(Vector3 p_global_position, real_t p_camera_di
continue;
}

Vector3 edited_position = brush_global_position;
edited_position.y = storage->get_height(edited_position);
edited_area = edited_area.expand(edited_position);

// Start brushing on the map
real_t brush_alpha = real_t(Math::pow(double(_brush.get_alpha(brush_pixel_position)), double(gamma)));
Color src = map->get_pixelv(map_pixel_position);
Expand Down Expand Up @@ -208,6 +247,9 @@ void Terrain3DEditor::_operate_map(Vector3 p_global_position, real_t p_camera_di
dest = Color(destf, 0.0f, 0.0f, 1.0f);
storage->update_heights(destf);

edited_position.y = destf;
edited_area = edited_area.expand(edited_position);

} else if (map_type == Terrain3DStorage::TYPE_CONTROL) {
// Get bit field from pixel
uint32_t base_id = Util::get_base(src.r);
Expand Down Expand Up @@ -307,8 +349,14 @@ void Terrain3DEditor::_operate_map(Vector3 p_global_position, real_t p_camera_di
}
}
}
if (_modified) {
_modified_area = _modified_area.merge(edited_area);
} else {
_modified_area = edited_area;
}
_modified = true;
storage->force_update_maps(map_type);
emit_signal("terrain_edited", edited_area);
}

bool Terrain3DEditor::_is_in_bounds(Vector2i p_position, Vector2i p_max_position) {
Expand Down Expand Up @@ -336,6 +384,7 @@ Vector2 Terrain3DEditor::_rotate_uv(Vector2 p_uv, real_t p_angle) {
* 0-2: map 0,1,2
* 3: Region offsets
* 4: height range
* 5: edited AABB
*/
void Terrain3DEditor::_setup_undo() {
ERR_FAIL_COND_MSG(_terrain == nullptr, "terrain is null, returning");
Expand All @@ -345,14 +394,16 @@ void Terrain3DEditor::_setup_undo() {
}
LOG(INFO, "Setting up undo snapshot...");
_undo_set.clear();
_undo_set.resize(Terrain3DStorage::TYPE_MAX + 2);
_undo_set.resize(Terrain3DStorage::TYPE_MAX + 3);
for (int i = 0; i < Terrain3DStorage::TYPE_MAX; i++) {
_undo_set[i] = _terrain->get_storage()->get_maps_copy(static_cast<Terrain3DStorage::MapType>(i));
LOG(DEBUG, "maps ", i, "(", static_cast<TypedArray<Image>>(_undo_set[i]).size(), "): ", _undo_set[i]);
}
_undo_set[Terrain3DStorage::TYPE_MAX] = _terrain->get_storage()->get_region_offsets().duplicate();
LOG(DEBUG, "region_offsets(", static_cast<TypedArray<Vector2i>>(_undo_set[Terrain3DStorage::TYPE_MAX]).size(), "): ", _undo_set[Terrain3DStorage::TYPE_MAX]);
_undo_set[Terrain3DStorage::TYPE_MAX + 1] = _terrain->get_storage()->get_height_range();

_undo_set[Terrain3DStorage::TYPE_MAX + 2] = _modified_area;
}

void Terrain3DEditor::_store_undo() {
Expand All @@ -368,12 +419,15 @@ void Terrain3DEditor::_store_undo() {
LOG(DEBUG, "Creating undo action: '", action_name, "'");
undo_redo->create_action(action_name);

LOG(DEBUG, "Updating undo snapshot modified area: ", _modified_area);
_undo_set[Terrain3DStorage::TYPE_MAX + 2] = _modified_area;

LOG(DEBUG, "Storing undo snapshot: ", _undo_set);
undo_redo->add_undo_method(this, "apply_undo", _undo_set.duplicate()); // Must be duplicated

LOG(DEBUG, "Setting up redo snapshot...");
Array redo_set;
redo_set.resize(Terrain3DStorage::TYPE_MAX + 2);
redo_set.resize(Terrain3DStorage::TYPE_MAX + 3);
for (int i = 0; i < Terrain3DStorage::TYPE_MAX; i++) {
redo_set[i] = _terrain->get_storage()->get_maps_copy(static_cast<Terrain3DStorage::MapType>(i));
LOG(DEBUG, "maps ", i, "(", static_cast<TypedArray<Image>>(redo_set[i]).size(), "): ", redo_set[i]);
Expand All @@ -382,6 +436,9 @@ void Terrain3DEditor::_store_undo() {
LOG(DEBUG, "region_offsets(", static_cast<TypedArray<Vector2i>>(redo_set[Terrain3DStorage::TYPE_MAX]).size(), "): ", redo_set[Terrain3DStorage::TYPE_MAX]);
redo_set[Terrain3DStorage::TYPE_MAX + 1] = _terrain->get_storage()->get_height_range();

LOG(DEBUG, "Storing modified area: ", _modified_area);
redo_set[Terrain3DStorage::TYPE_MAX + 2] = _modified_area;

LOG(DEBUG, "Storing redo snapshot: ", redo_set);
undo_redo->add_do_method(this, "apply_undo", redo_set);

Expand All @@ -406,20 +463,25 @@ void Terrain3DEditor::_apply_undo(const Array &p_set) {
LOG(DEBUG, "Calling GDScript update_grid()");
_terrain->get_plugin()->call("update_grid");
}

_pending_undo = false;
_modified = false;
}
_modified_area = AABB();

///////////////////////////
// Public Functions
///////////////////////////
AABB edited_area = p_set[Terrain3DStorage::TYPE_MAX + 2];
emit_signal("terrain_edited", edited_area);
}

Terrain3DEditor::Terrain3DEditor() {
}

Terrain3DEditor::~Terrain3DEditor() {
}

///////////////////////////
// Public Functions
///////////////////////////

void Terrain3DEditor::set_brush_data(Dictionary p_data) {
if (p_data.is_empty()) {
return;
Expand All @@ -437,6 +499,7 @@ void Terrain3DEditor::start_operation(Vector3 p_global_position) {
_setup_undo();
_pending_undo = true;
_modified = false;
_modified_area = AABB();
if (_tool == REGION) {
_operate_region(p_global_position);
}
Expand Down Expand Up @@ -468,6 +531,7 @@ void Terrain3DEditor::stop_operation() {
_store_undo();
_pending_undo = false;
_modified = false;
_modified_area = AABB();
}
}

Expand All @@ -494,6 +558,8 @@ void Terrain3DEditor::_bind_methods() {
BIND_ENUM_CONSTANT(REGION);
BIND_ENUM_CONSTANT(TOOL_MAX);

ClassDB::bind_static_method("Terrain3DEditor", D_METHOD("get_singleton"), &Terrain3DEditor::get_singleton);

ClassDB::bind_method(D_METHOD("set_terrain", "terrain"), &Terrain3DEditor::set_terrain);
ClassDB::bind_method(D_METHOD("get_terrain"), &Terrain3DEditor::get_terrain);

Expand All @@ -507,4 +573,6 @@ void Terrain3DEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("stop_operation"), &Terrain3DEditor::stop_operation);

ClassDB::bind_method(D_METHOD("apply_undo", "maps"), &Terrain3DEditor::_apply_undo);

ADD_SIGNAL(MethodInfo("terrain_edited", PropertyInfo(Variant::AABB, "edited_area")));
}
22 changes: 19 additions & 3 deletions src/terrain_3d_editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,10 @@ class Terrain3DEditor : public Object {
real_t _operation_interval = 0.0f;
bool _pending_undo = false;
bool _modified = false;
Array _undo_set; // 0-2: map 0,1,2, 3: Region offsets, 4: height range
AABB _modified_area;
Array _undo_set; // 0-2: map 0,1,2, 3: Region offsets, 4: height range, 5: edited AABB

void _region_modified(Vector3 p_global_position, Vector2 p_height_range = Vector2());
void _operate_region(Vector3 p_global_position);
void _operate_map(Vector3 p_global_position, real_t p_camera_direction);
bool _is_in_bounds(Vector2i p_position, Vector2i p_max_position);
Expand All @@ -126,10 +128,24 @@ class Terrain3DEditor : public Object {
void _store_undo();
void _apply_undo(const Array &p_set);

public:
// Singleton
friend void initialize_terrain_3d(ModuleInitializationLevel p_level);
friend void uninitialize_terrain_3d(ModuleInitializationLevel p_level);

static Terrain3DEditor *_singleton;

static void create() { _singleton = memnew(Terrain3DEditor); }
static void free() {
memdelete(_singleton);
_singleton = nullptr;
}

Terrain3DEditor();
~Terrain3DEditor();

public:
static Terrain3DEditor *get_singleton() { return _singleton; }

void set_terrain(Terrain3D *p_terrain) { _terrain = p_terrain; }
Terrain3D *get_terrain() const { return _terrain; }

Expand All @@ -150,4 +166,4 @@ class Terrain3DEditor : public Object {
VARIANT_ENUM_CAST(Terrain3DEditor::Operation);
VARIANT_ENUM_CAST(Terrain3DEditor::Tool);

#endif // TERRAIN3D_EDITOR_CLASS_H
#endif // TERRAIN3D_EDITOR_CLASS_H

0 comments on commit 4021de6

Please sign in to comment.