Added scale distribution and noise

master
Marc Gilleron 2021-02-16 01:14:04 +00:00
parent a94a8da157
commit 5bef3b6cc6
3 changed files with 215 additions and 9 deletions

View File

@ -1,5 +1,7 @@
#include "voxel_instance_generator.h"
#include "../../util/profiling.h"
#include <core/core_string_names.h>
#include <scene/resources/mesh.h>
namespace {
@ -8,6 +10,7 @@ const char *DENSITY_HINT_STRING = "0.0, 1.0, 0.01";
thread_local std::vector<Vector3> g_vertex_cache;
thread_local std::vector<Vector3> g_normal_cache;
thread_local std::vector<float> g_noise_cache;
} // namespace
@ -143,6 +146,45 @@ void VoxelInstanceGenerator::generate_transforms(
}
}
std::vector<float> &noise_cache = g_noise_cache;
if (_noise.is_valid()) {
noise_cache.clear();
switch (_noise_dimension) {
case DIMENSION_2D: {
for (size_t i = 0; i < vertex_cache.size(); ++i) {
const Vector3 &pos = vertex_cache[i] + block_local_transform.origin;
const float n = _noise->get_noise_2d(pos.x, pos.z);
if (n < 0) {
unordered_remove(vertex_cache, i);
unordered_remove(normal_cache, i);
--i;
} else {
noise_cache.push_back(n);
}
}
} break;
case DIMENSION_3D: {
for (size_t i = 0; i < vertex_cache.size(); ++i) {
const Vector3 &pos = vertex_cache[i] + block_local_transform.origin;
const float n = _noise->get_noise_3d(pos.x, pos.y, pos.z);
if (n < 0) {
unordered_remove(vertex_cache, i);
unordered_remove(normal_cache, i);
--i;
} else {
noise_cache.push_back(n);
}
}
} break;
default:
ERR_FAIL();
}
}
const float vertical_alignment = _vertical_alignment;
const float scale_min = _min_scale;
const float scale_range = _max_scale - _min_scale;
@ -248,7 +290,31 @@ void VoxelInstanceGenerator::generate_transforms(
Vector3(axis_x.z, axis_y.z, axis_z.z));
if (scale_range > 0.f) {
const float scale = scale_min + scale_range * pcg1.randf();
float r = pcg1.randf();
switch (_scale_distribution) {
case DISTRIBUTION_QUADRATIC:
r = r * r;
break;
case DISTRIBUTION_CUBIC:
r = r * r * r;
break;
case DISTRIBUTION_QUINTIC:
r = r * r * r * r * r;
break;
}
if (_noise.is_valid() && _noise_on_scale > 0.f) {
#ifdef DEBUG_ENABLED
CRASH_COND(vertex_index >= noise_cache.size());
#endif
// Multiplied noise because it gives more pronounced results
const float n = clamp(noise_cache[vertex_index] * 2.f, 0.f, 1.f);
r *= Math::lerp(1.f, n, _noise_on_scale);
}
float scale = scale_min + scale_range * r;
t.basis.scale(Vector3(scale, scale, scale));
} else if (scale_min != 1.f) {
@ -318,6 +384,19 @@ float VoxelInstanceGenerator::get_max_scale() const {
return _max_scale;
}
void VoxelInstanceGenerator::set_scale_distribution(Distribution distribution) {
ERR_FAIL_INDEX(distribution, DISTRIBUTION_COUNT);
if (distribution == _scale_distribution) {
return;
}
_scale_distribution = distribution;
emit_changed();
}
VoxelInstanceGenerator::Distribution VoxelInstanceGenerator::get_scale_distribution() const {
return _scale_distribution;
}
void VoxelInstanceGenerator::set_vertical_alignment(float amount) {
amount = clamp(amount, 0.f, 1.f);
if (_vertical_alignment == amount) {
@ -407,6 +486,54 @@ bool VoxelInstanceGenerator::get_random_vertical_flip() const {
return _random_vertical_flip;
}
void VoxelInstanceGenerator::set_noise(Ref<FastNoiseLite> noise) {
if (_noise == noise) {
return;
}
if (_noise.is_valid()) {
_noise->disconnect(CoreStringNames::get_singleton()->changed, this, "_on_noise_changed");
}
_noise = noise;
if (_noise.is_valid()) {
_noise->connect(CoreStringNames::get_singleton()->changed, this, "_on_noise_changed");
}
emit_changed();
}
Ref<FastNoiseLite> VoxelInstanceGenerator::get_noise() const {
return _noise;
}
void VoxelInstanceGenerator::set_noise_dimension(Dimension dim) {
ERR_FAIL_INDEX(dim, DIMENSION_COUNT);
if (dim == _noise_dimension) {
return;
}
_noise_dimension = dim;
emit_changed();
}
VoxelInstanceGenerator::Dimension VoxelInstanceGenerator::get_noise_dimension() const {
return _noise_dimension;
}
void VoxelInstanceGenerator::set_noise_on_scale(float amount) {
amount = clamp(amount, 0.f, 1.f);
if (amount == _noise_on_scale) {
return;
}
_noise_on_scale = amount;
emit_changed();
}
float VoxelInstanceGenerator::get_noise_on_scale() const {
return _noise_on_scale;
}
void VoxelInstanceGenerator::_on_noise_changed() {
emit_changed();
}
void VoxelInstanceGenerator::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_density", "density"), &VoxelInstanceGenerator::set_density);
ClassDB::bind_method(D_METHOD("get_density"), &VoxelInstanceGenerator::get_density);
@ -420,6 +547,10 @@ void VoxelInstanceGenerator::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_max_scale", "max_scale"), &VoxelInstanceGenerator::set_max_scale);
ClassDB::bind_method(D_METHOD("get_max_scale"), &VoxelInstanceGenerator::get_max_scale);
ClassDB::bind_method(D_METHOD("set_scale_distribution", "distribution"),
&VoxelInstanceGenerator::set_scale_distribution);
ClassDB::bind_method(D_METHOD("get_scale_distribution"), &VoxelInstanceGenerator::get_scale_distribution);
ClassDB::bind_method(D_METHOD("set_vertical_alignment", "amount"), &VoxelInstanceGenerator::set_vertical_alignment);
ClassDB::bind_method(D_METHOD("get_vertical_alignment"), &VoxelInstanceGenerator::get_vertical_alignment);
@ -443,27 +574,60 @@ void VoxelInstanceGenerator::_bind_methods() {
&VoxelInstanceGenerator::set_random_vertical_flip);
ClassDB::bind_method(D_METHOD("get_random_vertical_flip"), &VoxelInstanceGenerator::get_random_vertical_flip);
ClassDB::bind_method(D_METHOD("set_noise", "noise"), &VoxelInstanceGenerator::set_noise);
ClassDB::bind_method(D_METHOD("get_noise"), &VoxelInstanceGenerator::get_noise);
ClassDB::bind_method(D_METHOD("set_noise_dimension", "dim"), &VoxelInstanceGenerator::set_noise_dimension);
ClassDB::bind_method(D_METHOD("get_noise_dimension"), &VoxelInstanceGenerator::get_noise_dimension);
ClassDB::bind_method(D_METHOD("set_noise_on_scale", "amount"), &VoxelInstanceGenerator::set_noise_on_scale);
ClassDB::bind_method(D_METHOD("get_noise_on_scale"), &VoxelInstanceGenerator::get_noise_on_scale);
ClassDB::bind_method(D_METHOD("_on_noise_changed"), &VoxelInstanceGenerator::_on_noise_changed);
ADD_GROUP("Emission", "");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "density", PROPERTY_HINT_RANGE, DENSITY_HINT_STRING),
"set_density", "get_density");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emit_mode", PROPERTY_HINT_ENUM, "Vertices,Faces"),
"set_emit_mode", "get_emit_mode");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_scale", PROPERTY_HINT_RANGE, "0.0, 10.0, 0.01"),
"set_min_scale", "get_min_scale");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_scale", PROPERTY_HINT_RANGE, "0.0, 10.0, 0.01"),
"set_max_scale", "get_max_scale");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "vertical_alignment", PROPERTY_HINT_RANGE, "0.0, 1.0, 0.01"),
"set_vertical_alignment", "get_vertical_alignment");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset_along_normal"),
"set_offset_along_normal", "get_offset_along_normal");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_slope_degrees", PROPERTY_HINT_RANGE, "0.0, 180.0, 0.1"),
"set_min_slope_degrees", "get_min_slope_degrees");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_slope_degrees", PROPERTY_HINT_RANGE, "0.0, 180.0, 0.1"),
"set_max_slope_degrees", "get_max_slope_degrees");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_height"), "set_min_height", "get_min_height");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_height"), "set_max_height", "get_max_height");
ADD_GROUP("Scale", "");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_scale", PROPERTY_HINT_RANGE, "0.0, 10.0, 0.01"),
"set_min_scale", "get_min_scale");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_scale", PROPERTY_HINT_RANGE, "0.0, 10.0, 0.01"),
"set_max_scale", "get_max_scale");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scale_distribution", PROPERTY_HINT_ENUM, "Linear,Quadratic,Cubic,Quintic"),
"set_scale_distribution", "get_scale_distribution");
ADD_GROUP("Rotation", "");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "vertical_alignment", PROPERTY_HINT_RANGE, "0.0, 1.0, 0.01"),
"set_vertical_alignment", "get_vertical_alignment");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "random_vertical_flip"),
"set_random_vertical_flip", "get_random_vertical_flip");
ADD_GROUP("Offset", "");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset_along_normal"),
"set_offset_along_normal", "get_offset_along_normal");
ADD_GROUP("Noise", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "FastNoiseLite"),
"set_noise", "get_noise");
ADD_PROPERTY(PropertyInfo(Variant::INT, "noise_dimension", PROPERTY_HINT_ENUM, "2D,3D"),
"set_noise_dimension", "get_noise_dimension");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "noise_on_scale", PROPERTY_HINT_RANGE, "0.0, 1.0, 0.01"),
"set_noise_on_scale", "get_noise_on_scale");
BIND_ENUM_CONSTANT(EMIT_FROM_VERTICES);
BIND_ENUM_CONSTANT(EMIT_FROM_FACES);
}

