From 75259ddcbb005d92ed852767e9366d8a3d135f49 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Sun, 23 Aug 2026 15:12:51 +0200 Subject: [PATCH] feat(core): implement Worley and fractal noise variants for biome and cave shaping --- core/src/NoiseAdv.cpp | 339 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 core/src/NoiseAdv.cpp diff --git a/core/src/NoiseAdv.cpp b/core/src/NoiseAdv.cpp new file mode 100644 index 0000000..37566ea --- /dev/null +++ b/core/src/NoiseAdv.cpp @@ -0,0 +1,339 @@ +#include "vf/NoiseAdv.h" + +#include +#include +#include + +namespace vf { + +namespace { + +constexpr float simplexF2 = 0.3660254037844386f; // (sqrt(3) - 1) / 2 +constexpr float simplexG2 = 0.21132486540518713f; // (3 - sqrt(3)) / 6 +constexpr float simplexF3 = 1.0f / 3.0f; +constexpr float simplexG3 = 1.0f / 6.0f; + +constexpr float gradients2D[8][2] = { + {1.0f, 0.0f}, {-1.0f, 0.0f}, {0.0f, 1.0f}, {0.0f, -1.0f}, + {0.7071068f, 0.7071068f}, {-0.7071068f, 0.7071068f}, + {0.7071068f, -0.7071068f}, {-0.7071068f, -0.7071068f}, +}; + +constexpr float gradients3D[12][3] = { + {1, 1, 0}, {-1, 1, 0}, {1, -1, 0}, {-1, -1, 0}, + {1, 0, 1}, {-1, 0, 1}, {1, 0, -1}, {-1, 0, -1}, + {0, 1, 1}, {0, -1, 1}, {0, 1, -1}, {0, -1, -1}, +}; + +float quinticFade(float t) { + return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); +} + +float clampUnit(float v) { + return std::max(-1.0f, std::min(1.0f, v)); +} + +} // namespace + +void NoiseAdv::reseed(uint32_t seed) { + seedValue = seed; + + std::array base{}; + for (int i = 0; i < 256; ++i) base[i] = i; + + uint32_t state = seed ^ 0x9E3779B9u; + if (state == 0) state = 0x1u; + auto nextRandom = [&state]() { + state ^= state << 13; + state ^= state >> 17; + state ^= state << 5; + return state; + }; + + for (int i = 255; i > 0; --i) { + int j = static_cast(nextRandom() % static_cast(i + 1)); + std::swap(base[i], base[j]); + } + + for (int i = 0; i < 512; ++i) perm[static_cast(i)] = base[static_cast(i & 255)]; +} + +float NoiseAdv::gradDot2D(int hashIndex, float x, float y) const { + const float* g = gradients2D[hashIndex & 7]; + return g[0] * x + g[1] * y; +} + +float NoiseAdv::gradDot3D(int hashIndex, float x, float y, float z) const { + const float* g = gradients3D[hashIndex % 12]; + return g[0] * x + g[1] * y + g[2] * z; +} + +uint32_t NoiseAdv::hashCell(int x, int y) const { + uint32_t h = seedValue; + h ^= static_cast(x) * 0x27D4EB2Fu; + h ^= static_cast(y) * 0x165667B1u; + h ^= h >> 15; + h *= 0x85EBCA6Bu; + h ^= h >> 13; + h *= 0xC2B2AE35u; + h ^= h >> 16; + return h; +} + +float NoiseAdv::latticeValue(int x, int y) const { + uint32_t h = hashCell(x, y); + return (static_cast(h & 0xFFFFFFu) / 16777215.0f) * 2.0f - 1.0f; +} + +float NoiseAdv::simplex2D(float x, float y) const { + float s = (x + y) * simplexF2; + int i = static_cast(std::floor(x + s)); + int j = static_cast(std::floor(y + s)); + + float t = static_cast(i + j) * simplexG2; + float x0Origin = static_cast(i) - t; + float y0Origin = static_cast(j) - t; + float x0 = x - x0Origin; + float y0 = y - y0Origin; + + int i1 = 0, j1 = 0; + if (x0 > y0) { i1 = 1; j1 = 0; } else { i1 = 0; j1 = 1; } + + float x1 = x0 - static_cast(i1) + simplexG2; + float y1 = y0 - static_cast(j1) + simplexG2; + float x2 = x0 - 1.0f + 2.0f * simplexG2; + float y2 = y0 - 1.0f + 2.0f * simplexG2; + + int ii = i & 255; + int jj = j & 255; + + float n0 = 0.0f, n1 = 0.0f, n2 = 0.0f; + + float t0 = 0.5f - x0 * x0 - y0 * y0; + if (t0 > 0.0f) { + int hash0 = perm[static_cast(ii + perm[static_cast(jj)])]; + t0 *= t0; + n0 = t0 * t0 * gradDot2D(hash0, x0, y0); + } + + float t1 = 0.5f - x1 * x1 - y1 * y1; + if (t1 > 0.0f) { + int hash1 = perm[static_cast(ii + i1 + perm[static_cast(jj + j1)])]; + t1 *= t1; + n1 = t1 * t1 * gradDot2D(hash1, x1, y1); + } + + float t2 = 0.5f - x2 * x2 - y2 * y2; + if (t2 > 0.0f) { + int hash2 = perm[static_cast(ii + 1 + perm[static_cast(jj + 1)])]; + t2 *= t2; + n2 = t2 * t2 * gradDot2D(hash2, x2, y2); + } + + return clampUnit(70.0f * (n0 + n1 + n2)); +} + +float NoiseAdv::simplex3D(float x, float y, float z) const { + float s = (x + y + z) * simplexF3; + int i = static_cast(std::floor(x + s)); + int j = static_cast(std::floor(y + s)); + int k = static_cast(std::floor(z + s)); + + float t = static_cast(i + j + k) * simplexG3; + float x0 = x - (static_cast(i) - t); + float y0 = y - (static_cast(j) - t); + float z0 = z - (static_cast(k) - t); + + int i1, j1, k1; + int i2, j2, k2; + if (x0 >= y0) { + if (y0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; } + else if (x0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; } + else { i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; } + } else { + if (y0 < z0) { i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1; } + else if (x0 < z0) { i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1; } + else { i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0; } + } + + float x1 = x0 - static_cast(i1) + simplexG3; + float y1 = y0 - static_cast(j1) + simplexG3; + float z1 = z0 - static_cast(k1) + simplexG3; + float x2 = x0 - static_cast(i2) + 2.0f * simplexG3; + float y2 = y0 - static_cast(j2) + 2.0f * simplexG3; + float z2 = z0 - static_cast(k2) + 2.0f * simplexG3; + float x3 = x0 - 1.0f + 3.0f * simplexG3; + float y3 = y0 - 1.0f + 3.0f * simplexG3; + float z3 = z0 - 1.0f + 3.0f * simplexG3; + + int ii = i & 255; + int jj = j & 255; + int kk = k & 255; + + float n0 = 0.0f, n1 = 0.0f, n2 = 0.0f, n3 = 0.0f; + + float t0 = 0.6f - x0 * x0 - y0 * y0 - z0 * z0; + if (t0 > 0.0f) { + int hash0 = perm[static_cast(ii + perm[static_cast(jj + perm[static_cast(kk)])])]; + t0 *= t0; + n0 = t0 * t0 * gradDot3D(hash0, x0, y0, z0); + } + float t1 = 0.6f - x1 * x1 - y1 * y1 - z1 * z1; + if (t1 > 0.0f) { + int hash1 = perm[static_cast(ii + i1 + perm[static_cast(jj + j1 + perm[static_cast(kk + k1)])])]; + t1 *= t1; + n1 = t1 * t1 * gradDot3D(hash1, x1, y1, z1); + } + float t2 = 0.6f - x2 * x2 - y2 * y2 - z2 * z2; + if (t2 > 0.0f) { + int hash2 = perm[static_cast(ii + i2 + perm[static_cast(jj + j2 + perm[static_cast(kk + k2)])])]; + t2 *= t2; + n2 = t2 * t2 * gradDot3D(hash2, x2, y2, z2); + } + float t3 = 0.6f - x3 * x3 - y3 * y3 - z3 * z3; + if (t3 > 0.0f) { + int hash3 = perm[static_cast(ii + 1 + perm[static_cast(jj + 1 + perm[static_cast(kk + 1)])])]; + t3 *= t3; + n3 = t3 * t3 * gradDot3D(hash3, x3, y3, z3); + } + + return clampUnit(32.0f * (n0 + n1 + n2 + n3)); +} + +float NoiseAdv::value2D(float x, float y) const { + int xi = static_cast(std::floor(x)); + int yi = static_cast(std::floor(y)); + float xf = x - static_cast(xi); + float yf = y - static_cast(yi); + + float v00 = latticeValue(xi, yi); + float v10 = latticeValue(xi + 1, yi); + float v01 = latticeValue(xi, yi + 1); + float v11 = latticeValue(xi + 1, yi + 1); + + float u = quinticFade(xf); + float v = quinticFade(yf); + + float top = v00 + (v10 - v00) * u; + float bottom = v01 + (v11 - v01) * u; + return clampUnit(top + (bottom - top) * v); +} + +WorleyResult NoiseAdv::worley2D(float x, float y, DistanceMetric metric) const { + int cellX = static_cast(std::floor(x)); + int cellY = static_cast(std::floor(y)); + + float best1 = std::numeric_limits::max(); + float best2 = std::numeric_limits::max(); + int bestCellX = cellX; + int bestCellY = cellY; + + for (int dy = -1; dy <= 1; ++dy) { + for (int dx = -1; dx <= 1; ++dx) { + int neighborX = cellX + dx; + int neighborY = cellY + dy; + uint32_t h = hashCell(neighborX, neighborY); + float jitterX = static_cast(h & 0xFFFFu) / 65535.0f; + float jitterY = static_cast((h >> 16) & 0xFFFFu) / 65535.0f; + float pointX = static_cast(neighborX) + jitterX; + float pointY = static_cast(neighborY) + jitterY; + + float distX = x - pointX; + float distY = y - pointY; + float distance; + switch (metric) { + case DistanceMetric::Manhattan: + distance = std::fabs(distX) + std::fabs(distY); + break; + case DistanceMetric::Chebyshev: + distance = std::max(std::fabs(distX), std::fabs(distY)); + break; + default: + distance = std::sqrt(distX * distX + distY * distY); + break; + } + + if (distance < best1) { + best2 = best1; + best1 = distance; + bestCellX = neighborX; + bestCellY = neighborY; + } else if (distance < best2) { + best2 = distance; + } + } + } + + WorleyResult result; + result.f1 = best1; + result.f2 = best2; + result.cellX = bestCellX; + result.cellY = bestCellY; + return result; +} + +float NoiseAdv::ridged2D(float x, float y) const { + return 1.0f - std::fabs(simplex2D(x, y)); +} + +float NoiseAdv::ridgedFbm2D(float x, float y, const FbmParams& fbmParams) const { + float sum = 0.0f; + float freq = fbmParams.frequency; + float amp = fbmParams.amplitude; + float weight = 1.0f; + float norm = 0.0f; + + for (int octave = 0; octave < fbmParams.octaves; ++octave) { + float signal = 1.0f - std::fabs(simplex2D(x * freq, y * freq)); + signal *= signal; + signal *= weight; + weight = std::max(0.0f, std::min(1.0f, signal)); + + sum += signal * amp; + norm += amp; + freq *= fbmParams.lacunarity; + amp *= fbmParams.gain; + } + + return norm > 0.0f ? sum / norm : 0.0f; +} + +float NoiseAdv::fbm2D(float x, float y, const FbmParams& fbmParams) const { + float sum = 0.0f; + float freq = fbmParams.frequency; + float amp = fbmParams.amplitude; + float norm = 0.0f; + + for (int octave = 0; octave < fbmParams.octaves; ++octave) { + sum += simplex2D(x * freq, y * freq) * amp; + norm += amp; + freq *= fbmParams.lacunarity; + amp *= fbmParams.gain; + } + + return norm > 0.0f ? clampUnit(sum / norm) : 0.0f; +} + +float NoiseAdv::fbm3D(float x, float y, float z, const FbmParams& fbmParams) const { + float sum = 0.0f; + float freq = fbmParams.frequency; + float amp = fbmParams.amplitude; + float norm = 0.0f; + + for (int octave = 0; octave < fbmParams.octaves; ++octave) { + sum += simplex3D(x * freq, y * freq, z * freq) * amp; + norm += amp; + freq *= fbmParams.lacunarity; + amp *= fbmParams.gain; + } + + return norm > 0.0f ? clampUnit(sum / norm) : 0.0f; +} + +float NoiseAdv::warpedFbm2D(float x, float y, const FbmParams& fbmParams, float warpStrength) const { + float warpX = simplex2D(x * 0.5f + 17.0f, y * 0.5f - 43.0f); + float warpY = simplex2D(x * 0.5f - 89.0f, y * 0.5f + 5.0f); + return fbm2D(x + warpX * warpStrength, y + warpY * warpStrength, fbmParams); +} + +} // namespace vf