diff --git a/providers/voxel_provider_noise.cpp b/providers/voxel_provider_noise.cpp index 7dbd6d7..03baec4 100644 --- a/providers/voxel_provider_noise.cpp +++ b/providers/voxel_provider_noise.cpp @@ -42,7 +42,32 @@ void VoxelProviderNoise::emerge_block(Ref out_buffer, Vector3i orig } else { + FloatBuffer3D &noise_buffer = _noise_buffer; + const int noise_buffer_step = 2; + + Vector3i noise_buffer_size = buffer.get_size() / noise_buffer_step + Vector3i(1); + if (noise_buffer.get_size() != noise_buffer_size) { + noise_buffer.create(noise_buffer_size); + } + + // Cache noise at lower grid resolution and interpolate after, much cheaper + for (int z = 0; z < noise_buffer.get_size().z; ++z) { + for (int x = 0; x < noise_buffer.get_size().x; ++x) { + for (int y = 0; y < noise_buffer.get_size().y; ++y) { + + float lx = origin_in_voxels.x + (x << lod) * noise_buffer_step; + float ly = origin_in_voxels.y + (y << lod) * noise_buffer_step; + float lz = origin_in_voxels.z + (z << lod) * noise_buffer_step; + + float n = noise.get_noise_3d(lx, ly, lz); + + noise_buffer.set(x, y, z, n); + } + } + } + float iso_scale = noise.get_period() * 0.1; + float noise_buffer_scale = 1.f / static_cast(noise_buffer_step); for (int z = 0; z < buffer.get_size().z; ++z) { for (int x = 0; x < buffer.get_size().x; ++x) { @@ -50,13 +75,8 @@ void VoxelProviderNoise::emerge_block(Ref out_buffer, Vector3i orig float ly = origin_in_voxels.y + (y << lod); - float n = noise.get_noise_3d( - origin_in_voxels.x + (x << lod), - ly, - origin_in_voxels.z + (z << lod)); - + float n = noise_buffer.get_trilinear(x * noise_buffer_scale, y * noise_buffer_scale, z * noise_buffer_scale); float t = (ly - _height_start) / _height_range; - float d = (n + 2.0 * t - 1.0) * iso_scale; buffer.set_voxel_f(d, x, y, z, VoxelBuffer::CHANNEL_ISOLEVEL); diff --git a/providers/voxel_provider_noise.h b/providers/voxel_provider_noise.h index 0f1e348..1ee4131 100644 --- a/providers/voxel_provider_noise.h +++ b/providers/voxel_provider_noise.h @@ -1,6 +1,7 @@ #ifndef VOXEL_PROVIDER_NOISE_H #define VOXEL_PROVIDER_NOISE_H +#include "../util/float_buffer_3d.h" #include "voxel_provider.h" #include @@ -23,6 +24,7 @@ protected: private: Ref _noise; + FloatBuffer3D _noise_buffer; float _height_start = 0; float _height_range = 300; }; diff --git a/util/float_buffer_3d.cpp b/util/float_buffer_3d.cpp new file mode 100644 index 0000000..5a7a665 --- /dev/null +++ b/util/float_buffer_3d.cpp @@ -0,0 +1,80 @@ +#include "float_buffer_3d.h" +#include "utility.h" + +FloatBuffer3D::~FloatBuffer3D() { + clear(); +} + +void FloatBuffer3D::clear() { + if (_data) { + memfree(_data); + _data = nullptr; + } +} + +void FloatBuffer3D::create(Vector3i size) { + ERR_FAIL_COND(size.x <= 0); + ERR_FAIL_COND(size.y <= 0); + ERR_FAIL_COND(size.z <= 0); + clear(); + _data = (float *)memalloc(size.x * size.y * size.z * sizeof(float)); + _size = size; +} + +void FloatBuffer3D::fill(float v) { + ERR_FAIL_COND(_data == nullptr); + int count = _size.x * _size.y * _size.z; + for (int i = 0; i < count; ++i) { + _data[i] = v; + } +} + +float FloatBuffer3D::get(int x, int y, int z) const { + ERR_FAIL_COND_V(_data == nullptr, 0); + ERR_FAIL_COND_V(x < 0 || x >= _size.x, 0); + ERR_FAIL_COND_V(y < 0 || y >= _size.y, 0); + ERR_FAIL_COND_V(z < 0 || z >= _size.z, 0); + return _data[get_index(x, y, z)]; +} + +void FloatBuffer3D::set(int x, int y, int z, float v) { + ERR_FAIL_COND(_data == nullptr); + ERR_FAIL_COND(x < 0 || x >= _size.x); + ERR_FAIL_COND(y < 0 || y >= _size.y); + ERR_FAIL_COND(z < 0 || z >= _size.z); + _data[get_index(x, y, z)] = v; +} + +float FloatBuffer3D::get_clamped(int x, int y, int z) const { + ERR_FAIL_COND_V(_data == nullptr, 0); + x = x >= _size.x ? _size.x - 1 : x; + y = y >= _size.y ? _size.y - 1 : y; + z = z >= _size.z ? _size.z - 1 : z; + return _data[get_index(x, y, z)]; +} + +float FloatBuffer3D::get_trilinear(float x, float y, float z) const { + ERR_FAIL_COND_V(_data == nullptr, 0); + + int x0 = static_cast(x); + int y0 = static_cast(y); + int z0 = static_cast(z); + + int x1 = static_cast(Math::ceil(x)); + int y1 = static_cast(Math::ceil(y)); + int z1 = static_cast(Math::ceil(z)); + + float v0 = get_clamped(x0, y0, z0); + float v1 = get_clamped(x1, y0, z0); + float v2 = get_clamped(x1, y0, z1); + float v3 = get_clamped(x0, y0, z1); + + float v4 = get_clamped(x0, y1, z0); + float v5 = get_clamped(x1, y1, z0); + float v6 = get_clamped(x1, y1, z1); + float v7 = get_clamped(x0, y1, z1); + + Vector3 rpos(x - x0, y - y0, z - z0); + + return interpolate(v0, v1, v2, v3, v4, v5, v6, v7, rpos); +} diff --git a/util/float_buffer_3d.h b/util/float_buffer_3d.h new file mode 100644 index 0000000..a0f677c --- /dev/null +++ b/util/float_buffer_3d.h @@ -0,0 +1,32 @@ +#ifndef FLOAT_BUFFER_3D_H +#define FLOAT_BUFFER_3D_H + +#include "../math/vector3i.h" + +// Simple 3D array of floats, until VoxelBuffer supports higher-precision +class FloatBuffer3D { +public: + ~FloatBuffer3D(); + + void clear(); + void create(Vector3i size); + void fill(float v); + + inline const Vector3i &get_size() const { return _size; } + + float get(int x, int y, int z) const; + float get_clamped(int x, int y, int z) const; + float get_trilinear(float x, float y, float z) const; + + void set(int x, int y, int z, float v); + +private: + inline int get_index(int x, int y, int z) const { + return y + _size.y * (x + _size.x * z); + } + + float *_data = nullptr; + Vector3i _size; +}; + +#endif // FLOAT_BUFFER_3D_H