Optimized editing in TerrainWorldEditor.

This commit is contained in:
Relintai 2025-02-06 17:42:53 +01:00
parent c13a4d08a5
commit 2688162cf9
3 changed files with 131 additions and 8 deletions

View File

@ -375,6 +375,8 @@ void TerrainWorldEditor::isolevel_brush_draw(const Vector3 &p_world_position) {
// Value will likely need more fine tuning. // Value will likely need more fine tuning.
float s = 10.0 * _isolevel_brush_strength; float s = 10.0 * _isolevel_brush_strength;
Array draw_data;
// TODO use a proper circle drawing algorithm. // TODO use a proper circle drawing algorithm.
for (int x = -ilbh; x < ilbh; ++x) { for (int x = -ilbh; x < ilbh; ++x) {
for (int y = -ilbh; y < ilbh; ++y) { 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<uint8_t>(npil); uint8_t new_val = static_cast<uint8_t>(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)) { if (!_original_data.has(vwp)) {
_original_data[vwp] = orig_val; _original_data[vwp] = orig_val;
@ -421,6 +424,8 @@ void TerrainWorldEditor::isolevel_brush_draw(const Vector3 &p_world_position) {
_current_data[vwp] = new_val; _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) { 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; selected_terrain = _selected_type + 1;
uint8_t new_val = static_cast<uint8_t>(selected_terrain); uint8_t new_val = static_cast<uint8_t>(selected_terrain);
Array draw_data;
// TODO use a proper circle drawing algorithm. // TODO use a proper circle drawing algorithm.
for (int x = -ilbh; x < ilbh; ++x) { for (int x = -ilbh; x < ilbh; ++x) {
for (int y = -ilbh; y < ilbh; ++y) { 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); 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)) { if (!_original_data.has(vwp)) {
_original_data[vwp] = orig_val; _original_data[vwp] = orig_val;
@ -462,6 +470,8 @@ void TerrainWorldEditor::paint_brush_draw(const Vector3 &p_world_position) {
_current_data[vwp] = new_val; _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) { 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]; bool allow_create_chunks = p_data[2];
Array data = p_data[3]; Array data = p_data[3];
for (int i = 0; i < data.size(); i += 2) { _world->set_voxels_at_world_data_position(data, channel, true, allow_create_chunks);
Vector2i pos = data[i];
uint8_t d = data[i + 1];
_world->set_voxel_at_world_data_position(pos, d, channel, true, allow_create_chunks);
}
} }
void TerrainWorldEditor::create_undo_point(const String &p_action, const int p_channel, const bool p_allow_create_chunks) { void TerrainWorldEditor::create_undo_point(const String &p_action, const int p_channel, const bool p_allow_create_chunks) {

View File

@ -31,6 +31,8 @@
#include "terrain_world.h" #include "terrain_world.h"
#include "core/containers/hash_set.h"
#include "core/object/message_queue.h" #include "core/object/message_queue.h"
#include "terrain_chunk.h" #include "terrain_chunk.h"
#include "terrain_structure.h" #include "terrain_structure.h"
@ -1035,6 +1037,120 @@ Ref<TerrainChunk> TerrainWorld::get_or_create_chunk_at_world_data_position(const
return chunk_get_or_create(x, z); 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<Ref<TerrainChunk>> 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<int>(Math::floor(pos.x / get_chunk_size_x()));
int z = static_cast<int>(Math::floor(pos.y / get_chunk_size_z()));
int bx = static_cast<int>(Math::floor(pos.x)) % get_chunk_size_x();
int bz = static_cast<int>(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<TerrainChunk> 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<Ref<TerrainChunk>>::Iterator iter = chunks_to_rebuild.begin(); iter.valid(); iter.next()) {
Ref<TerrainChunk> chunk = iter.key();
if (p_immediate_build) {
chunk->build_immediate();
} else {
chunk->build();
}
}
}
int TerrainWorld::get_channel_index_info(const TerrainWorld::ChannelTypeInfo channel_type) { int TerrainWorld::get_channel_index_info(const TerrainWorld::ChannelTypeInfo channel_type) {
return call("_get_channel_index_info", 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("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_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("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))); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::INT, "ret"), "_get_channel_index_info", PropertyInfo(Variant::INT, "channel_type", PROPERTY_HINT_ENUM, BINDING_STRING_CHANNEL_TYPE_INFO)));

View File

@ -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); 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<TerrainChunk> get_chunk_at_world_data_position(const Vector2i &world_data_position); Ref<TerrainChunk> get_chunk_at_world_data_position(const Vector2i &world_data_position);
Ref<TerrainChunk> get_or_create_chunk_at_world_data_position(const Vector2i &world_data_position); Ref<TerrainChunk> 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); int get_channel_index_info(const ChannelTypeInfo channel_type);