From 73ca9189d672964b617c6e3972bc2f58567e65e6 Mon Sep 17 00:00:00 2001 From: Cory Petkovsek <632766+TokisanGames@users.noreply.github.com> Date: Sun, 25 Aug 2024 11:15:23 +0700 Subject: [PATCH] Undo operations works, but not operations + regions --- src/terrain_3d.cpp | 11 +- src/terrain_3d_editor.cpp | 278 ++++++++++++++++++++----------------- src/terrain_3d_editor.h | 49 ++++++- src/terrain_3d_region.cpp | 17 +-- src/terrain_3d_storage.cpp | 17 +-- src/terrain_3d_storage.h | 7 +- src/terrain_3d_util.cpp | 49 ++++++- src/terrain_3d_util.h | 3 +- 8 files changed, 267 insertions(+), 164 deletions(-) diff --git a/src/terrain_3d.cpp b/src/terrain_3d.cpp index 90644293a..63e616554 100644 --- a/src/terrain_3d.cpp +++ b/src/terrain_3d.cpp @@ -141,11 +141,9 @@ void Terrain3D::_destroy_containers() { void Terrain3D::_destroy_labels() { Array labels = _label_nodes->get_children(); + LOG(DEBUG, "Destroying ", labels.size(), " region labels"); for (int i = 0; i < labels.size(); i++) { Node *label = cast_to(labels[i]); - if (label != nullptr) { - LOG(DEBUG, "Destroying label: ", label->get_name()); - } memdelete_safely(label); } } @@ -1033,11 +1031,10 @@ void Terrain3D::update_aabbs() { } } -/* Iterate over ground to find intersection point between two rays: +/* Returns the point a ray intersects the ground using the GPU depth texture * p_src_pos (camera position) * p_direction (camera direction looking at the terrain) - * test_dir (camera direction 0 Y, traversing terrain along height - * Returns vec3(Double max 3.402823466e+38F) on no intersection. Test w/ if (var.x < 3.4e38) + * Returns Vec3(NAN) on error or vec3(3.402823466e+38F) on no intersection. Test w/ if (var.x < 3.4e38) */ Vector3 Terrain3D::get_intersection(const Vector3 &p_src_pos, const Vector3 &p_direction) { if (!is_instance_valid(_camera_instance_id)) { @@ -1102,10 +1099,10 @@ void Terrain3D::set_show_region_labels(const bool p_enabled) { } void Terrain3D::update_region_labels() { - LOG(DEBUG, "Updating region labels"); _destroy_labels(); if (_show_region_labels && _storage != nullptr) { Array region_locations = _storage->get_region_locations(); + LOG(DEBUG, "Creating ", region_locations.size(), " region labels"); for (int i = 0; i < region_locations.size(); i++) { Label3D *label = memnew(Label3D); String text = region_locations[i]; diff --git a/src/terrain_3d_editor.cpp b/src/terrain_3d_editor.cpp index 85c2a5f18..4aeccb33b 100644 --- a/src/terrain_3d_editor.cpp +++ b/src/terrain_3d_editor.cpp @@ -1,6 +1,7 @@ // Copyright © 2024 Cory Petkovsek, Roope Palmroos, and Contributors. #include +#include #include "logger.h" #include "terrain_3d_editor.h" @@ -22,41 +23,62 @@ void Terrain3DEditor::_send_region_aabb(const Vector2i &p_region_loc, const Vect _terrain->get_storage()->add_edited_area(edited_area); } -// Adds/removes region, returns after +// Process location to add new region, mark as deleted, or just retrieve Ref Terrain3DEditor::_operate_region(const Vector2i &p_region_loc) { bool changed = false; Vector2 height_range; Terrain3DStorage *storage = _terrain->get_storage(); + + // Check if in bounds, limiting errors + bool can_print = false; + uint64_t ticks = Time::get_singleton()->get_ticks_msec(); + if (ticks - _last_region_bounds_error > 1000) { + _last_region_bounds_error = ticks; + can_print = true; + } + if (storage->get_region_map_index(p_region_loc) < 0) { + if (can_print) { + LOG(ERROR, "Specified position outside of maximum bounds"); + } + return Ref(); + } + + // Get Region & dump data if debug Ref region = storage->get_region(p_region_loc); - // If no active region, create new one - if (region.is_null() || (region.is_valid() && region->is_deleted())) { - // Except if outside of regions and we can't add one - if (!(_tool == REGION || (_tool == HEIGHT && _brush_data["auto_regions"]))) { - return Ref(); + if (can_print) { + LOG(DEBUG, "Tool: ", _tool, " Op: ", _operation, " processing region ", p_region_loc, ": ", + region.is_valid() ? String::num_uint64(region->get_instance_id()) : "Null"); + if (region.is_valid()) { + LOG(DEBUG, region->get_data()); } - region = storage->add_region_blank(p_region_loc); - changed = true; - if (region.is_null()) { - // A new region can't be made - return Ref(); + } + + // Create new region if location is null or deleted + if (region.is_null() || (region.is_valid() && region->is_deleted())) { + // And tool is Add Region, or Height + auto_regions + if ((_tool == REGION && _operation == ADD) || (_tool == HEIGHT && _brush_data["auto_regions"])) { + region = storage->add_region_blank(p_region_loc); + changed = true; + if (region.is_null()) { + // A new region can't be made + LOG(ERROR, "A new region cannot be created"); + return region; + } } - // If removing region - } else if (_tool == REGION && _operation == SUBTRACT) { + } + + // If removing region + else if (region.is_valid() && _tool == REGION && _operation == SUBTRACT) { height_range = region->get_height_range(); _terrain->get_storage()->remove_region(region); changed = true; } - if (changed && !region->is_edited()) { - _original_regions.push_back(region); - _modified_regions.push_back(region); - region->set_edited(true); + if (changed) { + _addremoved_regions.push_back(region); region->set_modified(true); _send_region_aabb(p_region_loc, height_range); } - if (region->is_deleted()) { - return Ref(); - } return region; } @@ -66,34 +88,17 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_ } LOG(DEBUG_CONT, "Operating at ", p_global_position, " tool type ", _tool, " op ", _operation); - MapType map_type; - switch (_tool) { - case HEIGHT: - case INSTANCER: - map_type = TYPE_HEIGHT; - break; - case TEXTURE: - case AUTOSHADER: - case HOLES: - case NAVIGATION: - case ANGLE: - case SCALE: - map_type = TYPE_CONTROL; - break; - case COLOR: - case ROUGHNESS: - map_type = TYPE_COLOR; - break; - default: - LOG(ERROR, "Invalid tool selected"); - return; + MapType map_type = _get_map_type(); + if (map_type == TYPE_MAX) { + LOG(ERROR, "Invalid tool selected"); + return; } Terrain3DStorage *storage = _terrain->get_storage(); int region_size = storage->get_region_size(); Vector2i region_vsize = Vector2i(region_size, region_size); - // If no region and not height, skip whole function. Checked again later + // If no region and can't add one, skip whole function. Checked again later if (!storage->has_regionp(p_global_position) && (!_brush_data["auto_regions"] || _tool != HEIGHT)) { return; } @@ -418,13 +423,13 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_ break; } } - map->set_pixelv(map_pixel_position, dest); if (!region->is_edited()) { _original_regions.push_back(region->duplicate(map_type, true)); - _modified_regions.push_back(region); + _edited_regions.push_back(region); region->set_edited(true); region->set_modified(true); } + map->set_pixelv(map_pixel_position, dest); } } } @@ -432,19 +437,6 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_ storage->add_edited_area(edited_area); } -bool Terrain3DEditor::_is_in_bounds(const Vector2i &p_position, const Vector2i &p_max_position) const { - bool more_than_min = p_position.x >= 0 && p_position.y >= 0; - bool less_than_max = p_position.x < p_max_position.x && p_position.y < p_max_position.y; - return more_than_min && less_than_max; -} - -real_t Terrain3DEditor::_get_brush_alpha(const Vector2i &p_position) const { - if (_brush_image.is_valid()) { - return _brush_image->get_pixelv(p_position).r; - } - return NAN; -} - Vector2 Terrain3DEditor::_get_uv_position(const Vector3 &p_global_position, const int p_region_size) const { Vector2 descaled_position_2d = Vector2(p_global_position.x, p_global_position.z) / _terrain->get_mesh_vertex_spacing(); Vector2 region_position = descaled_position_2d / real_t(p_region_size); @@ -453,26 +445,37 @@ Vector2 Terrain3DEditor::_get_uv_position(const Vector3 &p_global_position, cons return uv_position; } -Vector2 Terrain3DEditor::_get_rotated_uv(const Vector2 &p_uv, const real_t p_angle) const { - Vector2 rotation_offset = Vector2(0.5f, 0.5f); - Vector2 uv = (p_uv - rotation_offset).rotated(p_angle) + rotation_offset; - return uv.clamp(V2_ZERO, Vector2(1.f, 1.f)); -} - void Terrain3DEditor::_store_undo() { IS_INIT_COND_MESG(_terrain->get_plugin() == nullptr, "_terrain isn't initialized, returning", VOID); if (_tool < 0 || _tool >= TOOL_MAX) { return; } - // Collect data - _undo_data["regions"] = _original_regions; + LOG(DEBUG, "Finalize undo & redo snapshots"); + Dictionary redo_data; + // Store current locations; Original backed up in start_operation() + redo_data["region_locations"] = _terrain->get_storage()->get_region_locations().duplicate(); + // Store original and current backups of edited regions + _undo_data["edited_regions"] = _original_regions; + redo_data["edited_regions"] = _edited_regions; + + // Store regions that were added or removed + if (_addremoved_regions.size() > 0) { + if (_tool == REGION && _operation == SUBTRACT) { + _undo_data["removed_regions"] = _addremoved_regions; + redo_data["added_regions"] = _addremoved_regions; + } else { + _undo_data["added_regions"] = _addremoved_regions; + redo_data["removed_regions"] = _addremoved_regions; + } + } + if (_undo_data.has("edited_area")) { _undo_data["edited_area"] = _terrain->get_storage()->get_edited_area(); LOG(DEBUG, "Updating undo snapshot edited area: ", _undo_data["edited_area"]); } - // Store data + // Store data in Godot's Undo/Redo Manager LOG(INFO, "Storing undo snapshot..."); EditorUndoRedoManager *undo_redo = _terrain->get_plugin()->get_undo_redo(); String action_name = String("Terrain3D ") + OPNAME[_operation] + String(" ") + TOOLNAME[_tool]; @@ -480,21 +483,18 @@ void Terrain3DEditor::_store_undo() { undo_redo->create_action(action_name); LOG(DEBUG, "Storing undo snapshot: ", _undo_data); - undo_redo->add_undo_method(this, "apply_undo", _undo_data.duplicate()); - - LOG(DEBUG, "Setting up redo snapshot..."); - Dictionary redo_data; - redo_data["tool"] = _tool; - if (_operation == ADD) { - redo_data["operation"] = SUBTRACT; - } - if (_operation == SUBTRACT) { - redo_data["operation"] = ADD; + undo_redo->add_undo_method(this, "apply_undo", _undo_data); + for (int i = 0; i < _original_regions.size(); i++) { + Ref region = _original_regions[i]; + LOG(DEBUG, "Original Region: ", region->get_data()); } - redo_data["regions"] = _modified_regions; LOG(DEBUG, "Storing redo snapshot: ", redo_data); undo_redo->add_do_method(this, "apply_undo", redo_data); + for (int i = 0; i < _edited_regions.size(); i++) { + Ref region = _edited_regions[i]; + LOG(DEBUG, "Edited Region: ", region->get_data()); + } LOG(DEBUG, "Committing undo action"); undo_redo->commit_action(false); @@ -502,47 +502,68 @@ void Terrain3DEditor::_store_undo() { void Terrain3DEditor::_apply_undo(const Dictionary &p_data) { IS_INIT_COND_MESG(_terrain->get_plugin() == nullptr, "_terrain isn't initialized, returning", VOID); - LOG(INFO, "Applying Undo/Redo set. Array size: ", p_data.size()); - LOG(DEBUG, "Apply undo received: ", p_data); + LOG(INFO, "Applying Undo/Redo data"); Terrain3DStorage *storage = _terrain->get_storage(); - Tool tool = (Tool)(int)p_data.get("tool", TOOL_MAX); - Operation op = (Operation)(int)p_data.get("operation", OP_MAX); - - TypedArray mod_regions = p_data["regions"]; - for (int i = 0; i < mod_regions.size(); i++) { - Ref region = mod_regions[i]; - switch (tool) { - case REGION: - if (op == ADD) { - storage->remove_region(region, false); - region->set_modified(true); - } else if (op == SUBTRACT) { - storage->add_region(region, false); - region->set_modified(true); - } + if (p_data.has("edited_regions")) { + Util::print_arr("Edited regions", p_data["edited_regions"]); + TypedArray undo_regions = p_data["edited_regions"]; + LOG(DEBUG, "Region has ", undo_regions.size(), " edited regions"); + for (int i = 0; i < undo_regions.size(); i++) { + Ref region = undo_regions[i]; + if (region.is_null()) { + LOG(ERROR, "Null region saved in undo data. Please report this error."); + continue; + } + region->sanitize_map(); // Live data may not have some maps so must be sanitized + Dictionary regions = storage->get_regions_all(); + regions[region->get_location()] = region; + region->set_modified(true); + LOG(DEBUG, "Edited: ", region->get_data()); } } - /* - Array keys = p_set.keys(); - for (int i = 0; i < keys.size(); i++) { - String key = keys[i]; - // Undo Add region - if (key == "region_locations") { - TypedArray new_locations = storage->get_region_locations(); - TypedArray old_locations = p_set[key]; - _terrain->get_storage()->set_region_locations(p_set[key]); + /// The added/removed regions are not the same instance as above. Only need the location. + if (p_data.has("added_regions")) { + Util::print_arr("Added regions", p_data["added_regions"]); + TypedArray regions = p_data["added_regions"]; + for (int i = 0; i < regions.size(); i++) { + Ref region = regions[i]; + if (region.is_null()) { + LOG(ERROR, "Null region saved in addeded_regions undo data. Please report this error."); + continue; + } + Vector2i location = region->get_location(); + storage->set_region_deleted(location, true); + storage->set_region_modified(location, true); + LOG(DEBUG, "Marking region: ", location, " +deleted, +modified"); + } + } + if (p_data.has("removed_regions")) { + Util::print_arr("Removed regions", p_data["removed_regions"]); + TypedArray regions = p_data["removed_regions"]; + for (int i = 0; i < regions.size(); i++) { + Ref region = regions[i]; + if (region.is_null()) { + LOG(ERROR, "Null region saved in removed_regions undo data. Please report this error."); + continue; + } + Vector2i location = region->get_location(); + storage->set_region_deleted(location, false); + storage->set_region_modified(location, true); + LOG(DEBUG, "Marking region: ", location, " -deleted, +modified"); } + } - else if (key == "height_map") { - //_terrain->get_storage()->set_maps(TYPE_HEIGHT, p_set[key]); - } else if (key == "control_map") { - //_terrain->get_storage()->set_maps(TYPE_CONTROL, p_set[key]); - } else if (key == "color_map") { - //_terrain->get_storage()->set_maps(TYPE_COLOR, p_set[key]); - } else if (key == "height_range") { + // After all regions are in place, reset the region map, which also updates + if (p_data.has("region_locations")) { + _terrain->get_storage()->set_region_locations(p_data["region_locations"]); + Array locations = storage->get_region_locations(); + LOG(DEBUG, "Locations(", locations.size(), "): ", locations); + } + + /* else if (key == "height_range") { //_terrain->get_storage()->set_height_range(p_set[key]); } else if (key == "edited_area") { _terrain->get_storage()->clear_edited_area(); @@ -553,9 +574,6 @@ void Terrain3DEditor::_apply_undo(const Dictionary &p_data) { } */ - //LOG(WARN, "Applying undo before clearing. Modified regions: ", _modified_regions); - //_pending_undo = false; - //_modified_regions.clear(); storage->force_update_maps(); if (_terrain->get_plugin()->has_method("update_grid")) { LOG(DEBUG, "Calling GDScript update_grid()"); @@ -624,13 +642,12 @@ void Terrain3DEditor::start_operation(const Vector3 &p_global_position) { IS_STORAGE_INIT_MESG("Terrain isn't initialized", VOID); LOG(INFO, "Setting up undo snapshot..."); LOG(WARN, "Starting op, clear"); - _undo_data.clear(); - //_undo_data = _get_undo_data(); - _undo_data["tool"] = _tool; - _undo_data["operation"] = _operation; + _undo_data = Dictionary(); // New pointer instead of clear + _undo_data["region_locations"] = _terrain->get_storage()->get_region_locations().duplicate(); _is_operating = true; - _original_regions = TypedArray(); // Get new pointers as original is stored in undo - _modified_regions = TypedArray(); + _original_regions = TypedArray(); // New pointers instead of clear + _edited_regions = TypedArray(); + _addremoved_regions = TypedArray(); // Reset counter at start to ensure first click places an instance _terrain->get_instancer()->reset_instance_counter(); _terrain->get_storage()->clear_edited_area(); @@ -671,17 +688,24 @@ void Terrain3DEditor::operate(const Vector3 &p_global_position, const real_t p_c void Terrain3DEditor::stop_operation() { IS_STORAGE_INIT_MESG("Terrain isn't initialized", VOID); // If undo was created and terrain actually modified, store it - LOG(WARN, "Original regions: ", _original_regions); - LOG(WARN, "Modified regions: ", _modified_regions); - //pending undo needed? - if (_is_operating && !_modified_regions.is_empty()) { - for (int i = 0; i < _modified_regions.size(); i++) { - Ref(_modified_regions[i])->set_edited(false); + if (_is_operating && (!_addremoved_regions.is_empty() || !_edited_regions.is_empty())) { + for (int i = 0; i < _edited_regions.size(); i++) { + Ref region = _edited_regions[i]; + region->set_edited(false); + // Make duplicate for redo backup + MapType map_type = _get_map_type(); + if (map_type != TYPE_MAX) { + region = region->duplicate(map_type, true); + } else { + region = region->duplicate(); + } + _edited_regions[i] = region; } _store_undo(); } - _original_regions = TypedArray(); // Get new pointers as original is stored in undo - _modified_regions = TypedArray(); + _original_regions = TypedArray(); //New pointers instead of clear + _edited_regions = TypedArray(); + _addremoved_regions = TypedArray(); _terrain->get_storage()->clear_edited_area(); _is_operating = false; } diff --git a/src/terrain_3d_editor.h b/src/terrain_3d_editor.h index 4944e192b..05a46b9e4 100644 --- a/src/terrain_3d_editor.h +++ b/src/terrain_3d_editor.h @@ -76,14 +76,17 @@ class Terrain3DEditor : public Object { Vector3 _operation_movement = Vector3(); Array _operation_movement_history; bool _is_operating = false; - TypedArray _original_regions; // Queued regions for undo/redo - TypedArray _modified_regions; // Queued regions for undo/redo + uint64_t _last_region_bounds_error = 0; + TypedArray _original_regions; // Queue for undo + TypedArray _edited_regions; // Queue for redo + TypedArray _addremoved_regions; // Queue for add/remove regions AABB _modified_area; Dictionary _undo_data; // See _get_undo_data for definition void _send_region_aabb(const Vector2i &p_region_loc, const Vector2 &p_height_range = Vector2()); Ref _operate_region(const Vector2i &p_region_loc); void _operate_map(const Vector3 &p_global_position, const real_t p_camera_direction); + MapType _get_map_type() const; bool _is_in_bounds(const Vector2i &p_position, const Vector2i &p_max_position) const; real_t _get_brush_alpha(const Vector2i &p_position) const; Vector2 _get_uv_position(const Vector3 &p_global_position, const int p_region_size) const; @@ -117,4 +120,46 @@ class Terrain3DEditor : public Object { VARIANT_ENUM_CAST(Terrain3DEditor::Operation); VARIANT_ENUM_CAST(Terrain3DEditor::Tool); +inline MapType Terrain3DEditor::_get_map_type() const { + switch (_tool) { + case HEIGHT: + case INSTANCER: + return TYPE_HEIGHT; + break; + case TEXTURE: + case AUTOSHADER: + case HOLES: + case NAVIGATION: + case ANGLE: + case SCALE: + return TYPE_CONTROL; + break; + case COLOR: + case ROUGHNESS: + return TYPE_COLOR; + break; + default: + return TYPE_MAX; + } +} + +inline bool Terrain3DEditor::_is_in_bounds(const Vector2i &p_position, const Vector2i &p_max_position) const { + bool more_than_min = p_position.x >= 0 && p_position.y >= 0; + bool less_than_max = p_position.x < p_max_position.x && p_position.y < p_max_position.y; + return more_than_min && less_than_max; +} + +inline real_t Terrain3DEditor::_get_brush_alpha(const Vector2i &p_position) const { + if (_brush_image.is_valid()) { + return _brush_image->get_pixelv(p_position).r; + } + return NAN; +} + +inline Vector2 Terrain3DEditor::_get_rotated_uv(const Vector2 &p_uv, const real_t p_angle) const { + Vector2 rotation_offset = Vector2(0.5f, 0.5f); + Vector2 uv = (p_uv - rotation_offset).rotated(p_angle) + rotation_offset; + return uv.clamp(V2_ZERO, Vector2(1.f, 1.f)); +} + #endif // TERRAIN3D_EDITOR_CLASS_H diff --git a/src/terrain_3d_region.cpp b/src/terrain_3d_region.cpp index 63b4aabf5..b7a66e3a7 100644 --- a/src/terrain_3d_region.cpp +++ b/src/terrain_3d_region.cpp @@ -258,6 +258,10 @@ void Terrain3DRegion::set_data(const Dictionary &p_data) { if (p_data.has(str)) { \ var = p_data[str]; \ } + SET_IF_HAS(_location, "location"); + SET_IF_HAS(_deleted, "deleted"); + SET_IF_HAS(_edited, "edited"); + SET_IF_HAS(_modified, "modified"); SET_IF_HAS(_version, "version"); SET_IF_HAS(_region_size, "region_size"); SET_IF_HAS(_height_range, "height_range"); @@ -265,14 +269,15 @@ void Terrain3DRegion::set_data(const Dictionary &p_data) { SET_IF_HAS(_control_map, "control_map"); SET_IF_HAS(_color_map, "color_map"); SET_IF_HAS(_multimeshes, "multimeshes"); - SET_IF_HAS(_location, "location"); - SET_IF_HAS(_deleted, "deleted"); - SET_IF_HAS(_edited, "edited"); - SET_IF_HAS(_modified, "modified"); } Dictionary Terrain3DRegion::get_data() const { Dictionary dict; + dict["location"] = _location; + dict["deleted"] = _deleted; + dict["edited"] = _edited; + dict["modified"] = _modified; + dict["instance_id"] = String::num_uint64(get_instance_id()); // don't commit dict["version"] = _version; dict["region_size"] = _region_size; dict["height_range"] = _height_range; @@ -280,10 +285,6 @@ Dictionary Terrain3DRegion::get_data() const { dict["control_map"] = _control_map; dict["color_map"] = _color_map; dict["multimeshes"] = _multimeshes; - dict["deleted"] = _deleted; - dict["edited"] = _edited; - dict["modified"] = _modified; - dict["location"] = _location; return dict; } diff --git a/src/terrain_3d_storage.cpp b/src/terrain_3d_storage.cpp index 8902a1db6..2e66908ee 100644 --- a/src/terrain_3d_storage.cpp +++ b/src/terrain_3d_storage.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include "logger.h" #include "terrain_3d_storage.h" @@ -200,13 +199,9 @@ Error Terrain3DStorage::add_region(const Ref &p_region, const b LOG(INFO, "Adding region at location ", region_loc, ", update maps: ", p_update ? "yes" : "no"); // Check bounds and slow report errors - if (_get_region_map_index(region_loc) < 0) { - uint64_t time = Time::get_singleton()->get_ticks_msec(); - if (time - _last_region_bounds_error > 1000) { - _last_region_bounds_error = time; - LOG(ERROR, "Specified position outside of maximum region map size: +/-", - real_t((REGION_MAP_SIZE / 2) * _region_size) * _mesh_vertex_spacing); - } + if (get_region_map_index(region_loc) < 0) { + LOG(ERROR, "Specified position outside of maximum region map size: +/-", + real_t((REGION_MAP_SIZE / 2) * _region_size) * _mesh_vertex_spacing); return FAILED; } @@ -243,10 +238,6 @@ void Terrain3DStorage::remove_region(const Ref &p_region, const LOG(ERROR, "Region not found or is null. Returning"); return; } - for (int i = 0; i < _region_locations.size(); i++) { - Vector2i region_loc = _region_locations[i]; - Ref region = get_region(region_loc); - } Vector2i region_loc = p_region->get_location(); int region_id = _region_locations.find(region_loc); @@ -314,7 +305,7 @@ void Terrain3DStorage::update_maps() { _region_map_dirty = false; for (int i = 0; i < _region_locations.size(); i++) { int region_id = i + 1; // Begin at 1 since 0 = no region - int map_index = _get_region_map_index(_region_locations[i]); + int map_index = get_region_map_index(_region_locations[i]); if (map_index >= 0) { _region_map[map_index] = region_id; } diff --git a/src/terrain_3d_storage.h b/src/terrain_3d_storage.h index a862cc31a..3feb50bfa 100644 --- a/src/terrain_3d_storage.h +++ b/src/terrain_3d_storage.h @@ -43,7 +43,6 @@ class Terrain3DStorage : public Object { AABB _edited_area; Vector2 _master_height_range = V2_ZERO; - uint64_t _last_region_bounds_error = 0; ///////// // Terrain3DRegions house the maps, instances, and other data for each region. @@ -86,7 +85,6 @@ class Terrain3DStorage : public Object { // Functions void _clear(); - int _get_region_map_index(const Vector2i &p_region_loc) const; public: Terrain3DStorage() {} @@ -106,6 +104,7 @@ class Terrain3DStorage : public Object { TypedArray get_regions_active(const bool p_copy = false, const bool p_deep = false) const; Dictionary get_regions_all() const { return _regions; } PackedInt32Array get_region_map() const { return _region_map; } + int get_region_map_index(const Vector2i &p_region_loc) const; bool has_region(const Vector2i &p_region_loc) const { return get_region_id(p_region_loc) != -1; } bool has_regionp(const Vector3 &p_global_position) const { return get_region_idp(p_global_position) != -1; } @@ -200,7 +199,7 @@ VARIANT_ENUM_CAST(Terrain3DStorage::HeightFilter); // This function verifies the location is within the bounds of the _region_map array and // thus the world. It returns the _region_map index if valid, -1 if not -inline int Terrain3DStorage::_get_region_map_index(const Vector2i &p_region_loc) const { +inline int Terrain3DStorage::get_region_map_index(const Vector2i &p_region_loc) const { // Offset locations centered on (0,0) to positive only Vector2i loc = Vector2i(p_region_loc + (REGION_MAP_VSIZE / 2)); int map_index = loc.y * REGION_MAP_SIZE + loc.x; @@ -226,7 +225,7 @@ inline Vector2i Terrain3DStorage::get_region_locationi(const int p_region_id) co // Returns id of any active region. -1 if out of bounds, 0 if no region, or region id inline int Terrain3DStorage::get_region_id(const Vector2i &p_region_loc) const { - int map_index = _get_region_map_index(p_region_loc); + int map_index = get_region_map_index(p_region_loc); if (map_index >= 0) { int region_id = _region_map[map_index] - 1; // 0 = no region if (region_id >= 0 && region_id < _region_locations.size()) { diff --git a/src/terrain_3d_util.cpp b/src/terrain_3d_util.cpp index 7f25b8b35..3122c6a44 100644 --- a/src/terrain_3d_util.cpp +++ b/src/terrain_3d_util.cpp @@ -10,11 +10,56 @@ // Public Functions /////////////////////////// +void Terrain3DUtil::print_arr(const String &p_name, const Array &p_arr, const int p_level) { + LOG(p_level, "Array[", p_arr.size(), "]: ", p_name); + for (int i = 0; i < p_arr.size(); i++) { + Variant var = p_arr[i]; + switch (var.get_type()) { + case Variant::ARRAY: { + print_arr(p_name + String::num_int64(i), var, p_level); + break; + } + case Variant::DICTIONARY: { + print_dict(p_name + String::num_int64(i), var, p_level); + break; + } + case Variant::OBJECT: { + String inst = "Object#" + String::num_uint64(cast_to(var)->get_instance_id()); + LOG(p_level, i, ": ", inst); + break; + } + default: { + LOG(p_level, i, ": ", p_arr[i]); + break; + } + } + } +} + void Terrain3DUtil::print_dict(const String &p_name, const Dictionary &p_dict, const int p_level) { - LOG(p_level, "Printing Dictionary: ", p_name); + LOG(p_level, "Dictionary: ", p_name); Array keys = p_dict.keys(); for (int i = 0; i < keys.size(); i++) { - LOG(p_level, "Key: ", keys[i], ", Value: ", p_dict[keys[i]]); + Variant var = p_dict[keys[i]]; + switch (var.get_type()) { + case Variant::ARRAY: { + print_arr(p_name + String::num_int64(i), var, p_level); + break; + } + case Variant::DICTIONARY: { + print_dict(p_name + String::num_int64(i), var, p_level); + break; + } + case Variant::OBJECT: { + String inst = "Object#" + String::num_uint64(cast_to(var)->get_instance_id()); + LOG(p_level, "\"", keys[i], "\": ", inst); + break; + } + default: { + LOG(p_level, "\"", keys[i], "\": Value: ", var); + break; + } + } } } diff --git a/src/terrain_3d_util.h b/src/terrain_3d_util.h index 5727650d8..6bceb2066 100644 --- a/src/terrain_3d_util.h +++ b/src/terrain_3d_util.h @@ -22,7 +22,8 @@ class Terrain3DUtil : public Object { public: // Print info to the console - static void print_dict(const String &name, const Dictionary &p_dict, const int p_level = 2); // Level 2: DEBUG + static void print_arr(const String &p_name, const Array &p_arr, const int p_level = 2); // Level 2: DEBUG + static void print_dict(const String &p_name, const Dictionary &p_dict, const int p_level = 2); // Level 2: DEBUG static void dump_gentex(const GeneratedTexture p_gen, const String &name = "", const int p_level = 2); static void dump_maps(const TypedArray &p_maps, const String &p_name = "");