Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/core/StarPerlin.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/core/StarPerlin.hpp')
-rw-r--r--source/core/StarPerlin.hpp718
1 files changed, 718 insertions, 0 deletions
diff --git a/source/core/StarPerlin.hpp b/source/core/StarPerlin.hpp
new file mode 100644
index 0000000..b042e47
--- /dev/null
+++ b/source/core/StarPerlin.hpp
@@ -0,0 +1,718 @@
+#ifndef STAR_PERLIN_HPP
+#define STAR_PERLIN_HPP
+
+#include "StarJson.hpp"
+#include "StarBiMap.hpp"
+#include "StarInterpolation.hpp"
+#include "StarRandom.hpp"
+
+namespace Star {
+
+STAR_EXCEPTION(PerlinException, StarException);
+
+enum class PerlinType {
+ Uninitialized,
+ Perlin,
+ Billow,
+ RidgedMulti
+};
+extern EnumMap<PerlinType> const PerlinTypeNames;
+
+int const PerlinSampleSize = 512;
+
+template <typename Float>
+class Perlin {
+public:
+ // Default constructed perlin noise is uninitialized and cannot be queried.
+ Perlin();
+
+ Perlin(unsigned octaves, Float freq, Float amp, Float bias, Float alpha, Float beta, uint64_t seed);
+ Perlin(PerlinType type, unsigned octaves, Float freq, Float amp, Float bias, Float alpha, Float beta, uint64_t seed);
+ Perlin(Json const& config, uint64_t seed);
+ explicit Perlin(Json const& json);
+
+ Perlin(Perlin const& perlin);
+ Perlin(Perlin&& perlin);
+
+ Perlin& operator=(Perlin const& perlin);
+ Perlin& operator=(Perlin&& perlin);
+
+ Float get(Float x) const;
+ Float get(Float x, Float y) const;
+ Float get(Float x, Float y, Float z) const;
+
+ PerlinType type() const;
+
+ unsigned octaves() const;
+ Float frequency() const;
+ Float amplitude() const;
+ Float bias() const;
+ Float alpha() const;
+ Float beta() const;
+
+ Json toJson() const;
+
+private:
+ static Float s_curve(Float t);
+ static void setup(Float v, int& b0, int& b1, Float& r0, Float& r1);
+
+ static Float at2(Float* q, Float rx, Float ry);
+ static Float at3(Float* q, Float rx, Float ry, Float rz);
+
+ Float noise1(Float arg) const;
+ Float noise2(Float vec[2]) const;
+ Float noise3(Float vec[3]) const;
+
+ void normalize2(Float v[2]) const;
+ void normalize3(Float v[3]) const;
+
+ void init(uint64_t seed);
+
+ Float perlin(Float x) const;
+ Float perlin(Float x, Float y) const;
+ Float perlin(Float x, Float y, Float z) const;
+
+ Float ridgedMulti(Float x) const;
+ Float ridgedMulti(Float x, Float y) const;
+ Float ridgedMulti(Float x, Float y, Float z) const;
+
+ Float billow(Float x) const;
+ Float billow(Float x, Float y) const;
+ Float billow(Float x, Float y, Float z) const;
+
+ PerlinType m_type;
+ uint64_t m_seed;
+
+ int m_octaves;
+ Float m_frequency;
+ Float m_amplitude;
+ Float m_bias;
+ Float m_alpha;
+ Float m_beta;
+
+ // Only used for RidgedMulti
+ Float m_offset;
+ Float m_gain;
+
+ unique_ptr<int[]> p;
+ unique_ptr<Float[][3]> g3;
+ unique_ptr<Float[][2]> g2;
+ unique_ptr<Float[]> g1;
+};
+
+typedef Perlin<float> PerlinF;
+typedef Perlin<double> PerlinD;
+
+template <typename Float>
+Float Perlin<Float>::s_curve(Float t) {
+ return t * t * (3.0 - 2.0 * t);
+}
+
+template <typename Float>
+void Perlin<Float>::setup(Float v, int& b0, int& b1, Float& r0, Float& r1) {
+ int iv = floor(v);
+ Float fv = v - iv;
+
+ b0 = iv & (PerlinSampleSize - 1);
+ b1 = (iv + 1) & (PerlinSampleSize - 1);
+ r0 = fv;
+ r1 = fv - 1.0;
+}
+
+template <typename Float>
+Float Perlin<Float>::at2(Float* q, Float rx, Float ry) {
+ return rx * q[0] + ry * q[1];
+}
+
+template <typename Float>
+Float Perlin<Float>::at3(Float* q, Float rx, Float ry, Float rz) {
+ return rx * q[0] + ry * q[1] + rz * q[2];
+}
+
+template <typename Float>
+Perlin<Float>::Perlin() {
+ m_type = PerlinType::Uninitialized;
+ m_alpha = 0;
+ m_amplitude = 0;
+ m_frequency = 0;
+ m_seed = 0;
+ m_gain = 0;
+ m_beta = 0;
+ m_offset = 0;
+ m_bias = 0;
+ m_octaves = 0;
+}
+
+template <typename Float>
+Perlin<Float>::Perlin(unsigned octaves, Float freq, Float amp, Float bias, Float alpha, Float beta, uint64_t seed) {
+ m_type = PerlinType::Perlin;
+ m_seed = seed;
+
+ m_octaves = octaves;
+ m_frequency = freq;
+ m_amplitude = amp;
+ m_bias = bias;
+ m_alpha = alpha;
+ m_beta = beta;
+
+ // TODO: These ought to be configurable
+ m_offset = 1.0;
+ m_gain = 2.0;
+
+ init(m_seed);
+}
+
+template <typename Float>
+Perlin<Float>::Perlin(PerlinType type, unsigned octaves, Float freq, Float amp, Float bias, Float alpha, Float beta, uint64_t seed) {
+ m_type = type;
+ m_seed = seed;
+
+ m_octaves = octaves;
+ m_frequency = freq;
+ m_amplitude = amp;
+ m_bias = bias;
+ m_alpha = alpha;
+ m_beta = beta;
+
+ // TODO: These ought to be configurable
+ m_offset = 1.0;
+ m_gain = 2.0;
+
+ init(m_seed);
+}
+
+template <typename Float>
+Perlin<Float>::Perlin(Json const& config, uint64_t seed)
+ : Perlin(config.set("seed", seed)) {}
+
+template <typename Float>
+Perlin<Float>::Perlin(Json const& json) {
+ m_seed = json.getUInt("seed");
+ m_octaves = json.getInt("octaves", 1);
+ m_frequency = json.getDouble("frequency", 1.0);
+ m_amplitude = json.getDouble("amplitude", 1.0);
+ m_bias = json.getDouble("bias", 0.0);
+ m_alpha = json.getDouble("alpha", 2.0);
+ m_beta = json.getDouble("beta", 2.0);
+
+ m_offset = json.getDouble("offset", 1.0);
+ m_gain = json.getDouble("gain", 2.0);
+
+ m_type = PerlinTypeNames.getLeft(json.getString("type"));
+
+ init(m_seed);
+}
+
+template <typename Float>
+Perlin<Float>::Perlin(Perlin const& perlin) {
+ *this = perlin;
+}
+
+template <typename Float>
+Perlin<Float>::Perlin(Perlin&& perlin) {
+ *this = move(perlin);
+}
+
+template <typename Float>
+Perlin<Float>& Perlin<Float>::operator=(Perlin const& perlin) {
+ if (perlin.m_type == PerlinType::Uninitialized) {
+ m_type = PerlinType::Uninitialized;
+ p.reset();
+ g3.reset();
+ g2.reset();
+ g1.reset();
+
+ } else if (this != &perlin) {
+ m_type = perlin.m_type;
+ m_seed = perlin.m_seed;
+ m_octaves = perlin.m_octaves;
+ m_frequency = perlin.m_frequency;
+ m_amplitude = perlin.m_amplitude;
+ m_bias = perlin.m_bias;
+ m_alpha = perlin.m_alpha;
+ m_beta = perlin.m_beta;
+ m_offset = perlin.m_offset;
+ m_gain = perlin.m_gain;
+
+ p.reset(new int[PerlinSampleSize + PerlinSampleSize + 2]);
+ g3.reset(new Float[PerlinSampleSize + PerlinSampleSize + 2][3]);
+ g2.reset(new Float[PerlinSampleSize + PerlinSampleSize + 2][2]);
+ g1.reset(new Float[PerlinSampleSize + PerlinSampleSize + 2]);
+
+ std::memcpy(p.get(), perlin.p.get(), (PerlinSampleSize + PerlinSampleSize + 2) * sizeof(int));
+ std::memcpy(g3.get(), perlin.g3.get(), (PerlinSampleSize + PerlinSampleSize + 2) * sizeof(Float) * 3);
+ std::memcpy(g2.get(), perlin.g2.get(), (PerlinSampleSize + PerlinSampleSize + 2) * sizeof(Float) * 2);
+ std::memcpy(g1.get(), perlin.g1.get(), (PerlinSampleSize + PerlinSampleSize + 2) * sizeof(Float));
+ }
+
+ return *this;
+}
+
+template <typename Float>
+Perlin<Float>& Perlin<Float>::operator=(Perlin&& perlin) {
+ m_type = perlin.m_type;
+ m_seed = perlin.m_seed;
+ m_octaves = perlin.m_octaves;
+ m_frequency = perlin.m_frequency;
+ m_amplitude = perlin.m_amplitude;
+ m_bias = perlin.m_bias;
+ m_alpha = perlin.m_alpha;
+ m_beta = perlin.m_beta;
+ m_offset = perlin.m_offset;
+ m_gain = perlin.m_gain;
+
+ p = move(perlin.p);
+ g3 = move(perlin.g3);
+ g2 = move(perlin.g2);
+ g1 = move(perlin.g1);
+
+ return *this;
+}
+
+template <typename Float>
+Float Perlin<Float>::get(Float x) const {
+ switch (m_type) {
+ case PerlinType::Perlin:
+ return perlin(x);
+ case PerlinType::Billow:
+ return billow(x);
+ case PerlinType::RidgedMulti:
+ return ridgedMulti(x);
+ default:
+ throw PerlinException("::get called on uninitialized Perlin");
+ }
+}
+
+template <typename Float>
+Float Perlin<Float>::get(Float x, Float y) const {
+ switch (m_type) {
+ case PerlinType::Perlin:
+ return perlin(x, y);
+ case PerlinType::Billow:
+ return billow(x, y);
+ case PerlinType::RidgedMulti:
+ return ridgedMulti(x, y);
+ default:
+ throw PerlinException("::get called on uninitialized Perlin");
+ }
+}
+
+template <typename Float>
+Float Perlin<Float>::get(Float x, Float y, Float z) const {
+ switch (m_type) {
+ case PerlinType::Perlin:
+ return perlin(x, y, z);
+ case PerlinType::Billow:
+ return billow(x, y, z);
+ case PerlinType::RidgedMulti:
+ return ridgedMulti(x, y, z);
+ default:
+ throw PerlinException("::get called on uninitialized Perlin");
+ }
+}
+
+template <typename Float>
+PerlinType Perlin<Float>::type() const {
+ return m_type;
+}
+
+template <typename Float>
+unsigned Perlin<Float>::octaves() const {
+ return m_octaves;
+}
+
+template <typename Float>
+Float Perlin<Float>::frequency() const {
+ return m_frequency;
+}
+
+template <typename Float>
+Float Perlin<Float>::amplitude() const {
+ return m_amplitude;
+}
+
+template <typename Float>
+Float Perlin<Float>::bias() const {
+ return m_bias;
+}
+
+template <typename Float>
+Float Perlin<Float>::alpha() const {
+ return m_alpha;
+}
+
+template <typename Float>
+Float Perlin<Float>::beta() const {
+ return m_beta;
+}
+
+template <typename Float>
+Json Perlin<Float>::toJson() const {
+ return JsonObject{
+ {"seed", m_seed},
+ {"octaves", m_octaves},
+ {"frequency", m_frequency},
+ {"amplitude", m_amplitude},
+ {"bias", m_bias},
+ {"alpha", m_alpha},
+ {"beta", m_beta},
+ {"offset", m_offset},
+ {"gain", m_gain},
+ {"type", PerlinTypeNames.getRight(m_type)}
+ };
+}
+
+template <typename Float>
+inline Float Perlin<Float>::noise1(Float arg) const {
+ int bx0, bx1;
+ Float rx0, rx1, sx, u, v;
+
+ setup(arg, bx0, bx1, rx0, rx1);
+
+ sx = s_curve(rx0);
+ u = rx0 * g1[p[bx0]];
+ v = rx1 * g1[p[bx1]];
+
+ return (lerp(sx, u, v));
+}
+
+template <typename Float>
+inline Float Perlin<Float>::noise2(Float vec[2]) const {
+ int bx0, bx1, by0, by1, b00, b10, b01, b11;
+ Float rx0, rx1, ry0, ry1, sx, sy, a, b, u, v;
+ int i, j;
+
+ setup(vec[0], bx0, bx1, rx0, rx1);
+ setup(vec[1], by0, by1, ry0, ry1);
+
+ i = p[bx0];
+ j = p[bx1];
+
+ b00 = p[i + by0];
+ b10 = p[j + by0];
+ b01 = p[i + by1];
+ b11 = p[j + by1];
+
+ sx = s_curve(rx0);
+ sy = s_curve(ry0);
+
+ u = at2(g2[b00], rx0, ry0);
+ v = at2(g2[b10], rx1, ry0);
+ a = lerp(sx, u, v);
+
+ u = at2(g2[b01], rx0, ry1);
+ v = at2(g2[b11], rx1, ry1);
+ b = lerp(sx, u, v);
+
+ return lerp(sy, a, b);
+}
+
+template <typename Float>
+inline Float Perlin<Float>::noise3(Float vec[3]) const {
+ int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
+ Float rx0, rx1, ry0, ry1, rz0, rz1, sx, sy, sz, a, b, c, d, u, v;
+ int i, j;
+
+ setup(vec[0], bx0, bx1, rx0, rx1);
+ setup(vec[1], by0, by1, ry0, ry1);
+ setup(vec[2], bz0, bz1, rz0, rz1);
+
+ i = p[bx0];
+ j = p[bx1];
+
+ b00 = p[i + by0];
+ b10 = p[j + by0];
+ b01 = p[i + by1];
+ b11 = p[j + by1];
+
+ sx = s_curve(rx0);
+ sy = s_curve(ry0);
+ sz = s_curve(rz0);
+
+ u = at3(g3[b00 + bz0], rx0, ry0, rz0);
+ v = at3(g3[b10 + bz0], rx1, ry0, rz0);
+ a = lerp(sx, u, v);
+
+ u = at3(g3[b01 + bz0], rx0, ry1, rz0);
+ v = at3(g3[b11 + bz0], rx1, ry1, rz0);
+ b = lerp(sx, u, v);
+
+ c = lerp(sy, a, b);
+
+ u = at3(g3[b00 + bz1], rx0, ry0, rz1);
+ v = at3(g3[b10 + bz1], rx1, ry0, rz1);
+ a = lerp(sx, u, v);
+
+ u = at3(g3[b01 + bz1], rx0, ry1, rz1);
+ v = at3(g3[b11 + bz1], rx1, ry1, rz1);
+ b = lerp(sx, u, v);
+
+ d = lerp(sy, a, b);
+
+ return lerp(sz, c, d);
+}
+
+template <typename Float>
+void Perlin<Float>::normalize2(Float v[2]) const {
+ Float s;
+
+ s = sqrt(v[0] * v[0] + v[1] * v[1]);
+ if (s == 0.0f) {
+ v[0] = 1.0f;
+ v[1] = 0.0f;
+ } else {
+ v[0] = v[0] / s;
+ v[1] = v[1] / s;
+ }
+}
+
+template <typename Float>
+void Perlin<Float>::normalize3(Float v[3]) const {
+ Float s;
+
+ s = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ if (s == 0.0f) {
+ v[0] = 1.0f;
+ v[1] = 0.0f;
+ v[2] = 0.0f;
+ } else {
+ v[0] = v[0] / s;
+ v[1] = v[1] / s;
+ v[2] = v[2] / s;
+ }
+}
+
+template <typename Float>
+void Perlin<Float>::init(uint64_t seed) {
+ RandomSource randomSource(seed);
+
+ p.reset(new int[PerlinSampleSize + PerlinSampleSize + 2]);
+ g3.reset(new Float[PerlinSampleSize + PerlinSampleSize + 2][3]);
+ g2.reset(new Float[PerlinSampleSize + PerlinSampleSize + 2][2]);
+ g1.reset(new Float[PerlinSampleSize + PerlinSampleSize + 2]);
+
+ int i, j, k;
+
+ for (i = 0; i < PerlinSampleSize; i++) {
+ p[i] = i;
+ g1[i] = (Float)(randomSource.randInt(-PerlinSampleSize, PerlinSampleSize)) / PerlinSampleSize;
+
+ for (j = 0; j < 2; j++)
+ g2[i][j] = (Float)(randomSource.randInt(-PerlinSampleSize, PerlinSampleSize)) / PerlinSampleSize;
+ normalize2(g2[i]);
+
+ for (j = 0; j < 3; j++)
+ g3[i][j] = (Float)(randomSource.randInt(-PerlinSampleSize, PerlinSampleSize)) / PerlinSampleSize;
+ normalize3(g3[i]);
+ }
+
+ while (--i) {
+ k = p[i];
+ p[i] = p[j = randomSource.randUInt(PerlinSampleSize - 1)];
+ p[j] = k;
+ }
+
+ for (i = 0; i < PerlinSampleSize + 2; i++) {
+ p[PerlinSampleSize + i] = p[i];
+ g1[PerlinSampleSize + i] = g1[i];
+ for (j = 0; j < 2; j++)
+ g2[PerlinSampleSize + i][j] = g2[i][j];
+ for (j = 0; j < 3; j++)
+ g3[PerlinSampleSize + i][j] = g3[i][j];
+ }
+}
+
+template <typename Float>
+inline Float Perlin<Float>::perlin(Float x) const {
+ int i;
+ Float val, sum = 0;
+ Float p, scale = 1;
+
+ p = x * m_frequency;
+ for (i = 0; i < m_octaves; i++) {
+ val = noise1(p);
+ sum += val / scale;
+ scale *= m_alpha;
+ p *= m_beta;
+ }
+ return sum * m_amplitude + m_bias;
+}
+
+template <typename Float>
+inline Float Perlin<Float>::perlin(Float x, Float y) const {
+ int i;
+ Float val, sum = 0;
+ Float p[2], scale = 1;
+
+ p[0] = x * m_frequency;
+ p[1] = y * m_frequency;
+ for (i = 0; i < m_octaves; i++) {
+ val = noise2(p);
+ sum += val / scale;
+ scale *= m_alpha;
+ p[0] *= m_beta;
+ p[1] *= m_beta;
+ }
+ return sum * m_amplitude + m_bias;
+}
+
+template <typename Float>
+inline Float Perlin<Float>::perlin(Float x, Float y, Float z) const {
+ int i;
+ Float val, sum = 0;
+ Float p[3], scale = 1;
+
+ p[0] = x * m_frequency;
+ p[1] = y * m_frequency;
+ p[2] = z * m_frequency;
+ for (i = 0; i < m_octaves; i++) {
+ val = noise3(p);
+ sum += val / scale;
+ scale *= m_alpha;
+ p[0] *= m_beta;
+ p[1] *= m_beta;
+ p[2] *= m_beta;
+ }
+
+ return sum * m_amplitude + m_bias;
+}
+
+template <typename Float>
+inline Float Perlin<Float>::ridgedMulti(Float x) const {
+ Float val, sum = 0;
+ Float scale = 1;
+ Float weight = 1.0;
+
+ x *= m_frequency;
+ for (int i = 0; i < m_octaves; ++i) {
+ val = noise1(x);
+
+ val = m_offset - fabs(val);
+ val *= val;
+ val *= weight;
+
+ weight = clamp<Float>(val * m_gain, 0.0, 1.0);
+
+ sum += val / scale;
+ scale *= m_alpha;
+ x *= m_beta;
+ }
+
+ return ((sum * 1.25) - 1.0) * m_amplitude + m_bias;
+}
+
+template <typename Float>
+inline Float Perlin<Float>::ridgedMulti(Float x, Float y) const {
+ Float val, sum = 0;
+ Float p[2], scale = 1;
+ Float weight = 1.0;
+
+ p[0] = x * m_frequency;
+ p[1] = y * m_frequency;
+ for (int i = 0; i < m_octaves; ++i) {
+ val = noise2(p);
+
+ val = m_offset - fabs(val);
+ val *= val;
+ val *= weight;
+
+ weight = clamp<Float>(val * m_gain, 0.0, 1.0);
+
+ sum += val / scale;
+ scale *= m_alpha;
+ p[0] *= m_beta;
+ p[1] *= m_beta;
+ }
+
+ return ((sum * 1.25) - 1.0) * m_amplitude + m_bias;
+}
+
+template <typename Float>
+inline Float Perlin<Float>::ridgedMulti(Float x, Float y, Float z) const {
+ Float val, sum = 0;
+ Float p[3], scale = 1;
+ Float weight = 1.0;
+
+ p[0] = x * m_frequency;
+ p[1] = y * m_frequency;
+ p[2] = z * m_frequency;
+ for (int i = 0; i < m_octaves; ++i) {
+ val = noise3(p);
+
+ val = m_offset - fabs(val);
+ val *= val;
+ val *= weight;
+
+ weight = clamp<Float>(val * m_gain, 0.0, 1.0);
+
+ sum += val / scale;
+ scale *= m_alpha;
+ p[0] *= m_beta;
+ p[1] *= m_beta;
+ p[2] *= m_beta;
+ }
+
+ return ((sum * 1.25) - 1.0) * m_amplitude + m_bias;
+}
+
+template <typename Float>
+inline Float Perlin<Float>::billow(Float x) const {
+ Float val, sum = 0;
+ Float p, scale = 1;
+
+ p = x * m_frequency;
+ for (int i = 0; i < m_octaves; i++) {
+ val = noise1(p);
+ val = 2.0 * fabs(val) - 1.0;
+
+ sum += val / scale;
+ scale *= m_alpha;
+ p *= m_beta;
+ }
+ return (sum + 0.5) * m_amplitude + m_bias;
+}
+
+template <typename Float>
+inline Float Perlin<Float>::billow(Float x, Float y) const {
+ Float val, sum = 0;
+ Float p[2], scale = 1;
+
+ p[0] = x * m_frequency;
+ p[1] = y * m_frequency;
+ for (int i = 0; i < m_octaves; i++) {
+ val = noise2(p);
+ val = 2.0 * fabs(val) - 1.0;
+
+ sum += val / scale;
+ scale *= m_alpha;
+ p[0] *= m_beta;
+ p[1] *= m_beta;
+ }
+ return (sum + 0.5) * m_amplitude + m_bias;
+}
+
+template <typename Float>
+inline Float Perlin<Float>::billow(Float x, Float y, Float z) const {
+ Float val, sum = 0;
+ Float p[3], scale = 1;
+
+ p[0] = x * m_frequency;
+ p[1] = y * m_frequency;
+ p[2] = z * m_frequency;
+ for (int i = 0; i < m_octaves; i++) {
+ val = noise3(p);
+ val = 2.0 * fabs(val) - 1.0;
+
+ sum += val / scale;
+ scale *= m_alpha;
+ p[0] *= m_beta;
+ p[1] *= m_beta;
+ p[2] *= m_beta;
+ }
+
+ return (sum + 0.5) * m_amplitude + m_bias;
+}
+
+}
+
+#endif