diff --git a/doc/en/world-generator.md b/doc/en/world-generator.md index 3c0a774f3..6432cb421 100644 --- a/doc/en/world-generator.md +++ b/doc/en/world-generator.md @@ -284,6 +284,14 @@ A method that generates simplex noise, adding it to the existing values. The noise seed can be specified in the `map.noiseSeed` field. +By default, noise octaves are mixed using addition: + +$noise_{out} = noise_{source} + noise_{applied} * 2^{-{octave}}$ + +To obtain more normalized multi-octave noise, you can enable the `map.normalNoise = true` flag, which changes the scheme: + +$noise_{out} = noise_{source} * (1 - 2^{-{octave}}) + noise_{applied} * 2^{-{octave}}$ + ```lua map:noise( -- coordinate offset @@ -313,6 +321,10 @@ The noise seed can be specified in the `map.noiseSeed` field. ![image](../images/cell-noise.gif) +> [!NOTE] cell noise looks better with `map.normalNoise = true` + +![image](../images/normal-cell-noise.png) + ### heightmap:resize(...) ```lua diff --git a/doc/images/normal-cell-noise.png b/doc/images/normal-cell-noise.png new file mode 100644 index 000000000..c1c103185 Binary files /dev/null and b/doc/images/normal-cell-noise.png differ diff --git a/doc/ru/world-generator.md b/doc/ru/world-generator.md index 3415a81d6..34738d4b3 100644 --- a/doc/ru/world-generator.md +++ b/doc/ru/world-generator.md @@ -287,6 +287,14 @@ map:dump('export:test.png') Зерно шума может быть указано в поле `map.noiseSeed`. +По-умолчанию октавы шума совмещаются через сложение: + +$шум_{результат} = шум_{исходный} + шум_{применяемый} * 2^{-{октава}}$ + +Для более получения нормализованного многооктавного шума можно включить флаг `map.normalNoise = true`, меняющий схему на: + +$шум_{результат} = шум_{исходный} * (1 - 2^{-{октава}}) + шум_{применяемый} * 2^{-{октава}}$ + ```lua map:noise( -- смещение координат @@ -316,6 +324,10 @@ map:noise( ![image](../images/cell-noise.gif) +> [!NOTE] клеточный шум выглядит лучше с `map.normalNoise = true` + +![image](../images/normal-cell-noise.png) + ### heightmap:resize(...) ```lua diff --git a/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp b/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp index df2af6106..6ae6a0d8d 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp @@ -110,6 +110,7 @@ static int l_noise(lua::State* L) { if (gettop(L) > 6) { shiftMapY = touserdata(L, 7); } + bool normalizedNoise = heightmap->normalizedNoise; noise->noise_type = noise_type; for (uint y = 0; y < h; y++) { for (uint x = 0; x < w; x++) { @@ -126,8 +127,14 @@ static int l_noise(lua::State* L) { v += shiftMapY->getValues()[i]; } - value += fnlGetNoise2D(noise, u, v) / - static_cast(1 << c) * multiplier; + float noiseValue = fnlGetNoise2D(noise, u, v); + if (normalizedNoise) { + float t = 1.0f / (static_cast(1 << c) * multiplier); + value = value * (1.0f - t) + noiseValue * t; + } else { + float t = 1.0f / static_cast(1 << c) * multiplier; + value += noiseValue * t; + } heights[i] = value; } } @@ -317,6 +324,8 @@ static int l_meta_newindex(lua::State* L) { auto fieldname = tostring(L, 2); if (!std::strcmp(fieldname, "noiseSeed")) { map->setSeed(tointeger(L, 3)); + } else if (!std::strcmp(fieldname, "normalNoise")) { + map->normalizedNoise = toboolean(L, 3); } } return 0; diff --git a/src/logic/scripting/lua/usertypes/lua_type_heightmap.hpp b/src/logic/scripting/lua/usertypes/lua_type_heightmap.hpp index 4109be675..5e74547d8 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_heightmap.hpp +++ b/src/logic/scripting/lua/usertypes/lua_type_heightmap.hpp @@ -10,6 +10,8 @@ namespace lua { std::shared_ptr map; std::unique_ptr noise; public: + bool normalizedNoise = false; + LuaHeightmap(const std::shared_ptr& map); LuaHeightmap(uint width, uint height); diff --git a/src/maths/util.hpp b/src/maths/util.hpp index c5a576f0d..08f0f4585 100644 --- a/src/maths/util.hpp +++ b/src/maths/util.hpp @@ -18,49 +18,66 @@ namespace util { constexpr inline float EPSILON = 1e-6f; class PseudoRandom { - unsigned short seed; + uint32_t seed; + public: - PseudoRandom(unsigned short seed) : seed(seed) {} + explicit PseudoRandom(uint32_t seed) : seed(seed) {} - PseudoRandom() { - seed = static_cast(time(nullptr)); - } + PseudoRandom() + : seed(static_cast(time(nullptr))) {} - int rand() { - seed = (seed + 0x7ed5 + (seed << 6)); - seed = (seed ^ 0xc23c ^ (seed >> 9)); - seed = (seed + 0x1656 + (seed << 3)); - seed = ((seed + 0xa264) ^ (seed << 4)); - seed = (seed + 0xfd70 - (seed << 3)); - seed = (seed ^ 0xba49 ^ (seed >> 8)); + uint32_t rand() { + seed += 0x7ed55d16u; + seed ^= seed >> 16; + seed *= 0x21f0aaadu; + seed ^= seed >> 15; + seed *= 0x735a2d97u; + seed ^= seed >> 15; - return static_cast(seed); - } - - void rand(unsigned char* dst, size_t n) { - for (size_t i = 0; i < n; i++) { - dst[i] = rand(); - } - } - - int32_t rand32() { - return (rand() << 16) | rand(); + return seed; } uint32_t randU32() { - return (rand() << 16) | rand(); - } - - int64_t rand64() { - uint64_t x = randU32(); - uint64_t y = randU32(); - return (x << 32ULL) | y; + return rand(); } uint64_t randU64() { uint64_t x = randU32(); uint64_t y = randU32(); - return (x << 32ULL) | y; + return (x << 32) | y; + } + + int32_t rand32() { + return static_cast(randU32()); + } + + int64_t rand64() { + uint64_t x = randU32(); + uint64_t y = randU32(); + return static_cast((x << 32) | y); + } + + void setSeed(uint32_t value) { + seed = value; + if (seed == 0) { + seed = 0x6d2b79f5u; + } + rand(); + } + + void setSeed(int32_t number1, int32_t number2) { + uint32_t a = static_cast(number1); + uint32_t b = static_cast(number2); + + uint32_t x = a * 23729u; + uint32_t y = b * 16786u; + + seed = (x ^ y ^ (a * b)); + + if (seed == 0) { + seed = 0x6d2b79f5u; + } + rand(); } float randFloat() { @@ -70,21 +87,6 @@ namespace util { double randDouble() { return randU64() / static_cast(UINT64_MAX); } - - void setSeed(int number1, int number2) { - seed = ((static_cast(number1 * 23729) | - static_cast(number2 % 16786)) ^ - static_cast(number2 * number1)); - rand(); - } - - void setSeed(long number) { - number = shuffle_bits_step(number, 0x2222222222222222ull, 1); - number = shuffle_bits_step(number, 0x0c0c0c0c0c0c0c0cull, 2); - number = shuffle_bits_step(number, 0x00f000f000f000f0ull, 4); - seed = number; - rand(); - } }; template