diff --git a/modules/terraman/editor/terrain_world_editor.cpp b/modules/terraman/editor/terrain_world_editor.cpp index e51732e19..ff41d436e 100644 --- a/modules/terraman/editor/terrain_world_editor.cpp +++ b/modules/terraman/editor/terrain_world_editor.cpp @@ -375,6 +375,8 @@ void TerrainWorldEditor::isolevel_brush_draw(const Vector3 &p_world_position) { // Value will likely need more fine tuning. float s = 10.0 * _isolevel_brush_strength; + Array draw_data; + // TODO use a proper circle drawing algorithm. for (int x = -ilbh; x < ilbh; ++x) { for (int y = -ilbh; y < ilbh; ++y) { @@ -412,7 +414,8 @@ void TerrainWorldEditor::isolevel_brush_draw(const Vector3 &p_world_position) { uint8_t new_val = static_cast(npil); - _world->set_voxel_at_world_data_position(vwp, new_val, _isolevel_brush_channel, true, _isolevel_brush_allow_create_chunks); + draw_data.push_back(vwp); + draw_data.push_back(new_val); if (!_original_data.has(vwp)) { _original_data[vwp] = orig_val; @@ -421,6 +424,8 @@ void TerrainWorldEditor::isolevel_brush_draw(const Vector3 &p_world_position) { _current_data[vwp] = new_val; } } + + _world->set_voxels_at_world_data_position(draw_data, _isolevel_brush_channel, true, _isolevel_brush_allow_create_chunks); } void TerrainWorldEditor::paint_brush_draw(const Vector3 &p_world_position) { @@ -440,6 +445,8 @@ void TerrainWorldEditor::paint_brush_draw(const Vector3 &p_world_position) { selected_terrain = _selected_type + 1; uint8_t new_val = static_cast(selected_terrain); + Array draw_data; + // TODO use a proper circle drawing algorithm. for (int x = -ilbh; x < ilbh; ++x) { for (int y = -ilbh; y < ilbh; ++y) { @@ -453,7 +460,8 @@ void TerrainWorldEditor::paint_brush_draw(const Vector3 &p_world_position) { uint8_t orig_val = _world->get_voxel_at_world_data_position(vwp, _paint_brush_channel); - _world->set_voxel_at_world_data_position(vwp, new_val, _paint_brush_channel, true, _paint_brush_allow_create_chunks); + draw_data.push_back(vwp); + draw_data.push_back(new_val); if (!_original_data.has(vwp)) { _original_data[vwp] = orig_val; @@ -462,6 +470,8 @@ void TerrainWorldEditor::paint_brush_draw(const Vector3 &p_world_position) { _current_data[vwp] = new_val; } } + + _world->set_voxels_at_world_data_position(draw_data, _paint_brush_channel, true, _paint_brush_allow_create_chunks); } void TerrainWorldEditor::edit(TerrainWorld *p_world) { @@ -946,12 +956,7 @@ void TerrainWorldEditor::apply_data(const Array &p_data) { bool allow_create_chunks = p_data[2]; Array data = p_data[3]; - for (int i = 0; i < data.size(); i += 2) { - Vector2i pos = data[i]; - uint8_t d = data[i + 1]; - - _world->set_voxel_at_world_data_position(pos, d, channel, true, allow_create_chunks); - } + _world->set_voxels_at_world_data_position(data, channel, true, allow_create_chunks); } void TerrainWorldEditor::create_undo_point(const String &p_action, const int p_channel, const bool p_allow_create_chunks) { diff --git a/modules/terraman/world/terrain_world.cpp b/modules/terraman/world/terrain_world.cpp index d84efd31d..a52da68a6 100644 --- a/modules/terraman/world/terrain_world.cpp +++ b/modules/terraman/world/terrain_world.cpp @@ -31,6 +31,8 @@ #include "terrain_world.h" +#include "core/containers/hash_set.h" + #include "core/object/message_queue.h" #include "terrain_chunk.h" #include "terrain_structure.h" @@ -1035,6 +1037,120 @@ Ref TerrainWorld::get_or_create_chunk_at_world_data_position(const return chunk_get_or_create(x, z); } +void TerrainWorld::set_voxels_at_world_data_position(const Array &p_data, const int p_channel_index, const bool p_immediate_build, const bool p_allow_creating_chunks) { + ERR_FAIL_COND(p_data.size() % 2 != 0); + + // TODO rework this so it works directly with ints. + + HashSet> chunks_to_rebuild; + + for (int i = 0; i < p_data.size(); i += 2) { + Vector2i world_data_position = p_data[i]; + uint8_t value = p_data[i + 1]; + + Vector2 pos = world_data_position; + + //Note: floor is needed to handle negative numbers properly + int x = static_cast(Math::floor(pos.x / get_chunk_size_x())); + int z = static_cast(Math::floor(pos.y / get_chunk_size_z())); + + int bx = static_cast(Math::floor(pos.x)) % get_chunk_size_x(); + int bz = static_cast(Math::floor(pos.y)) % get_chunk_size_z(); + + if (bx < 0) { + bx += get_chunk_size_x(); + } + + if (bz < 0) { + bz += get_chunk_size_z(); + } + + Ref chunk; + + if (get_data_margin_end() > 0) { + if (bx == 0) { + if (p_allow_creating_chunks) { + chunk = chunk_get_or_create(x - 1, z); + } else { + chunk = chunk_get(x - 1, z); + } + + if (chunk.is_valid()) { + chunk->set_voxel(value, get_chunk_size_x(), bz, p_channel_index); + + chunks_to_rebuild.insert(chunk); + } + } + + if (bz == 0) { + if (p_allow_creating_chunks) { + chunk = chunk_get_or_create(x, z - 1); + } else { + chunk = chunk_get(x, z - 1); + } + + if (chunk.is_valid()) { + chunk->set_voxel(value, bx, get_chunk_size_z(), p_channel_index); + + chunks_to_rebuild.insert(chunk); + } + } + } + + if (get_data_margin_start() > 0) { + if (bx == get_chunk_size_x() - 1) { + if (p_allow_creating_chunks) { + chunk = chunk_get_or_create(x + 1, z); + } else { + chunk = chunk_get(x + 1, z); + } + + if (chunk.is_valid()) { + chunk->set_voxel(value, -1, bz, p_channel_index); + + chunks_to_rebuild.insert(chunk); + } + } + + if (bz == get_chunk_size_z() - 1) { + if (p_allow_creating_chunks) { + chunk = chunk_get_or_create(x, z + 1); + } else { + chunk = chunk_get(x, z + 1); + } + + if (chunk.is_valid()) { + chunk->set_voxel(value, bx, -1, p_channel_index); + + chunks_to_rebuild.insert(chunk); + } + } + } + + if (p_allow_creating_chunks) { + chunk = chunk_get_or_create(x, z); + } else { + chunk = chunk_get(x, z); + } + + if (chunk.is_valid()) { + chunk->set_voxel(value, bx, bz, p_channel_index); + + chunks_to_rebuild.insert(chunk); + } + } + + for (HashSet>::Iterator iter = chunks_to_rebuild.begin(); iter.valid(); iter.next()) { + Ref chunk = iter.key(); + + if (p_immediate_build) { + chunk->build_immediate(); + } else { + chunk->build(); + } + } +} + int TerrainWorld::get_channel_index_info(const TerrainWorld::ChannelTypeInfo channel_type) { return call("_get_channel_index_info", channel_type); } @@ -1411,6 +1527,7 @@ void TerrainWorld::_bind_methods() { ClassDB::bind_method(D_METHOD("set_voxel_at_world_data_position", "world_data_position", "data", "channel_index", "rebuild", "allow_creating_chunks "), &TerrainWorld::set_voxel_at_world_data_position, DEFVAL(true), DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_chunk_at_world_data_position", "world_data_position"), &TerrainWorld::get_chunk_at_world_data_position); ClassDB::bind_method(D_METHOD("get_or_create_chunk_at_world_data_position", "world_data_position"), &TerrainWorld::get_or_create_chunk_at_world_data_position); + ClassDB::bind_method(D_METHOD("set_voxels_at_world_data_position", "data", "channel_index", "immediate_build", "allow_creating_chunks"), &TerrainWorld::set_voxels_at_world_data_position, DEFVAL(false), DEFVAL(true)); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::INT, "ret"), "_get_channel_index_info", PropertyInfo(Variant::INT, "channel_type", PROPERTY_HINT_ENUM, BINDING_STRING_CHANNEL_TYPE_INFO))); diff --git a/modules/terraman/world/terrain_world.h b/modules/terraman/world/terrain_world.h index 65d0bcf7f..0106d2ef6 100644 --- a/modules/terraman/world/terrain_world.h +++ b/modules/terraman/world/terrain_world.h @@ -198,6 +198,7 @@ public: void set_voxel_at_world_data_position(const Vector2i &world_data_position, const uint8_t data, const int channel_index, const bool p_immediate_build = true, const bool allow_creating_chunks = true); Ref get_chunk_at_world_data_position(const Vector2i &world_data_position); Ref get_or_create_chunk_at_world_data_position(const Vector2i &world_data_position); + void set_voxels_at_world_data_position(const Array &p_data, const int p_channel_index, const bool p_immediate_build = false, const bool p_allow_creating_chunks = true); int get_channel_index_info(const ChannelTypeInfo channel_type);