Split VoxelGeneratorTest into VoxelGeneratorWaves and VoxelGeneratorFlat
parent
b8ee140a89
commit
c33f46a437
|
@ -0,0 +1,111 @@
|
|||
#include "voxel_generator_flat.h"
|
||||
|
||||
VoxelGeneratorFlat::VoxelGeneratorFlat() {
|
||||
}
|
||||
|
||||
void VoxelGeneratorFlat::set_channel(VoxelBuffer::ChannelId channel) {
|
||||
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
|
||||
if (_channel != channel) {
|
||||
_channel = channel;
|
||||
emit_changed();
|
||||
}
|
||||
}
|
||||
|
||||
VoxelBuffer::ChannelId VoxelGeneratorFlat::get_channel() const {
|
||||
return _channel;
|
||||
}
|
||||
|
||||
int VoxelGeneratorFlat::get_used_channels_mask() const {
|
||||
return (1 << _channel);
|
||||
}
|
||||
|
||||
void VoxelGeneratorFlat::set_voxel_type(int t) {
|
||||
_voxel_type = t;
|
||||
}
|
||||
|
||||
int VoxelGeneratorFlat::get_voxel_type() const {
|
||||
return _voxel_type;
|
||||
}
|
||||
|
||||
void VoxelGeneratorFlat::set_height(float h) {
|
||||
_height = h;
|
||||
}
|
||||
|
||||
void VoxelGeneratorFlat::generate_block(VoxelBlockRequest &input) {
|
||||
ERR_FAIL_COND(input.voxel_buffer.is_null());
|
||||
|
||||
VoxelBuffer &out_buffer = **input.voxel_buffer;
|
||||
const Vector3i origin = input.origin_in_voxels;
|
||||
const int channel = _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) {
|
||||
// The bottom of the block is above the highest ground can go (default is air)
|
||||
return;
|
||||
}
|
||||
if (origin.y + (bs.y << lod) < _height - margin) {
|
||||
// The top of the block is below the lowest ground can go
|
||||
out_buffer.clear_channel(_channel, use_sdf ? 0 : _voxel_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) {
|
||||
|
||||
int gy = origin.y;
|
||||
for (int y = 0; y < bs.y; ++y, gy += stride) {
|
||||
float sdf = _iso_scale * (gy - _height);
|
||||
out_buffer.set_voxel_f(sdf, x, y, z, channel);
|
||||
}
|
||||
|
||||
} // for x
|
||||
} // for z
|
||||
|
||||
} else {
|
||||
// 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) {
|
||||
|
||||
float h = _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);
|
||||
}
|
||||
|
||||
} // for x
|
||||
} // for z
|
||||
} // use_sdf
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_voxel_type", "id"), &VoxelGeneratorFlat::set_voxel_type);
|
||||
ClassDB::bind_method(D_METHOD("get_voxel_type"), &VoxelGeneratorFlat::get_voxel_type);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_height", "h"), &VoxelGeneratorFlat::set_height);
|
||||
ClassDB::bind_method(D_METHOD("get_height"), &VoxelGeneratorFlat::get_height);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "channel", PROPERTY_HINT_ENUM, VoxelBuffer::CHANNEL_ID_HINT_STRING), "set_channel", "get_channel");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height"), "set_height", "get_height");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "voxel_type", PROPERTY_HINT_RANGE, "0,65536,1"), "set_voxel_type", "get_voxel_type");
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef VOXEL_GENERATOR_FLAT_H
|
||||
#define VOXEL_GENERATOR_FLAT_H
|
||||
|
||||
#include "voxel_generator.h"
|
||||
|
||||
class VoxelGeneratorFlat : public VoxelGenerator {
|
||||
GDCLASS(VoxelGeneratorFlat, VoxelGenerator)
|
||||
|
||||
public:
|
||||
VoxelGeneratorFlat();
|
||||
|
||||
void set_channel(VoxelBuffer::ChannelId channel);
|
||||
VoxelBuffer::ChannelId get_channel() const;
|
||||
int get_used_channels_mask() const override;
|
||||
|
||||
void generate_block(VoxelBlockRequest &input) override;
|
||||
|
||||
void set_voxel_type(int t);
|
||||
int get_voxel_type() const;
|
||||
|
||||
void set_height(float h);
|
||||
float get_height() const { return _height; }
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
VoxelBuffer::ChannelId _channel = VoxelBuffer::CHANNEL_SDF;
|
||||
int _voxel_type = 1;
|
||||
float _height = 0;
|
||||
float _iso_scale = 0.1;
|
||||
};
|
||||
|
||||
#endif // VOXEL_GENERATOR_FLAT_H
|
|
@ -7,7 +7,7 @@ VoxelGeneratorHeightmap::VoxelGeneratorHeightmap() {
|
|||
|
||||
void VoxelGeneratorHeightmap::set_channel(VoxelBuffer::ChannelId channel) {
|
||||
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
|
||||
if(_channel != channel) {
|
||||
if (_channel != channel) {
|
||||
_channel = channel;
|
||||
emit_changed();
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ VoxelBuffer::ChannelId VoxelGeneratorHeightmap::get_channel() const {
|
|||
}
|
||||
|
||||
int VoxelGeneratorHeightmap::get_used_channels_mask() const {
|
||||
return (1<<_channel);
|
||||
return (1 << _channel);
|
||||
}
|
||||
|
||||
void VoxelGeneratorHeightmap::set_height_start(float start) {
|
||||
|
@ -59,6 +59,7 @@ void VoxelGeneratorHeightmap::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_iso_scale", "scale"), &VoxelGeneratorHeightmap::set_iso_scale);
|
||||
ClassDB::bind_method(D_METHOD("get_iso_scale"), &VoxelGeneratorHeightmap::get_iso_scale);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "channel", PROPERTY_HINT_ENUM, VoxelBuffer::CHANNEL_ID_HINT_STRING), "set_channel", "get_channel");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height_start"), "set_height_start", "get_height_start");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height_range"), "set_height_range", "get_height_range");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "iso_scale"), "set_iso_scale", "get_iso_scale");
|
||||
|
|
|
@ -1,157 +0,0 @@
|
|||
#include "voxel_generator_test.h"
|
||||
|
||||
VARIANT_ENUM_CAST(VoxelGeneratorTest::Mode)
|
||||
|
||||
VoxelGeneratorTest::VoxelGeneratorTest() {
|
||||
_mode = MODE_WAVES;
|
||||
_voxel_type = 1;
|
||||
_pattern_size = Vector3i(10, 10, 10);
|
||||
}
|
||||
|
||||
void VoxelGeneratorTest::set_channel(VoxelBuffer::ChannelId channel) {
|
||||
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
|
||||
if(_channel != channel) {
|
||||
_channel = channel;
|
||||
emit_changed();
|
||||
}
|
||||
}
|
||||
|
||||
VoxelBuffer::ChannelId VoxelGeneratorTest::get_channel() const {
|
||||
return _channel;
|
||||
}
|
||||
|
||||
int VoxelGeneratorTest::get_used_channels_mask() const {
|
||||
return (1<<_channel);
|
||||
}
|
||||
|
||||
void VoxelGeneratorTest::set_mode(Mode mode) {
|
||||
ERR_FAIL_INDEX(mode, MODE_COUNT);
|
||||
_mode = mode;
|
||||
}
|
||||
|
||||
void VoxelGeneratorTest::set_voxel_type(int t) {
|
||||
_voxel_type = t;
|
||||
}
|
||||
|
||||
int VoxelGeneratorTest::get_voxel_type() const {
|
||||
return _voxel_type;
|
||||
}
|
||||
|
||||
void VoxelGeneratorTest::set_pattern_size(Vector3i size) {
|
||||
ERR_FAIL_COND(size.x < 1 || size.y < 1 || size.z < 1);
|
||||
_pattern_size = size;
|
||||
}
|
||||
|
||||
void VoxelGeneratorTest::set_pattern_offset(Vector3i offset) {
|
||||
_pattern_offset = offset;
|
||||
}
|
||||
|
||||
void VoxelGeneratorTest::generate_block(VoxelBlockRequest &input) {
|
||||
ERR_FAIL_COND(input.voxel_buffer.is_null());
|
||||
|
||||
if (input.lod != 0) {
|
||||
// TODO Handle higher lods
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_mode) {
|
||||
|
||||
case MODE_FLAT:
|
||||
generate_block_flat(**input.voxel_buffer, input.origin_in_voxels, input.lod);
|
||||
break;
|
||||
|
||||
case MODE_WAVES:
|
||||
generate_block_waves(**input.voxel_buffer, input.origin_in_voxels, input.lod);
|
||||
break;
|
||||
|
||||
default:
|
||||
CRASH_NOW_MSG("Mode is unrecognized.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelGeneratorTest::generate_block_flat(VoxelBuffer &out_buffer, Vector3i origin, int lod) {
|
||||
|
||||
// TODO Don't expect a block pos, but a voxel pos!
|
||||
Vector3i size = out_buffer.get_size();
|
||||
|
||||
int rh = _pattern_offset.y - origin.y;
|
||||
if (rh > size.y)
|
||||
rh = size.y;
|
||||
|
||||
for (int rz = 0; rz < size.z; ++rz) {
|
||||
for (int rx = 0; rx < size.x; ++rx) {
|
||||
for (int ry = 0; ry < rh; ++ry) {
|
||||
out_buffer.set_voxel(_voxel_type, rx, ry, rz, _channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelGeneratorTest::generate_block_waves(VoxelBuffer &out_buffer, Vector3i origin, int lod) {
|
||||
|
||||
// TODO Don't expect a block pos, but a voxel pos!
|
||||
Vector3i size = out_buffer.get_size();
|
||||
//origin += _pattern_offset;
|
||||
|
||||
float amplitude = static_cast<float>(_pattern_size.y);
|
||||
float period_x = 1.f / static_cast<float>(_pattern_size.x);
|
||||
float period_z = 1.f / static_cast<float>(_pattern_size.z);
|
||||
|
||||
//out_buffer.fill(0, 1); // TRANSVOXEL TEST
|
||||
|
||||
if (origin.y + size.y < Math::floor(_pattern_offset.y - 1.5 * amplitude)) {
|
||||
// Everything is ground
|
||||
out_buffer.fill(_voxel_type);
|
||||
|
||||
} else if (origin.y > Math::ceil(_pattern_offset.y + 1.5 * amplitude)) {
|
||||
// Everything is air
|
||||
return;
|
||||
|
||||
} else {
|
||||
for (int rz = 0; rz < size.z; ++rz) {
|
||||
for (int rx = 0; rx < size.x; ++rx) {
|
||||
|
||||
float x = origin.x + rx;
|
||||
float z = origin.z + rz;
|
||||
|
||||
int h = _pattern_offset.y + amplitude * (Math::cos(x * period_x) + Math::sin(z * period_z));
|
||||
int rh = h - origin.y;
|
||||
if (rh > size.y)
|
||||
rh = size.y;
|
||||
|
||||
for (int ry = 0; ry < rh; ++ry) {
|
||||
out_buffer.set_voxel(_voxel_type, rx, ry, rz, _channel);
|
||||
//out_buffer.set_voxel(255, rx, ry, rz, 1); // TRANSVOXEL TEST
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelGeneratorTest::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_channel", "channel"), &VoxelGeneratorTest::set_channel);
|
||||
ClassDB::bind_method(D_METHOD("get_channel"), &VoxelGeneratorTest::get_channel);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_mode", "mode"), &VoxelGeneratorTest::set_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_mode"), &VoxelGeneratorTest::get_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_voxel_type", "id"), &VoxelGeneratorTest::set_voxel_type);
|
||||
ClassDB::bind_method(D_METHOD("get_voxel_type"), &VoxelGeneratorTest::get_voxel_type);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_pattern_size", "size"), &VoxelGeneratorTest::_set_pattern_size);
|
||||
ClassDB::bind_method(D_METHOD("get_pattern_size"), &VoxelGeneratorTest::_get_pattern_size);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_pattern_offset", "offset"), &VoxelGeneratorTest::_set_pattern_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_pattern_offset"), &VoxelGeneratorTest::_get_pattern_offset);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "channel", PROPERTY_HINT_ENUM, VoxelBuffer::CHANNEL_ID_HINT_STRING), "set_channel", "get_channel");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Flat,Waves"), "set_mode", "get_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "voxel_type", PROPERTY_HINT_RANGE, "0,255,1"), "set_voxel_type", "get_voxel_type");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "pattern_size"), "set_pattern_size", "get_pattern_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "pattern_offset"), "set_pattern_offset", "get_pattern_offset");
|
||||
|
||||
BIND_ENUM_CONSTANT(MODE_FLAT);
|
||||
BIND_ENUM_CONSTANT(MODE_WAVES);
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
#ifndef VOXEL_GENERATOR_TEST_H
|
||||
#define VOXEL_GENERATOR_TEST_H
|
||||
|
||||
#include "voxel_generator.h"
|
||||
|
||||
class VoxelGeneratorTest : public VoxelGenerator {
|
||||
GDCLASS(VoxelGeneratorTest, VoxelGenerator)
|
||||
|
||||
public:
|
||||
enum Mode {
|
||||
MODE_FLAT,
|
||||
MODE_WAVES,
|
||||
MODE_COUNT
|
||||
};
|
||||
|
||||
VoxelGeneratorTest();
|
||||
|
||||
void set_channel(VoxelBuffer::ChannelId channel);
|
||||
VoxelBuffer::ChannelId get_channel() const;
|
||||
int get_used_channels_mask() const override;
|
||||
|
||||
void generate_block(VoxelBlockRequest &input) override;
|
||||
|
||||
void set_mode(Mode mode);
|
||||
Mode get_mode() const { return _mode; }
|
||||
|
||||
void set_voxel_type(int t);
|
||||
int get_voxel_type() const;
|
||||
|
||||
Vector3i get_pattern_size() const { return _pattern_size; }
|
||||
void set_pattern_size(Vector3i size);
|
||||
|
||||
Vector3i get_pattern_offset() const { return _pattern_offset; }
|
||||
void set_pattern_offset(Vector3i offset);
|
||||
|
||||
protected:
|
||||
void generate_block_flat(VoxelBuffer &out_buffer, Vector3i origin, int lod);
|
||||
void generate_block_waves(VoxelBuffer &out_buffer, Vector3i origin, int lod);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
Vector3 _get_pattern_size() const { return get_pattern_size().to_vec3(); }
|
||||
void _set_pattern_size(Vector3 size) { set_pattern_size(Vector3i(size)); }
|
||||
|
||||
Vector3 _get_pattern_offset() const { return get_pattern_offset().to_vec3(); }
|
||||
void _set_pattern_offset(Vector3 offset) { set_pattern_offset(Vector3i(offset)); }
|
||||
|
||||
private:
|
||||
VoxelBuffer::ChannelId _channel = VoxelBuffer::CHANNEL_SDF;
|
||||
Mode _mode;
|
||||
int _voxel_type;
|
||||
Vector3i _pattern_offset;
|
||||
Vector3i _pattern_size;
|
||||
};
|
||||
|
||||
#endif // VOXEL_GENERATOR_TEST_H
|
|
@ -0,0 +1,46 @@
|
|||
#include "voxel_generator_waves.h"
|
||||
#include "../util/utility.h"
|
||||
#include <cmath>
|
||||
|
||||
VoxelGeneratorWaves::VoxelGeneratorWaves() {
|
||||
_pattern_size = Vector2(30, 30);
|
||||
set_height_range(30);
|
||||
}
|
||||
|
||||
void VoxelGeneratorWaves::generate_block(VoxelBlockRequest &input) {
|
||||
|
||||
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;
|
||||
|
||||
VoxelGeneratorHeightmap::generate(out_buffer,
|
||||
[freq, offset](int x, int z) {
|
||||
return 0.5 + 0.25 * (Math::cos((x + offset.x) * freq.x) + Math::sin((z + offset.y) * freq.y));
|
||||
},
|
||||
input.origin_in_voxels, input.lod);
|
||||
}
|
||||
|
||||
void VoxelGeneratorWaves::set_pattern_size(Vector2 size) {
|
||||
size.x = max(size.x, 0.1f);
|
||||
size.y = max(size.y, 0.1f);
|
||||
_pattern_size = size;
|
||||
}
|
||||
|
||||
void VoxelGeneratorWaves::set_pattern_offset(Vector2 offset) {
|
||||
_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);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_pattern_offset", "offset"), &VoxelGeneratorWaves::set_pattern_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_pattern_offset"), &VoxelGeneratorWaves::get_pattern_offset);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "channel", PROPERTY_HINT_ENUM, VoxelBuffer::CHANNEL_ID_HINT_STRING), "set_channel", "get_channel");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "pattern_size"), "set_pattern_size", "get_pattern_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "pattern_offset"), "set_pattern_offset", "get_pattern_offset");
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef VOXEL_GENERATOR_WAVES_H
|
||||
#define VOXEL_GENERATOR_WAVES_H
|
||||
|
||||
#include "voxel_generator_heightmap.h"
|
||||
|
||||
class VoxelGeneratorWaves : public VoxelGeneratorHeightmap {
|
||||
GDCLASS(VoxelGeneratorWaves, VoxelGeneratorHeightmap)
|
||||
|
||||
public:
|
||||
VoxelGeneratorWaves();
|
||||
|
||||
void set_channel(VoxelBuffer::ChannelId channel);
|
||||
VoxelBuffer::ChannelId get_channel() const;
|
||||
|
||||
void generate_block(VoxelBlockRequest &input) override;
|
||||
|
||||
Vector2 get_pattern_size() const { return _pattern_size; }
|
||||
void set_pattern_size(Vector2 size);
|
||||
|
||||
Vector2 get_pattern_offset() const { return _pattern_offset; }
|
||||
void set_pattern_offset(Vector2 offset);
|
||||
|
||||
private:
|
||||
static void _bind_methods();
|
||||
|
||||
Vector2 _pattern_size;
|
||||
Vector2 _pattern_offset;
|
||||
};
|
||||
|
||||
#endif // VOXEL_GENERATOR_WAVES_H
|
|
@ -1,10 +1,11 @@
|
|||
#include "register_types.h"
|
||||
#include "edition/voxel_tool.h"
|
||||
#include "generators/voxel_generator_flat.h"
|
||||
#include "generators/voxel_generator_heightmap.h"
|
||||
#include "generators/voxel_generator_image.h"
|
||||
#include "generators/voxel_generator_noise.h"
|
||||
#include "generators/voxel_generator_noise_2d.h"
|
||||
#include "generators/voxel_generator_test.h"
|
||||
#include "generators/voxel_generator_waves.h"
|
||||
#include "meshers/blocky/voxel_library.h"
|
||||
#include "meshers/blocky/voxel_mesher_blocky.h"
|
||||
#include "meshers/dmc/voxel_mesher_dmc.h"
|
||||
|
@ -42,7 +43,8 @@ void register_voxel_types() {
|
|||
|
||||
// Generators
|
||||
ClassDB::register_class<VoxelGenerator>();
|
||||
ClassDB::register_class<VoxelGeneratorTest>();
|
||||
ClassDB::register_class<VoxelGeneratorFlat>();
|
||||
ClassDB::register_class<VoxelGeneratorWaves>();
|
||||
ClassDB::register_class<VoxelGeneratorHeightmap>();
|
||||
ClassDB::register_class<VoxelGeneratorImage>();
|
||||
ClassDB::register_class<VoxelGeneratorNoise2D>();
|
||||
|
|
Loading…
Reference in New Issue