From 78fcc46f79bf0f379a2b52af1ed44b41bca11aef Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 27 Oct 2024 23:14:06 +0300 Subject: [PATCH 1/5] update doc/*/world-generator.md --- doc/en/world-generator.md | 1 + doc/ru/world-generator.md | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/en/world-generator.md b/doc/en/world-generator.md index 15d9ea54..b6e053ac 100644 --- a/doc/en/world-generator.md +++ b/doc/en/world-generator.md @@ -299,6 +299,7 @@ Changes the heightmap size. Available interpolation modes: - 'nearest' - no interpolation - 'linear' - bilinear interpolation +- 'cubic' - bicubic interpolation ### heightmap:crop(...) diff --git a/doc/ru/world-generator.md b/doc/ru/world-generator.md index 98be0ff4..5ccdbc68 100644 --- a/doc/ru/world-generator.md +++ b/doc/ru/world-generator.md @@ -303,6 +303,7 @@ map:resize(ширина, высота, интерполяция) Доступные режимы интерполяции: - 'nearest' - без интерполяции - 'linear' - билинейная интерполяция +- 'cubic' - бикубическая интерполяция ### heightmap:crop(...) From 99d95334532ff5afc65c586e1594226931cf883f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 28 Oct 2024 01:38:34 +0300 Subject: [PATCH 2/5] reimplement bicubic interpolation --- src/maths/Heightmap.cpp | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/maths/Heightmap.cpp b/src/maths/Heightmap.cpp index 8db33f6b..99c27098 100644 --- a/src/maths/Heightmap.cpp +++ b/src/maths/Heightmap.cpp @@ -5,10 +5,6 @@ #include #include -static inline float smootherstep(float x) { - return glm::smoothstep(std::floor(x), std::floor(x)+1, x); -} - static inline float sample_at( const float* buffer, uint width, @@ -17,6 +13,27 @@ static inline float sample_at( return buffer[y*width+x]; } +static inline float sample_at( + const float* buffer, + uint width, uint height, + uint x, uint y +) { + return buffer[(y >= height ? height-1 : y)*width+(x >= width ? width-1 : x)]; +} + +static inline float interpolate_cubic(float p[4], float x) { + return p[1] + 0.5 * x*(p[2] - p[0] + x*(2.0*p[0] - 5.0*p[1] + 4.0*p[2] - + p[3] + x*(3.0*(p[1] - p[2]) + p[3] - p[0]))); +} + +static inline float interpolate_bicubic(float p[4][4], float x, float y) { + float q[4]; + for (int i = 0; i < 4; i++) { + q[i] = interpolate_cubic(p[i], y); + } + return interpolate_cubic(q, x); +} + static inline float sample_at( const float* buffer, uint width, uint height, @@ -50,7 +67,17 @@ static inline float sample_at( return a00 + a10*tx + a01*ty + a11*tx*ty; } - // TODO: implement CUBIC (Bicubic) interpolation + case InterpolationType::CUBIC: { + float p[4][4]; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + p[i][j] = sample_at( + buffer, width, height, ix + j - 1, iy + i - 1 + ); + } + } + return interpolate_bicubic(p, ty, tx); + } default: throw std::runtime_error("interpolation type is not implemented"); } From d8c39404516c69920170b28e69c51622276d476a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 28 Oct 2024 09:22:02 +0300 Subject: [PATCH 3/5] add 'heights-interpolation', 'biomes-interpolation' --- src/content/loading/GeneratorLoader.cpp | 10 ++++++++++ src/maths/Heightmap.cpp | 11 +++++++++++ src/maths/Heightmap.hpp | 3 +++ src/world/generator/GeneratorDef.hpp | 6 ++++++ src/world/generator/WorldGenerator.cpp | 6 +++--- 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index 299565a4..ebae9118 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -202,6 +202,16 @@ void ContentLoader::loadGenerator( map.at("biome-parameters").get(def.biomeParameters); map.at("biome-bpd").get(def.biomesBPD); map.at("heights-bpd").get(def.heightsBPD); + std::string interpName; + map.at("heights-interpolation").get(interpName); + if (auto interp = InterpolationType_from(interpName)) { + def.heightsInterpolation = *interp; + } + map.at("biomes-interpolation").get(interpName); + if (auto interp = InterpolationType_from(interpName)) { + def.biomesInterpolation = *interp; + } + map.at("sea-level").get(def.seaLevel); map.at("wide-structs-chunks-radius").get(def.wideStructsChunksRadius); if (map.has("heightmap-inputs")) { diff --git a/src/maths/Heightmap.cpp b/src/maths/Heightmap.cpp index 99c27098..ab6ad153 100644 --- a/src/maths/Heightmap.cpp +++ b/src/maths/Heightmap.cpp @@ -5,6 +5,17 @@ #include #include +std::optional InterpolationType_from(std::string_view str) { + if (str == "nearest") { + return InterpolationType::NEAREST; + } else if (str == "linear") { + return InterpolationType::LINEAR; + } else if (str == "cubic") { + return InterpolationType::CUBIC; + } + return std::nullopt; +} + static inline float sample_at( const float* buffer, uint width, diff --git a/src/maths/Heightmap.hpp b/src/maths/Heightmap.hpp index 5f903756..37f5130d 100644 --- a/src/maths/Heightmap.hpp +++ b/src/maths/Heightmap.hpp @@ -2,6 +2,7 @@ #include #include +#include #include "typedefs.hpp" #include "maths/Heightmap.hpp" @@ -12,6 +13,8 @@ enum class InterpolationType { CUBIC, }; +std::optional InterpolationType_from(std::string_view str); + class Heightmap { std::vector buffer; uint width, height; diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index f3abd893..b21a2425 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -209,6 +209,12 @@ struct GeneratorDef { /// @brief Heightmap blocks per dot uint heightsBPD = 4; + /// @brief Biome parameter maps interpolation method + InterpolationType biomesInterpolation = InterpolationType::LINEAR; + + /// @brief Height maps interpolation method + InterpolationType heightsInterpolation = InterpolationType::LINEAR; + /// @brief Number of chunks must be generated before and after wide /// structures placement triggered uint wideStructsChunksRadius = 3; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index f585e71b..c0084df4 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -307,13 +307,13 @@ void WorldGenerator::generateBiomes( copy->resize( floordiv(CHUNK_W, def.heightsBPD) + 1, floordiv(CHUNK_D, def.heightsBPD) + 1, - InterpolationType::LINEAR + def.heightsInterpolation ); prototype.heightmapInputs.push_back(std::move(copy)); } for (const auto& map : biomeParams) { map->resize( - CHUNK_W + bpd, CHUNK_D + bpd, InterpolationType::LINEAR + CHUNK_W + bpd, CHUNK_D + bpd, def.biomesInterpolation ); map->crop(0, 0, CHUNK_W, CHUNK_D); } @@ -345,7 +345,7 @@ void WorldGenerator::generateHeightmap( ); prototype.heightmap->clamp(); prototype.heightmap->resize( - CHUNK_W + bpd, CHUNK_D + bpd, InterpolationType::LINEAR + CHUNK_W + bpd, CHUNK_D + bpd, def.heightsInterpolation ); prototype.heightmap->crop(0, 0, CHUNK_W, CHUNK_D); prototype.level = ChunkPrototypeLevel::HEIGHTMAP; From 7819a5cd6814e43db792e79673061b20f5b8b24f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 28 Oct 2024 10:03:08 +0300 Subject: [PATCH 4/5] add fragment:place & add 'fragment.place' command --- res/scripts/stdcmd.lua | 14 +++++++++++ .../scripting/lua/libs/libgeneration.cpp | 1 + .../lua/usertypes/lua_type_voxelfragment.cpp | 13 ++++++++++ src/world/generator/VoxelFragment.cpp | 24 +++++++++++++++++++ src/world/generator/VoxelFragment.hpp | 6 +++++ 5 files changed, 58 insertions(+) diff --git a/res/scripts/stdcmd.lua b/res/scripts/stdcmd.lua index a2edd9b8..c5b832b7 100644 --- a/res/scripts/stdcmd.lua +++ b/res/scripts/stdcmd.lua @@ -187,3 +187,17 @@ console.add_command( " has been saved as "..file.resolve(filename)) end ) + +console.add_command( + "fragment.place file:str x:num~pos.x y:num~pos.y z:num~pos.z rotation:int=0", + "Place fragment to the world", + function(args, kwargs) + local filename = args[1] + local x = args[2] + local y = args[3] + local z = args[4] + local rotation = args[5] + local fragment = generation.load_fragment(filename) + fragment:place({x, y, z}, rotation) + end +) diff --git a/src/logic/scripting/lua/libs/libgeneration.cpp b/src/logic/scripting/lua/libs/libgeneration.cpp index ca97815c..034f6597 100644 --- a/src/logic/scripting/lua/libs/libgeneration.cpp +++ b/src/logic/scripting/lua/libs/libgeneration.cpp @@ -45,6 +45,7 @@ static int l_load_fragment(lua::State* L) { auto fragment = std::make_shared(); fragment->deserialize(map); + fragment->prepare(*content); return lua::newuserdata(L, std::move(fragment)); } diff --git a/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp b/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp index eb6a24ab..f123be6f 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp @@ -4,6 +4,7 @@ #include "world/generator/VoxelFragment.hpp" #include "util/stringutil.hpp" +#include "world/Level.hpp" using namespace lua; @@ -20,8 +21,20 @@ static int l_crop(lua::State* L) { return 0; } +static int l_place(lua::State* L) { + if (auto fragment = touserdata(L, 1)) { + auto offset = tovec3(L, 2); + int rotation = tointeger(L, 3) & 0b11; + fragment->getFragment()->place( + *scripting::level->chunks, offset, rotation + ); + } + return 0; +} + static std::unordered_map methods { {"crop", lua::wrap}, + {"place", lua::wrap}, }; static int l_meta_tostring(lua::State* L) { diff --git a/src/world/generator/VoxelFragment.cpp b/src/world/generator/VoxelFragment.cpp index 832c1109..b6e65094 100644 --- a/src/world/generator/VoxelFragment.cpp +++ b/src/world/generator/VoxelFragment.cpp @@ -6,6 +6,7 @@ #include "data/dv_util.hpp" #include "content/Content.hpp" +#include "voxels/Chunks.hpp" #include "voxels/Block.hpp" #include "voxels/ChunksStorage.hpp" #include "voxels/VoxelsVolume.hpp" @@ -168,6 +169,29 @@ void VoxelFragment::prepare(const Content& content) { } } +void VoxelFragment::place( + Chunks& chunks, const glm::ivec3& offset, ubyte rotation +) { + auto& structVoxels = getRuntimeVoxels(); + for (int y = 0; y < size.y; y++) { + int sy = y + offset.y; + if (sy < 0 || sy >= CHUNK_H) { + continue; + } + for (int z = 0; z < size.z; z++) { + int sz = z + offset.z; + for (int x = 0; x < size.x; x++) { + int sx = x + offset.x; + const auto& structVoxel = + structVoxels[vox_index(x, y, z, size.x, size.z)]; + if (structVoxel.id) { + chunks.set(sx, sy, sz, structVoxel.id, structVoxel.state); + } + } + } + } +} + std::unique_ptr VoxelFragment::rotated(const Content& content) const { std::vector newVoxels(voxels.size()); diff --git a/src/world/generator/VoxelFragment.hpp b/src/world/generator/VoxelFragment.hpp index 95a5375b..701cac0e 100644 --- a/src/world/generator/VoxelFragment.hpp +++ b/src/world/generator/VoxelFragment.hpp @@ -10,6 +10,7 @@ inline constexpr int STRUCTURE_FORMAT_VERSION = 1; class Level; class Content; +class Chunks; class VoxelFragment : public Serializable { glm::ivec3 size; @@ -41,6 +42,11 @@ public: /// @param content world content void prepare(const Content& content); + /// @brief Place fragment to the world + /// @param offset target location + /// @param rotation rotation index + void place(Chunks& chunks, const glm::ivec3& offset, ubyte rotation); + /// @brief Create structure copy rotated 90 deg. clockwise std::unique_ptr rotated(const Content& content) const; From 016336ee4da46c702bde332a3bbb8ab05ce45928 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 28 Oct 2024 10:07:56 +0300 Subject: [PATCH 5/5] update doc/*/world-generator.md --- doc/en/world-generator.md | 10 +++++++++- doc/ru/world-generator.md | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/doc/en/world-generator.md b/doc/en/world-generator.md index b6e053ac..47f46b80 100644 --- a/doc/en/world-generator.md +++ b/doc/en/world-generator.md @@ -351,7 +351,15 @@ generation.save_fragment( The fragment size is available as the `size` property. -A fragment can be cropped to fit its contents (air is ignored) by calling the `fragment:crop()` method. +### Methods + +```lua +-- Crop a fragment to content +fragment:crop() + +-- Set a fragment to the world at the specified position +fragment:place(position: vec3, [optional] rotation:int=0) +``` ## Generating a height map diff --git a/doc/ru/world-generator.md b/doc/ru/world-generator.md index 5ccdbc68..284ea067 100644 --- a/doc/ru/world-generator.md +++ b/doc/ru/world-generator.md @@ -355,7 +355,15 @@ generation.save_fragment( Размер фрагмента доступен как свойство `size`. -Фрагмент может быть обрезан до размеров содержимого (воздух игнорируется) вызовом метода `fragment:crop()`. +### Методы + +```lua +-- Обрезает фрагмент до размеров содержимого +fragment:crop() + +-- Устанавливает фрагмент в мир на указанной позиции +fragment:place(position: vec3, [опционально] rotation:int=0) +``` ## Генерация карты высот