godot_voxel/storage/voxel_buffer_gd.cpp

344 lines
13 KiB
C++

#include "voxel_buffer_gd.h"
#include "../edition/voxel_tool_buffer.h"
#include "../util/dstack.h"
#include "../util/math/color.h"
#include "../util/memory.h"
#include "voxel_metadata_variant.h"
#include <core/io/image.h>
namespace zylann::voxel::gd {
const char *VoxelBuffer::CHANNEL_ID_HINT_STRING = "Type,Sdf,Color,Indices,Weights,Data5,Data6,Data7";
static thread_local bool s_create_shared = false;
VoxelBuffer::VoxelBuffer() {
if (!s_create_shared) {
_buffer = make_shared_instance<VoxelBufferInternal>();
}
}
VoxelBuffer::VoxelBuffer(std::shared_ptr<VoxelBufferInternal> &other) {
CRASH_COND(other == nullptr);
_buffer = other;
}
Ref<VoxelBuffer> VoxelBuffer::create_shared(std::shared_ptr<VoxelBufferInternal> &other) {
Ref<VoxelBuffer> vb;
s_create_shared = true;
vb.instantiate();
s_create_shared = false;
vb->_buffer = other;
return vb;
}
VoxelBuffer::~VoxelBuffer() {}
void VoxelBuffer::clear() {
_buffer->clear();
}
real_t VoxelBuffer::get_voxel_f(int x, int y, int z, unsigned int channel_index) const {
return _buffer->get_voxel_f(x, y, z, channel_index);
}
void VoxelBuffer::set_voxel_f(real_t value, int x, int y, int z, unsigned int channel_index) {
ZN_DSTACK();
return _buffer->set_voxel_f(value, x, y, z, channel_index);
}
void VoxelBuffer::copy_channel_from(Ref<VoxelBuffer> other, unsigned int channel) {
ZN_DSTACK();
ERR_FAIL_COND(other.is_null());
_buffer->copy_from(other->get_buffer(), channel);
}
void VoxelBuffer::copy_channel_from_area(
Ref<VoxelBuffer> other, Vector3i src_min, Vector3i src_max, Vector3i dst_min, unsigned int channel) {
ZN_DSTACK();
ERR_FAIL_COND(other.is_null());
_buffer->copy_from(other->get_buffer(), src_min, src_max, dst_min, channel);
}
void VoxelBuffer::fill(uint64_t defval, unsigned int channel_index) {
ZN_DSTACK();
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
_buffer->fill(defval, channel_index);
}
void VoxelBuffer::fill_f(real_t value, unsigned int channel) {
ZN_DSTACK();
ERR_FAIL_INDEX(channel, MAX_CHANNELS);
_buffer->fill_f(value, channel);
}
bool VoxelBuffer::is_uniform(unsigned int channel_index) const {
ERR_FAIL_INDEX_V(channel_index, MAX_CHANNELS, true);
return _buffer->is_uniform(channel_index);
}
void VoxelBuffer::compress_uniform_channels() {
_buffer->compress_uniform_channels();
}
VoxelBuffer::Compression VoxelBuffer::get_channel_compression(unsigned int channel_index) const {
ERR_FAIL_INDEX_V(channel_index, MAX_CHANNELS, VoxelBuffer::COMPRESSION_NONE);
return VoxelBuffer::Compression(_buffer->get_channel_compression(channel_index));
}
void VoxelBuffer::downscale_to(Ref<VoxelBuffer> dst, Vector3i src_min, Vector3i src_max, Vector3i dst_min) const {
ZN_DSTACK();
ERR_FAIL_COND(dst.is_null());
_buffer->downscale_to(dst->get_buffer(), src_min, src_max, dst_min);
}
Ref<VoxelBuffer> VoxelBuffer::duplicate(bool include_metadata) const {
Ref<VoxelBuffer> d;
d.instantiate();
_buffer->duplicate_to(d->get_buffer(), include_metadata);
return d;
}
Ref<VoxelTool> VoxelBuffer::get_voxel_tool() {
// I can't make this function `const`, because `Ref<T>` has no constructor taking a `const T*`.
// The compiler would then choose Ref<T>(const Variant&), which fumbles `this` into a null pointer
Ref<VoxelBuffer> vb(this);
return Ref<VoxelTool>(memnew(VoxelToolBuffer(vb)));
}
void VoxelBuffer::set_channel_depth(unsigned int channel_index, Depth new_depth) {
_buffer->set_channel_depth(channel_index, VoxelBufferInternal::Depth(new_depth));
}
VoxelBuffer::Depth VoxelBuffer::get_channel_depth(unsigned int channel_index) const {
return VoxelBuffer::Depth(_buffer->get_channel_depth(channel_index));
}
Variant VoxelBuffer::get_block_metadata() const {
return get_as_variant(_buffer->get_block_metadata());
}
void VoxelBuffer::set_block_metadata(Variant meta) {
set_as_variant(_buffer->get_block_metadata(), meta);
}
Variant VoxelBuffer::get_voxel_metadata(Vector3i pos) const {
VoxelMetadata *meta = _buffer->get_voxel_metadata(pos);
if (meta == nullptr) {
return Variant();
}
return get_as_variant(*meta);
}
void VoxelBuffer::set_voxel_metadata(Vector3i pos, Variant meta) {
if (meta.get_type() == Variant::NIL) {
_buffer->erase_voxel_metadata(pos);
} else {
VoxelMetadata *mv = _buffer->get_or_create_voxel_metadata(pos);
ZN_ASSERT_RETURN(mv != nullptr);
set_as_variant(*mv, meta);
}
}
void VoxelBuffer::for_each_voxel_metadata(const Callable &callback) const {
ERR_FAIL_COND(callback.is_null());
//_buffer->for_each_voxel_metadata(callback);
const FlatMapMoveOnly<Vector3i, VoxelMetadata> &metadata = _buffer->get_voxel_metadata();
for (auto it = metadata.begin(); it != metadata.end(); ++it) {
Variant v = get_as_variant(it->value);
const Variant key = it->key;
const Variant *args[2] = { &key, &v };
Callable::CallError err;
Variant retval; // We don't care about the return value, Callable API requires it
callback.callp(args, 2, retval, err);
ERR_FAIL_COND_MSG(
err.error != Callable::CallError::CALL_OK, String("Callable failed at {0}").format(varray(key)));
// TODO Can't provide detailed error because FuncRef doesn't give us access to the object
// ERR_FAIL_COND_MSG(err.error != Variant::CallError::CALL_OK, false,
// Variant::get_call_error_text(callback->get_object(), method_name, nullptr, 0, err));
}
}
void VoxelBuffer::for_each_voxel_metadata_in_area(const Callable &callback, Vector3i min_pos, Vector3i max_pos) {
ERR_FAIL_COND(callback.is_null());
const Box3i box = Box3i::from_min_max(min_pos, max_pos);
_buffer->for_each_voxel_metadata_in_area(box, [&callback](Vector3i rel_pos, const VoxelMetadata &meta) {
Variant v = get_as_variant(meta);
const Variant key = rel_pos;
const Variant *args[2] = { &key, &v };
Callable::CallError err;
Variant retval; // We don't care about the return value, Callable API requires it
callback.callp(args, 2, retval, err);
ERR_FAIL_COND_MSG(
err.error != Callable::CallError::CALL_OK, String("Callable failed at {0}").format(varray(key)));
// TODO Can't provide detailed error because FuncRef doesn't give us access to the object
// ERR_FAIL_COND_MSG(err.error != Variant::CallError::CALL_OK, false,
// Variant::get_call_error_text(callback->get_object(), method_name, nullptr, 0, err));
});
}
void VoxelBuffer::copy_voxel_metadata_in_area(
Ref<VoxelBuffer> src_buffer, Vector3i src_min_pos, Vector3i src_max_pos, Vector3i dst_pos) {
ERR_FAIL_COND(src_buffer.is_null());
_buffer->copy_voxel_metadata_in_area(
src_buffer->get_buffer(), Box3i::from_min_max(src_min_pos, src_max_pos), dst_pos);
}
void VoxelBuffer::clear_voxel_metadata_in_area(Vector3i min_pos, Vector3i max_pos) {
_buffer->clear_voxel_metadata_in_area(Box3i::from_min_max(min_pos, max_pos));
}
void VoxelBuffer::clear_voxel_metadata() {
_buffer->clear_voxel_metadata();
}
Ref<Image> VoxelBuffer::debug_print_sdf_to_image_top_down() {
return debug_print_sdf_to_image_top_down(*_buffer);
}
Ref<Image> VoxelBuffer::debug_print_sdf_to_image_top_down(const VoxelBufferInternal &vb) {
Ref<Image> im;
im.instantiate();
const Vector3i size = vb.get_size();
im->create(size.x, size.z, false, Image::FORMAT_RGB8);
Vector3i pos;
for (pos.z = 0; pos.z < size.z; ++pos.z) {
for (pos.x = 0; pos.x < size.x; ++pos.x) {
for (pos.y = size.y - 1; pos.y >= 0; --pos.y) {
float v = vb.get_voxel_f(pos.x, pos.y, pos.z, VoxelBufferInternal::CHANNEL_SDF);
if (v < 0.0) {
break;
}
}
float h = pos.y;
float c = h / size.y;
im->set_pixel(pos.x, pos.z, Color(c, c, c));
}
}
return im;
}
Ref<Image> VoxelBuffer::debug_print_sdf_y_slice(float scale, int y) const {
const VoxelBufferInternal &buffer = *_buffer;
const Vector3i res = buffer.get_size();
ERR_FAIL_COND_V(y < 0 || y >= res.y, Ref<Image>());
Ref<Image> im;
im.instantiate();
im->create(res.x, res.z, false, Image::FORMAT_RGB8);
const Color nega_col(0.5f, 0.5f, 1.0f);
const Color posi_col(1.0f, 0.6f, 0.1f);
const Color black(0.f, 0.f, 0.f);
for (int z = 0; z < res.z; ++z) {
for (int x = 0; x < res.x; ++x) {
const float sd = scale * buffer.get_voxel_f(x, y, z, VoxelBufferInternal::CHANNEL_SDF);
const float nega = math::clamp(-sd, 0.0f, 1.0f);
const float posi = math::clamp(sd, 0.0f, 1.0f);
const Color col = math::lerp(black, nega_col, nega) + math::lerp(black, posi_col, posi);
im->set_pixel(x, z, col);
}
}
return im;
}
Array VoxelBuffer::debug_print_sdf_y_slices(float scale) const {
Array images;
const VoxelBufferInternal &buffer = *_buffer;
const Vector3i res = buffer.get_size();
for (int y = 0; y < res.y; ++y) {
images.append(debug_print_sdf_y_slice(scale, y));
}
return images;
}
void VoxelBuffer::_b_deprecated_optimize() {
ERR_PRINT_ONCE("VoxelBuffer.optimize() is deprecated. Use compress_uniform_channels() instead.");
compress_uniform_channels();
}
void VoxelBuffer::_bind_methods() {
ClassDB::bind_method(D_METHOD("create", "sx", "sy", "sz"), &VoxelBuffer::create);
ClassDB::bind_method(D_METHOD("clear"), &VoxelBuffer::clear);
ClassDB::bind_method(D_METHOD("get_size"), &VoxelBuffer::get_size);
ClassDB::bind_method(D_METHOD("set_voxel", "value", "x", "y", "z", "channel"), &VoxelBuffer::set_voxel, DEFVAL(0));
ClassDB::bind_method(
D_METHOD("set_voxel_f", "value", "x", "y", "z", "channel"), &VoxelBuffer::set_voxel_f, DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_voxel_v", "value", "pos", "channel"), &VoxelBuffer::set_voxel_v, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_voxel", "x", "y", "z", "channel"), &VoxelBuffer::get_voxel, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_voxel_f", "x", "y", "z", "channel"), &VoxelBuffer::get_voxel_f, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_voxel_tool"), &VoxelBuffer::get_voxel_tool);
ClassDB::bind_method(D_METHOD("get_channel_depth", "channel"), &VoxelBuffer::get_channel_depth);
ClassDB::bind_method(D_METHOD("set_channel_depth", "channel", "depth"), &VoxelBuffer::set_channel_depth);
ClassDB::bind_method(D_METHOD("fill", "value", "channel"), &VoxelBuffer::fill, DEFVAL(0));
ClassDB::bind_method(D_METHOD("fill_f", "value", "channel"), &VoxelBuffer::fill_f, DEFVAL(0));
ClassDB::bind_method(D_METHOD("fill_area", "value", "min", "max", "channel"), &VoxelBuffer::fill_area, DEFVAL(0));
ClassDB::bind_method(D_METHOD("copy_channel_from", "other", "channel"), &VoxelBuffer::copy_channel_from);
ClassDB::bind_method(D_METHOD("copy_channel_from_area", "other", "src_min", "src_max", "dst_min", "channel"),
&VoxelBuffer::copy_channel_from_area);
ClassDB::bind_method(D_METHOD("downscale_to", "dst", "src_min", "src_max", "dst_min"), &VoxelBuffer::downscale_to);
ClassDB::bind_method(D_METHOD("is_uniform", "channel"), &VoxelBuffer::is_uniform);
ClassDB::bind_method(D_METHOD("optimize"), &VoxelBuffer::_b_deprecated_optimize);
ClassDB::bind_method(D_METHOD("compress_uniform_channels"), &VoxelBuffer::compress_uniform_channels);
ClassDB::bind_method(D_METHOD("get_channel_compression", "channel"), &VoxelBuffer::get_channel_compression);
ClassDB::bind_method(D_METHOD("get_block_metadata"), &VoxelBuffer::get_block_metadata);
ClassDB::bind_method(D_METHOD("set_block_metadata", "meta"), &VoxelBuffer::set_block_metadata);
ClassDB::bind_method(D_METHOD("get_voxel_metadata", "pos"), &VoxelBuffer::get_voxel_metadata);
ClassDB::bind_method(D_METHOD("set_voxel_metadata", "pos", "value"), &VoxelBuffer::set_voxel_metadata);
ClassDB::bind_method(D_METHOD("for_each_voxel_metadata", "callback"), &VoxelBuffer::for_each_voxel_metadata);
ClassDB::bind_method(D_METHOD("for_each_voxel_metadata_in_area", "callback", "min_pos", "max_pos"),
&VoxelBuffer::for_each_voxel_metadata_in_area);
ClassDB::bind_method(D_METHOD("clear_voxel_metadata"), &VoxelBuffer::clear_voxel_metadata);
ClassDB::bind_method(
D_METHOD("clear_voxel_metadata_in_area", "min_pos", "max_pos"), &VoxelBuffer::clear_voxel_metadata_in_area);
ClassDB::bind_method(
D_METHOD("copy_voxel_metadata_in_area", "src_buffer", "src_min_pos", "src_max_pos", "dst_min_pos"),
&VoxelBuffer::copy_voxel_metadata_in_area);
ClassDB::bind_method(D_METHOD("debug_print_sdf_y_slices", "scale"), &VoxelBuffer::debug_print_sdf_y_slices);
BIND_ENUM_CONSTANT(CHANNEL_TYPE);
BIND_ENUM_CONSTANT(CHANNEL_SDF);
BIND_ENUM_CONSTANT(CHANNEL_COLOR);
BIND_ENUM_CONSTANT(CHANNEL_INDICES);
BIND_ENUM_CONSTANT(CHANNEL_WEIGHTS);
BIND_ENUM_CONSTANT(CHANNEL_DATA5);
BIND_ENUM_CONSTANT(CHANNEL_DATA6);
BIND_ENUM_CONSTANT(CHANNEL_DATA7);
BIND_ENUM_CONSTANT(MAX_CHANNELS);
BIND_ENUM_CONSTANT(DEPTH_8_BIT);
BIND_ENUM_CONSTANT(DEPTH_16_BIT);
BIND_ENUM_CONSTANT(DEPTH_32_BIT);
BIND_ENUM_CONSTANT(DEPTH_64_BIT);
BIND_ENUM_CONSTANT(DEPTH_COUNT);
BIND_ENUM_CONSTANT(COMPRESSION_NONE);
BIND_ENUM_CONSTANT(COMPRESSION_UNIFORM);
BIND_ENUM_CONSTANT(COMPRESSION_COUNT);
BIND_CONSTANT(MAX_SIZE);
}
} //namespace zylann::voxel::gd