#include "Maths/Vec3.h" #include #include #include #include constexpr float HEIGHT_COEFF = 255.0f; constexpr std::uint32_t WATER_LEVEL = 20; Noise::Noise(const std::uint32_t seed, float frequency, float amplitude, int octaves, float lacunarity, float persistance, int redistribution, float compensatory_factor): m_seed(std::mt19937(seed)), c_frequency(frequency), c_amplitude(amplitude), c_octaves(octaves), c_lacunarity(lacunarity), c_persistance(persistance), c_redistribution(redistribution), c_compensatory_factor(compensatory_factor) { if(c_amplitude > 1.0f || c_amplitude < -1.0f) Scop::FatalError("Amplitude value must be in [-1;1]"); if(c_redistribution <= 0) Scop::FatalError("Redistribution cannot be a negative integer"); InitPermutation(); } void Noise::InitPermutation() { std::array permutations; for(int i = 0; i < 256; ++i) permutations[i] = i; std::shuffle(permutations.begin(), permutations.end(), m_seed); for(int i = 0; i < 256; ++i) { this->perms[i] = permutations[i]; this->perms[i + 256] = permutations[i]; } } [[nodiscard]] const float Noise::fade(float t) noexcept { return t * t * t * (t * (t * 6 - 15) + 10); } const float Noise::Perlin2D(float x, float y) noexcept { int xi = static_cast(floor(x)) & 255; int yi = static_cast(floor(y)) & 255; float xf = x - floor(x); float yf = y - floor(y); float u = fade(xf); float v = fade(yf); int aa = perms[perms[xi] + yi]; int ab = perms[perms[xi] + yi + 1]; int ba = perms[perms[xi + 1] + yi]; int bb = perms[perms[xi + 1] + yi + 1]; float x1 = std::lerp(grad2D(aa, xf, yf), grad2D(ba, xf - 1, yf), u); float x2 = std::lerp(grad2D(ab, xf, yf - 1), grad2D(bb, xf - 1, yf - 1), u); return ((std::lerp(x1, x2, v) + 1.0f) / 2.0f); } const int Noise::ApplyPerlin2DParameters(float x, float y) noexcept // Wrapper to apply various mumbo jumbo to get a very worldlike generation { float total = 0.0f; float tmp_freq = c_frequency; float tmp_amp = c_amplitude; float maxValue = 0.0f; for(int i = 0; i < this->c_octaves; ++i) { total += Perlin2D(x * tmp_freq, y * tmp_freq) * tmp_amp; maxValue += tmp_amp; tmp_amp *= c_persistance; tmp_freq *= c_lacunarity; } float normalized = total / maxValue; normalized = std::clamp(normalized, 0.0f, 1.0f); normalized = std::pow(normalized * c_compensatory_factor, c_redistribution); return static_cast(normalized * HEIGHT_COEFF); } [[nodiscard]] const int Noise::Perlin2D(int x, int y) noexcept { // Wrapper to unnormalise input and output float scaledX = static_cast(x) * c_frequency; float scaledY = static_cast(y) * c_frequency; return floor(ApplyPerlin2DParameters(scaledX, scaledY)); } [[nodiscard]] const float Noise::grad2D(int hash, float x, float y) noexcept { int h = hash & 7; // 8 directions float u = h < 4 ? x : y; float v = h < 4 ? y : x; return ((h & 1) ? -u : u) + ((h & 2) ? -v : v); } [[nodiscard]] const float Noise::grad(int hash, float x, float y, float z) noexcept { int h = hash & 15; // 16 directions possibles float u = h < 8 ? x : y; float v = h < 4 ? y : (h == 12 || h == 14 ? x : z); return ((h & 1) ? -u : u) + ((h & 2) ? -v : v); } [[nodiscard]] const int Noise::Perlin3D(int x, int y, int z) noexcept { float scaledX = static_cast(x) * c_frequency; float scaledY = static_cast(y) * c_frequency; float scaledZ = static_cast(z) * c_frequency; return static_cast(ApplyPerlin3DParameters(scaledX, scaledY, scaledZ)); } [[nodiscard]] const float Noise::Perlin3D(float x, float y, float z) noexcept { int xi = static_cast(x) & 255; int yi = static_cast(y) & 255; int zi = static_cast(z) & 255; float xf = x - floor(x); float yf = y - floor(y); float zf = z - floor(z); float u = fade(xf); float v = fade(yf); float w = fade(zf); int aaa = perms[perms[perms[xi] + yi] + zi]; int aba = perms[perms[perms[xi] + yi + 1] + zi]; int aab = perms[perms[perms[xi] + yi] + zi + 1]; int abb = perms[perms[perms[xi] + yi + 1] + zi + 1]; int baa = perms[perms[perms[xi + 1] + yi] + zi]; int bba = perms[perms[perms[xi + 1] + yi + 1] + zi]; int bab = perms[perms[perms[xi + 1] + yi] + zi + 1]; int bbb = perms[perms[perms[xi + 1] + yi + 1] + zi + 1]; float x1, x2, y1, y2; x1 = std::lerp(grad(aaa, xf, yf, zf), grad(baa, xf - 1, yf, zf), u); x2 = std::lerp(grad(aba, xf, yf - 1, zf), grad(bba, xf - 1, yf - 1, zf), u); y1 = std::lerp(x1, x2, v); x1 = std::lerp(grad(aab, xf, yf, zf - 1), grad(bab, xf - 1, yf, zf - 1), u); x2 = std::lerp(grad(abb, xf, yf - 1, zf - 1), grad(bbb, xf - 1, yf - 1, zf - 1), u); y2 = std::lerp(x1, x2, v); return ((std::lerp(y1, y2, w) + 1.0f) / 2.0f) * c_amplitude; } [[nodiscard]] const int Noise::ApplyPerlin3DParameters(float x, float y, float z) noexcept { float total = 0.0f; float tmp_freq = c_frequency; float tmp_amp = c_amplitude; float maxValue = 0.0f; for(int i = 0; i < this->c_octaves; ++i) { total += Perlin3D(x * tmp_freq, y * tmp_freq, z * tmp_freq) * tmp_amp; maxValue += tmp_amp; tmp_amp *= c_persistance; tmp_freq *= c_lacunarity; } float normalized = total / maxValue; normalized = std::clamp(normalized, 0.0f, 1.0f); return static_cast(normalized * HEIGHT_COEFF); } [[nodiscard]] std::array Noise::GetHeight(Scop::Vec2i pos) { std::array data; std::memset(data.data(), static_cast(BlockType::Air), data.size() * sizeof(std::uint32_t)); //std::uint32_t height = std::abs(std::sin((float)pos.x / 20.0f) * std::cos((float)pos.y / 20.0f) * 60.0f) + 1; const std::uint32_t height = Perlin2D(pos.x, pos.y); for(std::uint32_t y = 0; y < std::min(height, CHUNK_SIZE.y); y++) { // const std::uint32_t value = Perlin3D(pos.x, y, pos.y); if(y > std::min(height, CHUNK_SIZE.y) - 2) { if (height <= 23) data[y] = static_cast(BlockType::Sand); else if (height < 140) data[y] = static_cast(BlockType::Grass); else data[y] = static_cast(BlockType::Stone); } else data[y] = static_cast(BlockType::Stone); } for (std::uint32_t y = 0; y < WATER_LEVEL; y++) { if (data[y] == static_cast(BlockType::Air)) data[y] = static_cast(BlockType::Sand); } return data; }