diff --git a/math/vector3i.h b/math/vector3i.h index eae6bfbd..7cf946b3 100644 --- a/math/vector3i.h +++ b/math/vector3i.h @@ -90,12 +90,12 @@ struct Vector3i { return coords[i]; } + // Clamps between min and max, where max is excluded void clamp_to(const Vector3i min, const Vector3i max) { if (x < min.x) x = min.x; if (y < min.y) y = min.y; if (z < min.z) z = min.z; - // TODO Not sure it should clamp like that... if (x >= max.x) x = max.x - 1; if (y >= max.y) y = max.y - 1; if (z >= max.z) z = max.z - 1; diff --git a/voxel_buffer.cpp b/voxel_buffer.cpp index a45c0495..c20c7fd6 100644 --- a/voxel_buffer.cpp +++ b/voxel_buffer.cpp @@ -328,6 +328,54 @@ void VoxelBuffer::delete_channel(int i) { channel.data = NULL; } +void VoxelBuffer::downscale_to(VoxelBuffer &dst, Vector3i src_min, Vector3i src_max, Vector3i dst_min) const { + + // TODO Align input to multiple of two + + src_min.clamp_to(Vector3i(), _size); + src_max.clamp_to(Vector3i(), _size); + + Vector3i dst_max = dst_min + (src_max - src_min) >> 2; + + dst_min.clamp_to(Vector3i(), dst._size); + dst_max.clamp_to(Vector3i(), dst._size); + + for (int channel_index = 0; channel_index < MAX_CHANNELS; ++channel_index) { + + const Channel &src_channel = _channels[channel_index]; + const Channel &dst_channel = _channels[channel_index]; + + if (src_channel.data == nullptr && dst_channel.data == nullptr && src_channel.defval == dst_channel.defval) { + // No action needed + continue; + } + + // Nearest-neighbor downscaling + + Vector3i pos; + for (pos.z = dst_min.z; pos.z < dst_max.z; ++pos.z) { + for (pos.x = dst_min.x; pos.x < dst_max.x; ++pos.x) { + for (pos.y = dst_min.y; pos.y < dst_max.y; ++pos.y) { + + Vector3i src_pos = src_min + (pos - dst_min) << 2; + + // TODO Remove check once it works + CRASH_COND(!validate_pos(src_pos.x, src_pos.y, src_pos.z)); + + int v; + if (src_channel.data) { + v = src_channel.data[index(src_pos.x, src_pos.y, src_pos.z)]; + } else { + v = src_channel.defval; + } + + dst.set_voxel(v, pos, channel_index); + } + } + } + } +} + void VoxelBuffer::_bind_methods() { ClassDB::bind_method(D_METHOD("create", "sx", "sy", "sz"), &VoxelBuffer::create); diff --git a/voxel_buffer.h b/voxel_buffer.h index c500a1e0..7cab3cff 100644 --- a/voxel_buffer.h +++ b/voxel_buffer.h @@ -118,6 +118,8 @@ public: uint8_t *get_channel_raw(unsigned int channel_index) const; + void downscale_to(VoxelBuffer &dst, Vector3i src_min, Vector3i src_max, Vector3i dst_min) const; + private: void create_channel_noinit(int i, Vector3i size); void create_channel(int i, Vector3i size, uint8_t defval);