Made all simple generators thread-safe
This commit is contained in:
parent
082cdf3e16
commit
a0941cea83
@ -1,54 +1,81 @@
|
||||
#include "voxel_generator_flat.h"
|
||||
|
||||
VoxelGeneratorFlat::VoxelGeneratorFlat() {
|
||||
_parameters_lock = RWLock::create();
|
||||
}
|
||||
|
||||
VoxelGeneratorFlat::~VoxelGeneratorFlat() {
|
||||
memdelete(_parameters_lock);
|
||||
}
|
||||
|
||||
void VoxelGeneratorFlat::set_channel(VoxelBuffer::ChannelId channel) {
|
||||
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
|
||||
if (_channel != channel) {
|
||||
_channel = channel;
|
||||
bool changed = false;
|
||||
{
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
if (_parameters.channel != channel) {
|
||||
_parameters.channel = channel;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
emit_changed();
|
||||
}
|
||||
}
|
||||
|
||||
VoxelBuffer::ChannelId VoxelGeneratorFlat::get_channel() const {
|
||||
return _channel;
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return _parameters.channel;
|
||||
}
|
||||
|
||||
int VoxelGeneratorFlat::get_used_channels_mask() const {
|
||||
return (1 << _channel);
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return (1 << _parameters.channel);
|
||||
}
|
||||
|
||||
void VoxelGeneratorFlat::set_voxel_type(int t) {
|
||||
_voxel_type = t;
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
_parameters.voxel_type = t;
|
||||
}
|
||||
|
||||
int VoxelGeneratorFlat::get_voxel_type() const {
|
||||
return _voxel_type;
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return _parameters.voxel_type;
|
||||
}
|
||||
|
||||
void VoxelGeneratorFlat::set_height(float h) {
|
||||
_height = h;
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
_parameters.height = h;
|
||||
}
|
||||
|
||||
float VoxelGeneratorFlat::get_height() const {
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return _parameters.height;
|
||||
}
|
||||
|
||||
void VoxelGeneratorFlat::generate_block(VoxelBlockRequest &input) {
|
||||
ERR_FAIL_COND(input.voxel_buffer.is_null());
|
||||
|
||||
Parameters params;
|
||||
{
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
params = _parameters;
|
||||
}
|
||||
|
||||
VoxelBuffer &out_buffer = **input.voxel_buffer;
|
||||
const Vector3i origin = input.origin_in_voxels;
|
||||
const int channel = _channel;
|
||||
const int channel = params.channel;
|
||||
const Vector3i bs = out_buffer.get_size();
|
||||
const bool use_sdf = channel == VoxelBuffer::CHANNEL_SDF;
|
||||
const float margin = 1 << input.lod;
|
||||
const int lod = input.lod;
|
||||
|
||||
if (origin.y > _height + margin) {
|
||||
if (origin.y > params.height + margin) {
|
||||
// The bottom of the block is above the highest ground can go (default is air)
|
||||
return;
|
||||
}
|
||||
if (origin.y + (bs.y << lod) < _height - margin) {
|
||||
if (origin.y + (bs.y << lod) < params.height - margin) {
|
||||
// The top of the block is below the lowest ground can go
|
||||
out_buffer.clear_channel(_channel, use_sdf ? 0 : _voxel_type);
|
||||
out_buffer.clear_channel(params.channel, use_sdf ? 0 : params.voxel_type);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -64,7 +91,7 @@ void VoxelGeneratorFlat::generate_block(VoxelBlockRequest &input) {
|
||||
|
||||
int gy = origin.y;
|
||||
for (int y = 0; y < bs.y; ++y, gy += stride) {
|
||||
float sdf = _iso_scale * (gy - _height);
|
||||
float sdf = params.iso_scale * (gy - params.height);
|
||||
out_buffer.set_voxel_f(sdf, x, y, z, channel);
|
||||
}
|
||||
|
||||
@ -80,13 +107,13 @@ void VoxelGeneratorFlat::generate_block(VoxelBlockRequest &input) {
|
||||
int gx = origin.x;
|
||||
for (int x = 0; x < bs.x; ++x, gx += stride) {
|
||||
|
||||
float h = _height - origin.y;
|
||||
float h = params.height - origin.y;
|
||||
int ih = int(h);
|
||||
if (ih > 0) {
|
||||
if (ih > bs.y) {
|
||||
ih = bs.y;
|
||||
}
|
||||
out_buffer.fill_area(_voxel_type, Vector3i(x, 0, z), Vector3i(x + 1, ih, z + 1), channel);
|
||||
out_buffer.fill_area(params.voxel_type, Vector3i(x, 0, z), Vector3i(x + 1, ih, z + 1), channel);
|
||||
}
|
||||
|
||||
} // for x
|
||||
@ -95,7 +122,6 @@ void VoxelGeneratorFlat::generate_block(VoxelBlockRequest &input) {
|
||||
}
|
||||
|
||||
void VoxelGeneratorFlat::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_channel", "channel"), &VoxelGeneratorFlat::set_channel);
|
||||
ClassDB::bind_method(D_METHOD("get_channel"), &VoxelGeneratorFlat::get_channel);
|
||||
|
||||
|
@ -8,6 +8,7 @@ class VoxelGeneratorFlat : public VoxelGenerator {
|
||||
|
||||
public:
|
||||
VoxelGeneratorFlat();
|
||||
~VoxelGeneratorFlat();
|
||||
|
||||
void set_channel(VoxelBuffer::ChannelId channel);
|
||||
VoxelBuffer::ChannelId get_channel() const;
|
||||
@ -19,16 +20,21 @@ public:
|
||||
int get_voxel_type() const;
|
||||
|
||||
void set_height(float h);
|
||||
float get_height() const { return _height; }
|
||||
float get_height() const;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
VoxelBuffer::ChannelId _channel = VoxelBuffer::CHANNEL_SDF;
|
||||
int _voxel_type = 1;
|
||||
float _height = 0;
|
||||
float _iso_scale = 0.1;
|
||||
struct Parameters {
|
||||
VoxelBuffer::ChannelId channel = VoxelBuffer::CHANNEL_SDF;
|
||||
int voxel_type = 1;
|
||||
float height = 0;
|
||||
float iso_scale = 0.1;
|
||||
};
|
||||
|
||||
Parameters _parameters;
|
||||
RWLock *_parameters_lock;
|
||||
};
|
||||
|
||||
#endif // VOXEL_GENERATOR_FLAT_H
|
||||
|
@ -3,50 +3,68 @@
|
||||
#include "../../util/fixed_array.h"
|
||||
|
||||
VoxelGeneratorHeightmap::VoxelGeneratorHeightmap() {
|
||||
_parameters_lock = RWLock::create();
|
||||
}
|
||||
|
||||
VoxelGeneratorHeightmap::~VoxelGeneratorHeightmap() {
|
||||
memdelete(_parameters_lock);
|
||||
}
|
||||
|
||||
void VoxelGeneratorHeightmap::set_channel(VoxelBuffer::ChannelId channel) {
|
||||
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
|
||||
if (_channel != channel) {
|
||||
_channel = channel;
|
||||
bool changed = false;
|
||||
{
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
if (_parameters.channel != channel) {
|
||||
_parameters.channel = channel;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
emit_changed();
|
||||
}
|
||||
}
|
||||
|
||||
VoxelBuffer::ChannelId VoxelGeneratorHeightmap::get_channel() const {
|
||||
return _channel;
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return _parameters.channel;
|
||||
}
|
||||
|
||||
int VoxelGeneratorHeightmap::get_used_channels_mask() const {
|
||||
return (1 << _channel);
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return (1 << _parameters.channel);
|
||||
}
|
||||
|
||||
void VoxelGeneratorHeightmap::set_height_start(float start) {
|
||||
_range.start = start;
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
_parameters.range.start = start;
|
||||
}
|
||||
|
||||
float VoxelGeneratorHeightmap::get_height_start() const {
|
||||
return _range.start;
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return _parameters.range.start;
|
||||
}
|
||||
|
||||
void VoxelGeneratorHeightmap::set_height_range(float range) {
|
||||
_range.height = range;
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
_parameters.range.height = range;
|
||||
}
|
||||
|
||||
float VoxelGeneratorHeightmap::get_height_range() const {
|
||||
return _range.height;
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return _parameters.range.height;
|
||||
}
|
||||
|
||||
void VoxelGeneratorHeightmap::set_iso_scale(float iso_scale) {
|
||||
_iso_scale = iso_scale;
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
_parameters.iso_scale = iso_scale;
|
||||
}
|
||||
|
||||
float VoxelGeneratorHeightmap::get_iso_scale() const {
|
||||
return _iso_scale;
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return _parameters.iso_scale;
|
||||
}
|
||||
|
||||
void VoxelGeneratorHeightmap::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_channel", "channel"), &VoxelGeneratorHeightmap::set_channel);
|
||||
ClassDB::bind_method(D_METHOD("get_channel"), &VoxelGeneratorHeightmap::get_channel);
|
||||
|
||||
|
@ -9,6 +9,7 @@ class VoxelGeneratorHeightmap : public VoxelGenerator {
|
||||
GDCLASS(VoxelGeneratorHeightmap, VoxelGenerator)
|
||||
public:
|
||||
VoxelGeneratorHeightmap();
|
||||
~VoxelGeneratorHeightmap();
|
||||
|
||||
void set_channel(VoxelBuffer::ChannelId channel);
|
||||
VoxelBuffer::ChannelId get_channel() const;
|
||||
@ -26,10 +27,15 @@ public:
|
||||
protected:
|
||||
template <typename Height_F>
|
||||
void generate(VoxelBuffer &out_buffer, Height_F height_func, Vector3i origin, int lod) {
|
||||
Parameters params;
|
||||
{
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
params = _parameters;
|
||||
}
|
||||
|
||||
const int channel = _channel;
|
||||
const int channel = params.channel;
|
||||
const Vector3i bs = out_buffer.get_size();
|
||||
bool use_sdf = channel == VoxelBuffer::CHANNEL_SDF;
|
||||
const bool use_sdf = channel == VoxelBuffer::CHANNEL_SDF;
|
||||
|
||||
if (origin.y > get_height_start() + get_height_range()) {
|
||||
// The bottom of the block is above the highest ground can go (default is air)
|
||||
@ -37,24 +43,23 @@ protected:
|
||||
}
|
||||
if (origin.y + (bs.y << lod) < get_height_start()) {
|
||||
// The top of the block is below the lowest ground can go
|
||||
out_buffer.clear_channel(_channel, use_sdf ? 0 : _matter_type);
|
||||
out_buffer.clear_channel(params.channel, use_sdf ? 0 : params.matter_type);
|
||||
return;
|
||||
}
|
||||
|
||||
const int stride = 1 << lod;
|
||||
|
||||
if (use_sdf) {
|
||||
|
||||
int gz = origin.z;
|
||||
|
||||
for (int z = 0; z < bs.z; ++z, gz += stride) {
|
||||
|
||||
int gx = origin.x;
|
||||
for (int x = 0; x < bs.x; ++x, gx += stride) {
|
||||
|
||||
float h = _range.xform(height_func(gx, gz));
|
||||
for (int x = 0; x < bs.x; ++x, gx += stride) {
|
||||
float h = params.range.xform(height_func(gx, gz));
|
||||
int gy = origin.y;
|
||||
for (int y = 0; y < bs.y; ++y, gy += stride) {
|
||||
float sdf = _iso_scale * (gy - h);
|
||||
float sdf = params.iso_scale * (gy - h);
|
||||
out_buffer.set_voxel_f(sdf, x, y, z, channel);
|
||||
}
|
||||
|
||||
@ -65,20 +70,21 @@ protected:
|
||||
// Blocky
|
||||
|
||||
int gz = origin.z;
|
||||
|
||||
for (int z = 0; z < bs.z; ++z, gz += stride) {
|
||||
|
||||
int gx = origin.x;
|
||||
for (int x = 0; x < bs.x; ++x, gx += stride) {
|
||||
|
||||
for (int x = 0; x < bs.x; ++x, gx += stride) {
|
||||
// Output is blocky, so we can go for just one sample
|
||||
float h = _range.xform(height_func(gx, gz));
|
||||
float h = params.range.xform(height_func(gx, gz));
|
||||
h -= origin.y;
|
||||
int ih = int(h);
|
||||
if (ih > 0) {
|
||||
if (ih > bs.y) {
|
||||
ih = bs.y;
|
||||
}
|
||||
out_buffer.fill_area(_matter_type, Vector3i(x, 0, z), Vector3i(x + 1, ih, z + 1), channel);
|
||||
out_buffer.fill_area(
|
||||
params.matter_type, Vector3i(x, 0, z), Vector3i(x + 1, ih, z + 1), channel);
|
||||
}
|
||||
|
||||
} // for x
|
||||
@ -98,10 +104,15 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
VoxelBuffer::ChannelId _channel = VoxelBuffer::CHANNEL_SDF;
|
||||
int _matter_type = 1;
|
||||
Range _range;
|
||||
float _iso_scale = 0.1;
|
||||
struct Parameters {
|
||||
VoxelBuffer::ChannelId channel = VoxelBuffer::CHANNEL_SDF;
|
||||
int matter_type = 1;
|
||||
Range range;
|
||||
float iso_scale = 0.1;
|
||||
};
|
||||
|
||||
RWLock *_parameters_lock;
|
||||
Parameters _parameters;
|
||||
};
|
||||
|
||||
#endif // VOXEL_GENERATOR_HEIGHTMAP_H
|
||||
|
@ -20,10 +20,21 @@ inline float get_height_blurred(Image &im, int x, int y) {
|
||||
} // namespace
|
||||
|
||||
VoxelGeneratorImage::VoxelGeneratorImage() {
|
||||
_parameters_lock = RWLock::create();
|
||||
}
|
||||
|
||||
VoxelGeneratorImage::~VoxelGeneratorImage() {
|
||||
memdelete(_parameters_lock);
|
||||
}
|
||||
|
||||
void VoxelGeneratorImage::set_image(Ref<Image> im) {
|
||||
if (im == _image) {
|
||||
return;
|
||||
}
|
||||
_image = im;
|
||||
Ref<Image> copy = im.is_valid() ? im->duplicate() : Ref<Image>();
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
_parameters.image = copy;
|
||||
}
|
||||
|
||||
Ref<Image> VoxelGeneratorImage::get_image() const {
|
||||
@ -31,23 +42,30 @@ Ref<Image> VoxelGeneratorImage::get_image() const {
|
||||
}
|
||||
|
||||
void VoxelGeneratorImage::set_blur_enabled(bool enable) {
|
||||
_blur_enabled = enable;
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
_parameters.blur_enabled = enable;
|
||||
}
|
||||
|
||||
bool VoxelGeneratorImage::is_blur_enabled() const {
|
||||
return _blur_enabled;
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return _parameters.blur_enabled;
|
||||
}
|
||||
|
||||
void VoxelGeneratorImage::generate_block(VoxelBlockRequest &input) {
|
||||
|
||||
ERR_FAIL_COND(_image.is_null());
|
||||
|
||||
VoxelBuffer &out_buffer = **input.voxel_buffer;
|
||||
Image &image = **_image;
|
||||
|
||||
Parameters params;
|
||||
{
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
params = _parameters;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(params.image.is_null());
|
||||
Image &image = **params.image;
|
||||
|
||||
image.lock();
|
||||
|
||||
if (_blur_enabled) {
|
||||
if (params.blur_enabled) {
|
||||
VoxelGeneratorHeightmap::generate(
|
||||
out_buffer,
|
||||
[&image](int x, int z) { return get_height_blurred(image, x, z); },
|
||||
@ -65,7 +83,6 @@ void VoxelGeneratorImage::generate_block(VoxelBlockRequest &input) {
|
||||
}
|
||||
|
||||
void VoxelGeneratorImage::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_image", "image"), &VoxelGeneratorImage::set_image);
|
||||
ClassDB::bind_method(D_METHOD("get_image"), &VoxelGeneratorImage::get_image);
|
||||
|
||||
|
@ -10,6 +10,7 @@ class VoxelGeneratorImage : public VoxelGeneratorHeightmap {
|
||||
|
||||
public:
|
||||
VoxelGeneratorImage();
|
||||
~VoxelGeneratorImage();
|
||||
|
||||
void set_image(Ref<Image> im);
|
||||
Ref<Image> get_image() const;
|
||||
@ -23,9 +24,20 @@ private:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
// Proper reference used for external access.
|
||||
Ref<Image> _image;
|
||||
// Mostly here as demo/tweak. It's better recommended to use an EXR/float image.
|
||||
bool _blur_enabled = false;
|
||||
|
||||
struct Parameters {
|
||||
// This is a read-only copy of the image.
|
||||
// It wastes memory for sure, but Godot does not offer any way to secure this better.
|
||||
// If this is a problem one day, we could add an option to dereference the external image in game.
|
||||
Ref<Image> image;
|
||||
// Mostly here as demo/tweak. It's better recommended to use an EXR/float image.
|
||||
bool blur_enabled = false;
|
||||
};
|
||||
|
||||
Parameters _parameters;
|
||||
RWLock *_parameters_lock;
|
||||
};
|
||||
|
||||
#endif // HEADER_VOXEL_GENERATOR_IMAGE
|
||||
|
@ -2,32 +2,56 @@
|
||||
#include <core/engine.h>
|
||||
|
||||
VoxelGeneratorNoise::VoxelGeneratorNoise() {
|
||||
_parameters_lock = RWLock::create();
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
// Have one by default in editor
|
||||
_noise.instance();
|
||||
Ref<OpenSimplexNoise> noise;
|
||||
noise.instance();
|
||||
set_noise(noise);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
VoxelGeneratorNoise::~VoxelGeneratorNoise() {
|
||||
memdelete(_parameters_lock);
|
||||
}
|
||||
|
||||
void VoxelGeneratorNoise::set_noise(Ref<OpenSimplexNoise> noise) {
|
||||
if (_noise == noise) {
|
||||
return;
|
||||
}
|
||||
_noise = noise;
|
||||
Ref<OpenSimplexNoise> copy;
|
||||
if (noise.is_valid()) {
|
||||
copy = noise->duplicate();
|
||||
}
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
_parameters.noise = copy;
|
||||
}
|
||||
|
||||
void VoxelGeneratorNoise::set_channel(VoxelBuffer::ChannelId channel) {
|
||||
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
|
||||
if (_channel != channel) {
|
||||
_channel = channel;
|
||||
bool changed = false;
|
||||
{
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
if (_parameters.channel != channel) {
|
||||
_parameters.channel = channel;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
emit_changed();
|
||||
}
|
||||
}
|
||||
|
||||
VoxelBuffer::ChannelId VoxelGeneratorNoise::get_channel() const {
|
||||
return _channel;
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return _parameters.channel;
|
||||
}
|
||||
|
||||
int VoxelGeneratorNoise::get_used_channels_mask() const {
|
||||
return (1 << _channel);
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return (1 << _parameters.channel);
|
||||
}
|
||||
|
||||
Ref<OpenSimplexNoise> VoxelGeneratorNoise::get_noise() const {
|
||||
@ -35,22 +59,26 @@ Ref<OpenSimplexNoise> VoxelGeneratorNoise::get_noise() const {
|
||||
}
|
||||
|
||||
void VoxelGeneratorNoise::set_height_start(real_t y) {
|
||||
_height_start = y;
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
_parameters.height_start = y;
|
||||
}
|
||||
|
||||
real_t VoxelGeneratorNoise::get_height_start() const {
|
||||
return _height_start;
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return _parameters.height_start;
|
||||
}
|
||||
|
||||
void VoxelGeneratorNoise::set_height_range(real_t hrange) {
|
||||
if (hrange < 0.1f) {
|
||||
hrange = 0.1f;
|
||||
}
|
||||
_height_range = hrange;
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
_parameters.height_range = hrange;
|
||||
}
|
||||
|
||||
real_t VoxelGeneratorNoise::get_height_range() const {
|
||||
return _height_range;
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return _parameters.height_range;
|
||||
}
|
||||
|
||||
// For isosurface use cases, noise can be "shaped" by calculating only the first octave,
|
||||
@ -58,7 +86,6 @@ real_t VoxelGeneratorNoise::get_height_range() const {
|
||||
// because then we assume next octaves won't change the sign (which crosses the surface).
|
||||
// This might reduce accuracy in some areas, but it speeds up the results.
|
||||
static inline float get_shaped_noise(OpenSimplexNoise &noise, float x, float y, float z, float threshold, float bias) {
|
||||
|
||||
x /= noise.get_period();
|
||||
y /= noise.get_period();
|
||||
z /= noise.get_period();
|
||||
@ -89,42 +116,46 @@ static inline float get_shaped_noise(OpenSimplexNoise &noise, float x, float y,
|
||||
|
||||
void VoxelGeneratorNoise::generate_block(VoxelBlockRequest &input) {
|
||||
ERR_FAIL_COND(input.voxel_buffer.is_null());
|
||||
ERR_FAIL_COND(_noise.is_null());
|
||||
|
||||
OpenSimplexNoise &noise = **_noise;
|
||||
Parameters params;
|
||||
{
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
params = _parameters;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(params.noise.is_null());
|
||||
|
||||
OpenSimplexNoise &noise = **params.noise;
|
||||
VoxelBuffer &buffer = **input.voxel_buffer;
|
||||
Vector3i origin_in_voxels = input.origin_in_voxels;
|
||||
int lod = input.lod;
|
||||
|
||||
int isosurface_lower_bound = static_cast<int>(Math::floor(_height_start));
|
||||
int isosurface_upper_bound = static_cast<int>(Math::ceil(_height_start + _height_range));
|
||||
int isosurface_lower_bound = static_cast<int>(Math::floor(params.height_start));
|
||||
int isosurface_upper_bound = static_cast<int>(Math::ceil(params.height_start + params.height_range));
|
||||
|
||||
const int air_type = 0;
|
||||
const int matter_type = 1;
|
||||
|
||||
if (origin_in_voxels.y >= isosurface_upper_bound) {
|
||||
|
||||
// Fill with air
|
||||
if (_channel == VoxelBuffer::CHANNEL_SDF) {
|
||||
buffer.clear_channel_f(_channel, 100.0);
|
||||
} else if (_channel == VoxelBuffer::CHANNEL_TYPE) {
|
||||
buffer.clear_channel(_channel, air_type);
|
||||
if (params.channel == VoxelBuffer::CHANNEL_SDF) {
|
||||
buffer.clear_channel_f(params.channel, 100.0);
|
||||
} else if (params.channel == VoxelBuffer::CHANNEL_TYPE) {
|
||||
buffer.clear_channel(params.channel, air_type);
|
||||
}
|
||||
|
||||
} else if (origin_in_voxels.y + (buffer.get_size().y << lod) < isosurface_lower_bound) {
|
||||
|
||||
// Fill with matter
|
||||
if (_channel == VoxelBuffer::CHANNEL_SDF) {
|
||||
buffer.clear_channel_f(_channel, -100.0);
|
||||
} else if (_channel == VoxelBuffer::CHANNEL_TYPE) {
|
||||
buffer.clear_channel(_channel, matter_type);
|
||||
if (params.channel == VoxelBuffer::CHANNEL_SDF) {
|
||||
buffer.clear_channel_f(params.channel, -100.0);
|
||||
} else if (params.channel == VoxelBuffer::CHANNEL_TYPE) {
|
||||
buffer.clear_channel(params.channel, matter_type);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const float iso_scale = noise.get_period() * 0.1;
|
||||
const Vector3i size = buffer.get_size();
|
||||
const float height_range_inv = 1.f / _height_range;
|
||||
const float height_range_inv = 1.f / params.height_range;
|
||||
const float one_minus_persistence = 1.f - noise.get_persistence();
|
||||
|
||||
for (int z = 0; z < size.z; ++z) {
|
||||
@ -134,40 +165,39 @@ void VoxelGeneratorNoise::generate_block(VoxelBlockRequest &input) {
|
||||
int lx = origin_in_voxels.x + (x << lod);
|
||||
|
||||
for (int y = 0; y < size.y; ++y) {
|
||||
|
||||
int ly = origin_in_voxels.y + (y << lod);
|
||||
const int ly = origin_in_voxels.y + (y << lod);
|
||||
|
||||
if (ly < isosurface_lower_bound) {
|
||||
// Below is only matter
|
||||
if (_channel == VoxelBuffer::CHANNEL_SDF) {
|
||||
buffer.set_voxel_f(-1, x, y, z, _channel);
|
||||
} else if (_channel == VoxelBuffer::CHANNEL_TYPE) {
|
||||
buffer.set_voxel(matter_type, x, y, z, _channel);
|
||||
if (params.channel == VoxelBuffer::CHANNEL_SDF) {
|
||||
buffer.set_voxel_f(-1, x, y, z, params.channel);
|
||||
} else if (params.channel == VoxelBuffer::CHANNEL_TYPE) {
|
||||
buffer.set_voxel(matter_type, x, y, z, params.channel);
|
||||
}
|
||||
continue;
|
||||
|
||||
} else if (ly >= isosurface_upper_bound) {
|
||||
// Above is only air
|
||||
if (_channel == VoxelBuffer::CHANNEL_SDF) {
|
||||
buffer.set_voxel_f(1, x, y, z, _channel);
|
||||
} else if (_channel == VoxelBuffer::CHANNEL_TYPE) {
|
||||
buffer.set_voxel(air_type, x, y, z, _channel);
|
||||
if (params.channel == VoxelBuffer::CHANNEL_SDF) {
|
||||
buffer.set_voxel_f(1, x, y, z, params.channel);
|
||||
} else if (params.channel == VoxelBuffer::CHANNEL_TYPE) {
|
||||
buffer.set_voxel(air_type, x, y, z, params.channel);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Bias is what makes noise become "matter" the lower we go, and "air" the higher we go
|
||||
float t = (ly - _height_start) * height_range_inv;
|
||||
float t = (ly - params.height_start) * height_range_inv;
|
||||
float bias = 2.0 * t - 1.0;
|
||||
|
||||
// We are near the isosurface, need to calculate noise value
|
||||
float n = get_shaped_noise(noise, lx, ly, lz, one_minus_persistence, bias);
|
||||
float d = (n + bias) * iso_scale;
|
||||
|
||||
if (_channel == VoxelBuffer::CHANNEL_SDF) {
|
||||
buffer.set_voxel_f(d, x, y, z, _channel);
|
||||
} else if (_channel == VoxelBuffer::CHANNEL_TYPE && d < 0) {
|
||||
buffer.set_voxel(matter_type, x, y, z, _channel);
|
||||
if (params.channel == VoxelBuffer::CHANNEL_SDF) {
|
||||
buffer.set_voxel_f(d, x, y, z, params.channel);
|
||||
} else if (params.channel == VoxelBuffer::CHANNEL_TYPE && d < 0) {
|
||||
buffer.set_voxel(matter_type, x, y, z, params.channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -176,7 +206,6 @@ void VoxelGeneratorNoise::generate_block(VoxelBlockRequest &input) {
|
||||
}
|
||||
|
||||
void VoxelGeneratorNoise::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_channel", "channel"), &VoxelGeneratorNoise::set_channel);
|
||||
ClassDB::bind_method(D_METHOD("get_channel"), &VoxelGeneratorNoise::get_channel);
|
||||
|
||||
|
@ -9,6 +9,7 @@ class VoxelGeneratorNoise : public VoxelGenerator {
|
||||
|
||||
public:
|
||||
VoxelGeneratorNoise();
|
||||
~VoxelGeneratorNoise();
|
||||
|
||||
void set_channel(VoxelBuffer::ChannelId channel);
|
||||
VoxelBuffer::ChannelId get_channel() const;
|
||||
@ -29,10 +30,17 @@ protected:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
VoxelBuffer::ChannelId _channel = VoxelBuffer::CHANNEL_SDF;
|
||||
Ref<OpenSimplexNoise> _noise;
|
||||
float _height_start = 0;
|
||||
float _height_range = 300;
|
||||
|
||||
struct Parameters {
|
||||
VoxelBuffer::ChannelId channel = VoxelBuffer::CHANNEL_SDF;
|
||||
Ref<OpenSimplexNoise> noise;
|
||||
float height_start = 0;
|
||||
float height_range = 300;
|
||||
};
|
||||
|
||||
Parameters _parameters;
|
||||
RWLock *_parameters_lock;
|
||||
};
|
||||
|
||||
#endif // VOXEL_GENERATOR_NOISE_H
|
||||
|
@ -2,16 +2,28 @@
|
||||
#include <core/engine.h>
|
||||
|
||||
VoxelGeneratorNoise2D::VoxelGeneratorNoise2D() {
|
||||
_parameters_lock = RWLock::create();
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
// Have one by default in editor
|
||||
_noise.instance();
|
||||
Ref<OpenSimplexNoise> noise;
|
||||
noise.instance();
|
||||
set_noise(noise);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
VoxelGeneratorNoise2D::~VoxelGeneratorNoise2D() {
|
||||
memdelete(_parameters_lock);
|
||||
}
|
||||
|
||||
void VoxelGeneratorNoise2D::set_noise(Ref<OpenSimplexNoise> noise) {
|
||||
if (_noise == noise) {
|
||||
return;
|
||||
}
|
||||
_noise = noise;
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
_parameters.noise = _noise.is_valid() ? _noise->duplicate() : Ref<OpenSimplexNoise>();
|
||||
}
|
||||
|
||||
Ref<OpenSimplexNoise> VoxelGeneratorNoise2D::get_noise() const {
|
||||
@ -19,7 +31,17 @@ Ref<OpenSimplexNoise> VoxelGeneratorNoise2D::get_noise() const {
|
||||
}
|
||||
|
||||
void VoxelGeneratorNoise2D::set_curve(Ref<Curve> curve) {
|
||||
if (_curve == curve) {
|
||||
return;
|
||||
}
|
||||
_curve = curve;
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
if (_curve.is_valid()) {
|
||||
_parameters.curve = _curve->duplicate();
|
||||
_parameters.curve->bake();
|
||||
} else {
|
||||
_parameters.curve.unref();
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Curve> VoxelGeneratorNoise2D::get_curve() const {
|
||||
@ -27,11 +49,16 @@ Ref<Curve> VoxelGeneratorNoise2D::get_curve() const {
|
||||
}
|
||||
|
||||
void VoxelGeneratorNoise2D::generate_block(VoxelBlockRequest &input) {
|
||||
Parameters params;
|
||||
{
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
params = _parameters;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(_noise.is_null());
|
||||
ERR_FAIL_COND(params.noise.is_null());
|
||||
OpenSimplexNoise &noise = **params.noise;
|
||||
|
||||
VoxelBuffer &out_buffer = **input.voxel_buffer;
|
||||
OpenSimplexNoise &noise = **_noise;
|
||||
|
||||
if (_curve.is_null()) {
|
||||
VoxelGeneratorHeightmap::generate(
|
||||
@ -39,7 +66,7 @@ void VoxelGeneratorNoise2D::generate_block(VoxelBlockRequest &input) {
|
||||
[&noise](int x, int z) { return 0.5 + 0.5 * noise.get_noise_2d(x, z); },
|
||||
input.origin_in_voxels, input.lod);
|
||||
} else {
|
||||
Curve &curve = **_curve;
|
||||
Curve &curve = **params.curve;
|
||||
VoxelGeneratorHeightmap::generate(
|
||||
out_buffer,
|
||||
[&noise, &curve](int x, int z) { return curve.interpolate_baked(0.5 + 0.5 * noise.get_noise_2d(x, z)); },
|
||||
@ -50,7 +77,6 @@ void VoxelGeneratorNoise2D::generate_block(VoxelBlockRequest &input) {
|
||||
}
|
||||
|
||||
void VoxelGeneratorNoise2D::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_noise", "noise"), &VoxelGeneratorNoise2D::set_noise);
|
||||
ClassDB::bind_method(D_METHOD("get_noise"), &VoxelGeneratorNoise2D::get_noise);
|
||||
|
||||
|
@ -6,9 +6,10 @@
|
||||
|
||||
class VoxelGeneratorNoise2D : public VoxelGeneratorHeightmap {
|
||||
GDCLASS(VoxelGeneratorNoise2D, VoxelGeneratorHeightmap)
|
||||
|
||||
|
||||
public:
|
||||
VoxelGeneratorNoise2D();
|
||||
~VoxelGeneratorNoise2D();
|
||||
|
||||
void set_noise(Ref<OpenSimplexNoise> noise);
|
||||
Ref<OpenSimplexNoise> get_noise() const;
|
||||
@ -24,6 +25,14 @@ private:
|
||||
private:
|
||||
Ref<OpenSimplexNoise> _noise;
|
||||
Ref<Curve> _curve;
|
||||
|
||||
struct Parameters {
|
||||
Ref<OpenSimplexNoise> noise;
|
||||
Ref<Curve> curve;
|
||||
};
|
||||
|
||||
Parameters _parameters;
|
||||
RWLock *_parameters_lock;
|
||||
};
|
||||
|
||||
#endif // VOXEL_GENERATOR_NOISE_2D_H
|
||||
|
@ -3,16 +3,22 @@
|
||||
#include <cmath>
|
||||
|
||||
VoxelGeneratorWaves::VoxelGeneratorWaves() {
|
||||
_pattern_size = Vector2(30, 30);
|
||||
_parameters.pattern_size = Vector2(30, 30);
|
||||
set_height_range(30);
|
||||
}
|
||||
|
||||
void VoxelGeneratorWaves::generate_block(VoxelBlockRequest &input) {
|
||||
Parameters params;
|
||||
{
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
params = _parameters;
|
||||
}
|
||||
|
||||
VoxelBuffer &out_buffer = **input.voxel_buffer;
|
||||
const Vector2 freq(
|
||||
Math_PI / static_cast<float>(_pattern_size.x),
|
||||
Math_PI / static_cast<float>(_pattern_size.y));
|
||||
const Vector2 offset = _pattern_offset;
|
||||
Math_PI / static_cast<float>(params.pattern_size.x),
|
||||
Math_PI / static_cast<float>(params.pattern_size.y));
|
||||
const Vector2 offset = params.pattern_offset;
|
||||
|
||||
VoxelGeneratorHeightmap::generate(
|
||||
out_buffer,
|
||||
@ -22,18 +28,29 @@ void VoxelGeneratorWaves::generate_block(VoxelBlockRequest &input) {
|
||||
input.origin_in_voxels, input.lod);
|
||||
}
|
||||
|
||||
Vector2 VoxelGeneratorWaves::get_pattern_size() const {
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return _parameters.pattern_size;
|
||||
}
|
||||
|
||||
void VoxelGeneratorWaves::set_pattern_size(Vector2 size) {
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
size.x = max(size.x, 0.1f);
|
||||
size.y = max(size.y, 0.1f);
|
||||
_pattern_size = size;
|
||||
_parameters.pattern_size = size;
|
||||
}
|
||||
|
||||
Vector2 VoxelGeneratorWaves::get_pattern_offset() const {
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return _parameters.pattern_offset;
|
||||
}
|
||||
|
||||
void VoxelGeneratorWaves::set_pattern_offset(Vector2 offset) {
|
||||
_pattern_offset = offset;
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
_parameters.pattern_offset = offset;
|
||||
}
|
||||
|
||||
void VoxelGeneratorWaves::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_pattern_size", "size"), &VoxelGeneratorWaves::set_pattern_size);
|
||||
ClassDB::bind_method(D_METHOD("get_pattern_size"), &VoxelGeneratorWaves::get_pattern_size);
|
||||
|
||||
|
@ -14,17 +14,22 @@ public:
|
||||
|
||||
void generate_block(VoxelBlockRequest &input) override;
|
||||
|
||||
Vector2 get_pattern_size() const { return _pattern_size; }
|
||||
Vector2 get_pattern_size() const;
|
||||
void set_pattern_size(Vector2 size);
|
||||
|
||||
Vector2 get_pattern_offset() const { return _pattern_offset; }
|
||||
Vector2 get_pattern_offset() const;
|
||||
void set_pattern_offset(Vector2 offset);
|
||||
|
||||
private:
|
||||
static void _bind_methods();
|
||||
|
||||
Vector2 _pattern_size;
|
||||
Vector2 _pattern_offset;
|
||||
struct Parameters {
|
||||
Vector2 pattern_size;
|
||||
Vector2 pattern_offset;
|
||||
};
|
||||
|
||||
Parameters _parameters;
|
||||
RWLock *_parameters_lock;
|
||||
};
|
||||
|
||||
#endif // VOXEL_GENERATOR_WAVES_H
|
||||
|
@ -3,7 +3,8 @@
|
||||
|
||||
#include "voxel_generator.h"
|
||||
|
||||
// Generator based on a script, like GDScript, C# or NativeScript
|
||||
// Generator based on a script, like GDScript, C# or NativeScript.
|
||||
// The script is expected to properly handle multithreading.
|
||||
class VoxelGeneratorScript : public VoxelGenerator {
|
||||
GDCLASS(VoxelGeneratorScript, VoxelGenerator)
|
||||
public:
|
||||
|
@ -61,6 +61,7 @@ public:
|
||||
void enqueue(IVoxelTask *task);
|
||||
void enqueue(ArraySlice<IVoxelTask *> tasks);
|
||||
|
||||
// TODO Lambda might not be the best API. memcpying to a vector would ensure we lock for a shorter time.
|
||||
template <typename F>
|
||||
void dequeue_completed_tasks(F f) {
|
||||
MutexLock lock(_completed_tasks_mutex);
|
||||
@ -102,8 +103,9 @@ private:
|
||||
FixedArray<ThreadData, MAX_THREADS> _threads;
|
||||
uint32_t _thread_count = 0;
|
||||
|
||||
// TODO Optimize this with a less naive design? Maybe moodycamel
|
||||
std::vector<TaskItem> _tasks;
|
||||
Mutex *_tasks_mutex = nullptr; // TODO Optimize this with a less naive design?
|
||||
Mutex *_tasks_mutex = nullptr;
|
||||
Semaphore *_tasks_semaphore = nullptr;
|
||||
|
||||
std::vector<IVoxelTask *> _completed_tasks;
|
||||
|
Loading…
x
Reference in New Issue
Block a user