View File

@ -3,6 +3,8 @@
//#include "../../storage/voxel_buffer.h"
#include "../../math/vector3i.h"
#include "../../util/noise/fast_noise_lite.h"
#include <core/resource.h>
#include <limits>
#include <vector>
@ -34,6 +36,20 @@ public:
EMIT_MODE_COUNT
};
enum Distribution {
DISTRIBUTION_LINEAR = 0,
DISTRIBUTION_QUADRATIC,
DISTRIBUTION_CUBIC,
DISTRIBUTION_QUINTIC,
DISTRIBUTION_COUNT
};
enum Dimension {
DIMENSION_2D = 0,
DIMENSION_3D,
DIMENSION_COUNT
};
// This API might change so for now it's not exposed to scripts
void generate_transforms(
std::vector<Transform> &out_transforms,
@ -59,6 +75,9 @@ public:
void set_max_scale(float max_scale);
float get_max_scale() const;
void set_scale_distribution(Distribution distribution);
Distribution get_scale_distribution() const;
// TODO Add scale curve, in real life there are way more small items than big ones
void set_offset_along_normal(float offset);
@ -79,7 +98,18 @@ public:
void set_random_vertical_flip(bool flip);
bool get_random_vertical_flip() const;
void set_noise(Ref<FastNoiseLite> noise);
Ref<FastNoiseLite> get_noise() const;
void set_noise_dimension(Dimension dim);
Dimension get_noise_dimension() const;
void set_noise_on_scale(float amount);
float get_noise_on_scale() const;
private:
void _on_noise_changed();
static void _bind_methods();
float _density = 0.1f;
@ -93,6 +123,10 @@ private:
float _max_height = std::numeric_limits<float>::max();
bool _random_vertical_flip = false;
EmitMode _emit_mode = EMIT_FROM_VERTICES;
Distribution _scale_distribution = DISTRIBUTION_QUADRATIC;
Ref<FastNoiseLite> _noise;
Dimension _noise_dimension = DIMENSION_3D;
float _noise_on_scale = 0.f;
// Stored separately for editor
float _min_slope_degrees = 0.f;
@ -100,5 +134,7 @@ private:
};
VARIANT_ENUM_CAST(VoxelInstanceGenerator::EmitMode);
VARIANT_ENUM_CAST(VoxelInstanceGenerator::Distribution);
VARIANT_ENUM_CAST(VoxelInstanceGenerator::Dimension);
#endif // VOXEL_INSTANCE_GENERATOR_H

View File

@ -45,6 +45,12 @@ void unordered_remove(Vector<T> &v, unsigned int pos) {
v.resize(last);
}
template <typename T>
void unordered_remove(std::vector<T> &v, unsigned int pos) {
v[pos] = v.back();
v.pop_back();
}
// Removes all items satisfying the given predicate.
// This can change the size of the container, and original order of items is not preserved.
template <typename T, typename F>