From da4b8bd7d4b2c7c523411a32328ef0bf62cf39d8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 13 Aug 2024 03:09:52 +0300 Subject: [PATCH 001/139] add Heightmap class & rename bytearray to Bytearray --- src/logic/scripting/lua/api_lua.hpp | 1 + src/logic/scripting/lua/libgeneration.cpp | 4 + src/logic/scripting/lua/lua_custom_types.hpp | 23 ++++++ src/logic/scripting/lua/lua_engine.cpp | 4 +- ...ustom_types.cpp => lua_type_bytearray.cpp} | 36 ++++----- .../scripting/lua/lua_type_heightmap.cpp | 73 +++++++++++++++++++ 6 files changed, 122 insertions(+), 19 deletions(-) create mode 100644 src/logic/scripting/lua/libgeneration.cpp rename src/logic/scripting/lua/{lua_custom_types.cpp => lua_type_bytearray.cpp} (81%) create mode 100644 src/logic/scripting/lua/lua_type_heightmap.cpp diff --git a/src/logic/scripting/lua/api_lua.hpp b/src/logic/scripting/lua/api_lua.hpp index 68a95cb8..6a8f3dd2 100644 --- a/src/logic/scripting/lua/api_lua.hpp +++ b/src/logic/scripting/lua/api_lua.hpp @@ -30,6 +30,7 @@ extern const luaL_Reg jsonlib[]; extern const luaL_Reg mat4lib[]; extern const luaL_Reg packlib[]; extern const luaL_Reg playerlib[]; +extern const luaL_Reg generationlib[]; extern const luaL_Reg quatlib[]; // quat.cpp extern const luaL_Reg timelib[]; extern const luaL_Reg tomllib[]; diff --git a/src/logic/scripting/lua/libgeneration.cpp b/src/logic/scripting/lua/libgeneration.cpp new file mode 100644 index 00000000..c9b55e1d --- /dev/null +++ b/src/logic/scripting/lua/libgeneration.cpp @@ -0,0 +1,4 @@ +#include "api_lua.hpp" + +const luaL_Reg generationlib[] = { + {NULL, NULL}}; diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index 4b603aca..b9d6f5ef 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -29,4 +29,27 @@ namespace lua { static int createMetatable(lua::State*); inline static std::string TYPENAME = "bytearray"; }; + + class Heightmap : public Userdata { + std::vector buffer; + uint width, height; + public: + Heightmap(uint width, uint height); + virtual ~Heightmap(); + + uint getWidth() const { + return width; + } + + uint getHeight() const { + return height; + } + + const std::string& getTypeName() const override { + return TYPENAME; + } + + static int createMetatable(lua::State*); + inline static std::string TYPENAME = "heightmap"; + }; } diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 01df4ea9..8008517a 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -37,6 +37,7 @@ static void create_libs(lua::State* L) { openlib(L, "gui", guilib); openlib(L, "input", inputlib); openlib(L, "inventory", inventorylib); + openlib(L, "generation", generationlib); openlib(L, "item", itemlib); openlib(L, "json", jsonlib); openlib(L, "mat4", mat4lib); @@ -95,7 +96,8 @@ void lua::initialize() { initialize_libs_extends(L); - newusertype(L, "bytearray"); + newusertype(L, "Bytearray"); + newusertype(L, "Heightmap"); } void lua::finalize() { diff --git a/src/logic/scripting/lua/lua_custom_types.cpp b/src/logic/scripting/lua/lua_type_bytearray.cpp similarity index 81% rename from src/logic/scripting/lua/lua_custom_types.cpp rename to src/logic/scripting/lua/lua_type_bytearray.cpp index 8ee90d92..9c81c380 100644 --- a/src/logic/scripting/lua/lua_custom_types.cpp +++ b/src/logic/scripting/lua/lua_type_bytearray.cpp @@ -16,7 +16,7 @@ Bytearray::Bytearray(std::vector buffer) : buffer(std::move(buffer)) { Bytearray::~Bytearray() { } -static int l_bytearray_append(lua::State* L) { +static int l_append(lua::State* L) { if (auto buffer = touserdata(L, 1)) { auto value = tointeger(L, 2); buffer->data().push_back(static_cast(value)); @@ -24,7 +24,7 @@ static int l_bytearray_append(lua::State* L) { return 0; } -static int l_bytearray_insert(lua::State* L) { +static int l_insert(lua::State* L) { auto buffer = touserdata(L, 1); if (buffer == nullptr) { return 0; @@ -39,7 +39,7 @@ static int l_bytearray_insert(lua::State* L) { return 0; } -static int l_bytearray_remove(lua::State* L) { +static int l_remove(lua::State* L) { auto buffer = touserdata(L, 1); if (buffer == nullptr) { return 0; @@ -54,12 +54,12 @@ static int l_bytearray_remove(lua::State* L) { } static std::unordered_map bytearray_methods { - {"append", lua::wrap}, - {"insert", lua::wrap}, - {"remove", lua::wrap}, + {"append", lua::wrap}, + {"insert", lua::wrap}, + {"remove", lua::wrap}, }; -static int l_bytearray_meta_meta_call(lua::State* L) { +static int l_meta_meta_call(lua::State* L) { if (lua_istable(L, 2)) { size_t len = objlen(L, 2); std::vector buffer(len); @@ -78,7 +78,7 @@ static int l_bytearray_meta_meta_call(lua::State* L) { return newuserdata(L, static_cast(size)); } -static int l_bytearray_meta_index(lua::State* L) { +static int l_meta_index(lua::State* L) { auto buffer = touserdata(L, 1); if (buffer == nullptr) { return 0; @@ -97,7 +97,7 @@ static int l_bytearray_meta_index(lua::State* L) { return pushinteger(L, data[index]); } -static int l_bytearray_meta_newindex(lua::State* L) { +static int l_meta_newindex(lua::State* L) { auto buffer = touserdata(L, 1); if (buffer == nullptr) { return 0; @@ -115,14 +115,14 @@ static int l_bytearray_meta_newindex(lua::State* L) { return 0; } -static int l_bytearray_meta_len(lua::State* L) { +static int l_meta_len(lua::State* L) { if (auto buffer = touserdata(L, 1)) { return pushinteger(L, buffer->data().size()); } return 0; } -static int l_bytearray_meta_tostring(lua::State* L) { +static int l_meta_tostring(lua::State* L) { auto buffer = touserdata(L, 1); if (buffer == nullptr) { return 0; @@ -146,7 +146,7 @@ static int l_bytearray_meta_tostring(lua::State* L) { } } -static int l_bytearray_meta_add(lua::State* L) { +static int l_meta_add(lua::State* L) { auto bufferA = touserdata(L, 1); auto bufferB = touserdata(L, 2); if (bufferA == nullptr || bufferB == nullptr) { @@ -164,19 +164,19 @@ static int l_bytearray_meta_add(lua::State* L) { int Bytearray::createMetatable(lua::State* L) { createtable(L, 0, 6); - pushcfunction(L, lua::wrap); + pushcfunction(L, lua::wrap); setfield(L, "__index"); - pushcfunction(L, lua::wrap); + pushcfunction(L, lua::wrap); setfield(L, "__newindex"); - pushcfunction(L, lua::wrap); + pushcfunction(L, lua::wrap); setfield(L, "__len"); - pushcfunction(L, lua::wrap); + pushcfunction(L, lua::wrap); setfield(L, "__tostring"); - pushcfunction(L, lua::wrap); + pushcfunction(L, lua::wrap); setfield(L, "__add"); createtable(L, 0, 1); - pushcfunction(L, lua::wrap); + pushcfunction(L, lua::wrap); setfield(L, "__call"); setmetatable(L); return 1; diff --git a/src/logic/scripting/lua/lua_type_heightmap.cpp b/src/logic/scripting/lua/lua_type_heightmap.cpp new file mode 100644 index 00000000..253d56f7 --- /dev/null +++ b/src/logic/scripting/lua/lua_type_heightmap.cpp @@ -0,0 +1,73 @@ +#include "lua_custom_types.hpp" + +#include +#include +#include + +#include "lua_util.hpp" + +using namespace lua; + +Heightmap::Heightmap(uint width, uint height) : width(width), height(height) { + buffer.resize(width*height); +} + +Heightmap::~Heightmap() { +} + +static int l_meta_meta_call(lua::State* L) { + auto width = tointeger(L, 2); + auto height = tointeger(L, 3); + if (width <= 0 || height <= 0) { + throw std::runtime_error("width and height must be greather than 0"); + } + return newuserdata( + L, static_cast(width), static_cast(height) + ); +} + +static int l_meta_index(lua::State* L) { + auto map = touserdata(L, 1); + if (map == nullptr) { + return 0; + } + if (isstring(L, 2)) { + auto fieldname = tostring(L, 2); + if (!std::strcmp(fieldname, "width")) { + return pushinteger(L, map->getWidth()); + } else if (!std::strcmp(fieldname, "height")) { + return pushinteger(L, map->getHeight()); + } + } + return 0; +} + +static int l_meta_tostring(lua::State* L) { + auto map = touserdata(L, 1); + if (map == nullptr) { + return 0; + } + + std::stringstream stream; + stream << std::hex << reinterpret_cast(map); + auto ptrstr = stream.str(); + + return pushstring( + L, "Heightmap(" + std::to_string(map->getWidth()) + + "*" + std::to_string(map->getHeight()) + " at 0x" + ptrstr + ")" + ); +} + +int Heightmap::createMetatable(lua::State* L) { + createtable(L, 0, 2); + pushcfunction(L, lua::wrap); + setfield(L, "__tostring"); + pushcfunction(L, lua::wrap); + setfield(L, "__index"); + + createtable(L, 0, 1); + pushcfunction(L, lua::wrap); + setfield(L, "__call"); + setmetatable(L); + return 1; +} From 13b97f439893e4effde99c8af87d622481afa68d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 13 Aug 2024 20:57:46 +0300 Subject: [PATCH 002/139] add Heightmap methods --- res/scripts/world.lua | 45 ++++ src/logic/scripting/lua/lua_custom_types.hpp | 8 + .../scripting/lua/lua_type_bytearray.cpp | 6 +- .../scripting/lua/lua_type_heightmap.cpp | 235 ++++++++++++++++++ 4 files changed, 291 insertions(+), 3 deletions(-) diff --git a/res/scripts/world.lua b/res/scripts/world.lua index 964100e7..a63f8ee1 100644 --- a/res/scripts/world.lua +++ b/res/scripts/world.lua @@ -1,3 +1,48 @@ -- use for engine development tests -- must be empty in release -- must not be modified by content-packs + +local W = 16 +local H = 16 + +for t=1,1 do + local tm = time.uptime() + + local umap = Heightmap(W, H) + local vmap = Heightmap(W, H) + umap:noise({521, 73}, 0.2, 1, 11.8) + umap:noise({51, 73}, 0.2, 1, 11.8) + umap:noise({521, 73}, 0.4, 6, 5.8) + vmap:noise({95, 246}, 0.6, 6, 5.8) + + local bmap = Heightmap(W, H) + bmap:noise({3, 6}, 0.1, 1, 3) + local map = Heightmap(W, H) + + + map:noise({0, 0}, 0.06, 4, 0.2, umap, vmap) + map:noise({0, 0}, 0.25, 6, 0.5, umap, vmap) + map:mul(bmap) + map:mul(0.7) + + local rivermap = Heightmap(W, H) + rivermap:noise({21, 12}, 0.05, 3) + rivermap:abs() + rivermap:min(0.02) + rivermap:mul(50.0) + rivermap:pow(0.4) + map:add(1.7) + map:mul(rivermap) + map:add(-1.0) + map:mul(0.5) + + local overmap = Heightmap(W, H) + overmap:noise({1, 5122}, 0.02, 2, 0.2) + overmap:abs() + overmap:pow(0.5) + overmap:mul(-1.0) + map:add(overmap) + + print(math.floor((time.uptime() - tm) * 1000000).." mcs") + map:dump("heightmap.png") +end diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index b9d6f5ef..885052ce 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -49,6 +49,14 @@ namespace lua { return TYPENAME; } + float* getValues() { + return buffer.data(); + } + + const float* getValues() const { + return buffer.data(); + } + static int createMetatable(lua::State*); inline static std::string TYPENAME = "heightmap"; }; diff --git a/src/logic/scripting/lua/lua_type_bytearray.cpp b/src/logic/scripting/lua/lua_type_bytearray.cpp index 9c81c380..cf41f318 100644 --- a/src/logic/scripting/lua/lua_type_bytearray.cpp +++ b/src/logic/scripting/lua/lua_type_bytearray.cpp @@ -53,7 +53,7 @@ static int l_remove(lua::State* L) { return 0; } -static std::unordered_map bytearray_methods { +static std::unordered_map methods { {"append", lua::wrap}, {"insert", lua::wrap}, {"remove", lua::wrap}, @@ -85,8 +85,8 @@ static int l_meta_index(lua::State* L) { } auto& data = buffer->data(); if (isstring(L, 2)) { - auto found = bytearray_methods.find(tostring(L, 2)); - if (found != bytearray_methods.end()) { + auto found = methods.find(tostring(L, 2)); + if (found != methods.end()) { return pushcfunction(L, found->second); } } diff --git a/src/logic/scripting/lua/lua_type_heightmap.cpp b/src/logic/scripting/lua/lua_type_heightmap.cpp index 253d56f7..e53ecb48 100644 --- a/src/logic/scripting/lua/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/lua_type_heightmap.cpp @@ -3,11 +3,17 @@ #include #include #include +#include +#include "maths/FastNoiseLite.h" +#include "coders/png.hpp" +#include "graphics/core/ImageData.hpp" #include "lua_util.hpp" using namespace lua; +static fnl_state noise = fnlCreateState(); + Heightmap::Heightmap(uint width, uint height) : width(width), height(height) { buffer.resize(width*height); } @@ -15,6 +21,230 @@ Heightmap::Heightmap(uint width, uint height) : width(width), height(height) { Heightmap::~Heightmap() { } +static int l_dump(lua::State* L) { + if (auto heightmap = touserdata(L, 1)) { + auto filename = tostring(L, 2); + uint w = heightmap->getWidth(); + uint h = heightmap->getHeight(); + ImageData image(ImageFormat::rgb888, w, h); + auto heights = heightmap->getValues(); + auto raster = image.getData(); + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + uint i = y * w + x; + int val = heights[i] * 127 + 128; + val = std::max(std::min(val, 255), 0); + raster[i*3] = val; + raster[i*3 + 1] = val; + raster[i*3 + 2] = val; + } + } + png::write_image(filename, &image); + } + return 0; +} + +static int l_noise(lua::State* L) { + if (auto heightmap = touserdata(L, 1)) { + uint w = heightmap->getWidth(); + uint h = heightmap->getHeight(); + auto heights = heightmap->getValues(); + + auto offset = tovec<2>(L, 2); + + float s = tonumber(L, 3); + int octaves = 1; + float multiplier = 1.0f; + if (gettop(L) > 3) { + octaves = tointeger(L, 4); + } + if (gettop(L) > 4) { + multiplier = tonumber(L, 5); + } + const Heightmap* shiftMapX = nullptr; + const Heightmap* shiftMapY = nullptr; + if (gettop(L) > 5) { + shiftMapX = touserdata(L, 6); + } + if (gettop(L) > 6) { + shiftMapY = touserdata(L, 7); + } + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + uint i = y * w + x; + for (uint c = 0; c < octaves; c++) { + float m = s * (1 << c); + float value = heights[i]; + float u = (x + offset.x) * m; + float v = (y + offset.y) * m; + if (shiftMapX) { + u += shiftMapX->getValues()[i]; + } + if (shiftMapY) { + v += shiftMapY->getValues()[i]; + } + + value += fnlGetNoise2D(&noise, u, v) / + static_cast(1 << c) * multiplier; + heights[i] = value; + } + } + } + } + return 0; +} + +static int l_pow(lua::State* L) { + if (auto heightmap = touserdata(L, 1)) { + uint w = heightmap->getWidth(); + uint h = heightmap->getHeight(); + auto heights = heightmap->getValues(); + float power = tonumber(L, 2); + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + uint i = y * w + x; + heights[i] = glm::pow(heights[i], power); + } + } + } + return 0; +} + +static int l_add(lua::State* L) { + if (auto heightmap = touserdata(L, 1)) { + uint w = heightmap->getWidth(); + uint h = heightmap->getHeight(); + auto heights = heightmap->getValues(); + + if (isnumber(L, 2)) { + float scalar = tonumber(L, 2); + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + uint i = y * w + x; + heights[i] += scalar; + } + } + } else { + auto map = touserdata(L, 2); + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + uint i = y * w + x; + heights[i] += map->getValues()[i]; + } + } + } + } + return 0; +} + +static int l_mul(lua::State* L) { + if (auto heightmap = touserdata(L, 1)) { + uint w = heightmap->getWidth(); + uint h = heightmap->getHeight(); + auto heights = heightmap->getValues(); + + if (isnumber(L, 2)) { + float scalar = tonumber(L, 2); + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + uint i = y * w + x; + heights[i] *= scalar; + } + } + } else { + auto map = touserdata(L, 2); + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + uint i = y * w + x; + heights[i] *= map->getValues()[i]; + } + } + } + } + return 0; +} + +static int l_max(lua::State* L) { + if (auto heightmap = touserdata(L, 1)) { + uint w = heightmap->getWidth(); + uint h = heightmap->getHeight(); + auto heights = heightmap->getValues(); + + if (isnumber(L, 2)) { + float scalar = tonumber(L, 2); + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + uint i = y * w + x; + heights[i] *= scalar; + } + } + } else { + auto map = touserdata(L, 2); + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + uint i = y * w + x; + heights[i] = glm::max(map->getValues()[i], heights[i]); + } + } + } + } + return 0; +} + +static int l_min(lua::State* L) { + if (auto heightmap = touserdata(L, 1)) { + uint w = heightmap->getWidth(); + uint h = heightmap->getHeight(); + auto heights = heightmap->getValues(); + + if (isnumber(L, 2)) { + float scalar = tonumber(L, 2); + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + uint i = y * w + x; + heights[i] *= scalar; + } + } + } else { + auto map = touserdata(L, 2); + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + uint i = y * w + x; + heights[i] = glm::min(map->getValues()[i], heights[i]); + } + } + } + } + return 0; +} + +static int l_abs(lua::State* L) { + if (auto heightmap = touserdata(L, 1)) { + uint w = heightmap->getWidth(); + uint h = heightmap->getHeight(); + auto heights = heightmap->getValues(); + float power = tonumber(L, 2); + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + uint i = y * w + x; + heights[i] = glm::abs(heights[i]); + } + } + } + return 0; +} + +static std::unordered_map methods { + {"dump", lua::wrap}, + {"noise", lua::wrap}, + {"pow", lua::wrap}, + {"add", lua::wrap}, + {"mul", lua::wrap}, + {"min", lua::wrap}, + {"max", lua::wrap}, + {"abs", lua::wrap}, +}; + static int l_meta_meta_call(lua::State* L) { auto width = tointeger(L, 2); auto height = tointeger(L, 3); @@ -37,6 +267,11 @@ static int l_meta_index(lua::State* L) { return pushinteger(L, map->getWidth()); } else if (!std::strcmp(fieldname, "height")) { return pushinteger(L, map->getHeight()); + } else { + auto found = methods.find(tostring(L, 2)); + if (found != methods.end()) { + return pushcfunction(L, found->second); + } } } return 0; From 6e99461b5fa83747e4d8eac81fe2110648aa8ec9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 13 Aug 2024 22:00:16 +0300 Subject: [PATCH 003/139] refactor Heightmap methods with templates --- res/scripts/world.lua | 12 +- src/logic/scripting/lua/libmat4.cpp | 6 +- .../scripting/lua/lua_type_heightmap.cpp | 172 ++++-------------- src/util/functional_util.hpp | 33 ++++ 4 files changed, 82 insertions(+), 141 deletions(-) create mode 100644 src/util/functional_util.hpp diff --git a/res/scripts/world.lua b/res/scripts/world.lua index a63f8ee1..c48b5615 100644 --- a/res/scripts/world.lua +++ b/res/scripts/world.lua @@ -2,8 +2,8 @@ -- must be empty in release -- must not be modified by content-packs -local W = 16 -local H = 16 +local W = 1024 +local H = 1024 for t=1,1 do local tm = time.uptime() @@ -28,10 +28,10 @@ for t=1,1 do local rivermap = Heightmap(W, H) rivermap:noise({21, 12}, 0.05, 3) rivermap:abs() - rivermap:min(0.02) - rivermap:mul(50.0) - rivermap:pow(0.4) - map:add(1.7) + rivermap:min(0.1) + rivermap:mul(10.0) + rivermap:pow(0.8) + map:add(1.2) map:mul(rivermap) map:add(-1.0) map:mul(0.5) diff --git a/src/logic/scripting/lua/libmat4.cpp b/src/logic/scripting/lua/libmat4.cpp index 44721ca6..5515fc0a 100644 --- a/src/logic/scripting/lua/libmat4.cpp +++ b/src/logic/scripting/lua/libmat4.cpp @@ -77,7 +77,7 @@ static int l_mul(lua::State* L) { /// transformed copy of matrix mat4.(matrix: float[16], vec: float[3], /// dst: float[16]) -> sets dst to transformed version of matrix template -inline int l_transform_func(lua::State* L) { +inline int l_binop_func(lua::State* L) { uint argc = lua::gettop(L); switch (argc) { case 1: { @@ -272,9 +272,9 @@ static int l_tostring(lua::State* L) { const luaL_Reg mat4lib[] = { {"idt", lua::wrap}, {"mul", lua::wrap}, - {"scale", lua::wrap>}, + {"scale", lua::wrap>}, {"rotate", lua::wrap}, - {"translate", lua::wrap>}, + {"translate", lua::wrap>}, {"inverse", lua::wrap}, {"transpose", lua::wrap}, {"determinant", lua::wrap}, diff --git a/src/logic/scripting/lua/lua_type_heightmap.cpp b/src/logic/scripting/lua/lua_type_heightmap.cpp index e53ecb48..ecbdc1bf 100644 --- a/src/logic/scripting/lua/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/lua_type_heightmap.cpp @@ -5,6 +5,7 @@ #include #include +#include "util/functional_util.hpp" #include "maths/FastNoiseLite.h" #include "coders/png.hpp" #include "graphics/core/ImageData.hpp" @@ -94,7 +95,38 @@ static int l_noise(lua::State* L) { return 0; } -static int l_pow(lua::State* L) { +template class Op> +static int l_binop_func(lua::State* L) { + Op op; + if (auto heightmap = touserdata(L, 1)) { + uint w = heightmap->getWidth(); + uint h = heightmap->getHeight(); + auto heights = heightmap->getValues(); + + if (isnumber(L, 2)) { + float scalar = tonumber(L, 2); + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + uint i = y * w + x; + heights[i] = op(heights[i], scalar); + } + } + } else { + auto map = touserdata(L, 2); + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + uint i = y * w + x; + heights[i] = op(heights[i], map->getValues()[i]); + } + } + } + } + return 0; +} + +template class Op> +static int l_unaryop_func(lua::State* L) { + Op op; if (auto heightmap = touserdata(L, 1)) { uint w = heightmap->getWidth(); uint h = heightmap->getHeight(); @@ -103,131 +135,7 @@ static int l_pow(lua::State* L) { for (uint y = 0; y < h; y++) { for (uint x = 0; x < w; x++) { uint i = y * w + x; - heights[i] = glm::pow(heights[i], power); - } - } - } - return 0; -} - -static int l_add(lua::State* L) { - if (auto heightmap = touserdata(L, 1)) { - uint w = heightmap->getWidth(); - uint h = heightmap->getHeight(); - auto heights = heightmap->getValues(); - - if (isnumber(L, 2)) { - float scalar = tonumber(L, 2); - for (uint y = 0; y < h; y++) { - for (uint x = 0; x < w; x++) { - uint i = y * w + x; - heights[i] += scalar; - } - } - } else { - auto map = touserdata(L, 2); - for (uint y = 0; y < h; y++) { - for (uint x = 0; x < w; x++) { - uint i = y * w + x; - heights[i] += map->getValues()[i]; - } - } - } - } - return 0; -} - -static int l_mul(lua::State* L) { - if (auto heightmap = touserdata(L, 1)) { - uint w = heightmap->getWidth(); - uint h = heightmap->getHeight(); - auto heights = heightmap->getValues(); - - if (isnumber(L, 2)) { - float scalar = tonumber(L, 2); - for (uint y = 0; y < h; y++) { - for (uint x = 0; x < w; x++) { - uint i = y * w + x; - heights[i] *= scalar; - } - } - } else { - auto map = touserdata(L, 2); - for (uint y = 0; y < h; y++) { - for (uint x = 0; x < w; x++) { - uint i = y * w + x; - heights[i] *= map->getValues()[i]; - } - } - } - } - return 0; -} - -static int l_max(lua::State* L) { - if (auto heightmap = touserdata(L, 1)) { - uint w = heightmap->getWidth(); - uint h = heightmap->getHeight(); - auto heights = heightmap->getValues(); - - if (isnumber(L, 2)) { - float scalar = tonumber(L, 2); - for (uint y = 0; y < h; y++) { - for (uint x = 0; x < w; x++) { - uint i = y * w + x; - heights[i] *= scalar; - } - } - } else { - auto map = touserdata(L, 2); - for (uint y = 0; y < h; y++) { - for (uint x = 0; x < w; x++) { - uint i = y * w + x; - heights[i] = glm::max(map->getValues()[i], heights[i]); - } - } - } - } - return 0; -} - -static int l_min(lua::State* L) { - if (auto heightmap = touserdata(L, 1)) { - uint w = heightmap->getWidth(); - uint h = heightmap->getHeight(); - auto heights = heightmap->getValues(); - - if (isnumber(L, 2)) { - float scalar = tonumber(L, 2); - for (uint y = 0; y < h; y++) { - for (uint x = 0; x < w; x++) { - uint i = y * w + x; - heights[i] *= scalar; - } - } - } else { - auto map = touserdata(L, 2); - for (uint y = 0; y < h; y++) { - for (uint x = 0; x < w; x++) { - uint i = y * w + x; - heights[i] = glm::min(map->getValues()[i], heights[i]); - } - } - } - } - return 0; -} - -static int l_abs(lua::State* L) { - if (auto heightmap = touserdata(L, 1)) { - uint w = heightmap->getWidth(); - uint h = heightmap->getHeight(); - auto heights = heightmap->getValues(); - float power = tonumber(L, 2); - for (uint y = 0; y < h; y++) { - for (uint x = 0; x < w; x++) { - uint i = y * w + x; - heights[i] = glm::abs(heights[i]); + heights[i] = op(heights[i]); } } } @@ -237,12 +145,12 @@ static int l_abs(lua::State* L) { static std::unordered_map methods { {"dump", lua::wrap}, {"noise", lua::wrap}, - {"pow", lua::wrap}, - {"add", lua::wrap}, - {"mul", lua::wrap}, - {"min", lua::wrap}, - {"max", lua::wrap}, - {"abs", lua::wrap}, + {"pow", lua::wrap>}, + {"add", lua::wrap>}, + {"mul", lua::wrap>}, + {"min", lua::wrap>}, + {"max", lua::wrap>}, + {"abs", lua::wrap>}, }; static int l_meta_meta_call(lua::State* L) { diff --git a/src/util/functional_util.hpp b/src/util/functional_util.hpp new file mode 100644 index 00000000..2672c4fc --- /dev/null +++ b/src/util/functional_util.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace util { + template + struct pow { + constexpr T operator()(T a, T b) const { + return glm::pow(a, b); + } + }; + + template + struct min { + constexpr T operator()(T a, T b) const { + return glm::min(a, b); + } + }; + + template + struct max { + constexpr T operator()(T a, T b) const { + return glm::max(a, b); + } + }; + + template + struct abs { + constexpr T operator()(T a) const { + return glm::abs(a); + } + }; +} From a8dc730c0e3635e3286c11786803629c20dcda69 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 13 Aug 2024 23:30:34 +0300 Subject: [PATCH 004/139] add generator script draft --- res/generators/default.lua | 37 +++++++++++++++++++++++++++++++ res/scripts/world.lua | 45 -------------------------------------- 2 files changed, 37 insertions(+), 45 deletions(-) create mode 100644 res/generators/default.lua diff --git a/res/generators/default.lua b/res/generators/default.lua new file mode 100644 index 00000000..65d7dcd2 --- /dev/null +++ b/res/generators/default.lua @@ -0,0 +1,37 @@ +local W = 256 +local H = 256 + +function generate_heightmap(x, y) + local umap = Heightmap(W, H) + local vmap = Heightmap(W, H) + umap:noise({x+521, y+73}, 0.05, 1, 20.8) + umap:noise({x+51, y+75}, 0.05, 1, 21.8) + umap:noise({x+521, y+70}, 0.1, 3, 35.8) + vmap:noise({x+95, y+246}, 0.15, 3, 35.8) + + local bmap = Heightmap(W, H) + bmap:noise({x+3, y+6}, 0.1, 1, 3) + local map = Heightmap(W, H) + + + map:noise({x, y}, 0.06, 5, 0.2, umap, vmap) + map:noise({x, y}, 0.12, 6, 0.5, umap, vmap) + map:mul(bmap) + map:mul(0.7) + + local rivermap = Heightmap(W, H) + rivermap:noise({x+21, y+12}, 0.1, 3) + rivermap:abs() + rivermap:min(0.5) + rivermap:mul(2.0) + rivermap:pow(0.6) + map:add(1.2) + map:mul(rivermap) + map:add(-1.0) + map:mul(0.5) + + return map +end + +local map = generate_heightmap(0, 0) +map:dump("heightmap.png") diff --git a/res/scripts/world.lua b/res/scripts/world.lua index c48b5615..964100e7 100644 --- a/res/scripts/world.lua +++ b/res/scripts/world.lua @@ -1,48 +1,3 @@ -- use for engine development tests -- must be empty in release -- must not be modified by content-packs - -local W = 1024 -local H = 1024 - -for t=1,1 do - local tm = time.uptime() - - local umap = Heightmap(W, H) - local vmap = Heightmap(W, H) - umap:noise({521, 73}, 0.2, 1, 11.8) - umap:noise({51, 73}, 0.2, 1, 11.8) - umap:noise({521, 73}, 0.4, 6, 5.8) - vmap:noise({95, 246}, 0.6, 6, 5.8) - - local bmap = Heightmap(W, H) - bmap:noise({3, 6}, 0.1, 1, 3) - local map = Heightmap(W, H) - - - map:noise({0, 0}, 0.06, 4, 0.2, umap, vmap) - map:noise({0, 0}, 0.25, 6, 0.5, umap, vmap) - map:mul(bmap) - map:mul(0.7) - - local rivermap = Heightmap(W, H) - rivermap:noise({21, 12}, 0.05, 3) - rivermap:abs() - rivermap:min(0.1) - rivermap:mul(10.0) - rivermap:pow(0.8) - map:add(1.2) - map:mul(rivermap) - map:add(-1.0) - map:mul(0.5) - - local overmap = Heightmap(W, H) - overmap:noise({1, 5122}, 0.02, 2, 0.2) - overmap:abs() - overmap:pow(0.5) - overmap:mul(-1.0) - map:add(overmap) - - print(math.floor((time.uptime() - tm) * 1000000).." mcs") - map:dump("heightmap.png") -end From cae314bc36753cba99bd73b9e2068949bb583ba6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 14 Aug 2024 00:47:57 +0300 Subject: [PATCH 005/139] update ContentType naming --- res/generators/default.lua | 3 +-- src/content/Content.hpp | 16 ++++++++-------- src/content/ContentBuilder.hpp | 14 +++++++------- src/content/ContentLUT.cpp | 4 ++-- src/content/ContentLUT.hpp | 6 +++--- src/content/content_fwd.hpp | 2 +- src/logic/EngineController.cpp | 2 +- 7 files changed, 23 insertions(+), 24 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 65d7dcd2..defe9131 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -11,9 +11,8 @@ function generate_heightmap(x, y) local bmap = Heightmap(W, H) bmap:noise({x+3, y+6}, 0.1, 1, 3) + local map = Heightmap(W, H) - - map:noise({x, y}, 0.06, 5, 0.2, umap, vmap) map:noise({x, y}, 0.12, 6, 0.5, umap, vmap) map:mul(bmap) diff --git a/src/content/Content.hpp b/src/content/Content.hpp index 4178b7d8..9a802d43 100644 --- a/src/content/Content.hpp +++ b/src/content/Content.hpp @@ -24,15 +24,15 @@ namespace rigging { class SkeletonConfig; } -constexpr const char* contenttype_name(contenttype type) { +constexpr const char* ContentType_name(ContentType type) { switch (type) { - case contenttype::none: + case ContentType::NONE: return "none"; - case contenttype::block: + case ContentType::BLOCK: return "block"; - case contenttype::item: + case ContentType::ITEM: return "item"; - case contenttype::entity: + case ContentType::ENTITY: return "entity"; default: return "unknown"; @@ -40,13 +40,13 @@ constexpr const char* contenttype_name(contenttype type) { } class namereuse_error : public std::runtime_error { - contenttype type; + ContentType type; public: - namereuse_error(const std::string& msg, contenttype type) + namereuse_error(const std::string& msg, ContentType type) : std::runtime_error(msg), type(type) { } - inline contenttype getType() const { + inline ContentType getType() const { return type; } }; diff --git a/src/content/ContentBuilder.hpp b/src/content/ContentBuilder.hpp index 9207316c..cd652883 100644 --- a/src/content/ContentBuilder.hpp +++ b/src/content/ContentBuilder.hpp @@ -12,8 +12,8 @@ template class ContentUnitBuilder { - std::unordered_map& allNames; - contenttype type; + std::unordered_map& allNames; + ContentType type; void checkIdentifier(const std::string& id) { const auto& found = allNames.find(id); @@ -28,7 +28,7 @@ public: std::vector names; ContentUnitBuilder( - std::unordered_map& allNames, contenttype type + std::unordered_map& allNames, ContentType type ) : allNames(allNames), type(type) { } @@ -54,11 +54,11 @@ class ContentBuilder { UptrsMap blockMaterials; UptrsMap skeletons; UptrsMap packs; - std::unordered_map allNames; + std::unordered_map allNames; public: - ContentUnitBuilder blocks {allNames, contenttype::block}; - ContentUnitBuilder items {allNames, contenttype::item}; - ContentUnitBuilder entities {allNames, contenttype::entity}; + ContentUnitBuilder blocks {allNames, ContentType::BLOCK}; + ContentUnitBuilder items {allNames, ContentType::ITEM}; + ContentUnitBuilder entities {allNames, ContentType::ENTITY}; ResourceIndicesSet resourceIndices {}; ~ContentBuilder(); diff --git a/src/content/ContentLUT.cpp b/src/content/ContentLUT.cpp index 8e670152..0ee747bb 100644 --- a/src/content/ContentLUT.cpp +++ b/src/content/ContentLUT.cpp @@ -14,8 +14,8 @@ ContentLUT::ContentLUT( const ContentIndices* indices, size_t blocksCount, size_t itemsCount ) - : blocks(blocksCount, indices->blocks, BLOCK_VOID, contenttype::block), - items(itemsCount, indices->items, ITEM_VOID, contenttype::item) { + : blocks(blocksCount, indices->blocks, BLOCK_VOID, ContentType::BLOCK), + items(itemsCount, indices->items, ITEM_VOID, ContentType::ITEM) { } template diff --git a/src/content/ContentLUT.hpp b/src/content/ContentLUT.hpp index f0b92c07..cdd4ad72 100644 --- a/src/content/ContentLUT.hpp +++ b/src/content/ContentLUT.hpp @@ -13,7 +13,7 @@ namespace fs = std::filesystem; struct contententry { - contenttype type; + ContentType type; std::string name; }; @@ -26,13 +26,13 @@ class ContentUnitLUT { bool missingContent = false; bool reorderContent = false; T missingValue; - contenttype type; + ContentType type; public: ContentUnitLUT( size_t count, const ContentUnitIndices& unitIndices, T missingValue, - contenttype type + ContentType type ) : missingValue(missingValue), type(type) { for (size_t i = 0; i < count; i++) { diff --git a/src/content/content_fwd.hpp b/src/content/content_fwd.hpp index 719fbfca..b1618593 100644 --- a/src/content/content_fwd.hpp +++ b/src/content/content_fwd.hpp @@ -5,7 +5,7 @@ class Content; class ContentPackRuntime; -enum class contenttype { none, block, item, entity }; +enum class ContentType { NONE, BLOCK, ITEM, ENTITY }; enum class ResourceType : size_t { CAMERA, LAST = CAMERA }; diff --git a/src/logic/EngineController.cpp b/src/logic/EngineController.cpp index 7e0d59c3..6ce12ee8 100644 --- a/src/logic/EngineController.cpp +++ b/src/logic/EngineController.cpp @@ -92,7 +92,7 @@ static void show_content_missing( auto root = create_map(); auto& contentEntries = root->putList("content"); for (auto& entry : lut->getMissingContent()) { - std::string contentName = contenttype_name(entry.type); + std::string contentName = ContentType_name(entry.type); auto& contentEntry = contentEntries.putMap(); contentEntry.put("type", contentName); contentEntry.put("name", entry.name); From 5fa6b14f05910f828198b5d44c5605d639d27157 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 14 Aug 2024 01:23:14 +0300 Subject: [PATCH 006/139] add GeneratorDef --- src/content/Content.cpp | 3 +++ src/content/Content.hpp | 13 +++++-------- src/content/ContentBuilder.cpp | 1 + src/content/ContentBuilder.hpp | 2 ++ src/content/content_fwd.hpp | 2 +- src/world/generator/GeneratorDef.hpp | 10 ++++++++++ 6 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 src/world/generator/GeneratorDef.hpp diff --git a/src/content/Content.cpp b/src/content/Content.cpp index 677258ad..98affbbf 100644 --- a/src/content/Content.cpp +++ b/src/content/Content.cpp @@ -10,6 +10,7 @@ #include "objects/EntityDef.hpp" #include "objects/rigging.hpp" #include "voxels/Block.hpp" +#include "world/generator/GeneratorDef.hpp" #include "ContentPack.hpp" ContentIndices::ContentIndices( @@ -28,6 +29,7 @@ Content::Content( ContentUnitDefs blocks, ContentUnitDefs items, ContentUnitDefs entities, + ContentUnitDefs generators, UptrsMap packs, UptrsMap blockMaterials, UptrsMap skeletons, @@ -40,6 +42,7 @@ Content::Content( blocks(std::move(blocks)), items(std::move(items)), entities(std::move(entities)), + generators(std::move(generators)), drawGroups(std::move(drawGroups)) { for (size_t i = 0; i < RESOURCE_TYPES_COUNT; i++) { this->resourceIndices[i] = std::move(resourceIndices[i]); diff --git a/src/content/Content.hpp b/src/content/Content.hpp index 9a802d43..8e152a51 100644 --- a/src/content/Content.hpp +++ b/src/content/Content.hpp @@ -19,6 +19,7 @@ class Block; struct BlockMaterial; struct ItemDef; struct EntityDef; +struct GeneratorDef; namespace rigging { class SkeletonConfig; @@ -34,6 +35,8 @@ constexpr const char* ContentType_name(ContentType type) { return "item"; case ContentType::ENTITY: return "entity"; + case ContentType::GENERATOR: + return "generator"; default: return "unknown"; } @@ -65,14 +68,6 @@ public: return defs[id]; } - [[deprecated]] - inline T* getWriteable(blockid_t id) const { // TODO: remove - if (id >= defs.size()) { - return nullptr; - } - return defs[id]; - } - inline const T& require(blockid_t id) const { return *defs.at(id); } @@ -197,6 +192,7 @@ public: ContentUnitDefs blocks; ContentUnitDefs items; ContentUnitDefs entities; + ContentUnitDefs generators; std::unique_ptr const drawGroups; ResourceIndicesSet resourceIndices {}; @@ -206,6 +202,7 @@ public: ContentUnitDefs blocks, ContentUnitDefs items, ContentUnitDefs entities, + ContentUnitDefs generators, UptrsMap packs, UptrsMap blockMaterials, UptrsMap skeletons, diff --git a/src/content/ContentBuilder.cpp b/src/content/ContentBuilder.cpp index 4bf194bc..b637532e 100644 --- a/src/content/ContentBuilder.cpp +++ b/src/content/ContentBuilder.cpp @@ -72,6 +72,7 @@ std::unique_ptr ContentBuilder::build() { blocks.build(), items.build(), entities.build(), + generators.build(), std::move(packs), std::move(blockMaterials), std::move(skeletons), diff --git a/src/content/ContentBuilder.hpp b/src/content/ContentBuilder.hpp index cd652883..22610eee 100644 --- a/src/content/ContentBuilder.hpp +++ b/src/content/ContentBuilder.hpp @@ -8,6 +8,7 @@ #include "ContentPack.hpp" #include "items/ItemDef.hpp" #include "objects/EntityDef.hpp" +#include "world/generator/GeneratorDef.hpp" #include "voxels/Block.hpp" template @@ -59,6 +60,7 @@ public: ContentUnitBuilder blocks {allNames, ContentType::BLOCK}; ContentUnitBuilder items {allNames, ContentType::ITEM}; ContentUnitBuilder entities {allNames, ContentType::ENTITY}; + ContentUnitBuilder generators {allNames, ContentType::GENERATOR}; ResourceIndicesSet resourceIndices {}; ~ContentBuilder(); diff --git a/src/content/content_fwd.hpp b/src/content/content_fwd.hpp index b1618593..076a433f 100644 --- a/src/content/content_fwd.hpp +++ b/src/content/content_fwd.hpp @@ -5,7 +5,7 @@ class Content; class ContentPackRuntime; -enum class ContentType { NONE, BLOCK, ITEM, ENTITY }; +enum class ContentType { NONE, BLOCK, ITEM, ENTITY, GENERATOR }; enum class ResourceType : size_t { CAMERA, LAST = CAMERA }; diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp new file mode 100644 index 00000000..fa26787f --- /dev/null +++ b/src/world/generator/GeneratorDef.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "typedefs.hpp" + +struct GeneratorDef { + std::string name; + scriptenv env; +}; From 6ff90bfef3f16f37a1a7e445dd5badd3251d8b9f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 14 Aug 2024 01:31:56 +0300 Subject: [PATCH 007/139] fix: slow world saving --- src/voxels/Chunks.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 86311359..1dbfd5a1 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -750,7 +750,9 @@ void Chunks::save(Chunk* chunk) { chunk->flags.entities = true; } worldFiles->getRegions().put( - chunk, json::to_binary(root, true) + chunk, + chunk->flags.entities ? json::to_binary(root, true) + : std::vector() ); } } From 6f5eb6be48c036d8e152b073a1e5f0aedcffd3e6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 14 Aug 2024 02:18:16 +0300 Subject: [PATCH 008/139] refactor ContentLoader --- src/content/ContentLoader.cpp | 56 ++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 26e8f5f0..96a5eadf 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -415,6 +415,18 @@ void ContentLoader::loadItem( } } +static std::tuple create_unit_id( + const std::string& packid, const std::string& name +) { + size_t colon = name.find(':'); + if (colon == std::string::npos) { + return {packid, packid + ":" + name, name}; + } + auto otherPackid = name.substr(0, colon); + auto full = otherPackid + ":" + name; + return {otherPackid, full, otherPackid + "/" + name}; +} + void ContentLoader::loadBlockMaterial( BlockMaterial& def, const fs::path& file ) { @@ -445,28 +457,28 @@ void ContentLoader::load() { if (auto blocksarr = root->list("blocks")) { for (size_t i = 0; i < blocksarr->size(); i++) { std::string name = blocksarr->str(i); - auto colon = name.find(':'); - std::string full = - colon == std::string::npos ? pack->id + ":" + name : name; - if (colon != std::string::npos) name[colon] = '/'; + auto [packid, full, filename] = create_unit_id(pack->id, name); + auto& def = builder.blocks.create(full); - if (colon != std::string::npos) - def.scriptName = name.substr(0, colon) + '/' + def.scriptName; - loadBlock(def, full, name); + if (filename != name) { + def.scriptName = packid + "/" + def.scriptName; + } + + loadBlock(def, full, filename); stats->totalBlocks++; } } if (auto itemsarr = root->list("items")) { for (size_t i = 0; i < itemsarr->size(); i++) { std::string name = itemsarr->str(i); - auto colon = name.find(':'); - std::string full = - colon == std::string::npos ? pack->id + ":" + name : name; - if (colon != std::string::npos) name[colon] = '/'; + auto [packid, full, filename] = create_unit_id(pack->id, name); + auto& def = builder.items.create(full); - if (colon != std::string::npos) - def.scriptName = name.substr(0, colon) + '/' + def.scriptName; - loadItem(def, full, name); + if (filename != name) { + def.scriptName = packid + "/" + def.scriptName; + } + + loadItem(def, full, filename); stats->totalItems++; } } @@ -474,11 +486,11 @@ void ContentLoader::load() { if (auto entitiesarr = root->list("entities")) { for (size_t i = 0; i < entitiesarr->size(); i++) { std::string name = entitiesarr->str(i); - auto colon = name.find(':'); - std::string full = - colon == std::string::npos ? pack->id + ":" + name : name; - if (colon != std::string::npos) name[colon] = '/'; + + auto [packid, full, filename] = create_unit_id(pack->id, name); + auto& def = builder.entities.create(full); + loadEntity(def, full, name); stats->totalEntities++; } @@ -488,8 +500,12 @@ void ContentLoader::load() { if (fs::is_directory(materialsDir)) { for (const auto& entry : fs::directory_iterator(materialsDir)) { const fs::path& file = entry.path(); - std::string name = pack->id + ":" + file.stem().u8string(); - loadBlockMaterial(builder.createBlockMaterial(name), file); + auto [packid, full, filename] = + create_unit_id(pack->id, file.stem().u8string()); + loadBlockMaterial( + builder.createBlockMaterial(full), + materialsDir / fs::u8path(filename + ".json") + ); } } From 95cf451cc808cf1206c9f749dd5a2769e3ca1186 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 14 Aug 2024 17:12:12 +0300 Subject: [PATCH 009/139] add GeneratorScript --- res/generators/{default.lua => flat.lua} | 20 +++------ src/content/ContentLoader.cpp | 32 ++++++++++++-- src/content/ContentLoader.hpp | 4 ++ src/files/util.hpp | 16 +++++++ src/logic/scripting/lua/lua_custom_types.hpp | 36 +++++++++------- src/logic/scripting/lua/lua_engine.cpp | 2 +- .../scripting/lua/lua_type_heightmap.cpp | 31 ++++++------- src/logic/scripting/lua/lua_util.hpp | 8 ++++ src/logic/scripting/scripting.cpp | 43 ++++++++++++++++++- src/logic/scripting/scripting.hpp | 6 +++ src/maths/Heightmap.hpp | 34 +++++++++++++++ src/world/generator/GeneratorDef.hpp | 15 ++++++- 12 files changed, 196 insertions(+), 51 deletions(-) rename res/generators/{default.lua => flat.lua} (60%) create mode 100644 src/files/util.hpp create mode 100644 src/maths/Heightmap.hpp diff --git a/res/generators/default.lua b/res/generators/flat.lua similarity index 60% rename from res/generators/default.lua rename to res/generators/flat.lua index defe9131..2d8c47b4 100644 --- a/res/generators/default.lua +++ b/res/generators/flat.lua @@ -1,24 +1,21 @@ -local W = 256 -local H = 256 - -function generate_heightmap(x, y) - local umap = Heightmap(W, H) - local vmap = Heightmap(W, H) +function generate_heightmap(x, y, w, h) + local umap = Heightmap(w, h) + local vmap = Heightmap(w, h) umap:noise({x+521, y+73}, 0.05, 1, 20.8) umap:noise({x+51, y+75}, 0.05, 1, 21.8) - umap:noise({x+521, y+70}, 0.1, 3, 35.8) + vmap:noise({x+521, y+70}, 0.1, 3, 35.8) vmap:noise({x+95, y+246}, 0.15, 3, 35.8) - local bmap = Heightmap(W, H) + local bmap = Heightmap(w, h) bmap:noise({x+3, y+6}, 0.1, 1, 3) - local map = Heightmap(W, H) + local map = Heightmap(w, h) map:noise({x, y}, 0.06, 5, 0.2, umap, vmap) map:noise({x, y}, 0.12, 6, 0.5, umap, vmap) map:mul(bmap) map:mul(0.7) - local rivermap = Heightmap(W, H) + local rivermap = Heightmap(w, h) rivermap:noise({x+21, y+12}, 0.1, 3) rivermap:abs() rivermap:min(0.5) @@ -31,6 +28,3 @@ function generate_heightmap(x, y) return map end - -local map = generate_heightmap(0, 0) -map:dump("heightmap.png") diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 96a5eadf..fef55cdb 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -376,6 +376,18 @@ void ContentLoader::loadEntity( if (fs::exists(configFile)) loadEntity(def, full, configFile); } + +void ContentLoader::loadGenerator( + GeneratorDef& def, const std::string& full, const std::string& name +) { + auto folder = pack->folder; + auto generatorFile = folder / fs::path("generators/" + name + ".lua"); + if (!fs::exists(generatorFile)) { + return; + } + def.script = scripting::load_generator(generatorFile); +} + void ContentLoader::loadBlock( Block& def, const std::string& full, const std::string& name ) { @@ -450,6 +462,21 @@ void ContentLoader::load() { ); } + fs::path generatorsDir = folder / fs::u8path("generators"); + if (fs::is_directory(generatorsDir)) { + for (const auto& entry : fs::directory_iterator(generatorsDir)) { + const auto& file = entry.path(); + + std::string name = file.stem().u8string(); + auto [packid, full, filename] = + create_unit_id(pack->id, file.stem().u8string()); + + auto& def = builder.generators.create(full); + + loadGenerator(def, full, name); + } + } + if (!fs::is_regular_file(pack->getContentFile())) return; auto root = files::read_json(pack->getContentFile()); @@ -486,7 +513,6 @@ void ContentLoader::load() { if (auto entitiesarr = root->list("entities")) { for (size_t i = 0; i < entitiesarr->size(); i++) { std::string name = entitiesarr->str(i); - auto [packid, full, filename] = create_unit_id(pack->id, name); auto& def = builder.entities.create(full); @@ -499,7 +525,7 @@ void ContentLoader::load() { fs::path materialsDir = folder / fs::u8path("block_materials"); if (fs::is_directory(materialsDir)) { for (const auto& entry : fs::directory_iterator(materialsDir)) { - const fs::path& file = entry.path(); + const auto& file = entry.path(); auto [packid, full, filename] = create_unit_id(pack->id, file.stem().u8string()); loadBlockMaterial( @@ -512,7 +538,7 @@ void ContentLoader::load() { fs::path skeletonsDir = folder / fs::u8path("skeletons"); if (fs::is_directory(skeletonsDir)) { for (const auto& entry : fs::directory_iterator(skeletonsDir)) { - const fs::path& file = entry.path(); + const auto& file = entry.path(); std::string name = pack->id + ":" + file.stem().u8string(); std::string text = files::read_string(file); builder.add( diff --git a/src/content/ContentLoader.hpp b/src/content/ContentLoader.hpp index 0673a96d..0a65f640 100644 --- a/src/content/ContentLoader.hpp +++ b/src/content/ContentLoader.hpp @@ -13,6 +13,7 @@ struct BlockMaterial; struct ItemDef; struct EntityDef; struct ContentPack; +struct GeneratorDef; class ContentBuilder; class ContentPackRuntime; @@ -39,6 +40,9 @@ class ContentLoader { void loadEntity( EntityDef& def, const std::string& full, const std::string& name ); + void loadGenerator( + GeneratorDef& def, const std::string& full, const std::string& name + ); static void loadCustomBlockModel(Block& def, dynamic::Map* primitives); static void loadBlockMaterial(BlockMaterial& def, const fs::path& file); diff --git a/src/files/util.hpp b/src/files/util.hpp new file mode 100644 index 00000000..5967299e --- /dev/null +++ b/src/files/util.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +namespace files { + inline bool is_valid_name(std::string_view name) { + static std::string illegalChars = "\\/%?!<>:; "; + for (char c : illegalChars) { + if (name.find(c) != std::string::npos) { + return false; + } + } + return !name.empty(); + } +} diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index 885052ce..951d52cf 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -4,6 +4,7 @@ #include #include "lua_commons.hpp" +#include "maths/Heightmap.hpp" namespace lua { class Userdata { @@ -27,37 +28,42 @@ namespace lua { } static int createMetatable(lua::State*); - inline static std::string TYPENAME = "bytearray"; + inline static std::string TYPENAME = "Bytearray"; }; - class Heightmap : public Userdata { - std::vector buffer; - uint width, height; + class LuaHeightmap : public Userdata { + std::shared_ptr map; public: - Heightmap(uint width, uint height); - virtual ~Heightmap(); + LuaHeightmap(uint width, uint height) + : map(std::make_shared(width, height)) {} + + virtual ~LuaHeightmap() = default; uint getWidth() const { - return width; + return map->getWidth(); } uint getHeight() const { - return height; + return map->getHeight(); + } + + float* getValues() { + return map->getValues(); + } + + const float* getValues() const { + return map->getValues(); } const std::string& getTypeName() const override { return TYPENAME; } - float* getValues() { - return buffer.data(); - } - - const float* getValues() const { - return buffer.data(); + std::shared_ptr getHeightmap() const { + return map; } static int createMetatable(lua::State*); - inline static std::string TYPENAME = "heightmap"; + inline static std::string TYPENAME = "Heightmap"; }; } diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 8008517a..879ec4c6 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -97,7 +97,7 @@ void lua::initialize() { initialize_libs_extends(L); newusertype(L, "Bytearray"); - newusertype(L, "Heightmap"); + newusertype(L, "Heightmap"); } void lua::finalize() { diff --git a/src/logic/scripting/lua/lua_type_heightmap.cpp b/src/logic/scripting/lua/lua_type_heightmap.cpp index ecbdc1bf..357011f3 100644 --- a/src/logic/scripting/lua/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/lua_type_heightmap.cpp @@ -8,6 +8,7 @@ #include "util/functional_util.hpp" #include "maths/FastNoiseLite.h" #include "coders/png.hpp" +#include "files/util.hpp" #include "graphics/core/ImageData.hpp" #include "lua_util.hpp" @@ -15,16 +16,12 @@ using namespace lua; static fnl_state noise = fnlCreateState(); -Heightmap::Heightmap(uint width, uint height) : width(width), height(height) { - buffer.resize(width*height); -} - -Heightmap::~Heightmap() { -} - static int l_dump(lua::State* L) { - if (auto heightmap = touserdata(L, 1)) { + if (auto heightmap = touserdata(L, 1)) { auto filename = tostring(L, 2); + if (!files::is_valid_name(filename)) { + throw std::runtime_error("invalid file name"); + } uint w = heightmap->getWidth(); uint h = heightmap->getHeight(); ImageData image(ImageFormat::rgb888, w, h); @@ -46,7 +43,7 @@ static int l_dump(lua::State* L) { } static int l_noise(lua::State* L) { - if (auto heightmap = touserdata(L, 1)) { + if (auto heightmap = touserdata(L, 1)) { uint w = heightmap->getWidth(); uint h = heightmap->getHeight(); auto heights = heightmap->getValues(); @@ -62,13 +59,13 @@ static int l_noise(lua::State* L) { if (gettop(L) > 4) { multiplier = tonumber(L, 5); } - const Heightmap* shiftMapX = nullptr; - const Heightmap* shiftMapY = nullptr; + const LuaHeightmap* shiftMapX = nullptr; + const LuaHeightmap* shiftMapY = nullptr; if (gettop(L) > 5) { - shiftMapX = touserdata(L, 6); + shiftMapX = touserdata(L, 6); } if (gettop(L) > 6) { - shiftMapY = touserdata(L, 7); + shiftMapY = touserdata(L, 7); } for (uint y = 0; y < h; y++) { for (uint x = 0; x < w; x++) { @@ -159,13 +156,13 @@ static int l_meta_meta_call(lua::State* L) { if (width <= 0 || height <= 0) { throw std::runtime_error("width and height must be greather than 0"); } - return newuserdata( + return newuserdata( L, static_cast(width), static_cast(height) ); } static int l_meta_index(lua::State* L) { - auto map = touserdata(L, 1); + auto map = touserdata(L, 1); if (map == nullptr) { return 0; } @@ -186,7 +183,7 @@ static int l_meta_index(lua::State* L) { } static int l_meta_tostring(lua::State* L) { - auto map = touserdata(L, 1); + auto map = touserdata(L, 1); if (map == nullptr) { return 0; } @@ -201,7 +198,7 @@ static int l_meta_tostring(lua::State* L) { ); } -int Heightmap::createMetatable(lua::State* L) { +int LuaHeightmap::createMetatable(lua::State* L) { createtable(L, 0, 2); pushcfunction(L, lua::wrap); setfield(L, "__tostring"); diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index 0822e5bc..ce77c429 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -153,6 +153,14 @@ namespace lua { return 3; } + template + inline int pushivec_stack(lua::State* L, glm::vec vec) { + for (int i = 0; i < n; i++) { + pushinteger(L, vec[i]); + } + return n; + } + inline int pushivec3_stack(lua::State* L, glm::ivec3 vec) { pushinteger(L, vec.x); pushinteger(L, vec.y); diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index aeedc1d9..03267e20 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -14,6 +14,9 @@ #include "items/ItemDef.hpp" #include "logic/BlocksController.hpp" #include "logic/LevelController.hpp" +#include "lua/lua_engine.hpp" +#include "lua/lua_custom_types.hpp" +#include "maths/Heightmap.hpp" #include "objects/Entities.hpp" #include "objects/EntityDef.hpp" #include "objects/Player.hpp" @@ -21,7 +24,7 @@ #include "util/timeutil.hpp" #include "voxels/Block.hpp" #include "world/Level.hpp" -#include "lua/lua_engine.hpp" +#include "world/generator/GeneratorDef.hpp" using namespace scripting; @@ -62,6 +65,15 @@ void scripting::initialize(Engine* engine) { return std::make_shared(0); } +[[nodiscard]] scriptenv scripting::create_environment() { + auto L = lua::get_main_thread(); + int id = lua::create_environment(L, 0); + return std::shared_ptr(new int(id), [=](int* id) { //-V508 + lua::removeEnvironment(L, *id); + delete id; + }); +} + [[nodiscard]] scriptenv scripting::create_pack_environment( const ContentPack& pack ) { @@ -672,6 +684,35 @@ void scripting::load_entity_component( lua::store_in(L, lua::CHUNKS_TABLE, name); } +class LuaGeneratorScript : public GeneratorScript { + scriptenv env; +public: + LuaGeneratorScript(scriptenv env) : env(std::move(env)) {} + + std::shared_ptr generateHeightmap( + const glm::ivec2& offset, const glm::ivec2& size + ) override { + auto L = lua::get_main_thread(); + lua::pushenv(L, *env); + if (lua::getfield(L, "generate_heightmap")) { + lua::pushivec_stack(L, offset); + lua::pushivec_stack(L, size); + if (lua::call_nothrow(L, 4)) { + return lua::touserdata(L, -1)->getHeightmap(); + } + } + return std::make_shared(size.x, size.y); + } +}; + +std::unique_ptr scripting::load_generator( + const fs::path& file +) { + auto env = create_environment(); + load_script(*env, "generator", file); + return std::make_unique(std::move(env)); +} + void scripting::load_world_script( const scriptenv& senv, const std::string& prefix, diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index 94723f05..85fd8323 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -32,6 +32,7 @@ class BlocksController; class LevelController; class Entity; struct EntityDef; +class GeneratorScript; namespace scripting { extern Engine* engine; @@ -50,6 +51,7 @@ namespace scripting { scriptenv get_root_environment(); scriptenv create_pack_environment(const ContentPack& pack); + scriptenv create_environment(); scriptenv create_doc_environment( const scriptenv& parent, const std::string& name ); @@ -144,6 +146,10 @@ namespace scripting { void load_entity_component(const std::string& name, const fs::path& file); + std::unique_ptr load_generator( + const fs::path& file + ); + /// @brief Load package-specific world script /// @param env environment /// @param packid content-pack id diff --git a/src/maths/Heightmap.hpp b/src/maths/Heightmap.hpp new file mode 100644 index 00000000..cd125d36 --- /dev/null +++ b/src/maths/Heightmap.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +#include "typedefs.hpp" + +class Heightmap { + std::vector buffer; + uint width, height; +public: + Heightmap(uint width, uint height) + : width(width), height(height) { + buffer.resize(width*height); + } + + ~Heightmap() = default; + + uint getWidth() const { + return width; + } + + uint getHeight() const { + return height; + } + + float* getValues() { + return buffer.data(); + } + + const float* getValues() const { + return buffer.data(); + } +}; diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index fa26787f..608d0749 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -1,10 +1,23 @@ #pragma once #include +#include #include "typedefs.hpp" +#include "maths/Heightmap.hpp" + +class GeneratorScript { +public: + virtual ~GeneratorScript() = default; + + virtual std::shared_ptr generateHeightmap( + const glm::ivec2& offset, const glm::ivec2& size) = 0; +}; struct GeneratorDef { std::string name; - scriptenv env; + std::unique_ptr script; + + GeneratorDef(std::string name) : name(std::move(name)) {} + GeneratorDef(const GeneratorDef&) = delete; }; From ae5671364af3c2dd5fea94ac06b07a905bd6e8da Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 14 Aug 2024 18:44:32 +0300 Subject: [PATCH 010/139] add heightmap-based test generation --- res/generators/default.lua | 15 ++ res/generators/flat.lua | 30 --- src/content/Content.hpp | 4 + src/engine.cpp | 9 - src/logic/ChunksController.cpp | 9 +- src/logic/scripting/lua/libcore.cpp | 12 +- src/logic/scripting/lua/lua_custom_types.hpp | 2 +- .../scripting/lua/lua_type_heightmap.cpp | 8 +- src/logic/scripting/scripting.cpp | 5 +- src/voxels/DefaultWorldGenerator.cpp | 241 ------------------ src/voxels/DefaultWorldGenerator.hpp | 15 -- src/voxels/FlatWorldGenerator.cpp | 28 -- src/voxels/FlatWorldGenerator.hpp | 15 -- src/voxels/WorldGenerator.cpp | 39 ++- src/voxels/WorldGenerator.hpp | 23 +- src/world/World.cpp | 5 +- src/world/WorldGenerators.cpp | 29 --- src/world/WorldGenerators.hpp | 33 --- 18 files changed, 82 insertions(+), 440 deletions(-) create mode 100644 res/generators/default.lua delete mode 100644 res/generators/flat.lua delete mode 100644 src/voxels/DefaultWorldGenerator.cpp delete mode 100644 src/voxels/DefaultWorldGenerator.hpp delete mode 100644 src/voxels/FlatWorldGenerator.cpp delete mode 100644 src/voxels/FlatWorldGenerator.hpp delete mode 100644 src/world/WorldGenerators.cpp delete mode 100644 src/world/WorldGenerators.hpp diff --git a/res/generators/default.lua b/res/generators/default.lua new file mode 100644 index 00000000..564339b0 --- /dev/null +++ b/res/generators/default.lua @@ -0,0 +1,15 @@ +function generate_heightmap(x, y, w, h) + local umap = Heightmap(w, h) + local vmap = Heightmap(w, h) + umap:noise({x+521, y+73}, 0.05, 1, 20.8) + umap:noise({x+51, y+75}, 0.05, 1, 21.8) + vmap:noise({x+521, y+70}, 0.1, 3, 35.8) + vmap:noise({x+95, y+246}, 0.15, 3, 35.8) + + local map = Heightmap(w, h) + map:noise({x, y}, 0.02, 6, 0.2) + map:noise({x, y}, 0.06, 7, 0.5, umap, vmap) + map:mul(0.3) + map:add(0.3) + return map +end diff --git a/res/generators/flat.lua b/res/generators/flat.lua deleted file mode 100644 index 2d8c47b4..00000000 --- a/res/generators/flat.lua +++ /dev/null @@ -1,30 +0,0 @@ -function generate_heightmap(x, y, w, h) - local umap = Heightmap(w, h) - local vmap = Heightmap(w, h) - umap:noise({x+521, y+73}, 0.05, 1, 20.8) - umap:noise({x+51, y+75}, 0.05, 1, 21.8) - vmap:noise({x+521, y+70}, 0.1, 3, 35.8) - vmap:noise({x+95, y+246}, 0.15, 3, 35.8) - - local bmap = Heightmap(w, h) - bmap:noise({x+3, y+6}, 0.1, 1, 3) - - local map = Heightmap(w, h) - map:noise({x, y}, 0.06, 5, 0.2, umap, vmap) - map:noise({x, y}, 0.12, 6, 0.5, umap, vmap) - map:mul(bmap) - map:mul(0.7) - - local rivermap = Heightmap(w, h) - rivermap:noise({x+21, y+12}, 0.1, 3) - rivermap:abs() - rivermap:min(0.5) - rivermap:mul(2.0) - rivermap:pow(0.6) - map:add(1.2) - map:mul(rivermap) - map:add(-1.0) - map:mul(0.5) - - return map -end diff --git a/src/content/Content.hpp b/src/content/Content.hpp index 8e152a51..06d4fb47 100644 --- a/src/content/Content.hpp +++ b/src/content/Content.hpp @@ -120,6 +120,10 @@ public: } return *found->second; } + + const auto& getDefs() const { + return defs; + } }; class ResourceIndices { diff --git a/src/engine.cpp b/src/engine.cpp index 787a257b..6a98e1d3 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -30,13 +30,10 @@ #include "logic/scripting/scripting.hpp" #include "util/listutil.hpp" #include "util/platform.hpp" -#include "voxels/DefaultWorldGenerator.hpp" -#include "voxels/FlatWorldGenerator.hpp" #include "window/Camera.hpp" #include "window/Events.hpp" #include "window/input.hpp" #include "window/Window.hpp" -#include "world/WorldGenerators.hpp" #include "settings.hpp" #include @@ -50,11 +47,6 @@ static debug::Logger logger("engine"); namespace fs = std::filesystem; -static void add_world_generators() { - WorldGenerators::addGenerator("core:default"); - WorldGenerators::addGenerator("core:flat"); -} - static void create_channel(Engine* engine, std::string name, NumberSetting& setting) { if (name != "master") { audio::create_channel(name); @@ -114,7 +106,6 @@ Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, Engin keepAlive(settings.ui.language.observe([=](auto lang) { setLanguage(lang); }, true)); - add_world_generators(); scripting::initialize(this); basePacks = files::read_list(resdir/fs::path("config/builtins.list")); diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 66d9f539..29d9e65f 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -18,7 +18,6 @@ #include "voxels/WorldGenerator.hpp" #include "world/Level.hpp" #include "world/World.hpp" -#include "world/WorldGenerators.hpp" const uint MAX_WORK_PER_FRAME = 128; const uint MIN_SURROUNDING = 9; @@ -28,10 +27,10 @@ ChunksController::ChunksController(Level* level, uint padding) chunks(level->chunks.get()), lighting(level->lighting.get()), padding(padding), - generator(WorldGenerators::createGenerator( - level->getWorld()->getGenerator(), level->content - )) { -} + generator(std::make_unique( + level->content->generators.require(level->getWorld()->getGenerator()), + level->content + )) {} ChunksController::~ChunksController() = default; diff --git a/src/logic/scripting/lua/libcore.cpp b/src/logic/scripting/lua/libcore.cpp index 2e1034a0..a1f4ee20 100644 --- a/src/logic/scripting/lua/libcore.cpp +++ b/src/logic/scripting/lua/libcore.cpp @@ -3,6 +3,7 @@ #include "constants.hpp" #include "engine.hpp" +#include "content/Content.hpp" #include "files/engine_paths.hpp" #include "files/settings_io.hpp" #include "frontend/menu.hpp" @@ -11,8 +12,8 @@ #include "logic/LevelController.hpp" #include "window/Events.hpp" #include "window/Window.hpp" +#include "voxels/WorldGenerator.hpp" #include "world/Level.hpp" -#include "world/WorldGenerators.hpp" #include "api_lua.hpp" using namespace scripting; @@ -173,18 +174,19 @@ static int l_quit(lua::State*) { /// @brief Get the default world generator /// @return The ID of the default world generator static int l_get_default_generator(lua::State* L) { - return lua::pushstring(L, WorldGenerators::getDefaultGeneratorID()); + return lua::pushstring(L, WorldGenerator::DEFAULT); } /// @brief Get a list of all world generators /// @return A table with the IDs of all world generators static int l_get_generators(lua::State* L) { - const auto& generators = WorldGenerators::getGeneratorsIDs(); + const auto& generators = content->generators.getDefs(); lua::createtable(L, generators.size(), 0); int i = 0; - for (auto& id : generators) { - lua::pushstring(L, id); + for (auto& [name, _] : generators) { + std::cout << name << std::endl; + lua::pushstring(L, name); lua::rawseti(L, i + 1); i++; } diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index 951d52cf..1e6406b6 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -37,7 +37,7 @@ namespace lua { LuaHeightmap(uint width, uint height) : map(std::make_shared(width, height)) {} - virtual ~LuaHeightmap() = default; + virtual ~LuaHeightmap(); uint getWidth() const { return map->getWidth(); diff --git a/src/logic/scripting/lua/lua_type_heightmap.cpp b/src/logic/scripting/lua/lua_type_heightmap.cpp index 357011f3..506177ea 100644 --- a/src/logic/scripting/lua/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/lua_type_heightmap.cpp @@ -6,6 +6,7 @@ #include #include "util/functional_util.hpp" +#define FNL_IMPL #include "maths/FastNoiseLite.h" #include "coders/png.hpp" #include "files/util.hpp" @@ -16,6 +17,9 @@ using namespace lua; static fnl_state noise = fnlCreateState(); +LuaHeightmap::~LuaHeightmap() {; +} + static int l_dump(lua::State* L) { if (auto heightmap = touserdata(L, 1)) { auto filename = tostring(L, 2); @@ -95,7 +99,7 @@ static int l_noise(lua::State* L) { template class Op> static int l_binop_func(lua::State* L) { Op op; - if (auto heightmap = touserdata(L, 1)) { + if (auto heightmap = touserdata(L, 1)) { uint w = heightmap->getWidth(); uint h = heightmap->getHeight(); auto heights = heightmap->getValues(); @@ -124,7 +128,7 @@ static int l_binop_func(lua::State* L) { template class Op> static int l_unaryop_func(lua::State* L) { Op op; - if (auto heightmap = touserdata(L, 1)) { + if (auto heightmap = touserdata(L, 1)) { uint w = heightmap->getWidth(); uint h = heightmap->getHeight(); auto heights = heightmap->getValues(); diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 03267e20..b50880bd 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -698,9 +698,12 @@ public: lua::pushivec_stack(L, offset); lua::pushivec_stack(L, size); if (lua::call_nothrow(L, 4)) { - return lua::touserdata(L, -1)->getHeightmap(); + auto map = lua::touserdata(L, -1)->getHeightmap(); + lua::pop(L, 2); + return map; } } + lua::pop(L); return std::make_shared(size.x, size.y); } }; diff --git a/src/voxels/DefaultWorldGenerator.cpp b/src/voxels/DefaultWorldGenerator.cpp deleted file mode 100644 index dc5361e8..00000000 --- a/src/voxels/DefaultWorldGenerator.cpp +++ /dev/null @@ -1,241 +0,0 @@ -#include "DefaultWorldGenerator.hpp" - -#include "Block.hpp" -#include "Chunk.hpp" -#include "voxel.hpp" - -#define FNL_IMPL -#include -#include - -#include -#include -#include -#include -#include - -#include "content/Content.hpp" -#include "core_defs.hpp" -#include "maths/FastNoiseLite.h" -#include "maths/util.hpp" -#include "maths/voxmaths.hpp" - -// will be refactored in generators update - -const int SEA_LEVEL = 55; - -enum class MAPS { SAND, TREE, CLIFF, HEIGHT }; -#define MAPS_LEN 4 - -class Map2D { - int x, z; - int w, d; - float* heights[MAPS_LEN]; -public: - Map2D(int x, int z, int w, int d) : x(x), z(z), w(w), d(d) { - for (int i = 0; i < MAPS_LEN; i++) heights[i] = new float[w * d]; - } - ~Map2D() { - for (int i = 0; i < MAPS_LEN; i++) delete[] heights[i]; - } - - inline float get(MAPS map, int x, int z) { - x -= this->x; - z -= this->z; - if (x < 0 || z < 0 || x >= w || z >= d) { - throw std::runtime_error("out of heightmap"); - } - return heights[(int)map][z * w + x]; - } - - inline void set(MAPS map, int x, int z, float value) { - x -= this->x; - z -= this->z; - if (x < 0 || z < 0 || x >= w || z >= d) { - throw std::runtime_error("out of heightmap"); - } - heights[(int)map][z * w + x] = value; - } -}; - -float calc_height(fnl_state* noise, int cur_x, int cur_z) { - float height = 0; - - height += fnlGetNoise2D( - noise, cur_x * 0.0125f * 8 - 125567, cur_z * 0.0125f * 8 + 3546 - ); - height += fnlGetNoise2D( - noise, cur_x * 0.025f * 8 + 4647, cur_z * 0.025f * 8 - 3436 - ) * - 0.5f; - height += fnlGetNoise2D( - noise, cur_x * 0.05f * 8 - 834176, cur_z * 0.05f * 8 + 23678 - ) * - 0.25f; - height += - fnlGetNoise2D( - noise, - cur_x * 0.2f * 8 + - fnlGetNoise2D( - noise, cur_x * 0.1f * 8 - 23557, cur_z * 0.1f * 8 - 6568 - ) * 50, - cur_z * 0.2f * 8 + - fnlGetNoise2D( - noise, cur_x * 0.1f * 8 + 4363, cur_z * 0.1f * 8 + 4456 - ) * 50 - ) * - fnlGetNoise2D(noise, cur_x * 0.01f - 834176, cur_z * 0.01f + 23678) * - 0.25; - height += - fnlGetNoise2D(noise, cur_x * 0.1f * 8 - 3465, cur_z * 0.1f * 8 + 4534) * - 0.125f; - height *= - fnlGetNoise2D(noise, cur_x * 0.1f + 1000, cur_z * 0.1f + 1000) * 0.5f + - 0.5f; - height += 1.0f; - height *= 64.0f; - return height; -} - -int generate_tree( - fnl_state* noise, - PseudoRandom* random, - Map2D& heights, - // Map2D& humidity, - int cur_x, - int cur_y, - int cur_z, - int tileSize, - blockid_t idWood, - blockid_t idLeaves -) { - const int tileX = floordiv(cur_x, tileSize); - const int tileZ = floordiv(cur_z, tileSize); - - random->setSeed(tileX * 4325261 + tileZ * 12160951 + tileSize * 9431111); - - int randomX = (random->rand() % (tileSize / 2)) - tileSize / 4; - int randomZ = (random->rand() % (tileSize / 2)) - tileSize / 4; - - int centerX = tileX * tileSize + tileSize / 2 + randomX; - int centerZ = tileZ * tileSize + tileSize / 2 + randomZ; - - bool gentree = - (random->rand() % 10) < heights.get(MAPS::TREE, centerX, centerZ) * 13; - if (!gentree) return 0; - - int height = (int)(heights.get(MAPS::HEIGHT, centerX, centerZ)); - if (height < SEA_LEVEL + 1) return 0; - int lx = cur_x - centerX; - int radius = random->rand() % 4 + 2; - int ly = cur_y - height - 3 * radius; - int lz = cur_z - centerZ; - if (lx == 0 && lz == 0 && cur_y - height < (3 * radius + radius / 2)) - return idWood; - if (lx * lx + ly * ly / 2 + lz * lz < radius * radius) return idLeaves; - return 0; -} - -void DefaultWorldGenerator::generate(voxel* voxels, int cx, int cz, int seed) { - const int treesTile = 12; - fnl_state noise = fnlCreateState(); - noise.noise_type = FNL_NOISE_OPENSIMPLEX2; - noise.seed = seed * 60617077 % 25896307; - PseudoRandom randomtree; - PseudoRandom randomgrass; - - int padding = 8; - Map2D heights( - cx * CHUNK_W - padding, - cz * CHUNK_D - padding, - CHUNK_W + padding * 2, - CHUNK_D + padding * 2 - ); - - for (int z = -padding; z < CHUNK_D + padding; z++) { - for (int x = -padding; x < CHUNK_W + padding; x++) { - int cur_x = x + cx * CHUNK_W; - int cur_z = z + cz * CHUNK_D; - float height = calc_height(&noise, cur_x, cur_z); - float hum = fnlGetNoise2D(&noise, cur_x * 0.3 + 633, cur_z * 0.3); - float sand = - fnlGetNoise2D(&noise, cur_x * 0.1 - 633, cur_z * 0.1 + 1000); - float cliff = pow((sand + abs(sand)) / 2, 2); - float w = pow(fmax(-abs(height - SEA_LEVEL) + 4, 0) / 6, 2) * cliff; - float h1 = -abs(height - SEA_LEVEL - 0.03); - float h2 = abs(height - SEA_LEVEL + 0.04); - float h = (h1 + h2) * 100; - height += (h * w); - heights.set(MAPS::HEIGHT, cur_x, cur_z, height); - heights.set(MAPS::TREE, cur_x, cur_z, hum); - heights.set(MAPS::SAND, cur_x, cur_z, sand); - heights.set(MAPS::CLIFF, cur_x, cur_z, cliff); - } - } - - for (int z = 0; z < CHUNK_D; z++) { - int cur_z = z + cz * CHUNK_D; - for (int x = 0; x < CHUNK_W; x++) { - int cur_x = x + cx * CHUNK_W; - float height = heights.get(MAPS::HEIGHT, cur_x, cur_z); - - for (int cur_y = 0; cur_y < CHUNK_H; cur_y++) { - // int cur_y = y; - int id = cur_y < SEA_LEVEL ? idWater : BLOCK_AIR; - blockstate state {}; - if ((cur_y == (int)height) && (SEA_LEVEL - 2 < cur_y)) { - id = idGrassBlock; - } else if (cur_y < (height - 6)) { - id = idStone; - } else if (cur_y < height) { - id = idDirt; - } else { - int tree = generate_tree( - &noise, - &randomtree, - heights, - cur_x, - cur_y, - cur_z, - treesTile, - idWood, - idLeaves - ); - if (tree) { - id = tree; - state.rotation = BLOCK_DIR_UP; - } - } - float sand = fmax( - heights.get(MAPS::SAND, cur_x, cur_z), - heights.get(MAPS::CLIFF, cur_x, cur_z) - ); - if (((height - (1.1 - 0.2 * pow(height - 54, 4)) + (5 * sand)) < - cur_y + (height - 0.01 - (int)height)) && - (cur_y < height)) { - id = idSand; - } - if (cur_y <= 2) id = idBazalt; - - randomgrass.setSeed(cur_x, cur_z); - if ((id == 0) && ((height > SEA_LEVEL + 0.4) || (sand > 0.1)) && - ((int)(height + 1) == cur_y) && - ((unsigned short)randomgrass.rand() > 56000)) { - id = idGrass; - } - if ((id == 0) && (height > SEA_LEVEL + 0.4) && - ((int)(height + 1) == cur_y) && - ((unsigned short)randomgrass.rand() > 65000)) { - id = idFlower; - } - if ((height > SEA_LEVEL + 1) && ((int)(height + 1) == cur_y) && - ((unsigned short)randomgrass.rand() > 65533)) { - id = idWood; - state.rotation = BLOCK_DIR_UP; - } - voxels[(cur_y * CHUNK_D + z) * CHUNK_W + x].id = id; - voxels[(cur_y * CHUNK_D + z) * CHUNK_W + x].state = state; - } - } - } -} diff --git a/src/voxels/DefaultWorldGenerator.hpp b/src/voxels/DefaultWorldGenerator.hpp deleted file mode 100644 index a53770c8..00000000 --- a/src/voxels/DefaultWorldGenerator.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "typedefs.hpp" -#include "WorldGenerator.hpp" - -struct voxel; -class Content; - -class DefaultWorldGenerator : WorldGenerator { -public: - DefaultWorldGenerator(const Content* content) : WorldGenerator(content) { - } - - void generate(voxel* voxels, int x, int z, int seed); -}; diff --git a/src/voxels/FlatWorldGenerator.cpp b/src/voxels/FlatWorldGenerator.cpp deleted file mode 100644 index d97ab545..00000000 --- a/src/voxels/FlatWorldGenerator.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "FlatWorldGenerator.hpp" - -#include "content/Content.hpp" -#include "core_defs.hpp" -#include "Chunk.hpp" -#include "voxel.hpp" - -void FlatWorldGenerator::generate(voxel* voxels, int cx, int cz, int seed) { - for (int z = 0; z < CHUNK_D; z++) { - for (int x = 0; x < CHUNK_W; x++) { - for (int cur_y = 0; cur_y < CHUNK_H; cur_y++) { - int id = BLOCK_AIR; - blockstate state {}; - - if (cur_y == 2) { - id = idBazalt; - } else if (cur_y == 6) { - id = idGrassBlock; - } else if (cur_y > 2 && cur_y <= 5) { - id = idDirt; - } - - voxels[(cur_y * CHUNK_D + z) * CHUNK_W + x].id = id; - voxels[(cur_y * CHUNK_D + z) * CHUNK_W + x].state = state; - } - } - } -} diff --git a/src/voxels/FlatWorldGenerator.hpp b/src/voxels/FlatWorldGenerator.hpp deleted file mode 100644 index 8d10ae4b..00000000 --- a/src/voxels/FlatWorldGenerator.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "typedefs.hpp" -#include "WorldGenerator.hpp" - -struct voxel; -class Content; - -class FlatWorldGenerator : WorldGenerator { -public: - FlatWorldGenerator(const Content* content) : WorldGenerator(content) { - } - - void generate(voxel* voxels, int x, int z, int seed); -}; diff --git a/src/voxels/WorldGenerator.cpp b/src/voxels/WorldGenerator.cpp index 525a04ca..4217edd5 100644 --- a/src/voxels/WorldGenerator.cpp +++ b/src/voxels/WorldGenerator.cpp @@ -4,16 +4,33 @@ #include "Block.hpp" #include "Chunk.hpp" #include "voxel.hpp" +#include "world/generator/GeneratorDef.hpp" -WorldGenerator::WorldGenerator(const Content* content) - : idStone(content->blocks.require("base:stone").rt.id), - idDirt(content->blocks.require("base:dirt").rt.id), - idGrassBlock(content->blocks.require("base:grass_block").rt.id), - idSand(content->blocks.require("base:sand").rt.id), - idWater(content->blocks.require("base:water").rt.id), - idWood(content->blocks.require("base:wood").rt.id), - idLeaves(content->blocks.require("base:leaves").rt.id), - idGrass(content->blocks.require("base:grass").rt.id), - idFlower(content->blocks.require("base:flower").rt.id), - idBazalt(content->blocks.require("base:bazalt").rt.id) { +WorldGenerator::WorldGenerator( + const GeneratorDef& def, + const Content* content +) : def(def), content(content) { +} + +#include "util/timeutil.hpp" +void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ, int seed) { + timeutil::ScopeLogTimer log(555); + auto heightmap = def.script->generateHeightmap( + {chunkX*CHUNK_W, chunkZ*CHUNK_D}, {CHUNK_W, CHUNK_D} + ); + auto& baseStone = content->blocks.require("base:stone"); + auto& baseWater = content->blocks.require("base:water"); + for (uint z = 0; z < CHUNK_D; z++) { + for (uint x = 0; x < CHUNK_W; x++) { + auto height = heightmap->getValues()[z * CHUNK_W + x] * 255 + 10; + for (uint y = 0; y < CHUNK_H; y++) { + voxels[vox_index(x, y, z)].state = {}; + if (y > height) { + voxels[vox_index(x, y, z)].id = y <= 64 ? baseWater.rt.id : 0; + } else { + voxels[vox_index(x, y, z)].id = baseStone.rt.id; + } + } + } + } } diff --git a/src/voxels/WorldGenerator.hpp b/src/voxels/WorldGenerator.hpp index 7a8c98f3..6f3f22fc 100644 --- a/src/voxels/WorldGenerator.hpp +++ b/src/voxels/WorldGenerator.hpp @@ -6,22 +6,19 @@ struct voxel; class Content; +class GeneratorDef; class WorldGenerator { -protected: - blockid_t const idStone; - blockid_t const idDirt; - blockid_t const idGrassBlock; - blockid_t const idSand; - blockid_t const idWater; - blockid_t const idWood; - blockid_t const idLeaves; - blockid_t const idGrass; - blockid_t const idFlower; - blockid_t const idBazalt; + const GeneratorDef& def; + const Content* content; public: - WorldGenerator(const Content* content); + WorldGenerator( + const GeneratorDef& def, + const Content* content + ); virtual ~WorldGenerator() = default; - virtual void generate(voxel* voxels, int x, int z, int seed) = 0; + virtual void generate(voxel* voxels, int x, int z, int seed); + + inline static std::string DEFAULT = "core:default"; }; diff --git a/src/world/World.cpp b/src/world/World.cpp index f8358960..a694d146 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -15,7 +15,8 @@ #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" #include "voxels/ChunksStorage.hpp" -#include "WorldGenerators.hpp" +#include "voxels/WorldGenerator.hpp" +#include "world/generator/GeneratorDef.hpp" #include "Level.hpp" static debug::Logger logger("world"); @@ -205,7 +206,7 @@ void WorldInfo::deserialize(dynamic::Map* root) { seed = root->get("seed", seed); if (generator.empty()) { - generator = WorldGenerators::getDefaultGeneratorID(); + generator = WorldGenerator::DEFAULT; } if (auto verobj = root->map("version")) { verobj->num("major", major); diff --git a/src/world/WorldGenerators.cpp b/src/world/WorldGenerators.cpp deleted file mode 100644 index c64ab756..00000000 --- a/src/world/WorldGenerators.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "WorldGenerators.hpp" - -#include - -#include "content/Content.hpp" -#include "voxels/FlatWorldGenerator.hpp" -#include "voxels/WorldGenerator.hpp" - -std::vector WorldGenerators::getGeneratorsIDs() { - std::vector ids; - for (auto& entry : generators) { - ids.push_back(entry.first); - } - return ids; -} - -std::string WorldGenerators::getDefaultGeneratorID() { - return "core:default"; -} - -std::unique_ptr WorldGenerators::createGenerator( - const std::string& id, const Content* content -) { - auto found = generators.find(id); - if (found == generators.end()) { - throw std::runtime_error("unknown generator id: " + id); - } - return std::unique_ptr(found->second(content)); -} diff --git a/src/world/WorldGenerators.hpp b/src/world/WorldGenerators.hpp deleted file mode 100644 index 91c3a121..00000000 --- a/src/world/WorldGenerators.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "voxels/WorldGenerator.hpp" - -class Content; - -typedef WorldGenerator* (*gen_constructor)(const Content*); - -class WorldGenerators { - static inline std::map generators; -public: - template - static void addGenerator(std::string id); - - static std::vector getGeneratorsIDs(); - - static std::string getDefaultGeneratorID(); - - static std::unique_ptr createGenerator( - const std::string& id, const Content* content - ); -}; - -template -void WorldGenerators::addGenerator(std::string id) { - generators[id] = [](const Content* content) { - return (WorldGenerator*)new T(content); - }; -} From eab1519597b60039984e583b360a43d605e57a55 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 14 Aug 2024 19:15:17 +0300 Subject: [PATCH 011/139] fix GeneratorDef forward declaration --- src/voxels/WorldGenerator.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/voxels/WorldGenerator.hpp b/src/voxels/WorldGenerator.hpp index 6f3f22fc..98cb955c 100644 --- a/src/voxels/WorldGenerator.hpp +++ b/src/voxels/WorldGenerator.hpp @@ -6,7 +6,7 @@ struct voxel; class Content; -class GeneratorDef; +struct GeneratorDef; class WorldGenerator { const GeneratorDef& def; From 347a01e0c20ad6f23c97d71a5812061d61692972 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 15 Aug 2024 03:38:32 +0300 Subject: [PATCH 012/139] fix heightmaps binary operations --- res/generators/default.lua | 17 ++++++++++++++--- src/logic/scripting/lua/lua_type_heightmap.cpp | 3 +-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 564339b0..5163e0f8 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -7,9 +7,20 @@ function generate_heightmap(x, y, w, h) vmap:noise({x+95, y+246}, 0.15, 3, 35.8) local map = Heightmap(w, h) - map:noise({x, y}, 0.02, 6, 0.2) - map:noise({x, y}, 0.06, 7, 0.5, umap, vmap) - map:mul(0.3) + map:noise({x, y}, 0.02, 7, 0.2) + map:noise({x, y}, 0.06, 8, 0.5, umap, vmap) + map:mul(0.5) map:add(0.3) + map:pow(2.0) + + local rivermap = Heightmap(w, h) + rivermap:noise({x+21, y+12}, 0.03, 4) + rivermap:abs() + rivermap:mul(2.0) + rivermap:pow(0.18) + map:add(0.4) + map:mul(rivermap) + map:add(-0.2) + return map end diff --git a/src/logic/scripting/lua/lua_type_heightmap.cpp b/src/logic/scripting/lua/lua_type_heightmap.cpp index 506177ea..b2555103 100644 --- a/src/logic/scripting/lua/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/lua_type_heightmap.cpp @@ -113,7 +113,7 @@ static int l_binop_func(lua::State* L) { } } } else { - auto map = touserdata(L, 2); + auto map = touserdata(L, 2); for (uint y = 0; y < h; y++) { for (uint x = 0; x < w; x++) { uint i = y * w + x; @@ -132,7 +132,6 @@ static int l_unaryop_func(lua::State* L) { uint w = heightmap->getWidth(); uint h = heightmap->getHeight(); auto heights = heightmap->getValues(); - float power = tonumber(L, 2); for (uint y = 0; y < h; y++) { for (uint x = 0; x < w; x++) { uint i = y * w + x; From 33a73a0014e7525e2334a1f37b4e4060aa330e69 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 16 Aug 2024 01:20:35 +0300 Subject: [PATCH 013/139] fix rivers depth --- res/generators/default.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 5163e0f8..991a54f8 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -8,16 +8,17 @@ function generate_heightmap(x, y, w, h) local map = Heightmap(w, h) map:noise({x, y}, 0.02, 7, 0.2) - map:noise({x, y}, 0.06, 8, 0.5, umap, vmap) + map:noise({x, y}, 0.06, 8, 0.4, umap, vmap) map:mul(0.5) - map:add(0.3) + map:add(0.1) map:pow(2.0) local rivermap = Heightmap(w, h) - rivermap:noise({x+21, y+12}, 0.03, 4) + rivermap:noise({x+21, y+12}, 0.1, 4) rivermap:abs() rivermap:mul(2.0) - rivermap:pow(0.18) + rivermap:pow(0.4) + rivermap:max(0.6) map:add(0.4) map:mul(rivermap) map:add(-0.2) From 30925df319944fbda95194596f8d6170c8aa9cf3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 16 Aug 2024 18:39:44 +0300 Subject: [PATCH 014/139] move WorldGenerator to world/generator --- src/logic/ChunksController.cpp | 2 +- src/logic/scripting/lua/libcore.cpp | 2 +- src/voxels/Chunks.cpp | 1 - src/world/World.cpp | 2 +- src/{voxels => world/generator}/WorldGenerator.cpp | 6 +++--- src/{voxels => world/generator}/WorldGenerator.hpp | 0 6 files changed, 6 insertions(+), 7 deletions(-) rename src/{voxels => world/generator}/WorldGenerator.cpp (93%) rename src/{voxels => world/generator}/WorldGenerator.hpp (100%) diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 29d9e65f..d84ed8d4 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -15,9 +15,9 @@ #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" #include "voxels/ChunksStorage.hpp" -#include "voxels/WorldGenerator.hpp" #include "world/Level.hpp" #include "world/World.hpp" +#include "world/generator/WorldGenerator.hpp" const uint MAX_WORK_PER_FRAME = 128; const uint MIN_SURROUNDING = 9; diff --git a/src/logic/scripting/lua/libcore.cpp b/src/logic/scripting/lua/libcore.cpp index a1f4ee20..3ab665c2 100644 --- a/src/logic/scripting/lua/libcore.cpp +++ b/src/logic/scripting/lua/libcore.cpp @@ -12,7 +12,7 @@ #include "logic/LevelController.hpp" #include "window/Events.hpp" #include "window/Window.hpp" -#include "voxels/WorldGenerator.hpp" +#include "world/generator/WorldGenerator.hpp" #include "world/Level.hpp" #include "api_lua.hpp" diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 1dbfd5a1..6725074a 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -20,7 +20,6 @@ #include "world/LevelEvents.hpp" #include "Block.hpp" #include "Chunk.hpp" -#include "WorldGenerator.hpp" #include "voxel.hpp" Chunks::Chunks( diff --git a/src/world/World.cpp b/src/world/World.cpp index a694d146..daab3ada 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -15,7 +15,7 @@ #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" #include "voxels/ChunksStorage.hpp" -#include "voxels/WorldGenerator.hpp" +#include "world/generator/WorldGenerator.hpp" #include "world/generator/GeneratorDef.hpp" #include "Level.hpp" diff --git a/src/voxels/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp similarity index 93% rename from src/voxels/WorldGenerator.cpp rename to src/world/generator/WorldGenerator.cpp index 4217edd5..41454fff 100644 --- a/src/voxels/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -1,9 +1,9 @@ #include "WorldGenerator.hpp" #include "content/Content.hpp" -#include "Block.hpp" -#include "Chunk.hpp" -#include "voxel.hpp" +#include "voxels/Block.hpp" +#include "voxels/Chunk.hpp" +#include "voxels/voxel.hpp" #include "world/generator/GeneratorDef.hpp" WorldGenerator::WorldGenerator( diff --git a/src/voxels/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp similarity index 100% rename from src/voxels/WorldGenerator.hpp rename to src/world/generator/WorldGenerator.hpp From 2a767a36382e65431e0d9da2e03e5e28c8067c49 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 17 Aug 2024 18:11:52 +0300 Subject: [PATCH 015/139] add generator layers --- res/generators/default.lua | 7 +++ src/content/ContentBuilder.cpp | 4 ++ src/content/ContentLoader.cpp | 6 ++- src/logic/scripting/lua/lua_util.hpp | 11 ++++- src/logic/scripting/scripting.cpp | 60 +++++++++++++++++++++++++- src/world/generator/GeneratorDef.hpp | 18 ++++++++ src/world/generator/WorldGenerator.cpp | 33 +++++++++----- 7 files changed, 125 insertions(+), 14 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 991a54f8..6ecf5f48 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -1,3 +1,10 @@ +layers = { + {block="base:grass_block", height=1}, + {block="base:dirt", height=1}, + {block="base:stone", height=-1}, + {block="base:bazalt", height=1}, +} + function generate_heightmap(x, y, w, h) local umap = Heightmap(w, h) local vmap = Heightmap(w, h) diff --git a/src/content/ContentBuilder.cpp b/src/content/ContentBuilder.cpp index b637532e..46d4f909 100644 --- a/src/content/ContentBuilder.cpp +++ b/src/content/ContentBuilder.cpp @@ -88,5 +88,9 @@ std::unique_ptr ContentBuilder::build() { def->rt.placingBlock = content->blocks.require(def->placingBlock).rt.id; } + for (auto& [name, def] : content->generators.getDefs()) { + def->script->prepare(content.get()); + } + return content; } diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index fef55cdb..c08e1c43 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -473,7 +473,11 @@ void ContentLoader::load() { auto& def = builder.generators.create(full); - loadGenerator(def, full, name); + try { + loadGenerator(def, full, name); + } catch (const std::runtime_error& err) { + throw std::runtime_error("generator '"+full+"': "+err.what()); + } } } diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index ce77c429..f9fadecd 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -496,17 +496,26 @@ namespace lua { } int pushvalue(lua::State*, const dynamic::Value& value); + + [[nodiscard]] dynamic::Value tovalue(lua::State*, int idx); inline bool getfield(lua::State* L, const std::string& name, int idx = -1) { lua_getfield(L, idx, name.c_str()); - if (isnil(L, -1)) { + if (isnil(L, idx)) { pop(L); return false; } return true; } + inline int requirefield(lua::State* L, const std::string& name, int idx = -1) { + if (getfield(L, name, idx)) { + return 1; + } + throw std::runtime_error("object has no member '"+name+"'"); + } + inline bool hasfield(lua::State* L, const std::string& name, int idx = -1) { lua_getfield(L, idx, name.c_str()); if (isnil(L, -1)) { diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index b50880bd..51345fbb 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -686,8 +686,11 @@ void scripting::load_entity_component( class LuaGeneratorScript : public GeneratorScript { scriptenv env; + std::vector layers; + uint lastLayersHeight; public: - LuaGeneratorScript(scriptenv env) : env(std::move(env)) {} + LuaGeneratorScript(scriptenv env, std::vector layers, uint lastLayersHeight) + : env(std::move(env)), layers(std::move(layers)), lastLayersHeight(lastLayersHeight) {} std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size @@ -706,6 +709,20 @@ public: lua::pop(L); return std::make_shared(size.x, size.y); } + + void prepare(const Content* content) override { + for (auto& layer : layers) { + layer.rt.id = content->blocks.require(layer.block).rt.id; + } + } + + const std::vector& getLayers() const override { + return layers; + } + + uint getLastLayersHeight() const override { + return lastLayersHeight; + } }; std::unique_ptr scripting::load_generator( @@ -713,7 +730,46 @@ std::unique_ptr scripting::load_generator( ) { auto env = create_environment(); load_script(*env, "generator", file); - return std::make_unique(std::move(env)); + + auto L = lua::get_main_thread(); + lua::pushenv(L, *env); + + uint lastLayersHeight = 0; + bool hasResizeableLayer = false; + + std::vector layers; + if (lua::getfield(L, "layers")) { + int len = lua::objlen(L, -1); + for (int i = 1; i <= len; i++) { + try { + lua::rawgeti(L, i); + lua::requirefield(L, "block"); + auto name = lua::require_string(L, -1); + lua::pop(L); + lua::requirefield(L, "height"); + int height = lua::tointeger(L, -1); + lua::pop(L, 2); + + if (hasResizeableLayer) { + lastLayersHeight += height; + } + if (height == -1) { + if (hasResizeableLayer) { + throw std::runtime_error("only one resizeable layer allowed"); + } + hasResizeableLayer = true; + } + layers.push_back(BlocksLayer {name, height, {}}); + } catch (const std::runtime_error& err) { + lua::pop(L, 2); + throw std::runtime_error( + "layer #"+std::to_string(i)+": "+err.what()); + } + } + lua::pop(L); + } + lua::pop(L); + return std::make_unique(std::move(env), std::move(layers), lastLayersHeight); } void scripting::load_world_script( diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 608d0749..5bfd6717 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -6,12 +6,30 @@ #include "typedefs.hpp" #include "maths/Heightmap.hpp" +class Content; + +struct BlocksLayer { + std::string block; + int height; + + struct { + blockid_t id; + } rt; +}; + class GeneratorScript { public: virtual ~GeneratorScript() = default; virtual std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size) = 0; + + virtual const std::vector& getLayers() const = 0; + + /// @brief Total height of all layers after resizeable one + virtual uint getLastLayersHeight() const = 0; + + virtual void prepare(const Content* content) = 0; }; struct GeneratorDef { diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 41454fff..d1b3884f 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -1,5 +1,7 @@ #include "WorldGenerator.hpp" +#include + #include "content/Content.hpp" #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" @@ -14,21 +16,32 @@ WorldGenerator::WorldGenerator( #include "util/timeutil.hpp" void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ, int seed) { - timeutil::ScopeLogTimer log(555); auto heightmap = def.script->generateHeightmap( {chunkX*CHUNK_W, chunkZ*CHUNK_D}, {CHUNK_W, CHUNK_D} ); - auto& baseStone = content->blocks.require("base:stone"); - auto& baseWater = content->blocks.require("base:water"); + timeutil::ScopeLogTimer log(555); + auto values = heightmap->getValues(); + const auto& layers = def.script->getLayers(); + uint lastLayersHeight = def.script->getLastLayersHeight(); + auto baseWater = content->blocks.require("base:water").rt.id; + + std::memset(voxels, 0, sizeof(voxel) * CHUNK_VOL); + for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { - auto height = heightmap->getValues()[z * CHUNK_W + x] * 255 + 10; - for (uint y = 0; y < CHUNK_H; y++) { - voxels[vox_index(x, y, z)].state = {}; - if (y > height) { - voxels[vox_index(x, y, z)].id = y <= 64 ? baseWater.rt.id : 0; - } else { - voxels[vox_index(x, y, z)].id = baseStone.rt.id; + int height = values[z * CHUNK_W + x] * 255 + 10; + for (uint y = height+1; y < 64; y++) { + voxels[vox_index(x, y, z)].id = baseWater; + } + + uint y = height; + for (const auto& layer : layers) { + uint layerHeight = layer.height; + if (layerHeight == -1) { + layerHeight = y - lastLayersHeight + 1; + } + for (uint i = 0; i < layerHeight; i++, y--) { + voxels[vox_index(x, y, z)].id = layer.rt.id; } } } From c5877684eb61cbb032fcf189009e8c731bd6e6f7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 17 Aug 2024 18:44:08 +0300 Subject: [PATCH 016/139] add 'below_sea_level' layer property --- res/generators/default.lua | 4 ++-- src/logic/scripting/scripting.cpp | 10 ++++++++-- src/world/generator/GeneratorDef.hpp | 1 + src/world/generator/WorldGenerator.cpp | 13 +++++++++++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 6ecf5f48..c35470a9 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -1,6 +1,6 @@ layers = { - {block="base:grass_block", height=1}, - {block="base:dirt", height=1}, + {block="base:grass_block", height=1, below_sea_level=false}, + {block="base:dirt", height=5, below_sea_level=false}, {block="base:stone", height=-1}, {block="base:bazalt", height=1}, } diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 51345fbb..dd497b90 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -748,7 +748,13 @@ std::unique_ptr scripting::load_generator( lua::pop(L); lua::requirefield(L, "height"); int height = lua::tointeger(L, -1); - lua::pop(L, 2); + lua::pop(L); + bool below_sea_level = true; + if (lua::getfield(L, "below_sea_level")) { + below_sea_level = lua::toboolean(L, -1); + lua::pop(L); + } + lua::pop(L); if (hasResizeableLayer) { lastLayersHeight += height; @@ -759,7 +765,7 @@ std::unique_ptr scripting::load_generator( } hasResizeableLayer = true; } - layers.push_back(BlocksLayer {name, height, {}}); + layers.push_back(BlocksLayer {name, height, below_sea_level, {}}); } catch (const std::runtime_error& err) { lua::pop(L, 2); throw std::runtime_error( diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 5bfd6717..263f8b4f 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -11,6 +11,7 @@ class Content; struct BlocksLayer { std::string block; int height; + bool below_sea_level; struct { blockid_t id; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index d1b3884f..eb88c0f0 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -16,10 +16,11 @@ WorldGenerator::WorldGenerator( #include "util/timeutil.hpp" void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ, int seed) { + timeutil::ScopeLogTimer log(555); auto heightmap = def.script->generateHeightmap( {chunkX*CHUNK_W, chunkZ*CHUNK_D}, {CHUNK_W, CHUNK_D} ); - timeutil::ScopeLogTimer log(555); + uint seaLevel = 64; auto values = heightmap->getValues(); const auto& layers = def.script->getLayers(); uint lastLayersHeight = def.script->getLastLayersHeight(); @@ -30,19 +31,27 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ, int seed) { for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { int height = values[z * CHUNK_W + x] * 255 + 10; - for (uint y = height+1; y < 64; y++) { + for (uint y = height+1; y <= seaLevel; y++) { voxels[vox_index(x, y, z)].id = baseWater; } uint y = height; + uint layerExtension = 0; for (const auto& layer : layers) { + if (y < seaLevel && !layer.below_sea_level) { + layerExtension = std::max(0, layer.height); + continue; + } uint layerHeight = layer.height; if (layerHeight == -1) { layerHeight = y - lastLayersHeight + 1; + } else { + layerHeight += layerExtension; } for (uint i = 0; i < layerHeight; i++, y--) { voxels[vox_index(x, y, z)].id = layer.rt.id; } + layerExtension = 0; } } } From 510c45ffe94dc219b26ae91aa15c5b68bec822b1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 17 Aug 2024 19:06:14 +0300 Subject: [PATCH 017/139] add sea_level variable --- res/generators/default.lua | 2 + src/logic/scripting/scripting.cpp | 99 +++++++++++++++++--------- src/world/generator/GeneratorDef.hpp | 2 + src/world/generator/WorldGenerator.cpp | 4 +- 4 files changed, 70 insertions(+), 37 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index c35470a9..a28b2e33 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -1,3 +1,5 @@ +sea_level = 64 + layers = { {block="base:grass_block", height=1, below_sea_level=false}, {block="base:dirt", height=5, below_sea_level=false}, diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index dd497b90..8705fb50 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -630,12 +630,13 @@ int scripting::get_values_on_stack() { return lua::gettop(lua::get_main_thread()); } -static void load_script( +[[nodiscard]] +static int load_script( int env, const std::string& type, const fs::path& file ) { std::string src = files::read_string(file); logger.info() << "script (" << type << ") " << file.u8string(); - lua::execute(lua::get_main_thread(), env, src, file.u8string()); + return lua::execute(lua::get_main_thread(), env, src, file.u8string()); } void scripting::load_block_script( @@ -645,7 +646,7 @@ void scripting::load_block_script( block_funcs_set& funcsset ) { int env = *senv; - load_script(env, "block", file); + lua::pop(lua::get_main_thread(), load_script(env, "block", file)); funcsset.init = register_event(env, "init", prefix + ".init"); funcsset.update = register_event(env, "on_update", prefix + ".update"); funcsset.randupdate = @@ -665,7 +666,7 @@ void scripting::load_item_script( item_funcs_set& funcsset ) { int env = *senv; - load_script(env, "item", file); + lua::pop(lua::get_main_thread(), load_script(env, "item", file)); funcsset.init = register_event(env, "init", prefix + ".init"); funcsset.on_use = register_event(env, "on_use", prefix + ".use"); funcsset.on_use_on_block = @@ -688,9 +689,18 @@ class LuaGeneratorScript : public GeneratorScript { scriptenv env; std::vector layers; uint lastLayersHeight; + uint seaLevel; public: - LuaGeneratorScript(scriptenv env, std::vector layers, uint lastLayersHeight) - : env(std::move(env)), layers(std::move(layers)), lastLayersHeight(lastLayersHeight) {} + LuaGeneratorScript( + scriptenv env, + std::vector layers, + uint lastLayersHeight, + uint seaLevel) + : env(std::move(env)), + layers(std::move(layers)), + lastLayersHeight(lastLayersHeight), + seaLevel(seaLevel) + {} std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size @@ -723,17 +733,55 @@ public: uint getLastLayersHeight() const override { return lastLayersHeight; } + + uint getSeaLevel() const override { + return seaLevel; + } }; +static BlocksLayer load_layer( + lua::State* L, int idx, uint& lastLayersHeight, bool& hasResizeableLayer +) { + lua::requirefield(L, "block"); + auto name = lua::require_string(L, -1); + lua::pop(L); + lua::requirefield(L, "height"); + int height = lua::tointeger(L, -1); + lua::pop(L); + bool belowSeaLevel = true; + if (lua::getfield(L, "below_sea_level")) { + belowSeaLevel = lua::toboolean(L, -1); + lua::pop(L); + } + + if (hasResizeableLayer) { + lastLayersHeight += height; + } + if (height == -1) { + if (hasResizeableLayer) { + throw std::runtime_error("only one resizeable layer allowed"); + } + hasResizeableLayer = true; + } + return BlocksLayer {name, height, belowSeaLevel, {}}; +} + std::unique_ptr scripting::load_generator( const fs::path& file ) { auto env = create_environment(); - load_script(*env, "generator", file); - auto L = lua::get_main_thread(); + + lua::pop(L, load_script(*env, "generator", file)); + lua::pushenv(L, *env); + uint seaLevel = 0; + if (lua::getfield(L, "sea_level")) { + seaLevel = lua::tointeger(L, -1); + lua::pop(L); + } + uint lastLayersHeight = 0; bool hasResizeableLayer = false; @@ -741,41 +789,22 @@ std::unique_ptr scripting::load_generator( if (lua::getfield(L, "layers")) { int len = lua::objlen(L, -1); for (int i = 1; i <= len; i++) { + lua::rawgeti(L, i); try { - lua::rawgeti(L, i); - lua::requirefield(L, "block"); - auto name = lua::require_string(L, -1); - lua::pop(L); - lua::requirefield(L, "height"); - int height = lua::tointeger(L, -1); - lua::pop(L); - bool below_sea_level = true; - if (lua::getfield(L, "below_sea_level")) { - below_sea_level = lua::toboolean(L, -1); - lua::pop(L); - } - lua::pop(L); - - if (hasResizeableLayer) { - lastLayersHeight += height; - } - if (height == -1) { - if (hasResizeableLayer) { - throw std::runtime_error("only one resizeable layer allowed"); - } - hasResizeableLayer = true; - } - layers.push_back(BlocksLayer {name, height, below_sea_level, {}}); + layers.push_back( + load_layer(L, -1, lastLayersHeight, hasResizeableLayer)); } catch (const std::runtime_error& err) { lua::pop(L, 2); throw std::runtime_error( "layer #"+std::to_string(i)+": "+err.what()); } + lua::pop(L); } lua::pop(L); } lua::pop(L); - return std::make_unique(std::move(env), std::move(layers), lastLayersHeight); + return std::make_unique( + std::move(env), std::move(layers), lastLayersHeight, seaLevel); } void scripting::load_world_script( @@ -785,7 +814,7 @@ void scripting::load_world_script( world_funcs_set& funcsset ) { int env = *senv; - load_script(env, "world", file); + lua::pop(lua::get_main_thread(), load_script(env, "world", file)); register_event(env, "init", prefix + ".init"); register_event(env, "on_world_open", prefix + ".worldopen"); register_event(env, "on_world_tick", prefix + ".worldtick"); @@ -804,7 +833,7 @@ void scripting::load_layout_script( uidocscript& script ) { int env = *senv; - load_script(env, "layout", file); + lua::pop(lua::get_main_thread(), load_script(env, "layout", file)); script.onopen = register_event(env, "on_open", prefix + ".open"); script.onprogress = register_event(env, "on_progress", prefix + ".progress"); diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 263f8b4f..4b90997d 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -30,6 +30,8 @@ public: /// @brief Total height of all layers after resizeable one virtual uint getLastLayersHeight() const = 0; + virtual uint getSeaLevel() const = 0; + virtual void prepare(const Content* content) = 0; }; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index eb88c0f0..6c72aac0 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -20,17 +20,17 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ, int seed) { auto heightmap = def.script->generateHeightmap( {chunkX*CHUNK_W, chunkZ*CHUNK_D}, {CHUNK_W, CHUNK_D} ); - uint seaLevel = 64; auto values = heightmap->getValues(); const auto& layers = def.script->getLayers(); uint lastLayersHeight = def.script->getLastLayersHeight(); + uint seaLevel = def.script->getSeaLevel(); auto baseWater = content->blocks.require("base:water").rt.id; std::memset(voxels, 0, sizeof(voxel) * CHUNK_VOL); for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { - int height = values[z * CHUNK_W + x] * 255 + 10; + int height = values[z * CHUNK_W + x] * CHUNK_H; for (uint y = height+1; y <= seaLevel; y++) { voxels[vox_index(x, y, z)].id = baseWater; } From e560236a8cb0c19c8a3103cee48332f9294b8632 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 17 Aug 2024 20:17:52 +0300 Subject: [PATCH 018/139] add heightmap write-only property 'noiseSeed' --- res/generators/default.lua | 6 +++- src/logic/scripting/lua/lua_custom_types.hpp | 12 +++++-- .../scripting/lua/lua_type_heightmap.cpp | 32 ++++++++++++++++--- src/logic/scripting/scripting.cpp | 5 +-- src/world/generator/GeneratorDef.hpp | 2 +- src/world/generator/WorldGenerator.cpp | 10 ++++-- src/world/generator/WorldGenerator.hpp | 2 +- 7 files changed, 56 insertions(+), 13 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index a28b2e33..38c33ed0 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -7,15 +7,18 @@ layers = { {block="base:bazalt", height=1}, } -function generate_heightmap(x, y, w, h) +function generate_heightmap(x, y, w, h, seed) local umap = Heightmap(w, h) local vmap = Heightmap(w, h) + umap.noiseSeed = seed + vmap.noiseSeed = seed umap:noise({x+521, y+73}, 0.05, 1, 20.8) umap:noise({x+51, y+75}, 0.05, 1, 21.8) vmap:noise({x+521, y+70}, 0.1, 3, 35.8) vmap:noise({x+95, y+246}, 0.15, 3, 35.8) local map = Heightmap(w, h) + map.noiseSeed = seed map:noise({x, y}, 0.02, 7, 0.2) map:noise({x, y}, 0.06, 8, 0.4, umap, vmap) map:mul(0.5) @@ -23,6 +26,7 @@ function generate_heightmap(x, y, w, h) map:pow(2.0) local rivermap = Heightmap(w, h) + rivermap.noiseSeed = seed rivermap:noise({x+21, y+12}, 0.1, 4) rivermap:abs() rivermap:mul(2.0) diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index 1e6406b6..d6a7ba0b 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -6,6 +6,8 @@ #include "lua_commons.hpp" #include "maths/Heightmap.hpp" +struct fnl_state; + namespace lua { class Userdata { public: @@ -33,9 +35,9 @@ namespace lua { class LuaHeightmap : public Userdata { std::shared_ptr map; + std::unique_ptr noise; public: - LuaHeightmap(uint width, uint height) - : map(std::make_shared(width, height)) {} + LuaHeightmap(uint width, uint height); virtual ~LuaHeightmap(); @@ -63,6 +65,12 @@ namespace lua { return map; } + fnl_state* getNoise() { + return noise.get(); + } + + void setSeed(int64_t seed); + static int createMetatable(lua::State*); inline static std::string TYPENAME = "Heightmap"; }; diff --git a/src/logic/scripting/lua/lua_type_heightmap.cpp b/src/logic/scripting/lua/lua_type_heightmap.cpp index b2555103..4fc7d278 100644 --- a/src/logic/scripting/lua/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/lua_type_heightmap.cpp @@ -15,9 +15,16 @@ using namespace lua; -static fnl_state noise = fnlCreateState(); +LuaHeightmap::LuaHeightmap(uint width, uint height) + : map(std::make_shared(width, height)), + noise(std::make_unique(fnlCreateState())) +{} -LuaHeightmap::~LuaHeightmap() {; +LuaHeightmap::~LuaHeightmap() { +} + +void LuaHeightmap::setSeed(int64_t seed) { + noise->seed = seed; } static int l_dump(lua::State* L) { @@ -51,6 +58,7 @@ static int l_noise(lua::State* L) { uint w = heightmap->getWidth(); uint h = heightmap->getHeight(); auto heights = heightmap->getValues(); + auto noise = heightmap->getNoise(); auto offset = tovec<2>(L, 2); @@ -86,7 +94,7 @@ static int l_noise(lua::State* L) { v += shiftMapY->getValues()[i]; } - value += fnlGetNoise2D(&noise, u, v) / + value += fnlGetNoise2D(noise, u, v) / static_cast(1 << c) * multiplier; heights[i] = value; } @@ -185,6 +193,20 @@ static int l_meta_index(lua::State* L) { return 0; } +static int l_meta_newindex(lua::State* L) { + auto map = touserdata(L, 1); + if (map == nullptr) { + return 0; + } + if (isstring(L, 2)) { + auto fieldname = tostring(L, 2); + if (!std::strcmp(fieldname, "noiseSeed")) { + map->setSeed(tointeger(L, 3)); + } + } + return 0; +} + static int l_meta_tostring(lua::State* L) { auto map = touserdata(L, 1); if (map == nullptr) { @@ -202,11 +224,13 @@ static int l_meta_tostring(lua::State* L) { } int LuaHeightmap::createMetatable(lua::State* L) { - createtable(L, 0, 2); + createtable(L, 0, 3); pushcfunction(L, lua::wrap); setfield(L, "__tostring"); pushcfunction(L, lua::wrap); setfield(L, "__index"); + pushcfunction(L, lua::wrap); + setfield(L, "__newindex"); createtable(L, 0, 1); pushcfunction(L, lua::wrap); diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 8705fb50..572c2ce2 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -703,14 +703,15 @@ public: {} std::shared_ptr generateHeightmap( - const glm::ivec2& offset, const glm::ivec2& size + const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed ) override { auto L = lua::get_main_thread(); lua::pushenv(L, *env); if (lua::getfield(L, "generate_heightmap")) { lua::pushivec_stack(L, offset); lua::pushivec_stack(L, size); - if (lua::call_nothrow(L, 4)) { + lua::pushinteger(L, seed); + if (lua::call_nothrow(L, 5)) { auto map = lua::touserdata(L, -1)->getHeightmap(); lua::pop(L, 2); return map; diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 4b90997d..afb5eec3 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -23,7 +23,7 @@ public: virtual ~GeneratorScript() = default; virtual std::shared_ptr generateHeightmap( - const glm::ivec2& offset, const glm::ivec2& size) = 0; + const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; virtual const std::vector& getLayers() const = 0; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 6c72aac0..d92050a8 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -15,10 +15,12 @@ WorldGenerator::WorldGenerator( } #include "util/timeutil.hpp" -void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ, int seed) { +void WorldGenerator::generate( + voxel* voxels, int chunkX, int chunkZ, uint64_t seed +) { timeutil::ScopeLogTimer log(555); auto heightmap = def.script->generateHeightmap( - {chunkX*CHUNK_W, chunkZ*CHUNK_D}, {CHUNK_W, CHUNK_D} + {chunkX*CHUNK_W, chunkZ*CHUNK_D}, {CHUNK_W, CHUNK_D}, seed ); auto values = heightmap->getValues(); const auto& layers = def.script->getLayers(); @@ -30,6 +32,7 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ, int seed) { for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { + // generate water int height = values[z * CHUNK_W + x] * CHUNK_H; for (uint y = height+1; y <= seaLevel; y++) { voxels[vox_index(x, y, z)].id = baseWater; @@ -38,12 +41,15 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ, int seed) { uint y = height; uint layerExtension = 0; for (const auto& layer : layers) { + // skip layer if can't be generated under sea level if (y < seaLevel && !layer.below_sea_level) { layerExtension = std::max(0, layer.height); continue; } + uint layerHeight = layer.height; if (layerHeight == -1) { + // resizeable layer layerHeight = y - lastLayersHeight + 1; } else { layerHeight += layerExtension; diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 98cb955c..67be501e 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -18,7 +18,7 @@ public: ); virtual ~WorldGenerator() = default; - virtual void generate(voxel* voxels, int x, int z, int seed); + virtual void generate(voxel* voxels, int x, int z, uint64_t seed); inline static std::string DEFAULT = "core:default"; }; From 8c0a3f426002ed6296a21bb1171d146aef52e0cd Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 17 Aug 2024 22:09:31 +0300 Subject: [PATCH 019/139] add heightmap:resize(int, int, str) --- .../scripting/lua/lua_type_heightmap.cpp | 20 ++++- src/logic/scripting/lua/lua_util.hpp | 7 ++ src/maths/Heightmap.cpp | 77 +++++++++++++++++++ src/maths/Heightmap.hpp | 11 +++ 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 src/maths/Heightmap.cpp diff --git a/src/logic/scripting/lua/lua_type_heightmap.cpp b/src/logic/scripting/lua/lua_type_heightmap.cpp index 4fc7d278..ebc58952 100644 --- a/src/logic/scripting/lua/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/lua_type_heightmap.cpp @@ -53,6 +53,7 @@ static int l_dump(lua::State* L) { return 0; } +template static int l_noise(lua::State* L) { if (auto heightmap = touserdata(L, 1)) { uint w = heightmap->getWidth(); @@ -79,6 +80,7 @@ static int l_noise(lua::State* L) { if (gettop(L) > 6) { shiftMapY = touserdata(L, 7); } + noise->noise_type = noise_type; for (uint y = 0; y < h; y++) { for (uint x = 0; x < w; x++) { uint i = y * w + x; @@ -150,15 +152,31 @@ static int l_unaryop_func(lua::State* L) { return 0; } +static int l_resize(lua::State* L) { + if (auto heightmap = touserdata(L, 1)) { + uint width = touinteger(L, 2); + uint height = touinteger(L, 3); + auto interpName = tostring(L, 4); + auto interpolation = InterpolationType::NEAREST; + if (!std::strcmp(interpName, "linear")) { + interpolation = InterpolationType::LINEAR; + } + heightmap->getHeightmap()->resize(width, height, interpolation); + } + return 0; +} + static std::unordered_map methods { {"dump", lua::wrap}, - {"noise", lua::wrap}, + {"noise", lua::wrap>}, + {"cellnoise", lua::wrap>}, {"pow", lua::wrap>}, {"add", lua::wrap>}, {"mul", lua::wrap>}, {"min", lua::wrap>}, {"max", lua::wrap>}, {"abs", lua::wrap>}, + {"resize", lua::wrap}, }; static int l_meta_meta_call(lua::State* L) { diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index f9fadecd..5c2287b0 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -306,6 +306,13 @@ namespace lua { inline lua::Integer tointeger(lua::State* L, int idx) { return lua_tointeger(L, idx); } + inline uint64_t touinteger(lua::State* L, int idx) { + auto val = lua_tointeger(L, idx); + if (val < 0) { + throw std::runtime_error("negative value"); + } + return static_cast(val); + } inline lua::Number tonumber(lua::State* L, int idx) { return lua_tonumber(L, idx); } diff --git a/src/maths/Heightmap.cpp b/src/maths/Heightmap.cpp new file mode 100644 index 00000000..5b7996a7 --- /dev/null +++ b/src/maths/Heightmap.cpp @@ -0,0 +1,77 @@ +#include "Heightmap.hpp" + +#include +#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, uint height, + uint x, uint y +) { + return buffer[y*width+x]; +} + +static inline float sample_at( + const float* buffer, + uint width, uint height, + float x, float y, + InterpolationType interp +) { + // std::floor is redundant here because x and y are positive values + uint ix = static_cast(x); + uint iy = static_cast(y); + float val = buffer[iy*width+ix]; + if (interp == InterpolationType::NEAREST) { + return val; + } + float tx = x - ix; + float ty = y - iy; + + switch (interp) { + case InterpolationType::LINEAR: { + float s00 = val; + float s10 = sample_at(buffer, width, height, + ix + 1 < width ? ix + 1 : ix, iy); + float s01 = sample_at(buffer, width, height, ix, + iy + 1 < height ? iy + 1 : iy); + float s11 = sample_at(buffer, width, height, + ix + 1 < width ? ix + 1 : ix, iy + 1 < height ? iy + 1 : iy); + + float a00 = s00; + float a10 = s10 - s00; + float a01 = s01 - s00; + float a11 = s11 - s10 - s01 + s00; + + return a00 + a10*tx + a01*ty + a11*tx*ty; + } + // TODO: implement CUBIC (Bicubic) interpolation + default: + throw std::runtime_error("interpolation type is not implemented"); + } + return val; +} + +void Heightmap::resize( + uint dstwidth, uint dstheight, InterpolationType interp +) { + std::vector dst; + dst.resize(dstwidth*dstheight); + + uint index = 0; + for (uint y = 0; y < dstheight; y++) { + for (uint x = 0; x < dstwidth; x++, index++) { + float sx = static_cast(x) / dstwidth * width; + float sy = static_cast(y) / dstheight * height; + dst[index] = sample_at(buffer.data(), width, height, sx, sy, interp); + } + } + + width = dstwidth; + height = dstheight; + buffer = std::move(dst); +} diff --git a/src/maths/Heightmap.hpp b/src/maths/Heightmap.hpp index cd125d36..f8185f4c 100644 --- a/src/maths/Heightmap.hpp +++ b/src/maths/Heightmap.hpp @@ -5,6 +5,12 @@ #include "typedefs.hpp" +enum class InterpolationType { + NEAREST, + LINEAR, + CUBIC, +}; + class Heightmap { std::vector buffer; uint width, height; @@ -14,8 +20,13 @@ public: buffer.resize(width*height); } + Heightmap(uint width, uint height, std::vector buffer) + : width(width), height(height), buffer(std::move(buffer)) {} + ~Heightmap() = default; + void resize(uint width, uint height, InterpolationType interpolation); + uint getWidth() const { return width; } From 8fb0f6a1bbe8dacb815921dfdf9360972582158f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 18 Aug 2024 00:08:36 +0300 Subject: [PATCH 020/139] add basic heightmaps generator optimization --- res/generators/default.lua | 39 +++++++++++++------ .../scripting/lua/lua_type_heightmap.cpp | 14 +++++++ src/maths/Heightmap.cpp | 30 ++++++++++++++ src/maths/Heightmap.hpp | 2 + src/world/generator/WorldGenerator.cpp | 1 + 5 files changed, 74 insertions(+), 12 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 38c33ed0..22f7e9a0 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -7,34 +7,49 @@ layers = { {block="base:bazalt", height=1}, } -function generate_heightmap(x, y, w, h, seed) +local function _generate_heightmap(x, y, w, h, seed, s) local umap = Heightmap(w, h) local vmap = Heightmap(w, h) umap.noiseSeed = seed vmap.noiseSeed = seed - umap:noise({x+521, y+73}, 0.05, 1, 20.8) - umap:noise({x+51, y+75}, 0.05, 1, 21.8) - vmap:noise({x+521, y+70}, 0.1, 3, 35.8) - vmap:noise({x+95, y+246}, 0.15, 3, 35.8) + vmap:noise({x+521, y+70}, 0.1*s, 3, 25.8) + vmap:noise({x+95, y+246}, 0.15*s, 3, 25.8) local map = Heightmap(w, h) map.noiseSeed = seed - map:noise({x, y}, 0.02, 7, 0.2) - map:noise({x, y}, 0.06, 8, 0.4, umap, vmap) + map:noise({x, y}, 0.8*s, 4, 0.04) + map:cellnoise({x, y}, 0.1*s, 3, 0.7, umap, vmap) map:mul(0.5) - map:add(0.1) - map:pow(2.0) + map:add(0.3) local rivermap = Heightmap(w, h) rivermap.noiseSeed = seed - rivermap:noise({x+21, y+12}, 0.1, 4) + rivermap:noise({x+21, y+12}, 0.1*s, 4) rivermap:abs() rivermap:mul(2.0) - rivermap:pow(0.4) + rivermap:pow(0.3) rivermap:max(0.6) map:add(0.4) map:mul(rivermap) - map:add(-0.2) + map:add(-0.15) return map end + +function generate_heightmap(x, y, w, h, seed) + -- blocks per dot + -- 8 - linear interpolation is visible, but not so much (Minecraft) + -- 4 - high quality, but slower + -- 2 - you really don't need it + -- 1 - please have mercy on your CPU + local bpd = 8 + local map = _generate_heightmap( + math.floor(x/bpd), math.floor(y/bpd), + math.floor(w/bpd)+1, math.floor(h/bpd)+1, seed, bpd) + map:resize(w+bpd, h+bpd, 'linear') + map:crop(0, 0, w, h) + return map +end + +local map = generate_heightmap(0, 0, 1024, 1024, 0) +map:dump("heightmap.png") diff --git a/src/logic/scripting/lua/lua_type_heightmap.cpp b/src/logic/scripting/lua/lua_type_heightmap.cpp index ebc58952..e5fd46ac 100644 --- a/src/logic/scripting/lua/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/lua_type_heightmap.cpp @@ -166,6 +166,19 @@ static int l_resize(lua::State* L) { return 0; } +static int l_crop(lua::State* L) { + if (auto heightmap = touserdata(L, 1)) { + uint srcX = touinteger(L, 2); + uint srcY = touinteger(L, 3); + + uint dstWidth = touinteger(L, 4); + uint dstHeight = touinteger(L, 5); + + heightmap->getHeightmap()->crop(srcX, srcY, dstWidth, dstHeight); + } + return 0; +} + static std::unordered_map methods { {"dump", lua::wrap}, {"noise", lua::wrap>}, @@ -177,6 +190,7 @@ static std::unordered_map methods { {"max", lua::wrap>}, {"abs", lua::wrap>}, {"resize", lua::wrap}, + {"crop", lua::wrap}, }; static int l_meta_meta_call(lua::State* L) { diff --git a/src/maths/Heightmap.cpp b/src/maths/Heightmap.cpp index 5b7996a7..165c0040 100644 --- a/src/maths/Heightmap.cpp +++ b/src/maths/Heightmap.cpp @@ -1,6 +1,7 @@ #include "Heightmap.hpp" #include +#include #include #include @@ -59,6 +60,9 @@ static inline float sample_at( void Heightmap::resize( uint dstwidth, uint dstheight, InterpolationType interp ) { + if (width == dstwidth && height == dstheight) { + return; + } std::vector dst; dst.resize(dstwidth*dstheight); @@ -75,3 +79,29 @@ void Heightmap::resize( height = dstheight; buffer = std::move(dst); } + +void Heightmap::crop( + uint srcx, uint srcy, uint dstwidth, uint dstheight +) { + if (srcx + dstwidth > width || srcy + dstheight > height) { + throw std::runtime_error( + "crop zone is not fully inside of the source image"); + } + if (dstwidth == width && dstheight == height) { + return; + } + + std::vector dst; + dst.resize(dstwidth*dstheight); + + for (uint y = 0; y < dstheight; y++) { + std::memcpy( + dst.data()+y*dstwidth, + buffer.data()+(y+srcy)*width+srcx, + dstwidth*sizeof(float)); + } + + width = dstwidth; + height = dstheight; + buffer = std::move(dst); +} diff --git a/src/maths/Heightmap.hpp b/src/maths/Heightmap.hpp index f8185f4c..5b9f4cfa 100644 --- a/src/maths/Heightmap.hpp +++ b/src/maths/Heightmap.hpp @@ -27,6 +27,8 @@ public: void resize(uint width, uint height, InterpolationType interpolation); + void crop(uint srcX, uint srcY, uint dstWidth, uint dstHeight); + uint getWidth() const { return width; } diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index d92050a8..fa25d49d 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -34,6 +34,7 @@ void WorldGenerator::generate( for (uint x = 0; x < CHUNK_W; x++) { // generate water int height = values[z * CHUNK_W + x] * CHUNK_H; + height = std::max(0, height); for (uint y = height+1; y <= seaLevel; y++) { voxels[vox_index(x, y, z)].id = baseWater; } From af485dd7cba63869d6553d59a2f628e5c1d2bd4e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 18 Aug 2024 01:30:59 +0300 Subject: [PATCH 021/139] add sea_layers --- res/generators/default.lua | 9 ++-- src/logic/scripting/scripting.cpp | 43 ++++++++++++++++- src/world/generator/GeneratorDef.hpp | 2 + src/world/generator/WorldGenerator.cpp | 64 +++++++++++++++----------- 4 files changed, 86 insertions(+), 32 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 22f7e9a0..d77fb9f1 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -1,5 +1,9 @@ sea_level = 64 +sea_layers = { + {block="base:water", height=-1}, +} + layers = { {block="base:grass_block", height=1, below_sea_level=false}, {block="base:dirt", height=5, below_sea_level=false}, @@ -20,7 +24,7 @@ local function _generate_heightmap(x, y, w, h, seed, s) map:noise({x, y}, 0.8*s, 4, 0.04) map:cellnoise({x, y}, 0.1*s, 3, 0.7, umap, vmap) map:mul(0.5) - map:add(0.3) + map:add(0.5) local rivermap = Heightmap(w, h) rivermap.noiseSeed = seed @@ -50,6 +54,3 @@ function generate_heightmap(x, y, w, h, seed) map:crop(0, 0, w, h) return map end - -local map = generate_heightmap(0, 0, 1024, 1024, 0) -map:dump("heightmap.png") diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 572c2ce2..334e7923 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -688,17 +688,23 @@ void scripting::load_entity_component( class LuaGeneratorScript : public GeneratorScript { scriptenv env; std::vector layers; + std::vector seaLayers; uint lastLayersHeight; + uint lastSeaLayersHeight; uint seaLevel; public: LuaGeneratorScript( scriptenv env, - std::vector layers, + std::vector layers, + std::vector seaLayers, uint lastLayersHeight, + uint lastSeaLayersHeight, uint seaLevel) : env(std::move(env)), layers(std::move(layers)), + seaLayers(std::move(seaLayers)), lastLayersHeight(lastLayersHeight), + lastSeaLayersHeight(lastSeaLayersHeight), seaLevel(seaLevel) {} @@ -725,15 +731,26 @@ public: for (auto& layer : layers) { layer.rt.id = content->blocks.require(layer.block).rt.id; } + for (auto& layer : seaLayers) { + layer.rt.id = content->blocks.require(layer.block).rt.id; + } } const std::vector& getLayers() const override { return layers; } + const std::vector& getSeaLayers() const override { + return seaLayers; + } + uint getLastLayersHeight() const override { return lastLayersHeight; } + + uint getLastSeaLayersHeight() const override { + return lastSeaLayersHeight; + } uint getSeaLevel() const override { return seaLevel; @@ -784,9 +801,12 @@ std::unique_ptr scripting::load_generator( } uint lastLayersHeight = 0; + uint lastSeaLayersHeight = 0; bool hasResizeableLayer = false; + bool hasResizeableSeaLayer = false; std::vector layers; + std::vector seaLayers; if (lua::getfield(L, "layers")) { int len = lua::objlen(L, -1); for (int i = 1; i <= len; i++) { @@ -803,9 +823,28 @@ std::unique_ptr scripting::load_generator( } lua::pop(L); } + if (lua::getfield(L, "sea_layers")) { + int len = lua::objlen(L, -1); + for (int i = 1; i <= len; i++) { + lua::rawgeti(L, i); + try { + seaLayers.push_back( + load_layer(L, -1, lastSeaLayersHeight, hasResizeableSeaLayer)); + } catch (const std::runtime_error& err) { + lua::pop(L, 2); + throw std::runtime_error( + "sea layer #"+std::to_string(i)+": "+err.what()); + } + lua::pop(L); + } + lua::pop(L); + } lua::pop(L); return std::make_unique( - std::move(env), std::move(layers), lastLayersHeight, seaLevel); + std::move(env), + std::move(layers), std::move(seaLayers), + lastLayersHeight, lastSeaLayersHeight, + seaLevel); } void scripting::load_world_script( diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index afb5eec3..72c8c470 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -26,9 +26,11 @@ public: const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; virtual const std::vector& getLayers() const = 0; + virtual const std::vector& getSeaLayers() const = 0; /// @brief Total height of all layers after resizeable one virtual uint getLastLayersHeight() const = 0; + virtual uint getLastSeaLayersHeight() const = 0; virtual uint getSeaLevel() const = 0; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index fa25d49d..53dc559a 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -1,6 +1,7 @@ #include "WorldGenerator.hpp" #include +#include #include "content/Content.hpp" #include "voxels/Block.hpp" @@ -14,6 +15,37 @@ WorldGenerator::WorldGenerator( ) : def(def), content(content) { } +static inline void generate_pole( + const std::vector& layers, + int height, int bottom, int seaLevel, int lastLayersHeight, + voxel* voxels, + int x, int z +) { + uint y = height; + uint layerExtension = 0; + for (const auto& layer : layers) { + // skip layer if can't be generated under sea level + if (y < seaLevel && !layer.below_sea_level) { + layerExtension = std::max(0, layer.height); + continue; + } + + int layerHeight = layer.height; + if (layerHeight == -1) { + // resizeable layer + layerHeight = y - lastLayersHeight - bottom + 1; + } else { + layerHeight += layerExtension; + } + layerHeight = std::min(static_cast(layerHeight), y); + + for (uint i = 0; i < layerHeight; i++, y--) { + voxels[vox_index(x, y, z)].id = layer.rt.id; + } + layerExtension = 0; + } +} + #include "util/timeutil.hpp" void WorldGenerator::generate( voxel* voxels, int chunkX, int chunkZ, uint64_t seed @@ -24,42 +56,22 @@ void WorldGenerator::generate( ); auto values = heightmap->getValues(); const auto& layers = def.script->getLayers(); + const auto& seaLayers = def.script->getSeaLayers(); + uint lastLayersHeight = def.script->getLastLayersHeight(); + uint lastSeaLayersHeight = def.script->getLastSeaLayersHeight(); + uint seaLevel = def.script->getSeaLevel(); - auto baseWater = content->blocks.require("base:water").rt.id; std::memset(voxels, 0, sizeof(voxel) * CHUNK_VOL); for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { - // generate water int height = values[z * CHUNK_W + x] * CHUNK_H; height = std::max(0, height); - for (uint y = height+1; y <= seaLevel; y++) { - voxels[vox_index(x, y, z)].id = baseWater; - } - uint y = height; - uint layerExtension = 0; - for (const auto& layer : layers) { - // skip layer if can't be generated under sea level - if (y < seaLevel && !layer.below_sea_level) { - layerExtension = std::max(0, layer.height); - continue; - } - - uint layerHeight = layer.height; - if (layerHeight == -1) { - // resizeable layer - layerHeight = y - lastLayersHeight + 1; - } else { - layerHeight += layerExtension; - } - for (uint i = 0; i < layerHeight; i++, y--) { - voxels[vox_index(x, y, z)].id = layer.rt.id; - } - layerExtension = 0; - } + generate_pole(seaLayers, seaLevel, height, seaLevel, lastSeaLayersHeight, voxels, x, z); + generate_pole(layers, height, 0, seaLevel, lastLayersHeight, voxels, x, z); } } } From 2a506e98918f941e7c8c77d804d249455d18d0bf Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 18 Aug 2024 16:54:58 +0300 Subject: [PATCH 022/139] refactor blocks layers --- res/generators/default.lua | 4 +- src/logic/scripting/scripting.cpp | 107 +++++++++++-------------- src/world/generator/GeneratorDef.hpp | 13 +-- src/world/generator/WorldGenerator.cpp | 32 ++++---- 4 files changed, 71 insertions(+), 85 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index d77fb9f1..05df5ce9 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -42,11 +42,11 @@ end function generate_heightmap(x, y, w, h, seed) -- blocks per dot - -- 8 - linear interpolation is visible, but not so much (Minecraft) + -- 8 - linear interpolation is visible, but not so much -- 4 - high quality, but slower -- 2 - you really don't need it -- 1 - please have mercy on your CPU - local bpd = 8 + local bpd = 4 local map = _generate_heightmap( math.floor(x/bpd), math.floor(y/bpd), math.floor(w/bpd)+1, math.floor(h/bpd)+1, seed, bpd) diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 334e7923..d1a94118 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -687,24 +687,18 @@ void scripting::load_entity_component( class LuaGeneratorScript : public GeneratorScript { scriptenv env; - std::vector layers; - std::vector seaLayers; - uint lastLayersHeight; - uint lastSeaLayersHeight; + BlocksLayers groundLayers; + BlocksLayers seaLayers; uint seaLevel; public: LuaGeneratorScript( scriptenv env, - std::vector layers, - std::vector seaLayers, - uint lastLayersHeight, - uint lastSeaLayersHeight, + BlocksLayers groundLayers, + BlocksLayers seaLayers, uint seaLevel) : env(std::move(env)), - layers(std::move(layers)), + groundLayers(std::move(groundLayers)), seaLayers(std::move(seaLayers)), - lastLayersHeight(lastLayersHeight), - lastSeaLayersHeight(lastSeaLayersHeight), seaLevel(seaLevel) {} @@ -728,30 +722,22 @@ public: } void prepare(const Content* content) override { - for (auto& layer : layers) { + for (auto& layer : groundLayers.layers) { layer.rt.id = content->blocks.require(layer.block).rt.id; } - for (auto& layer : seaLayers) { + for (auto& layer : seaLayers.layers) { layer.rt.id = content->blocks.require(layer.block).rt.id; } } - const std::vector& getLayers() const override { - return layers; + const BlocksLayers& getGroundLayers() const override { + return groundLayers; } - const std::vector& getSeaLayers() const override { + const BlocksLayers& getSeaLayers() const override { return seaLayers; } - uint getLastLayersHeight() const override { - return lastLayersHeight; - } - - uint getLastSeaLayersHeight() const override { - return lastSeaLayersHeight; - } - uint getSeaLevel() const override { return seaLevel; } @@ -784,6 +770,32 @@ static BlocksLayer load_layer( return BlocksLayer {name, height, belowSeaLevel, {}}; } +static inline BlocksLayers load_layers( + lua::State* L, const std::string& fieldname +) { + uint lastLayersHeight = 0; + bool hasResizeableLayer = false; + std::vector layers; + + if (lua::getfield(L, fieldname)) { + int len = lua::objlen(L, -1); + for (int i = 1; i <= len; i++) { + lua::rawgeti(L, i); + try { + layers.push_back( + load_layer(L, -1, lastLayersHeight, hasResizeableLayer)); + } catch (const std::runtime_error& err) { + lua::pop(L, 2); + throw std::runtime_error( + fieldname+" #"+std::to_string(i)+": "+err.what()); + } + lua::pop(L); + } + lua::pop(L); + } + return BlocksLayers {std::move(layers), lastLayersHeight}; +} + std::unique_ptr scripting::load_generator( const fs::path& file ) { @@ -800,50 +812,25 @@ std::unique_ptr scripting::load_generator( lua::pop(L); } - uint lastLayersHeight = 0; + uint lastGroundLayersHeight = 0; uint lastSeaLayersHeight = 0; - bool hasResizeableLayer = false; + bool hasResizeableGroundLayer = false; bool hasResizeableSeaLayer = false; - std::vector layers; - std::vector seaLayers; - if (lua::getfield(L, "layers")) { - int len = lua::objlen(L, -1); - for (int i = 1; i <= len; i++) { - lua::rawgeti(L, i); - try { - layers.push_back( - load_layer(L, -1, lastLayersHeight, hasResizeableLayer)); - } catch (const std::runtime_error& err) { - lua::pop(L, 2); - throw std::runtime_error( - "layer #"+std::to_string(i)+": "+err.what()); - } - lua::pop(L); - } - lua::pop(L); - } - if (lua::getfield(L, "sea_layers")) { - int len = lua::objlen(L, -1); - for (int i = 1; i <= len; i++) { - lua::rawgeti(L, i); - try { - seaLayers.push_back( - load_layer(L, -1, lastSeaLayersHeight, hasResizeableSeaLayer)); - } catch (const std::runtime_error& err) { - lua::pop(L, 2); - throw std::runtime_error( - "sea layer #"+std::to_string(i)+": "+err.what()); - } - lua::pop(L); - } + BlocksLayers groundLayers; + BlocksLayers seaLayers; + try { + groundLayers = load_layers(L, "layers"); + seaLayers = load_layers(L, "sea_layers"); + } catch (const std::runtime_error& err) { lua::pop(L); + throw std::runtime_error(file.u8string()+": "+err.what()); } lua::pop(L); return std::make_unique( std::move(env), - std::move(layers), std::move(seaLayers), - lastLayersHeight, lastSeaLayersHeight, + std::move(groundLayers), + std::move(seaLayers), seaLevel); } diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 72c8c470..7852637c 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -18,6 +18,11 @@ struct BlocksLayer { } rt; }; +struct BlocksLayers { + std::vector layers; + uint lastLayersHeight; +}; + class GeneratorScript { public: virtual ~GeneratorScript() = default; @@ -25,12 +30,8 @@ public: virtual std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; - virtual const std::vector& getLayers() const = 0; - virtual const std::vector& getSeaLayers() const = 0; - - /// @brief Total height of all layers after resizeable one - virtual uint getLastLayersHeight() const = 0; - virtual uint getLastSeaLayersHeight() const = 0; + virtual const BlocksLayers& getGroundLayers() const = 0; + virtual const BlocksLayers& getSeaLayers() const = 0; virtual uint getSeaLevel() const = 0; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 53dc559a..0f0dea6e 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -9,31 +9,32 @@ #include "voxels/voxel.hpp" #include "world/generator/GeneratorDef.hpp" -WorldGenerator::WorldGenerator( - const GeneratorDef& def, - const Content* content -) : def(def), content(content) { +WorldGenerator::WorldGenerator(const GeneratorDef& def, const Content* content) + : def(def), content(content) { } static inline void generate_pole( - const std::vector& layers, - int height, int bottom, int seaLevel, int lastLayersHeight, + const BlocksLayers& layers, + int height, + int bottom, + int seaLevel, voxel* voxels, - int x, int z + int x, + int z ) { uint y = height; uint layerExtension = 0; - for (const auto& layer : layers) { + for (const auto& layer : layers.layers) { // skip layer if can't be generated under sea level if (y < seaLevel && !layer.below_sea_level) { layerExtension = std::max(0, layer.height); continue; } - + int layerHeight = layer.height; if (layerHeight == -1) { // resizeable layer - layerHeight = y - lastLayersHeight - bottom + 1; + layerHeight = y - layers.lastLayersHeight - bottom + 1; } else { layerHeight += layerExtension; } @@ -52,15 +53,12 @@ void WorldGenerator::generate( ) { timeutil::ScopeLogTimer log(555); auto heightmap = def.script->generateHeightmap( - {chunkX*CHUNK_W, chunkZ*CHUNK_D}, {CHUNK_W, CHUNK_D}, seed + {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed ); auto values = heightmap->getValues(); - const auto& layers = def.script->getLayers(); + const auto& groundLayers = def.script->getGroundLayers(); const auto& seaLayers = def.script->getSeaLayers(); - uint lastLayersHeight = def.script->getLastLayersHeight(); - uint lastSeaLayersHeight = def.script->getLastSeaLayersHeight(); - uint seaLevel = def.script->getSeaLevel(); std::memset(voxels, 0, sizeof(voxel) * CHUNK_VOL); @@ -70,8 +68,8 @@ void WorldGenerator::generate( int height = values[z * CHUNK_W + x] * CHUNK_H; height = std::max(0, height); - generate_pole(seaLayers, seaLevel, height, seaLevel, lastSeaLayersHeight, voxels, x, z); - generate_pole(layers, height, 0, seaLevel, lastLayersHeight, voxels, x, z); + generate_pole(seaLayers, seaLevel, height, seaLevel, voxels, x, z); + generate_pole(groundLayers, height, 0, seaLevel, voxels, x, z); } } } From 810519fb4d0cfbd7303c8518908deaaeb57a7ccd Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 18 Aug 2024 17:29:02 +0300 Subject: [PATCH 023/139] add docs --- src/world/generator/GeneratorDef.hpp | 19 +++++++++++++++++++ src/world/generator/WorldGenerator.cpp | 4 ++-- src/world/generator/WorldGenerator.hpp | 6 ++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 7852637c..649d3548 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -9,24 +9,40 @@ class Content; struct BlocksLayer { + /// @brief Layer block std::string block; + + /// @brief Layer height. -1 is resizeable layer int height; + + /// @brief Layer can present under the sea level (default: true) else will + /// extend the next layer bool below_sea_level; struct { + /// @brief Layer block index blockid_t id; } rt; }; +/// @brief Set of blocks layers with additional info struct BlocksLayers { std::vector layers; + + /// @brief Total height of all layers after the resizeable one uint lastLayersHeight; }; +/// @brief Generator behaviour and settings interface class GeneratorScript { public: virtual ~GeneratorScript() = default; + /// @brief Generates a heightmap with values in range 0..1 + /// @param offset position of the heightmap top left corner in the world + /// @param size size of the heightmap + /// @param seed world seed + /// @return generated heightmap of given size (can't be nullptr) virtual std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; @@ -35,9 +51,12 @@ public: virtual uint getSeaLevel() const = 0; + /// @brief Build the runtime cache + /// @param content built content virtual void prepare(const Content* content) = 0; }; +/// @brief Generator information struct GeneratorDef { std::string name; std::unique_ptr script; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 0f0dea6e..a8cd1763 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -15,14 +15,14 @@ WorldGenerator::WorldGenerator(const GeneratorDef& def, const Content* content) static inline void generate_pole( const BlocksLayers& layers, - int height, + int top, int bottom, int seaLevel, voxel* voxels, int x, int z ) { - uint y = height; + uint y = top; uint layerExtension = 0; for (const auto& layer : layers.layers) { // skip layer if can't be generated under sea level diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 67be501e..93127d10 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -8,6 +8,7 @@ struct voxel; class Content; struct GeneratorDef; +/// @brief High-level world generation controller class WorldGenerator { const GeneratorDef& def; const Content* content; @@ -18,6 +19,11 @@ public: ); virtual ~WorldGenerator() = default; + /// @brief Generate complete chunk voxels + /// @param voxels destinatiopn chunk voxels buffer + /// @param x chunk position X divided by CHUNK_W + /// @param z chunk position Y divided by CHUNK_D + /// @param seed world seed virtual void generate(voxel* voxels, int x, int z, uint64_t seed); inline static std::string DEFAULT = "core:default"; From 685cd414c4aeb8ac6d8182a3d339b4a14acd7083 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 18 Aug 2024 22:25:36 +0300 Subject: [PATCH 024/139] add struct Biome & move generator-related scripting to scripting_world_generation.cpp --- src/logic/scripting/scripting.cpp | 170 ++---------------- src/logic/scripting/scripting_commons.hpp | 11 ++ .../scripting/scripting_world_generation.cpp | 150 ++++++++++++++++ src/world/generator/GeneratorDef.hpp | 9 +- src/world/generator/WorldGenerator.cpp | 5 +- 5 files changed, 181 insertions(+), 164 deletions(-) create mode 100644 src/logic/scripting/scripting_commons.hpp create mode 100644 src/logic/scripting/scripting_world_generation.cpp diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index d1a94118..e8db021b 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -3,6 +3,7 @@ #include #include +#include "scripting_commons.hpp" #include "content/Content.hpp" #include "content/ContentPack.hpp" #include "debug/Logger.hpp" @@ -24,7 +25,6 @@ #include "util/timeutil.hpp" #include "voxels/Block.hpp" #include "world/Level.hpp" -#include "world/generator/GeneratorDef.hpp" using namespace scripting; @@ -39,7 +39,7 @@ const ContentIndices* scripting::indices = nullptr; BlocksController* scripting::blocks = nullptr; LevelController* scripting::controller = nullptr; -static void load_script(const fs::path& name, bool throwable) { +void scripting::load_script(const fs::path& name, bool throwable) { auto paths = scripting::engine->getPaths(); fs::path file = paths->getResourcesFolder() / fs::path("scripts") / name; std::string src = files::read_string(file); @@ -52,6 +52,14 @@ static void load_script(const fs::path& name, bool throwable) { } } +int scripting::load_script( + int env, const std::string& type, const fs::path& file +) { + std::string src = files::read_string(file); + logger.info() << "script (" << type << ") " << file.u8string(); + return lua::execute(lua::get_main_thread(), env, src, file.u8string()); +} + void scripting::initialize(Engine* engine) { scripting::engine = engine; lua::initialize(); @@ -630,15 +638,6 @@ int scripting::get_values_on_stack() { return lua::gettop(lua::get_main_thread()); } -[[nodiscard]] -static int load_script( - int env, const std::string& type, const fs::path& file -) { - std::string src = files::read_string(file); - logger.info() << "script (" << type << ") " << file.u8string(); - return lua::execute(lua::get_main_thread(), env, src, file.u8string()); -} - void scripting::load_block_script( const scriptenv& senv, const std::string& prefix, @@ -685,155 +684,6 @@ void scripting::load_entity_component( lua::store_in(L, lua::CHUNKS_TABLE, name); } -class LuaGeneratorScript : public GeneratorScript { - scriptenv env; - BlocksLayers groundLayers; - BlocksLayers seaLayers; - uint seaLevel; -public: - LuaGeneratorScript( - scriptenv env, - BlocksLayers groundLayers, - BlocksLayers seaLayers, - uint seaLevel) - : env(std::move(env)), - groundLayers(std::move(groundLayers)), - seaLayers(std::move(seaLayers)), - seaLevel(seaLevel) - {} - - std::shared_ptr generateHeightmap( - const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed - ) override { - auto L = lua::get_main_thread(); - lua::pushenv(L, *env); - if (lua::getfield(L, "generate_heightmap")) { - lua::pushivec_stack(L, offset); - lua::pushivec_stack(L, size); - lua::pushinteger(L, seed); - if (lua::call_nothrow(L, 5)) { - auto map = lua::touserdata(L, -1)->getHeightmap(); - lua::pop(L, 2); - return map; - } - } - lua::pop(L); - return std::make_shared(size.x, size.y); - } - - void prepare(const Content* content) override { - for (auto& layer : groundLayers.layers) { - layer.rt.id = content->blocks.require(layer.block).rt.id; - } - for (auto& layer : seaLayers.layers) { - layer.rt.id = content->blocks.require(layer.block).rt.id; - } - } - - const BlocksLayers& getGroundLayers() const override { - return groundLayers; - } - - const BlocksLayers& getSeaLayers() const override { - return seaLayers; - } - - uint getSeaLevel() const override { - return seaLevel; - } -}; - -static BlocksLayer load_layer( - lua::State* L, int idx, uint& lastLayersHeight, bool& hasResizeableLayer -) { - lua::requirefield(L, "block"); - auto name = lua::require_string(L, -1); - lua::pop(L); - lua::requirefield(L, "height"); - int height = lua::tointeger(L, -1); - lua::pop(L); - bool belowSeaLevel = true; - if (lua::getfield(L, "below_sea_level")) { - belowSeaLevel = lua::toboolean(L, -1); - lua::pop(L); - } - - if (hasResizeableLayer) { - lastLayersHeight += height; - } - if (height == -1) { - if (hasResizeableLayer) { - throw std::runtime_error("only one resizeable layer allowed"); - } - hasResizeableLayer = true; - } - return BlocksLayer {name, height, belowSeaLevel, {}}; -} - -static inline BlocksLayers load_layers( - lua::State* L, const std::string& fieldname -) { - uint lastLayersHeight = 0; - bool hasResizeableLayer = false; - std::vector layers; - - if (lua::getfield(L, fieldname)) { - int len = lua::objlen(L, -1); - for (int i = 1; i <= len; i++) { - lua::rawgeti(L, i); - try { - layers.push_back( - load_layer(L, -1, lastLayersHeight, hasResizeableLayer)); - } catch (const std::runtime_error& err) { - lua::pop(L, 2); - throw std::runtime_error( - fieldname+" #"+std::to_string(i)+": "+err.what()); - } - lua::pop(L); - } - lua::pop(L); - } - return BlocksLayers {std::move(layers), lastLayersHeight}; -} - -std::unique_ptr scripting::load_generator( - const fs::path& file -) { - auto env = create_environment(); - auto L = lua::get_main_thread(); - - lua::pop(L, load_script(*env, "generator", file)); - - lua::pushenv(L, *env); - - uint seaLevel = 0; - if (lua::getfield(L, "sea_level")) { - seaLevel = lua::tointeger(L, -1); - lua::pop(L); - } - - uint lastGroundLayersHeight = 0; - uint lastSeaLayersHeight = 0; - bool hasResizeableGroundLayer = false; - bool hasResizeableSeaLayer = false; - - BlocksLayers groundLayers; - BlocksLayers seaLayers; - try { - groundLayers = load_layers(L, "layers"); - seaLayers = load_layers(L, "sea_layers"); - } catch (const std::runtime_error& err) { - lua::pop(L); - throw std::runtime_error(file.u8string()+": "+err.what()); - } - lua::pop(L); - return std::make_unique( - std::move(env), - std::move(groundLayers), - std::move(seaLayers), - seaLevel); -} - void scripting::load_world_script( const scriptenv& senv, const std::string& prefix, diff --git a/src/logic/scripting/scripting_commons.hpp b/src/logic/scripting/scripting_commons.hpp new file mode 100644 index 00000000..93e05ca7 --- /dev/null +++ b/src/logic/scripting/scripting_commons.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +namespace scripting { + void load_script(const std::filesystem::path& name, bool throwable); + + [[nodiscard]] + int load_script(int env, const std::string& type, const std::filesystem::path& file); +} diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp new file mode 100644 index 00000000..712aaa13 --- /dev/null +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -0,0 +1,150 @@ +#include "scripting.hpp" + +#include "scripting_commons.hpp" +#include "typedefs.hpp" +#include "lua/lua_engine.hpp" +#include "lua/lua_custom_types.hpp" +#include "content/Content.hpp" +#include "voxels/Block.hpp" +#include "world/generator/GeneratorDef.hpp" + +class LuaGeneratorScript : public GeneratorScript { + scriptenv env; + Biome biome; + uint seaLevel; +public: + LuaGeneratorScript( + scriptenv env, + Biome biome, + uint seaLevel) + : env(std::move(env)), + biome(std::move(biome)), + seaLevel(seaLevel) + {} + + std::shared_ptr generateHeightmap( + const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed + ) override { + auto L = lua::get_main_thread(); + lua::pushenv(L, *env); + if (lua::getfield(L, "generate_heightmap")) { + lua::pushivec_stack(L, offset); + lua::pushivec_stack(L, size); + lua::pushinteger(L, seed); + if (lua::call_nothrow(L, 5)) { + auto map = lua::touserdata(L, -1)->getHeightmap(); + lua::pop(L, 2); + return map; + } + } + lua::pop(L); + return std::make_shared(size.x, size.y); + } + + void prepare(const Content* content) override { + for (auto& layer : biome.groundLayers.layers) { + layer.rt.id = content->blocks.require(layer.block).rt.id; + } + for (auto& layer : biome.seaLayers.layers) { + layer.rt.id = content->blocks.require(layer.block).rt.id; + } + } + + const Biome& getBiome() const override { + return biome; + } + + uint getSeaLevel() const override { + return seaLevel; + } +}; + +static BlocksLayer load_layer( + lua::State* L, int idx, uint& lastLayersHeight, bool& hasResizeableLayer +) { + lua::requirefield(L, "block"); + auto name = lua::require_string(L, -1); + lua::pop(L); + lua::requirefield(L, "height"); + int height = lua::tointeger(L, -1); + lua::pop(L); + bool belowSeaLevel = true; + if (lua::getfield(L, "below_sea_level")) { + belowSeaLevel = lua::toboolean(L, -1); + lua::pop(L); + } + + if (hasResizeableLayer) { + lastLayersHeight += height; + } + if (height == -1) { + if (hasResizeableLayer) { + throw std::runtime_error("only one resizeable layer allowed"); + } + hasResizeableLayer = true; + } + return BlocksLayer {name, height, belowSeaLevel, {}}; +} + +static inline BlocksLayers load_layers( + lua::State* L, const std::string& fieldname +) { + uint lastLayersHeight = 0; + bool hasResizeableLayer = false; + std::vector layers; + + if (lua::getfield(L, fieldname)) { + int len = lua::objlen(L, -1); + for (int i = 1; i <= len; i++) { + lua::rawgeti(L, i); + try { + layers.push_back( + load_layer(L, -1, lastLayersHeight, hasResizeableLayer)); + } catch (const std::runtime_error& err) { + lua::pop(L, 2); + throw std::runtime_error( + fieldname+" #"+std::to_string(i)+": "+err.what()); + } + lua::pop(L); + } + lua::pop(L); + } + return BlocksLayers {std::move(layers), lastLayersHeight}; +} + +std::unique_ptr scripting::load_generator( + const fs::path& file +) { + auto env = create_environment(); + auto L = lua::get_main_thread(); + + lua::pop(L, load_script(*env, "generator", file)); + + lua::pushenv(L, *env); + + uint seaLevel = 0; + if (lua::getfield(L, "sea_level")) { + seaLevel = lua::tointeger(L, -1); + lua::pop(L); + } + + uint lastGroundLayersHeight = 0; + uint lastSeaLayersHeight = 0; + bool hasResizeableGroundLayer = false; + bool hasResizeableSeaLayer = false; + + BlocksLayers groundLayers; + BlocksLayers seaLayers; + try { + groundLayers = load_layers(L, "layers"); + seaLayers = load_layers(L, "sea_layers"); + } catch (const std::runtime_error& err) { + lua::pop(L); + throw std::runtime_error(file.u8string()+": "+err.what()); + } + lua::pop(L); + return std::make_unique( + std::move(env), + Biome {"default", std::move(groundLayers), std::move(seaLayers)}, + seaLevel); +} diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 649d3548..356d876d 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -33,6 +33,12 @@ struct BlocksLayers { uint lastLayersHeight; }; +struct Biome { + std::string name; + BlocksLayers groundLayers; + BlocksLayers seaLayers; +}; + /// @brief Generator behaviour and settings interface class GeneratorScript { public: @@ -46,8 +52,7 @@ public: virtual std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; - virtual const BlocksLayers& getGroundLayers() const = 0; - virtual const BlocksLayers& getSeaLayers() const = 0; + virtual const Biome& getBiome() const = 0; virtual uint getSeaLevel() const = 0; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index a8cd1763..c506dc9a 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -56,8 +56,9 @@ void WorldGenerator::generate( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed ); auto values = heightmap->getValues(); - const auto& groundLayers = def.script->getGroundLayers(); - const auto& seaLayers = def.script->getSeaLayers(); + const auto& biome = def.script->getBiome(); + const auto& groundLayers = biome.groundLayers; + const auto& seaLayers = biome.seaLayers; uint seaLevel = def.script->getSeaLevel(); From 706cabeebddb414be5537873d95f9a8c57fd425b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 18 Aug 2024 23:26:37 +0300 Subject: [PATCH 025/139] add lua::stackguard --- src/logic/scripting/lua/lua_commons.hpp | 24 +++++++++++++++++-- .../scripting/scripting_world_generation.cpp | 3 +-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/logic/scripting/lua/lua_commons.hpp b/src/logic/scripting/lua/lua_commons.hpp index d0b99f67..ff94ade7 100644 --- a/src/logic/scripting/lua/lua_commons.hpp +++ b/src/logic/scripting/lua/lua_commons.hpp @@ -24,9 +24,29 @@ namespace lua { luaerror(const std::string& message); }; - void log_error(const std::string& text); - using State = lua_State; using Number = lua_Number; using Integer = lua_Integer; + + /// @brief Automatically resets stack top element index to the initial state + /// (when stackguard was created). Prevents Lua stack leak on exception + /// occurred out of Lua execution time, when engine controls scripting. + /// + /// + /// stackguard allows to not place lua::pop(...) into 'catch' blocks. + class stackguard { + int top; + State* state; + public: + stackguard(State* state) : state(state) { + top = lua_gettop(state); + } + + ~stackguard() { + lua_settop(state, top); + } + }; + + void log_error(const std::string& text); + } diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 712aaa13..a1abee83 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -101,7 +101,6 @@ static inline BlocksLayers load_layers( layers.push_back( load_layer(L, -1, lastLayersHeight, hasResizeableLayer)); } catch (const std::runtime_error& err) { - lua::pop(L, 2); throw std::runtime_error( fieldname+" #"+std::to_string(i)+": "+err.what()); } @@ -117,6 +116,7 @@ std::unique_ptr scripting::load_generator( ) { auto env = create_environment(); auto L = lua::get_main_thread(); + lua::stackguard _(L); lua::pop(L, load_script(*env, "generator", file)); @@ -139,7 +139,6 @@ std::unique_ptr scripting::load_generator( groundLayers = load_layers(L, "layers"); seaLayers = load_layers(L, "sea_layers"); } catch (const std::runtime_error& err) { - lua::pop(L); throw std::runtime_error(file.u8string()+": "+err.what()); } lua::pop(L); From 71f56334cb402ed9609dc900365be167cab57225 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 19 Aug 2024 00:49:28 +0300 Subject: [PATCH 026/139] refactor lua scripting a bit --- src/logic/scripting/lua/libaudio.cpp | 4 +- src/logic/scripting/lua/libblock.cpp | 28 ++--- src/logic/scripting/lua/libmat4.cpp | 4 +- src/logic/scripting/lua/libplayer.cpp | 14 +-- src/logic/scripting/lua/lua_util.hpp | 152 +++++++----------------- src/logic/scripting/lua/lua_wrapper.hpp | 85 +++++++++++++ src/logic/scripting/scripting.cpp | 18 +-- 7 files changed, 158 insertions(+), 147 deletions(-) create mode 100644 src/logic/scripting/lua/lua_wrapper.hpp diff --git a/src/logic/scripting/lua/libaudio.cpp b/src/logic/scripting/lua/libaudio.cpp index 03214e6e..ee311585 100644 --- a/src/logic/scripting/lua/libaudio.cpp +++ b/src/logic/scripting/lua/libaudio.cpp @@ -335,7 +335,7 @@ static int l_audio_get_duration(lua::State* L) { static int l_audio_get_position(lua::State* L) { auto speaker = audio::get_speaker(lua::tointeger(L, 1)); if (speaker != nullptr) { - return lua::pushvec3_stack(L, speaker->getPosition()); + return lua::pushvec_stack(L, speaker->getPosition()); } return 0; } @@ -344,7 +344,7 @@ static int l_audio_get_position(lua::State* L) { static int l_audio_get_velocity(lua::State* L) { auto speaker = audio::get_speaker(lua::tointeger(L, 1)); if (speaker != nullptr) { - return lua::pushvec3_stack(L, speaker->getVelocity()); + return lua::pushvec_stack(L, speaker->getVelocity()); } return 0; } diff --git a/src/logic/scripting/lua/libblock.cpp b/src/logic/scripting/lua/libblock.cpp index 145362b7..f346098a 100644 --- a/src/logic/scripting/lua/libblock.cpp +++ b/src/logic/scripting/lua/libblock.cpp @@ -57,7 +57,7 @@ static int l_is_extended(lua::State* L) { static int l_get_size(lua::State* L) { if (auto def = require_block(L)) { - return lua::pushivec3_stack(L, def->size.x, def->size.y, def->size.z); + return lua::pushivec_stack(L, glm::ivec3(def->size)); } return 0; } @@ -76,7 +76,7 @@ static int l_seek_origin(lua::State* L) { auto z = lua::tointeger(L, 3); auto vox = level->chunks->get(x, y, z); auto& def = indices->blocks.require(vox->id); - return lua::pushivec3_stack( + return lua::pushivec_stack( L, level->chunks->seekOrigin({x, y, z}, def, vox->state) ); } @@ -117,14 +117,14 @@ static int l_get_x(lua::State* L) { auto z = lua::tointeger(L, 3); auto vox = level->chunks->get(x, y, z); if (vox == nullptr) { - return lua::pushivec3_stack(L, 1, 0, 0); + return lua::pushivec_stack(L, glm::ivec3(1, 0, 0)); } - auto& def = level->content->getIndices()->blocks.require(vox->id); + const auto& def = level->content->getIndices()->blocks.require(vox->id); if (!def.rotatable) { - return lua::pushivec3_stack(L, 1, 0, 0); + return lua::pushivec_stack(L, glm::ivec3(1, 0, 0)); } else { const CoordSystem& rot = def.rotations.variants[vox->state.rotation]; - return lua::pushivec3_stack(L, rot.axisX.x, rot.axisX.y, rot.axisX.z); + return lua::pushivec_stack(L, rot.axisX); } } @@ -134,14 +134,14 @@ static int l_get_y(lua::State* L) { auto z = lua::tointeger(L, 3); auto vox = level->chunks->get(x, y, z); if (vox == nullptr) { - return lua::pushivec3_stack(L, 0, 1, 0); + return lua::pushivec_stack(L, glm::ivec3(0, 1, 0)); } - auto& def = level->content->getIndices()->blocks.require(vox->id); + const auto& def = level->content->getIndices()->blocks.require(vox->id); if (!def.rotatable) { - return lua::pushivec3_stack(L, 0, 1, 0); + return lua::pushivec_stack(L, glm::ivec3(0, 1, 0)); } else { const CoordSystem& rot = def.rotations.variants[vox->state.rotation]; - return lua::pushivec3_stack(L, rot.axisY.x, rot.axisY.y, rot.axisY.z); + return lua::pushivec_stack(L, rot.axisY); } } @@ -151,14 +151,14 @@ static int l_get_z(lua::State* L) { auto z = lua::tointeger(L, 3); auto vox = level->chunks->get(x, y, z); if (vox == nullptr) { - return lua::pushivec3_stack(L, 0, 0, 1); + return lua::pushivec_stack(L, glm::ivec3(0, 0, 1)); } - auto& def = level->content->getIndices()->blocks.require(vox->id); + const auto& def = level->content->getIndices()->blocks.require(vox->id); if (!def.rotatable) { - return lua::pushivec3_stack(L, 0, 0, 1); + return lua::pushivec_stack(L, glm::ivec3(0, 0, 1)); } else { const CoordSystem& rot = def.rotations.variants[vox->state.rotation]; - return lua::pushivec3_stack(L, rot.axisZ.x, rot.axisZ.y, rot.axisZ.z); + return lua::pushivec_stack(L, rot.axisZ); } } diff --git a/src/logic/scripting/lua/libmat4.cpp b/src/logic/scripting/lua/libmat4.cpp index 5515fc0a..fb3d9c25 100644 --- a/src/logic/scripting/lua/libmat4.cpp +++ b/src/logic/scripting/lua/libmat4.cpp @@ -49,9 +49,9 @@ static int l_mul(lua::State* L) { switch (argc) { case 2: { if (len2 == 4) { - return lua::pushvec4_stack(L, matrix1 * lua::tovec4(L, 2)); + return lua::pushvec(L, matrix1 * lua::tovec4(L, 2)); } else if (len2 == 3) { - return lua::pushvec3_stack( + return lua::pushvec( L, matrix1 * glm::vec4(lua::tovec3(L, 2), 1.0f) ); } diff --git a/src/logic/scripting/lua/libplayer.cpp b/src/logic/scripting/lua/libplayer.cpp index 943e12da..fb4c9f0f 100644 --- a/src/logic/scripting/lua/libplayer.cpp +++ b/src/logic/scripting/lua/libplayer.cpp @@ -17,7 +17,7 @@ inline std::shared_ptr get_player(lua::State* L, int idx) { static int l_get_pos(lua::State* L) { if (auto player = get_player(L, 1)) { - return lua::pushvec3_stack(L, player->getPosition()); + return lua::pushvec_stack(L, player->getPosition()); } return 0; } @@ -37,7 +37,7 @@ static int l_set_pos(lua::State* L) { static int l_get_vel(lua::State* L) { if (auto player = get_player(L, 1)) { if (auto hitbox = player->getHitbox()) { - return lua::pushvec3_stack(L, hitbox->velocity); + return lua::pushvec_stack(L, hitbox->velocity); } } return 0; @@ -59,7 +59,7 @@ static int l_set_vel(lua::State* L) { static int l_get_rot(lua::State* L) { if (auto player = get_player(L, 1)) { - return lua::pushvec3_stack(L, player->cam); + return lua::pushvec_stack(L, player->cam); } return 0; } @@ -133,7 +133,7 @@ static int l_get_selected_block(lua::State* L) { if (player->selection.vox.id == BLOCK_VOID) { return 0; } - return lua::pushivec3_stack(L, player->selection.position); + return lua::pushivec_stack(L, player->selection.position); } return 0; } @@ -149,15 +149,13 @@ static int l_get_selected_entity(lua::State* L) { static int l_get_spawnpoint(lua::State* L) { if (auto player = get_player(L, 1)) { - return lua::pushvec3_stack(L, player->getSpawnPoint()); + return lua::pushvec_stack(L, player->getSpawnPoint()); } return 0; } static int l_set_spawnpoint(lua::State* L) { - auto player = get_player(L, 1); - - if (player) { + if (auto player = get_player(L, 1)) { auto x = lua::tonumber(L, 2); auto y = lua::tonumber(L, 3); auto z = lua::tonumber(L, 4); diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index 5c2287b0..d957e7be 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -4,7 +4,7 @@ #include #include -#include "lua_commons.hpp" +#include "lua_wrapper.hpp" #include "lua_custom_types.hpp" #define GLM_ENABLE_EXPERIMENTAL #include @@ -18,68 +18,6 @@ namespace lua { std::string env_name(int env); - template - int wrap(lua_State* L) { - int result = 0; - try { - result = func(L); - } - // transform exception with description into lua_error - catch (std::exception& e) { - luaL_error(L, e.what()); - } - // Rethrow any other exception (lua error for example) - catch (...) { - throw; - } - return result; - } - - inline void pop(lua::State* L, int n = 1) { - lua_pop(L, n); - } - inline void insert(lua::State* L, int idx) { - lua_insert(L, idx); - } - inline void remove(lua::State* L, int idx) { - lua_remove(L, idx); - } - inline int gettop(lua::State* L) { - return lua_gettop(L); - } - inline size_t objlen(lua::State* L, int idx) { - return lua_objlen(L, idx); - } - inline int next(lua::State* L, int idx) { - return lua_next(L, idx); - } - inline int type(lua::State* L, int idx) { - return lua_type(L, idx); - } - inline const char* type_name(lua::State* L, int idx) { - return lua_typename(L, idx); - } - inline int rawget(lua::State* L, int idx = -2) { - lua_rawget(L, idx); - return 1; - } - inline int rawgeti(lua::State* L, int n, int idx = -1) { - lua_rawgeti(L, idx, n); - return 1; - } - inline void rawseti(lua::State* L, int n, int idx = -2) { - lua_rawseti(L, idx, n); - } - - inline int createtable(lua::State* L, int narr, int nrec) { - lua_createtable(L, narr, nrec); - return 1; - } - - inline bool isnil(lua::State* L, int idx) { - return lua_isnil(L, idx); - } - inline bool getglobal(lua::State* L, const std::string& name) { lua_getglobal(L, name.c_str()); if (isnil(L, -1)) { @@ -107,25 +45,8 @@ namespace lua { return true; } - // function wrappers with number of pushed values as return value - - inline int pushnil(lua::State* L) { - lua_pushnil(L); - return 1; - } - - inline int pushinteger(lua::State* L, lua::Integer x) { - lua_pushinteger(L, x); - return 1; - } - - inline int pushnumber(lua::State* L, lua::Number x) { - lua_pushnumber(L, x); - return 1; - } - template - inline int pushvec(lua::State* L, glm::vec vec) { + inline int pushvec(lua::State* L, const glm::vec& vec) { createtable(L, n, 0); for (int i = 0; i < n; i++) { pushnumber(L, vec[i]); @@ -135,7 +56,7 @@ namespace lua { } template - inline int pushivec(lua::State* L, glm::vec vec) { + inline int pushivec(lua::State* L, const glm::vec& vec) { createtable(L, n, 0); for (int i = 0; i < n; i++) { pushinteger(L, vec[i]); @@ -144,42 +65,20 @@ namespace lua { return 1; } - inline int pushivec3_stack( - lua::State* L, lua::Integer x, lua::Integer y, lua::Integer z - ) { - pushinteger(L, x); - pushinteger(L, y); - pushinteger(L, z); - return 3; - } - template - inline int pushivec_stack(lua::State* L, glm::vec vec) { + inline int pushvec_stack(lua::State* L, const glm::vec& vec) { for (int i = 0; i < n; i++) { - pushinteger(L, vec[i]); + pushnumber(L, vec[i]); } return n; } - inline int pushivec3_stack(lua::State* L, glm::ivec3 vec) { - pushinteger(L, vec.x); - pushinteger(L, vec.y); - pushinteger(L, vec.z); - return 3; - } - - inline int pushvec3_stack(lua::State* L, glm::vec3 vec) { - pushnumber(L, vec.x); - pushnumber(L, vec.y); - pushnumber(L, vec.z); - return 3; - } - inline int pushvec4_stack(lua::State* L, glm::vec4 vec) { - pushnumber(L, vec.x); - pushnumber(L, vec.y); - pushnumber(L, vec.z); - pushnumber(L, vec.w); - return 4; + template + inline int pushivec_stack(lua::State* L, const glm::vec& vec) { + for (int i = 0; i < n; i++) { + pushinteger(L, vec[i]); + } + return n; } inline void setmetatable(lua::State* L, int idx = -2) { @@ -661,4 +560,33 @@ namespace lua { luaL_setfuncs(L, libfuncs, 0); setglobal(L, name); } + + inline const char* require_string_field( + lua::State* L, const std::string& name, int idx=-1 + ) { + requirefield(L, name, idx); + auto value = require_string(L, -1); + lua::pop(L); + return value; + } + + inline Integer require_integer_field( + lua::State* L, const std::string& name, int idx=-1 + ) { + requirefield(L, name, idx); + auto value = tointeger(L, -1); + lua::pop(L); + return value; + } + + inline bool get_boolean_field( + lua::State* L, const std::string& name, bool def, int idx=-1 + ) { + if (getfield(L, name, idx)) { + bool value = toboolean(L, -1); + pop(L); + return value; + } + return def; + } } diff --git a/src/logic/scripting/lua/lua_wrapper.hpp b/src/logic/scripting/lua/lua_wrapper.hpp new file mode 100644 index 00000000..ee184cb1 --- /dev/null +++ b/src/logic/scripting/lua/lua_wrapper.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include "lua_commons.hpp" + +namespace lua { + template + int wrap(lua_State* L) { + int result = 0; + try { + result = func(L); + } + // transform exception with description into lua_error + catch (std::exception& e) { + luaL_error(L, e.what()); + } + // Rethrow any other exception (lua error for example) + catch (...) { + throw; + } + return result; + } + + inline void pop(lua::State* L, int n = 1) { + lua_pop(L, n); + } + inline void insert(lua::State* L, int idx) { + lua_insert(L, idx); + } + inline void remove(lua::State* L, int idx) { + lua_remove(L, idx); + } + inline int gettop(lua::State* L) { + return lua_gettop(L); + } + inline size_t objlen(lua::State* L, int idx) { + return lua_objlen(L, idx); + } + inline int next(lua::State* L, int idx) { + return lua_next(L, idx); + } + inline int type(lua::State* L, int idx) { + return lua_type(L, idx); + } + inline const char* type_name(lua::State* L, int idx) { + return lua_typename(L, idx); + } + inline int rawget(lua::State* L, int idx = -2) { + lua_rawget(L, idx); + return 1; + } + inline int rawgeti(lua::State* L, int n, int idx = -1) { + lua_rawgeti(L, idx, n); + return 1; + } + inline void rawseti(lua::State* L, int n, int idx = -2) { + lua_rawseti(L, idx, n); + } + + inline int createtable(lua::State* L, int narr, int nrec) { + lua_createtable(L, narr, nrec); + return 1; + } + + inline bool isnil(lua::State* L, int idx) { + return lua_isnil(L, idx); + } + + // function wrappers with number of pushed values as return value + + inline int pushnil(lua::State* L) { + lua_pushnil(L); + return 1; + } + + inline int pushinteger(lua::State* L, lua::Integer x) { + lua_pushinteger(L, x); + return 1; + } + + inline int pushnumber(lua::State* L, lua::Number x) { + lua_pushnumber(L, x); + return 1; + } + +} diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index e8db021b..d8bf0d51 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -225,14 +225,14 @@ void scripting::on_blocks_tick(const Block& block, int tps) { void scripting::update_block(const Block& block, int x, int y, int z) { std::string name = block.name + ".update"; lua::emit_event(lua::get_main_thread(), name, [x, y, z](auto L) { - return lua::pushivec3_stack(L, x, y, z); + return lua::pushivec_stack(L, glm::ivec3(x, y, z)); }); } void scripting::random_update_block(const Block& block, int x, int y, int z) { std::string name = block.name + ".randupdate"; lua::emit_event(lua::get_main_thread(), name, [x, y, z](auto L) { - return lua::pushivec3_stack(L, x, y, z); + return lua::pushivec_stack(L, glm::ivec3(x, y, z)); }); } @@ -241,13 +241,13 @@ void scripting::on_block_placed( ) { std::string name = block.name + ".placed"; lua::emit_event(lua::get_main_thread(), name, [x, y, z, player](auto L) { - lua::pushivec3_stack(L, x, y, z); + lua::pushivec_stack(L, glm::ivec3(x, y, z)); lua::pushinteger(L, player ? player->getId() : -1); return 4; }); auto world_event_args = [&](lua::State* L) { lua::pushinteger(L, block.rt.id); - lua::pushivec3_stack(L, x, y, z); + lua::pushivec_stack(L, glm::ivec3(x, y, z)); lua::pushinteger(L, player ? player->getId() : -1); return 5; }; @@ -271,7 +271,7 @@ void scripting::on_block_broken( lua::get_main_thread(), name, [x, y, z, player](auto L) { - lua::pushivec3_stack(L, x, y, z); + lua::pushivec_stack(L, glm::ivec3(x, y, z)); lua::pushinteger(L, player ? player->getId() : -1); return 4; } @@ -279,7 +279,7 @@ void scripting::on_block_broken( } auto world_event_args = [&](lua::State* L) { lua::pushinteger(L, block.rt.id); - lua::pushivec3_stack(L, x, y, z); + lua::pushivec_stack(L, glm::ivec3(x, y, z)); lua::pushinteger(L, player ? player->getId() : -1); return 5; }; @@ -299,7 +299,7 @@ bool scripting::on_block_interact( ) { std::string name = block.name + ".interact"; return lua::emit_event(lua::get_main_thread(), name, [pos, player](auto L) { - lua::pushivec3_stack(L, pos.x, pos.y, pos.z); + lua::pushivec_stack(L, pos); lua::pushinteger(L, player->getId()); return 4; }); @@ -322,7 +322,7 @@ bool scripting::on_item_use_on_block( lua::get_main_thread(), name, [ipos, normal, player](auto L) { - lua::pushivec3_stack(L, ipos.x, ipos.y, ipos.z); + lua::pushivec_stack(L, ipos); lua::pushinteger(L, player->getId()); lua::pushivec(L, normal); return 5; @@ -338,7 +338,7 @@ bool scripting::on_item_break_block( lua::get_main_thread(), name, [x, y, z, player](auto L) { - lua::pushivec3_stack(L, x, y, z); + lua::pushivec_stack(L, glm::ivec3(x, y, z)); lua::pushinteger(L, player->getId()); return 4; } From efa27496abdbd21bf9837f518444cf783e1133dd Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 19 Aug 2024 17:13:50 +0300 Subject: [PATCH 027/139] add biome as table & 'biome_parameters' variable --- res/generators/default.lua | 21 +++--- src/logic/scripting/lua/lua_util.hpp | 28 ++++++++ .../scripting/scripting_world_generation.cpp | 64 ++++++++++--------- src/world/generator/GeneratorDef.hpp | 6 +- src/world/generator/WorldGenerator.cpp | 2 +- 5 files changed, 81 insertions(+), 40 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 05df5ce9..010489a0 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -1,14 +1,19 @@ sea_level = 64 -sea_layers = { - {block="base:water", height=-1}, -} +-- 1 - temperature +-- 2 - humidity +biome_parameters = 2 -layers = { - {block="base:grass_block", height=1, below_sea_level=false}, - {block="base:dirt", height=5, below_sea_level=false}, - {block="base:stone", height=-1}, - {block="base:bazalt", height=1}, +biome = { + sea_layers = { + {block="base:water", height=-1}, + }, + layers = { + {block="base:grass_block", height=1, below_sea_level=false}, + {block="base:dirt", height=5, below_sea_level=false}, + {block="base:stone", height=-1}, + {block="base:bazalt", height=1}, + } } local function _generate_heightmap(x, y, w, h, seed, s) diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index d957e7be..a02fdd03 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -589,4 +589,32 @@ namespace lua { } return def; } + + inline Integer get_integer_field( + lua::State* L, const std::string& name, Integer def, int idx=-1 + ) { + if (getfield(L, name, idx)) { + auto value = tointeger(L, -1); + pop(L); + return value; + } + return def; + } + + inline Integer get_integer_field( + lua::State* L, const std::string& name, + Integer def, Integer min, Integer max, int idx=-1 + ) { + if (getfield(L, name, idx)) { + auto value = tointeger(L, -1); + if (value < min || value > max) { + throw std::runtime_error( + "value is out of range [" + +std::to_string(min)+", "+std::to_string(max)+"]"); + } + pop(L); + return value; + } + return def; + } } diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index a1abee83..9a2535bb 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -6,19 +6,23 @@ #include "lua/lua_custom_types.hpp" #include "content/Content.hpp" #include "voxels/Block.hpp" +#include "voxels/Chunk.hpp" #include "world/generator/GeneratorDef.hpp" class LuaGeneratorScript : public GeneratorScript { scriptenv env; Biome biome; + uint biomeParameters; uint seaLevel; public: LuaGeneratorScript( scriptenv env, Biome biome, + uint biomeParameters, uint seaLevel) : env(std::move(env)), biome(std::move(biome)), + biomeParameters(biomeParameters), seaLevel(seaLevel) {} @@ -54,6 +58,10 @@ public: return biome; } + uint getBiomeParameters() const override { + return biomeParameters; + } + uint getSeaLevel() const override { return seaLevel; } @@ -62,17 +70,9 @@ public: static BlocksLayer load_layer( lua::State* L, int idx, uint& lastLayersHeight, bool& hasResizeableLayer ) { - lua::requirefield(L, "block"); - auto name = lua::require_string(L, -1); - lua::pop(L); - lua::requirefield(L, "height"); - int height = lua::tointeger(L, -1); - lua::pop(L); - bool belowSeaLevel = true; - if (lua::getfield(L, "below_sea_level")) { - belowSeaLevel = lua::toboolean(L, -1); - lua::pop(L); - } + auto name = lua::require_string_field(L, "block"); + int height = lua::require_integer_field(L, "height"); + bool belowSeaLevel = lua::get_boolean_field(L, "below_sea_level", true); if (hasResizeableLayer) { lastLayersHeight += height; @@ -111,6 +111,22 @@ static inline BlocksLayers load_layers( return BlocksLayers {std::move(layers), lastLayersHeight}; } +static inline Biome load_biome( + lua::State* L, const std::string& name, int idx +) { + lua::pushvalue(L, idx); + BlocksLayers groundLayers; + BlocksLayers seaLayers; + try { + groundLayers = load_layers(L, "layers"); + seaLayers = load_layers(L, "sea_layers"); + } catch (const std::runtime_error& err) { + throw std::runtime_error("biome "+name+": "+err.what()); + } + lua::pop(L); + return Biome {name, std::move(groundLayers), std::move(seaLayers)}; +} + std::unique_ptr scripting::load_generator( const fs::path& file ) { @@ -122,28 +138,16 @@ std::unique_ptr scripting::load_generator( lua::pushenv(L, *env); - uint seaLevel = 0; - if (lua::getfield(L, "sea_level")) { - seaLevel = lua::tointeger(L, -1); - lua::pop(L); - } + uint biomeParameters = lua::get_integer_field(L, "biome_parameters", 0, 0, 16); + uint seaLevel = lua::get_integer_field(L, "sea_level", 0, 0, CHUNK_H); + lua::requirefield(L, "biome"); + Biome biome = load_biome(L, "default", -1); + lua::pop(L); - uint lastGroundLayersHeight = 0; - uint lastSeaLayersHeight = 0; - bool hasResizeableGroundLayer = false; - bool hasResizeableSeaLayer = false; - - BlocksLayers groundLayers; - BlocksLayers seaLayers; - try { - groundLayers = load_layers(L, "layers"); - seaLayers = load_layers(L, "sea_layers"); - } catch (const std::runtime_error& err) { - throw std::runtime_error(file.u8string()+": "+err.what()); - } lua::pop(L); return std::make_unique( std::move(env), - Biome {"default", std::move(groundLayers), std::move(seaLayers)}, + std::move(biome), + biomeParameters, seaLevel); } diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 356d876d..79c537ed 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -17,7 +17,7 @@ struct BlocksLayer { /// @brief Layer can present under the sea level (default: true) else will /// extend the next layer - bool below_sea_level; + bool belowSeaLevel; struct { /// @brief Layer block index @@ -54,6 +54,10 @@ public: virtual const Biome& getBiome() const = 0; + /// @return Number of biome parameters, that biome choosing depending on + virtual uint getBiomeParameters() const = 0; + + /// @return Sea level (top of seaLayers) virtual uint getSeaLevel() const = 0; /// @brief Build the runtime cache diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index c506dc9a..3773b6f9 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -26,7 +26,7 @@ static inline void generate_pole( uint layerExtension = 0; for (const auto& layer : layers.layers) { // skip layer if can't be generated under sea level - if (y < seaLevel && !layer.below_sea_level) { + if (y < seaLevel && !layer.belowSeaLevel) { layerExtension = std::max(0, layer.height); continue; } From c3831afd194e7364332b177cf88b77e744cd488e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 19 Aug 2024 17:42:33 +0300 Subject: [PATCH 028/139] add biome 'parameters' field --- res/generators/default.lua | 4 +++ src/logic/scripting/lua/lua_util.hpp | 13 ++++++++-- .../scripting/scripting_world_generation.cpp | 26 ++++++++++++++++--- src/world/generator/GeneratorDef.hpp | 10 +++++++ 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 010489a0..af59f259 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -5,6 +5,10 @@ sea_level = 64 biome_parameters = 2 biome = { + parameters = { + {value=0.5, weight=1.0}, + {value=0.5, weight=1.0}, + }, sea_layers = { {block="base:water", height=-1}, }, diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index a02fdd03..11615ba1 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -566,7 +566,7 @@ namespace lua { ) { requirefield(L, name, idx); auto value = require_string(L, -1); - lua::pop(L); + pop(L); return value; } @@ -575,7 +575,16 @@ namespace lua { ) { requirefield(L, name, idx); auto value = tointeger(L, -1); - lua::pop(L); + pop(L); + return value; + } + + inline Number require_number_field( + lua::State* L, const std::string& name, int idx=-1 + ) { + requirefield(L, name, idx); + auto value = tonumber(L, -1); + pop(L); return value; } diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 9a2535bb..612346d6 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -112,9 +112,25 @@ static inline BlocksLayers load_layers( } static inline Biome load_biome( - lua::State* L, const std::string& name, int idx + lua::State* L, const std::string& name, uint parametersCount, int idx ) { lua::pushvalue(L, idx); + + std::vector parameters(parametersCount); + lua::requirefield(L, "parameters"); + if (lua::objlen(L, -1) < parametersCount) { + throw std::runtime_error( + std::to_string(parametersCount)+" parameters expected"); + } + for (uint i = 1; i <= parametersCount; i++) { + lua::rawgeti(L, i); + float value = lua::require_number_field(L, "value"); + float weight = lua::require_number_field(L, "weight"); + parameters.push_back(BiomeParameter {value, weight}); + lua::pop(L); + } + lua::pop(L); + BlocksLayers groundLayers; BlocksLayers seaLayers; try { @@ -124,7 +140,11 @@ static inline Biome load_biome( throw std::runtime_error("biome "+name+": "+err.what()); } lua::pop(L); - return Biome {name, std::move(groundLayers), std::move(seaLayers)}; + return Biome { + name, + std::move(parameters), + std::move(groundLayers), + std::move(seaLayers)}; } std::unique_ptr scripting::load_generator( @@ -141,7 +161,7 @@ std::unique_ptr scripting::load_generator( uint biomeParameters = lua::get_integer_field(L, "biome_parameters", 0, 0, 16); uint seaLevel = lua::get_integer_field(L, "sea_level", 0, 0, CHUNK_H); lua::requirefield(L, "biome"); - Biome biome = load_biome(L, "default", -1); + Biome biome = load_biome(L, "default", biomeParameters, -1); lua::pop(L); lua::pop(L); diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 79c537ed..2828ad42 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -33,8 +33,17 @@ struct BlocksLayers { uint lastLayersHeight; }; +struct BiomeParameter { + /// @brief Central parameter value for the biome + float origin; + /// @brief Parameter score multiplier + /// (the higher the value, the greater the chance of biome choosing) + float weight; +}; + struct Biome { std::string name; + std::vector parameters; BlocksLayers groundLayers; BlocksLayers seaLayers; }; @@ -67,6 +76,7 @@ public: /// @brief Generator information struct GeneratorDef { + /// @brief Generator full name - packid:name std::string name; std::unique_ptr script; From 4bd5f1b6298c57a6a5c373d750f04d31e0dc130b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 19 Aug 2024 17:57:09 +0300 Subject: [PATCH 029/139] add biomes table --- res/generators/default.lua | 28 ++++++------ .../scripting/scripting_world_generation.cpp | 44 +++++++++++++------ src/world/generator/GeneratorDef.hpp | 2 +- src/world/generator/WorldGenerator.cpp | 3 +- 4 files changed, 49 insertions(+), 28 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index af59f259..21fae723 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -4,19 +4,21 @@ sea_level = 64 -- 2 - humidity biome_parameters = 2 -biome = { - parameters = { - {value=0.5, weight=1.0}, - {value=0.5, weight=1.0}, - }, - sea_layers = { - {block="base:water", height=-1}, - }, - layers = { - {block="base:grass_block", height=1, below_sea_level=false}, - {block="base:dirt", height=5, below_sea_level=false}, - {block="base:stone", height=-1}, - {block="base:bazalt", height=1}, +biomes = { + plains = { + parameters = { + {value=0.5, weight=1.0}, + {value=0.5, weight=1.0}, + }, + sea_layers = { + {block="base:water", height=-1}, + }, + layers = { + {block="base:grass_block", height=1, below_sea_level=false}, + {block="base:dirt", height=5, below_sea_level=false}, + {block="base:stone", height=-1}, + {block="base:bazalt", height=1}, + } } } diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 612346d6..d14a7fc8 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -11,17 +11,17 @@ class LuaGeneratorScript : public GeneratorScript { scriptenv env; - Biome biome; + std::vector biomes; uint biomeParameters; uint seaLevel; public: LuaGeneratorScript( scriptenv env, - Biome biome, + std::vector biomes, uint biomeParameters, uint seaLevel) : env(std::move(env)), - biome(std::move(biome)), + biomes(std::move(biomes)), biomeParameters(biomeParameters), seaLevel(seaLevel) {} @@ -46,16 +46,18 @@ public: } void prepare(const Content* content) override { - for (auto& layer : biome.groundLayers.layers) { - layer.rt.id = content->blocks.require(layer.block).rt.id; - } - for (auto& layer : biome.seaLayers.layers) { - layer.rt.id = content->blocks.require(layer.block).rt.id; + for (auto& biome : biomes) { + for (auto& layer : biome.groundLayers.layers) { + layer.rt.id = content->blocks.require(layer.block).rt.id; + } + for (auto& layer : biome.seaLayers.layers) { + layer.rt.id = content->blocks.require(layer.block).rt.id; + } } } - const Biome& getBiome() const override { - return biome; + const std::vector& getBiomes() const override { + return biomes; } uint getBiomeParameters() const override { @@ -160,14 +162,30 @@ std::unique_ptr scripting::load_generator( uint biomeParameters = lua::get_integer_field(L, "biome_parameters", 0, 0, 16); uint seaLevel = lua::get_integer_field(L, "sea_level", 0, 0, CHUNK_H); - lua::requirefield(L, "biome"); - Biome biome = load_biome(L, "default", biomeParameters, -1); + + std::vector biomes; + lua::requirefield(L, "biomes"); + if (!lua::istable(L, -1)) { + throw std::runtime_error("'biomes' must be a table"); + } + lua::pushnil(L); + while (lua::next(L, -2)) { + lua::pushvalue(L, -2); + std::string biomeName = lua::tostring(L, -1); + try { + biomes.push_back( + load_biome(L, biomeName, biomeParameters, -2)); + } catch (const std::runtime_error& err) { + throw std::runtime_error("biome "+biomeName+": "+err.what()); + } + lua::pop(L, 2); + } lua::pop(L); lua::pop(L); return std::make_unique( std::move(env), - std::move(biome), + std::move(biomes), biomeParameters, seaLevel); } diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 2828ad42..dcc8220b 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -61,7 +61,7 @@ public: virtual std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; - virtual const Biome& getBiome() const = 0; + virtual const std::vector& getBiomes() const = 0; /// @return Number of biome parameters, that biome choosing depending on virtual uint getBiomeParameters() const = 0; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 3773b6f9..c18747eb 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -56,7 +56,8 @@ void WorldGenerator::generate( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed ); auto values = heightmap->getValues(); - const auto& biome = def.script->getBiome(); + const auto& biomes = def.script->getBiomes(); + const auto& biome = biomes.at(0); const auto& groundLayers = biome.groundLayers; const auto& seaLayers = biome.seaLayers; From 572eaec4293fa450f02e29cac966976a799b0b9a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 19 Aug 2024 20:11:55 +0300 Subject: [PATCH 030/139] add test biomes --- res/generators/default.lua | 21 ++++++++++ .../scripting/scripting_world_generation.cpp | 32 ++++++++++++++- src/maths/Heightmap.hpp | 8 ++++ src/world/generator/GeneratorDef.hpp | 3 ++ src/world/generator/WorldGenerator.cpp | 40 +++++++++++++++++-- 5 files changed, 99 insertions(+), 5 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 21fae723..393de9f5 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -19,6 +19,20 @@ biomes = { {block="base:stone", height=-1}, {block="base:bazalt", height=1}, } + }, + desert = { + parameters = { + {value=0.0, weight=1.0}, + {value=0.5, weight=1.0}, + }, + sea_layers = { + {block="base:water", height=-1}, + }, + layers = { + {block="base:sand", height=6}, + {block="base:stone", height=-1}, + {block="base:bazalt", height=1}, + } } } @@ -65,3 +79,10 @@ function generate_heightmap(x, y, w, h, seed) map:crop(0, 0, w, h) return map end + +function generate_biome_parameters(x, y, w, h, seed) + local tempmap = Heightmap(w, h) + tempmap:noise({x, y}, 0.4, 3) + local hummap = Heightmap(w, h) + return tempmap, hummap +end diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index d14a7fc8..8efdf0bb 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -9,6 +9,8 @@ #include "voxels/Chunk.hpp" #include "world/generator/GeneratorDef.hpp" +// TODO: use dynamic::* for parsing + class LuaGeneratorScript : public GeneratorScript { scriptenv env; std::vector biomes; @@ -45,6 +47,34 @@ public: return std::make_shared(size.x, size.y); } + std::vector> generateParameterMaps( + const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed + ) override { + std::vector> maps; + + auto L = lua::get_main_thread(); + lua::pushenv(L, *env); + if (lua::getfield(L, "generate_biome_parameters")) { + lua::pushivec_stack(L, offset); + lua::pushivec_stack(L, size); + lua::pushinteger(L, seed); + if (lua::call_nothrow(L, 5, biomeParameters)) { + for (int i = biomeParameters-1; i >= 0; i--) { + maps.push_back( + lua::touserdata(L, -1-i)->getHeightmap()); + + } + lua::pop(L, 1+biomeParameters); + return maps; + } + } + lua::pop(L); + for (uint i = 0; i < biomeParameters; i++) { + maps.push_back(std::make_shared(size.x, size.y)); + } + return maps; + } + void prepare(const Content* content) override { for (auto& biome : biomes) { for (auto& layer : biome.groundLayers.layers) { @@ -118,7 +148,7 @@ static inline Biome load_biome( ) { lua::pushvalue(L, idx); - std::vector parameters(parametersCount); + std::vector parameters; lua::requirefield(L, "parameters"); if (lua::objlen(L, -1) < parametersCount) { throw std::runtime_error( diff --git a/src/maths/Heightmap.hpp b/src/maths/Heightmap.hpp index 5b9f4cfa..52a3d394 100644 --- a/src/maths/Heightmap.hpp +++ b/src/maths/Heightmap.hpp @@ -37,6 +37,14 @@ public: return height; } + float get(uint x, uint y) { + return buffer.at(y * width + x); + } + + float getUnchecked(uint x, uint y) { + return buffer[y * width + x]; + } + float* getValues() { return buffer.data(); } diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index dcc8220b..7b469bd3 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -61,6 +61,9 @@ public: virtual std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; + virtual std::vector> generateParameterMaps( + const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; + virtual const std::vector& getBiomes() const = 0; /// @return Number of biome parameters, that biome choosing depending on diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index c18747eb..eeea5f64 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -9,6 +9,8 @@ #include "voxels/voxel.hpp" #include "world/generator/GeneratorDef.hpp" +static inline constexpr uint MAX_PARAMETERS = 16; + WorldGenerator::WorldGenerator(const GeneratorDef& def, const Content* content) : def(def), content(content) { } @@ -30,7 +32,6 @@ static inline void generate_pole( layerExtension = std::max(0, layer.height); continue; } - int layerHeight = layer.height; if (layerHeight == -1) { // resizeable layer @@ -47,6 +48,32 @@ static inline void generate_pole( } } +static inline const Biome* choose_biome( + const std::vector& biomes, + const std::vector>& maps, + uint x, uint z +) { + uint paramsCount = maps.size(); + float params[MAX_PARAMETERS]; + for (uint i = 0; i < paramsCount; i++) { + params[i] = maps[i]->getUnchecked(x, z); + } + const Biome* chosenBiome = nullptr; + float chosenScore = std::numeric_limits::infinity(); + for (const auto& biome : biomes) { + float score = 0.0f; + for (uint i = 0; i < paramsCount; i++) { + score += glm::abs((params[i] - biome.parameters[i].origin) / + biome.parameters[i].weight); + } + if (score < chosenScore) { + chosenScore = score; + chosenBiome = &biome; + } + } + return chosenBiome; +} + #include "util/timeutil.hpp" void WorldGenerator::generate( voxel* voxels, int chunkX, int chunkZ, uint64_t seed @@ -55,11 +82,11 @@ void WorldGenerator::generate( auto heightmap = def.script->generateHeightmap( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed ); + auto biomeParams = def.script->generateParameterMaps( + {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed + ); auto values = heightmap->getValues(); const auto& biomes = def.script->getBiomes(); - const auto& biome = biomes.at(0); - const auto& groundLayers = biome.groundLayers; - const auto& seaLayers = biome.seaLayers; uint seaLevel = def.script->getSeaLevel(); @@ -67,9 +94,14 @@ void WorldGenerator::generate( for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { + const Biome* biome = choose_biome(biomes, biomeParams, x, z); + int height = values[z * CHUNK_W + x] * CHUNK_H; height = std::max(0, height); + const auto& groundLayers = biome->groundLayers; + const auto& seaLayers = biome->seaLayers; + generate_pole(seaLayers, seaLevel, height, seaLevel, voxels, x, z); generate_pole(groundLayers, height, 0, seaLevel, voxels, x, z); } From 6efc942a1d595ffd362be6dc7f510a13ea946e75 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 19 Aug 2024 20:31:42 +0300 Subject: [PATCH 031/139] advanced biomes test --- res/generators/default.lua | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 393de9f5..17e92e34 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -8,7 +8,7 @@ biomes = { plains = { parameters = { {value=0.5, weight=1.0}, - {value=0.5, weight=1.0}, + {value=0.5, weight=0.3}, }, sea_layers = { {block="base:water", height=-1}, @@ -22,8 +22,8 @@ biomes = { }, desert = { parameters = { - {value=0.0, weight=1.0}, - {value=0.5, weight=1.0}, + {value=0.0, weight=0.36}, + {value=0.0, weight=0.4}, }, sea_layers = { {block="base:water", height=-1}, @@ -33,6 +33,20 @@ biomes = { {block="base:stone", height=-1}, {block="base:bazalt", height=1}, } + }, + mountains = { + parameters = { + {value=1.0, weight=1.0}, + {value=0.2, weight=0.5}, + }, + sea_layers = { + {block="base:water", height=-1}, + }, + layers = { + {block="base:stone", height=6}, + {block="base:stone", height=-1}, + {block="base:bazalt", height=1}, + } } } @@ -82,7 +96,10 @@ end function generate_biome_parameters(x, y, w, h, seed) local tempmap = Heightmap(w, h) - tempmap:noise({x, y}, 0.4, 3) + tempmap.noiseSeed = seed + 5324 + tempmap:noise({x, y}, 0.8, 3) local hummap = Heightmap(w, h) + hummap.noiseSeed = seed + 953 + hummap:noise({x, y}, 0.2, 2) return tempmap, hummap end From 5bdabaea425536b69452be0f19f3ab7eadafbeb2 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 20 Aug 2024 20:46:23 +0300 Subject: [PATCH 032/139] add plants --- res/generators/default.lua | 4 ++ src/logic/scripting/lua/lua_util.hpp | 11 +++++ .../scripting/scripting_world_generation.cpp | 36 ++++++++++++--- src/world/generator/GeneratorDef.hpp | 45 ++++++++++++++++++- src/world/generator/WorldGenerator.cpp | 15 ++++++- 5 files changed, 101 insertions(+), 10 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 17e92e34..789569e1 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -18,6 +18,10 @@ biomes = { {block="base:dirt", height=5, below_sea_level=false}, {block="base:stone", height=-1}, {block="base:bazalt", height=1}, + }, + plant_chance = 0.5, + plants = { + {block="base:grass", weight=1} } }, desert = { diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index 11615ba1..bd9df34c 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -610,6 +610,17 @@ namespace lua { return def; } + inline Number get_number_field( + lua::State* L, const std::string& name, Number def, int idx=-1 + ) { + if (getfield(L, name, idx)) { + auto value = tonumber(L, -1); + pop(L); + return value; + } + return def; + } + inline Integer get_integer_field( lua::State* L, const std::string& name, Integer def, Integer min, Integer max, int idx=-1 diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 8efdf0bb..2b500cbd 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -83,6 +83,9 @@ public: for (auto& layer : biome.seaLayers.layers) { layer.rt.id = content->blocks.require(layer.block).rt.id; } + for (auto& plant : biome.plants.plants) { + plant.rt.id = content->blocks.require(plant.block).rt.id; + } } } @@ -163,18 +166,37 @@ static inline Biome load_biome( } lua::pop(L); - BlocksLayers groundLayers; - BlocksLayers seaLayers; - try { - groundLayers = load_layers(L, "layers"); - seaLayers = load_layers(L, "sea_layers"); - } catch (const std::runtime_error& err) { - throw std::runtime_error("biome "+name+": "+err.what()); + float plantChance = lua::get_number_field(L, "plant_chance", 0.0); + float plantsWeightSum = 0.0f; + std::vector plants; + if (lua::getfield(L, "plants")) { + if (!lua::istable(L, -1)) { + throw std::runtime_error("'plants' must be a table"); + } + int plantsCount = lua::objlen(L, -1); + for (int i = 1; i <= plantsCount; i++) { + lua::rawgeti(L, i); + if (!lua::istable(L, -1)) { + throw std::runtime_error("plant must be a table"); + } + auto block = lua::require_string_field(L, "block"); + float weight = lua::require_number_field(L, "weight"); + // TODO: range check (positive) + plantsWeightSum += weight; + plants.push_back(PlantEntry {block, weight, {}}); + lua::pop(L); + } + lua::pop(L); } + // TODO: sort by weight descending + + BlocksLayers groundLayers = load_layers(L, "layers"); + BlocksLayers seaLayers = load_layers(L, "sea_layers"); lua::pop(L); return Biome { name, std::move(parameters), + BiomePlants {plants, plantsWeightSum, plantChance}, std::move(groundLayers), std::move(seaLayers)}; } diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 7b469bd3..b424b636 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -35,15 +35,56 @@ struct BlocksLayers { struct BiomeParameter { /// @brief Central parameter value for the biome - float origin; + float value; /// @brief Parameter score multiplier - /// (the higher the value, the greater the chance of biome choosing) + /// (the higher the weight, the greater the chance of biome choosing) float weight; }; +struct PlantEntry { + /// @brief Plant block id + std::string block; + /// @brief Plant weight + float weight; + + struct { + blockid_t id; + } rt; +}; + +struct BiomePlants { + static inline float MIN_CHANCE = 0.000001f; + + /// @brief Plant entries sorted by weight descending. + std::vector plants; + /// @brief Sum of weight values + float weightsSum; + /// @brief Plant generation chance + float chance; + + /// @brief Choose plant based on weight + /// @param rand some random value in range [0, 1) + /// @return index of chosen plant block + inline blockid_t choose(float rand) const { + if (plants.empty() || rand > chance || chance < MIN_CHANCE) { + return 0; + } + rand = rand / chance; + rand *= weightsSum; + for (const auto& plant : plants) { + rand -= plant.weight; + if (rand <= 0.0f) { + return plant.rt.id; + } + } + return plants[plants.size()-1].rt.id; + } +}; + struct Biome { std::string name; std::vector parameters; + BiomePlants plants; BlocksLayers groundLayers; BlocksLayers seaLayers; }; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index eeea5f64..be44f6f3 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -3,6 +3,7 @@ #include #include +#include "maths/util.hpp" #include "content/Content.hpp" #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" @@ -63,7 +64,7 @@ static inline const Biome* choose_biome( for (const auto& biome : biomes) { float score = 0.0f; for (uint i = 0; i < paramsCount; i++) { - score += glm::abs((params[i] - biome.parameters[i].origin) / + score += glm::abs((params[i] - biome.parameters[i].value) / biome.parameters[i].weight); } if (score < chosenScore) { @@ -92,6 +93,9 @@ void WorldGenerator::generate( std::memset(voxels, 0, sizeof(voxel) * CHUNK_VOL); + PseudoRandom plantsRand; + plantsRand.setSeed(chunkX, chunkZ); + for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { const Biome* biome = choose_biome(biomes, biomeParams, x, z); @@ -104,6 +108,15 @@ void WorldGenerator::generate( generate_pole(seaLayers, seaLevel, height, seaLevel, voxels, x, z); generate_pole(groundLayers, height, 0, seaLevel, voxels, x, z); + + if (height+1 > seaLevel) { + // TODO: add underwater plants support + float rand = (plantsRand.randU32() % RAND_MAX) / static_cast(RAND_MAX); + blockid_t plant = biome->plants.choose(rand); + if (plant) { + voxels[vox_index(x, height+1, z)].id = plant; + } + } } } } From a81555b979523dbec69ffa7b0ace21404b1c4ccb Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 20 Aug 2024 20:58:36 +0300 Subject: [PATCH 033/139] advanced plants test --- res/generators/default.lua | 3 ++- src/logic/scripting/scripting_world_generation.cpp | 5 ++++- src/world/generator/GeneratorDef.hpp | 5 +++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 789569e1..3560f706 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -21,7 +21,8 @@ biomes = { }, plant_chance = 0.5, plants = { - {block="base:grass", weight=1} + {block="base:grass", weight=1}, + {block="base:flower", weight=0.03}, } }, desert = { diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 2b500cbd..0f704e7e 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -1,5 +1,8 @@ #include "scripting.hpp" +#include +#include + #include "scripting_commons.hpp" #include "typedefs.hpp" #include "lua/lua_engine.hpp" @@ -188,7 +191,7 @@ static inline Biome load_biome( } lua::pop(L); } - // TODO: sort by weight descending + std::sort(plants.begin(), plants.end(), std::greater()); BlocksLayers groundLayers = load_layers(L, "layers"); BlocksLayers seaLayers = load_layers(L, "sea_layers"); diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index b424b636..4824a39e 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -41,6 +41,7 @@ struct BiomeParameter { float weight; }; +/// @brief Plant is a single-block structure randomly generating in world struct PlantEntry { /// @brief Plant block id std::string block; @@ -50,6 +51,10 @@ struct PlantEntry { struct { blockid_t id; } rt; + + bool operator>(const PlantEntry& other) const { + return weight > other.weight; + } }; struct BiomePlants { From 0a7ab154d4791fcebe68b2dbc1e5d9d2fe5db9ab Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 21 Aug 2024 22:44:33 +0300 Subject: [PATCH 034/139] refactor --- .../scripting/scripting_world_generation.cpp | 57 +++++++++++-------- src/world/generator/WorldGenerator.cpp | 4 +- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 0f704e7e..94fceb7b 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -149,6 +149,36 @@ static inline BlocksLayers load_layers( return BlocksLayers {std::move(layers), lastLayersHeight}; } +static inline BiomePlants load_plants(lua::State* L) { + float plantChance = lua::get_number_field(L, "plant_chance", 0.0); + float plantsWeightSum = 0.0f; + std::vector plants; + if (lua::getfield(L, "plants")) { + if (!lua::istable(L, -1)) { + throw std::runtime_error("'plants' must be a table"); + } + int plantsCount = lua::objlen(L, -1); + for (int i = 1; i <= plantsCount; i++) { + lua::rawgeti(L, i); + if (!lua::istable(L, -1)) { + throw std::runtime_error("plant must be a table"); + } + auto block = lua::require_string_field(L, "block"); + float weight = lua::require_number_field(L, "weight"); + if (weight <= 0.0f) { + throw std::runtime_error("weight must be positive"); + } + plantsWeightSum += weight; + plants.push_back(PlantEntry {block, weight, {}}); + lua::pop(L); + } + lua::pop(L); + } + std::sort(plants.begin(), plants.end(), std::greater()); + return BiomePlants { + std::move(plants), plantsWeightSum, plantChance}; +} + static inline Biome load_biome( lua::State* L, const std::string& name, uint parametersCount, int idx ) { @@ -169,37 +199,14 @@ static inline Biome load_biome( } lua::pop(L); - float plantChance = lua::get_number_field(L, "plant_chance", 0.0); - float plantsWeightSum = 0.0f; - std::vector plants; - if (lua::getfield(L, "plants")) { - if (!lua::istable(L, -1)) { - throw std::runtime_error("'plants' must be a table"); - } - int plantsCount = lua::objlen(L, -1); - for (int i = 1; i <= plantsCount; i++) { - lua::rawgeti(L, i); - if (!lua::istable(L, -1)) { - throw std::runtime_error("plant must be a table"); - } - auto block = lua::require_string_field(L, "block"); - float weight = lua::require_number_field(L, "weight"); - // TODO: range check (positive) - plantsWeightSum += weight; - plants.push_back(PlantEntry {block, weight, {}}); - lua::pop(L); - } - lua::pop(L); - } - std::sort(plants.begin(), plants.end(), std::greater()); - + BiomePlants plants = load_plants(L); BlocksLayers groundLayers = load_layers(L, "layers"); BlocksLayers seaLayers = load_layers(L, "sea_layers"); lua::pop(L); return Biome { name, std::move(parameters), - BiomePlants {plants, plantsWeightSum, plantChance}, + std::move(plants), std::move(groundLayers), std::move(seaLayers)}; } diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index be44f6f3..1ffcfef5 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -110,8 +110,8 @@ void WorldGenerator::generate( generate_pole(groundLayers, height, 0, seaLevel, voxels, x, z); if (height+1 > seaLevel) { - // TODO: add underwater plants support - float rand = (plantsRand.randU32() % RAND_MAX) / static_cast(RAND_MAX); + float rand = (plantsRand.randU32() % RAND_MAX) / + static_cast(RAND_MAX); blockid_t plant = biome->plants.choose(rand); if (plant) { voxels[vox_index(x, height+1, z)].id = plant; From b4e9cfa0bd897ad786e19765e9e7a007b9c5f373 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 22 Aug 2024 00:09:59 +0300 Subject: [PATCH 035/139] refactor: use dynamic::* for parsing --- .../scripting/scripting_world_generation.cpp | 112 ++++++++---------- 1 file changed, 48 insertions(+), 64 deletions(-) diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 94fceb7b..646a80e5 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -10,10 +10,9 @@ #include "content/Content.hpp" #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" +#include "data/dynamic.hpp" #include "world/generator/GeneratorDef.hpp" -// TODO: use dynamic::* for parsing - class LuaGeneratorScript : public GeneratorScript { scriptenv env; std::vector biomes; @@ -106,11 +105,11 @@ public: }; static BlocksLayer load_layer( - lua::State* L, int idx, uint& lastLayersHeight, bool& hasResizeableLayer + const dynamic::Map_sptr& map, int idx, uint& lastLayersHeight, bool& hasResizeableLayer ) { - auto name = lua::require_string_field(L, "block"); - int height = lua::require_integer_field(L, "height"); - bool belowSeaLevel = lua::get_boolean_field(L, "below_sea_level", true); + auto name = map->get("block"); + int height = map->get("height"); + bool belowSeaLevel = map->get("below_sea_level", true); if (hasResizeableLayer) { lastLayersHeight += height; @@ -125,54 +124,44 @@ static BlocksLayer load_layer( } static inline BlocksLayers load_layers( - lua::State* L, const std::string& fieldname + const dynamic::List_sptr& layersArr, const std::string& fieldname ) { uint lastLayersHeight = 0; bool hasResizeableLayer = false; std::vector layers; - if (lua::getfield(L, fieldname)) { - int len = lua::objlen(L, -1); - for (int i = 1; i <= len; i++) { - lua::rawgeti(L, i); - try { - layers.push_back( - load_layer(L, -1, lastLayersHeight, hasResizeableLayer)); - } catch (const std::runtime_error& err) { - throw std::runtime_error( - fieldname+" #"+std::to_string(i)+": "+err.what()); - } - lua::pop(L); + for (int i = 0; i < layersArr->size(); i++) { + const auto& layerMap = layersArr->map(i); + try { + layers.push_back( + load_layer(layerMap, -1, lastLayersHeight, hasResizeableLayer)); + } catch (const std::runtime_error& err) { + throw std::runtime_error( + fieldname+" #"+std::to_string(i)+": "+err.what()); } - lua::pop(L); } return BlocksLayers {std::move(layers), lastLayersHeight}; } -static inline BiomePlants load_plants(lua::State* L) { - float plantChance = lua::get_number_field(L, "plant_chance", 0.0); +static inline BiomePlants load_plants( + const dynamic::Map_sptr& biomeMap +) { + float plantChance = biomeMap->get("plant_chance", 0.0); float plantsWeightSum = 0.0f; + std::vector plants; - if (lua::getfield(L, "plants")) { - if (!lua::istable(L, -1)) { - throw std::runtime_error("'plants' must be a table"); - } - int plantsCount = lua::objlen(L, -1); - for (int i = 1; i <= plantsCount; i++) { - lua::rawgeti(L, i); - if (!lua::istable(L, -1)) { - throw std::runtime_error("plant must be a table"); - } - auto block = lua::require_string_field(L, "block"); - float weight = lua::require_number_field(L, "weight"); + if (biomeMap->has("plants")) { + auto plantsArr = biomeMap->list("plants"); + for (size_t i = 0; i < plantsArr->size(); i++) { + auto entry = plantsArr->map(i); + auto block = entry->get("block"); + float weight = entry->get("weight"); if (weight <= 0.0f) { throw std::runtime_error("weight must be positive"); } plantsWeightSum += weight; plants.push_back(PlantEntry {block, weight, {}}); - lua::pop(L); } - lua::pop(L); } std::sort(plants.begin(), plants.end(), std::greater()); return BiomePlants { @@ -180,29 +169,28 @@ static inline BiomePlants load_plants(lua::State* L) { } static inline Biome load_biome( - lua::State* L, const std::string& name, uint parametersCount, int idx + const dynamic::Map_sptr& biomeMap, + const std::string& name, + uint parametersCount, + int idx ) { - lua::pushvalue(L, idx); - std::vector parameters; - lua::requirefield(L, "parameters"); - if (lua::objlen(L, -1) < parametersCount) { + + const auto& paramsArr = biomeMap->list("parameters"); + if (paramsArr->size() < parametersCount) { throw std::runtime_error( std::to_string(parametersCount)+" parameters expected"); } - for (uint i = 1; i <= parametersCount; i++) { - lua::rawgeti(L, i); - float value = lua::require_number_field(L, "value"); - float weight = lua::require_number_field(L, "weight"); + for (size_t i = 0; i < parametersCount; i++) { + const auto& paramMap = paramsArr->map(i); + float value = paramMap->get("value"); + float weight = paramMap->get("weight"); parameters.push_back(BiomeParameter {value, weight}); - lua::pop(L); } - lua::pop(L); - BiomePlants plants = load_plants(L); - BlocksLayers groundLayers = load_layers(L, "layers"); - BlocksLayers seaLayers = load_layers(L, "sea_layers"); - lua::pop(L); + BiomePlants plants = load_plants(biomeMap); + BlocksLayers groundLayers = load_layers(biomeMap->list("layers"), "layers"); + BlocksLayers seaLayers = load_layers(biomeMap->list("sea_layers"), "sea_layers"); return Biome { name, std::move(parameters), @@ -221,30 +209,26 @@ std::unique_ptr scripting::load_generator( lua::pop(L, load_script(*env, "generator", file)); lua::pushenv(L, *env); + auto val = lua::tovalue(L, -1); + lua::pop(L); + + auto root = std::get(val); uint biomeParameters = lua::get_integer_field(L, "biome_parameters", 0, 0, 16); uint seaLevel = lua::get_integer_field(L, "sea_level", 0, 0, CHUNK_H); std::vector biomes; - lua::requirefield(L, "biomes"); - if (!lua::istable(L, -1)) { - throw std::runtime_error("'biomes' must be a table"); - } - lua::pushnil(L); - while (lua::next(L, -2)) { - lua::pushvalue(L, -2); - std::string biomeName = lua::tostring(L, -1); + + const auto& biomesMap = root->map("biomes"); + for (const auto& [biomeName, value] : biomesMap->values) { + const auto& biomeMap = std::get(value); try { biomes.push_back( - load_biome(L, biomeName, biomeParameters, -2)); + load_biome(biomeMap, biomeName, biomeParameters, -2)); } catch (const std::runtime_error& err) { throw std::runtime_error("biome "+biomeName+": "+err.what()); } - lua::pop(L, 2); } - lua::pop(L); - - lua::pop(L); return std::make_unique( std::move(env), std::move(biomes), From 3654d99cea464e50b1b3554fda3148ec97ae0d4d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 22 Aug 2024 01:50:38 +0300 Subject: [PATCH 036/139] update Serializable.hpp --- src/files/WorldRegions.cpp | 1 + src/interfaces/Serializable.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/files/WorldRegions.cpp b/src/files/WorldRegions.cpp index d47d64c9..bd97a1e7 100644 --- a/src/files/WorldRegions.cpp +++ b/src/files/WorldRegions.cpp @@ -4,6 +4,7 @@ #include #include +#include "coders/json.hpp" #include "coders/byte_utils.hpp" #include "coders/rle.hpp" #include "data/dynamic.hpp" diff --git a/src/interfaces/Serializable.hpp b/src/interfaces/Serializable.hpp index 89962079..881f599e 100644 --- a/src/interfaces/Serializable.hpp +++ b/src/interfaces/Serializable.hpp @@ -1,6 +1,6 @@ #pragma once -#include "coders/json.hpp" +#include "data/dynamic_fwd.hpp" #include From 259f40d5cfc578c0fce5f1d3e0e7a729ae37def1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 22 Aug 2024 01:58:51 +0300 Subject: [PATCH 037/139] update dynamic_util functions signatures --- src/data/dynamic_util.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/data/dynamic_util.hpp b/src/data/dynamic_util.hpp index 81ba5be0..c8a82ce9 100644 --- a/src/data/dynamic_util.hpp +++ b/src/data/dynamic_util.hpp @@ -5,8 +5,8 @@ #include "dynamic.hpp" namespace dynamic { - template - inline dynamic::List_sptr to_value(glm::vec vec) { + template + inline dynamic::List_sptr to_value(glm::vec vec) { auto list = dynamic::create_list(); for (size_t i = 0; i < n; i++) { list->put(vec[i]); @@ -14,8 +14,8 @@ namespace dynamic { return list; } - template - inline dynamic::List_sptr to_value(glm::mat mat) { + template + inline dynamic::List_sptr to_value(glm::mat mat) { auto list = dynamic::create_list(); for (size_t i = 0; i < n; i++) { for (size_t j = 0; j < m; j++) { From 3eec97f631b0624ac6e82d16234684928b2ec96a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 22 Aug 2024 02:29:11 +0300 Subject: [PATCH 038/139] fix generator loading --- src/logic/scripting/lua/lua_type_heightmap.cpp | 2 +- src/logic/scripting/scripting_world_generation.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/logic/scripting/lua/lua_type_heightmap.cpp b/src/logic/scripting/lua/lua_type_heightmap.cpp index e5fd46ac..f467dc35 100644 --- a/src/logic/scripting/lua/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/lua_type_heightmap.cpp @@ -29,7 +29,7 @@ void LuaHeightmap::setSeed(int64_t seed) { static int l_dump(lua::State* L) { if (auto heightmap = touserdata(L, 1)) { - auto filename = tostring(L, 2); + auto filename = require_string(L, 2); if (!files::is_valid_name(filename)) { throw std::runtime_error("invalid file name"); } diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 646a80e5..29ab2cc8 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -105,7 +105,7 @@ public: }; static BlocksLayer load_layer( - const dynamic::Map_sptr& map, int idx, uint& lastLayersHeight, bool& hasResizeableLayer + const dynamic::Map_sptr& map, uint& lastLayersHeight, bool& hasResizeableLayer ) { auto name = map->get("block"); int height = map->get("height"); @@ -134,7 +134,7 @@ static inline BlocksLayers load_layers( const auto& layerMap = layersArr->map(i); try { layers.push_back( - load_layer(layerMap, -1, lastLayersHeight, hasResizeableLayer)); + load_layer(layerMap, lastLayersHeight, hasResizeableLayer)); } catch (const std::runtime_error& err) { throw std::runtime_error( fieldname+" #"+std::to_string(i)+": "+err.what()); @@ -214,8 +214,8 @@ std::unique_ptr scripting::load_generator( auto root = std::get(val); - uint biomeParameters = lua::get_integer_field(L, "biome_parameters", 0, 0, 16); - uint seaLevel = lua::get_integer_field(L, "sea_level", 0, 0, CHUNK_H); + uint biomeParameters = root->get("biome_parameters"); + uint seaLevel = root->get("sea_level"); std::vector biomes; From 2d7d03efbb7498b5a9d117536aecd6234055cd19 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 22 Aug 2024 20:35:41 +0300 Subject: [PATCH 039/139] add structures (WIP) --- res/scripts/stdcmd.lua | 17 ++++++++ src/data/dynamic_util.hpp | 25 ++++++++++++ src/logic/scripting/lua/libgeneration.cpp | 26 ++++++++++++ src/world/generator/Structure.cpp | 50 +++++++++++++++++++++++ src/world/generator/Structure.hpp | 27 ++++++++++++ 5 files changed, 145 insertions(+) create mode 100644 src/world/generator/Structure.cpp create mode 100644 src/world/generator/Structure.hpp diff --git a/res/scripts/stdcmd.lua b/res/scripts/stdcmd.lua index 61dccba8..9d05a3b5 100644 --- a/res/scripts/stdcmd.lua +++ b/res/scripts/stdcmd.lua @@ -149,3 +149,20 @@ console.add_command( end end ) + +console.add_command( + "structure.save x:int y:int z:int w:int h:int d:int name:str='untitled'", + "Save structure", + function(args, kwargs) + local x = args[1] + local y = args[2] + local z = args[3] + + local w = args[4] + local h = args[5] + local d = args[6] + + local name = args[7] + generation.save_structure({x, y, z}, {x+w, y+h, z+d}, name..'.vox', false) + end +) diff --git a/src/data/dynamic_util.hpp b/src/data/dynamic_util.hpp index c8a82ce9..836eb21f 100644 --- a/src/data/dynamic_util.hpp +++ b/src/data/dynamic_util.hpp @@ -38,6 +38,20 @@ namespace dynamic { } } + /// TODO: remove raw pointer overloads + template + void get_vec( + const dynamic::Map* root, + const std::string& name, + glm::vec& vec + ) { + if (const auto& list = root->list(name)) { + for (size_t i = 0; i < n; i++) { + vec[i] = list->integer(i); + } + } + } + template void get_vec( const dynamic::List_sptr& root, size_t index, glm::vec& vec @@ -49,6 +63,17 @@ namespace dynamic { } } + template + void get_vec( + const dynamic::List_sptr& root, size_t index, glm::vec& vec + ) { + if (const auto& list = root->list(index)) { + for (size_t i = 0; i < n; i++) { + vec[i] = list->integer(i); + } + } + } + template void get_mat( const dynamic::Map_sptr& root, diff --git a/src/logic/scripting/lua/libgeneration.cpp b/src/logic/scripting/lua/libgeneration.cpp index c9b55e1d..106ddbb1 100644 --- a/src/logic/scripting/lua/libgeneration.cpp +++ b/src/logic/scripting/lua/libgeneration.cpp @@ -1,4 +1,30 @@ #include "api_lua.hpp" +#include "files/files.hpp" +#include "files/util.hpp" +#include "coders/binary_json.hpp" +#include "world/Level.hpp" +#include "world/generator/Structure.hpp" + +using namespace scripting; + +static int l_save_structure(lua::State* L) { + auto pointA = lua::tovec<3>(L, 1); + auto pointB = lua::tovec<3>(L, 2); + auto filename = lua::require_string(L, 3); + if (!files::is_valid_name(filename)) { + throw std::runtime_error("invalid file name"); + } + bool saveEntities = lua::toboolean(L, 4); + + auto structure = Structure::create(level, pointA, pointB, saveEntities); + auto map = structure->serialize(); + + auto bytes = json::to_binary(map.get()); + files::write_bytes(fs::u8path(filename), bytes.data(), bytes.size()); + return 0; +} + const luaL_Reg generationlib[] = { + {"save_structure", lua::wrap}, {NULL, NULL}}; diff --git a/src/world/generator/Structure.cpp b/src/world/generator/Structure.cpp new file mode 100644 index 00000000..0ce55c67 --- /dev/null +++ b/src/world/generator/Structure.cpp @@ -0,0 +1,50 @@ +#include "Structure.hpp" + +#include + +#include "data/dynamic.hpp" +#include "data/dynamic_util.hpp" +#include "voxels/ChunksStorage.hpp" +#include "voxels/VoxelsVolume.hpp" +#include "world/Level.hpp" + +std::unique_ptr Structure::create( + Level* level, const glm::ivec3& a, const glm::ivec3& b, bool entities +) { + auto start = glm::min(a, b); + auto size = glm::abs(a - b); + + VoxelsVolume volume(size.x, size.y, size.z); + level->chunksStorage->getVoxels(&volume); + + auto volVoxels = volume.getVoxels(); + std::vector voxels(size.x*size.y*size.z); + std::memcpy(voxels.data(), volVoxels, sizeof(voxel) * voxels.size()); + + return std::make_unique(size, std::move(voxels)); +} + +std::unique_ptr Structure::serialize() const { + auto root = std::make_unique(); + root->put("version", STRUCTURE_FORMAT_VERSION); + root->put("size", dynamic::to_value(size)); + + auto& voxelsArr = root->putList("voxels"); + for (size_t i = 0; i < voxels.size(); i++) { + voxelsArr.put(voxels[i].id); + voxelsArr.put(blockstate2int(voxels[i].state)); + } + return root; +} + +void Structure::deserialize(dynamic::Map* src) { + size = glm::ivec3(); + dynamic::get_vec(src, "size", size); + voxels.resize(size.x*size.y*size.z); + + auto voxelsArr = src->list("voxels"); + for (size_t i = 0; i < size.x*size.y*size.z; i++) { + voxels[i].id = voxelsArr->integer(i * 2); + voxels[i].state = int2blockstate(voxelsArr->integer(i * 2 + 1)); + } +} diff --git a/src/world/generator/Structure.hpp b/src/world/generator/Structure.hpp new file mode 100644 index 00000000..93d93a4d --- /dev/null +++ b/src/world/generator/Structure.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include "interfaces/Serializable.hpp" +#include "voxels/voxel.hpp" + +inline constexpr int STRUCTURE_FORMAT_VERSION = 1; + +class Level; + +struct Structure : public Serializable { + glm::ivec3 size; + std::vector voxels; + + Structure() : size() {} + + Structure(glm::ivec3 size, std::vector voxels) + : size(size), voxels(std::move(voxels)) {} + + std::unique_ptr serialize() const override; + void deserialize(dynamic::Map* src) override; + + static std::unique_ptr create( + Level* level, const glm::ivec3& a, const glm::ivec3& b, bool entities); +}; From c454c095a0f5a3051f22c05301bc41d2c5d88aa0 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 22 Aug 2024 21:26:30 +0300 Subject: [PATCH 040/139] fix msvc build --- src/world/generator/Structure.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/world/generator/Structure.cpp b/src/world/generator/Structure.cpp index 0ce55c67..1d4837b7 100644 --- a/src/world/generator/Structure.cpp +++ b/src/world/generator/Structure.cpp @@ -31,8 +31,8 @@ std::unique_ptr Structure::serialize() const { auto& voxelsArr = root->putList("voxels"); for (size_t i = 0; i < voxels.size(); i++) { - voxelsArr.put(voxels[i].id); - voxelsArr.put(blockstate2int(voxels[i].state)); + voxelsArr.put(static_cast(voxels[i].id)); + voxelsArr.put(static_cast(blockstate2int(voxels[i].state))); } return root; } From 5aa1ef52f0460e1c61d0b7ddf1af2bd1665670c4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 22 Aug 2024 22:19:53 +0300 Subject: [PATCH 041/139] fix msvc build 2 --- src/data/dynamic_util.hpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/data/dynamic_util.hpp b/src/data/dynamic_util.hpp index 836eb21f..ff389086 100644 --- a/src/data/dynamic_util.hpp +++ b/src/data/dynamic_util.hpp @@ -5,8 +5,8 @@ #include "dynamic.hpp" namespace dynamic { - template - inline dynamic::List_sptr to_value(glm::vec vec) { + template + inline dynamic::List_sptr to_value(glm::vec vec) { auto list = dynamic::create_list(); for (size_t i = 0; i < n; i++) { list->put(vec[i]); @@ -14,6 +14,15 @@ namespace dynamic { return list; } + template + inline dynamic::List_sptr to_value(glm::vec vec) { + auto list = dynamic::create_list(); + for (size_t i = 0; i < n; i++) { + list->put(static_cast(vec[i])); + } + return list; + } + template inline dynamic::List_sptr to_value(glm::mat mat) { auto list = dynamic::create_list(); From e0a14c9c16041c453c4584e112542be1252f97df Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 22 Aug 2024 22:43:39 +0300 Subject: [PATCH 042/139] refactor dynamic_util --- src/data/dynamic.hpp | 3 +++ src/data/dynamic_util.hpp | 13 ++----------- src/world/generator/Structure.cpp | 4 ++-- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/data/dynamic.hpp b/src/data/dynamic.hpp index 65320fbb..7aca99a5 100644 --- a/src/data/dynamic.hpp +++ b/src/data/dynamic.hpp @@ -65,6 +65,9 @@ namespace dynamic { return put(List_sptr(value.release())); } List& put(const Value& value); + List& put(uint64_t value) { + return put(static_cast(value)); + } Value* getValueWriteable(size_t index); diff --git a/src/data/dynamic_util.hpp b/src/data/dynamic_util.hpp index ff389086..836eb21f 100644 --- a/src/data/dynamic_util.hpp +++ b/src/data/dynamic_util.hpp @@ -5,8 +5,8 @@ #include "dynamic.hpp" namespace dynamic { - template - inline dynamic::List_sptr to_value(glm::vec vec) { + template + inline dynamic::List_sptr to_value(glm::vec vec) { auto list = dynamic::create_list(); for (size_t i = 0; i < n; i++) { list->put(vec[i]); @@ -14,15 +14,6 @@ namespace dynamic { return list; } - template - inline dynamic::List_sptr to_value(glm::vec vec) { - auto list = dynamic::create_list(); - for (size_t i = 0; i < n; i++) { - list->put(static_cast(vec[i])); - } - return list; - } - template inline dynamic::List_sptr to_value(glm::mat mat) { auto list = dynamic::create_list(); diff --git a/src/world/generator/Structure.cpp b/src/world/generator/Structure.cpp index 1d4837b7..0ce55c67 100644 --- a/src/world/generator/Structure.cpp +++ b/src/world/generator/Structure.cpp @@ -31,8 +31,8 @@ std::unique_ptr Structure::serialize() const { auto& voxelsArr = root->putList("voxels"); for (size_t i = 0; i < voxels.size(); i++) { - voxelsArr.put(static_cast(voxels[i].id)); - voxelsArr.put(static_cast(blockstate2int(voxels[i].state))); + voxelsArr.put(voxels[i].id); + voxelsArr.put(blockstate2int(voxels[i].state)); } return root; } From 33531ae0a4fc01591bcbe35eb2cec392deb7ca2f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 22 Aug 2024 23:28:40 +0300 Subject: [PATCH 043/139] fix --- src/data/dynamic.hpp | 3 --- src/data/dynamic_util.hpp | 13 +++++++++++-- src/world/generator/Structure.cpp | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/data/dynamic.hpp b/src/data/dynamic.hpp index 7aca99a5..65320fbb 100644 --- a/src/data/dynamic.hpp +++ b/src/data/dynamic.hpp @@ -65,9 +65,6 @@ namespace dynamic { return put(List_sptr(value.release())); } List& put(const Value& value); - List& put(uint64_t value) { - return put(static_cast(value)); - } Value* getValueWriteable(size_t index); diff --git a/src/data/dynamic_util.hpp b/src/data/dynamic_util.hpp index 836eb21f..ff389086 100644 --- a/src/data/dynamic_util.hpp +++ b/src/data/dynamic_util.hpp @@ -5,8 +5,8 @@ #include "dynamic.hpp" namespace dynamic { - template - inline dynamic::List_sptr to_value(glm::vec vec) { + template + inline dynamic::List_sptr to_value(glm::vec vec) { auto list = dynamic::create_list(); for (size_t i = 0; i < n; i++) { list->put(vec[i]); @@ -14,6 +14,15 @@ namespace dynamic { return list; } + template + inline dynamic::List_sptr to_value(glm::vec vec) { + auto list = dynamic::create_list(); + for (size_t i = 0; i < n; i++) { + list->put(static_cast(vec[i])); + } + return list; + } + template inline dynamic::List_sptr to_value(glm::mat mat) { auto list = dynamic::create_list(); diff --git a/src/world/generator/Structure.cpp b/src/world/generator/Structure.cpp index 0ce55c67..1d4837b7 100644 --- a/src/world/generator/Structure.cpp +++ b/src/world/generator/Structure.cpp @@ -31,8 +31,8 @@ std::unique_ptr Structure::serialize() const { auto& voxelsArr = root->putList("voxels"); for (size_t i = 0; i < voxels.size(); i++) { - voxelsArr.put(voxels[i].id); - voxelsArr.put(blockstate2int(voxels[i].state)); + voxelsArr.put(static_cast(voxels[i].id)); + voxelsArr.put(static_cast(blockstate2int(voxels[i].state))); } return root; } From 777453718aa758ec245c0606ba3b31e3fe4be62b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 22 Aug 2024 23:50:46 +0300 Subject: [PATCH 044/139] fix animation loading --- src/assets/AssetsLoader.cpp | 3 +++ src/assets/assetload_funcs.cpp | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index 9a570fc2..a4853606 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -194,6 +194,9 @@ void AssetsLoader::processPreloadConfigs(const Content* content) { return; } for (auto& entry : content->getPacks()) { + if (entry.first == "core") { + continue; + } const auto& pack = entry.second; auto preloadFile = pack->getInfo().folder / fs::path("preload.json"); if (fs::exists(preloadFile)) { diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index 97107862..35133264 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -12,6 +12,7 @@ #include "coders/obj.hpp" #include "constants.hpp" #include "data/dynamic.hpp" +#include "debug/Logger.hpp" #include "files/engine_paths.hpp" #include "files/files.hpp" #include "frontend/UiDocument.hpp" @@ -26,6 +27,8 @@ #include "Assets.hpp" #include "AssetsLoader.hpp" +static debug::Logger logger("assetload-funcs"); + namespace fs = std::filesystem; static bool animation( @@ -263,7 +266,7 @@ static TextureAnimation create_animation( for (const auto& elem : frameList) { if (!srcAtlas->has(elem.first)) { - std::cerr << "Unknown frame name: " << elem.first << std::endl; + logger.error() << "unknown frame name: " << elem.first; continue; } region = srcAtlas->get(elem.first); From f40ff7cd28684dc800f75ffc1b2fdf4798aa235b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 23 Aug 2024 03:07:02 +0300 Subject: [PATCH 045/139] refactor WorldGenerator & update test generator script --- res/generators/default.lua | 2 +- src/logic/ChunksController.cpp | 5 +++-- src/world/generator/WorldGenerator.cpp | 22 ++++++++++------------ src/world/generator/WorldGenerator.hpp | 12 +++++++++--- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 3560f706..75bfb868 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -19,7 +19,7 @@ biomes = { {block="base:stone", height=-1}, {block="base:bazalt", height=1}, }, - plant_chance = 0.5, + plant_chance = 0.3, plants = { {block="base:grass", weight=1}, {block="base:flower", weight=0.03}, diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index d84ed8d4..a1382df5 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -29,7 +29,8 @@ ChunksController::ChunksController(Level* level, uint padding) padding(padding), generator(std::make_unique( level->content->generators.require(level->getWorld()->getGenerator()), - level->content + level->content, + level->getWorld()->getSeed() )) {} ChunksController::~ChunksController() = default; @@ -116,7 +117,7 @@ void ChunksController::createChunk(int x, int z) { auto& chunkFlags = chunk->flags; if (!chunkFlags.loaded) { - generator->generate(chunk->voxels, x, z, level->getWorld()->getSeed()); + generator->generate(chunk->voxels, x, z); chunkFlags.unsaved = true; } chunk->updateHeights(); diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 1ffcfef5..5fdfb131 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -7,23 +7,24 @@ #include "content/Content.hpp" #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" -#include "voxels/voxel.hpp" #include "world/generator/GeneratorDef.hpp" +#include "util/timeutil.hpp" static inline constexpr uint MAX_PARAMETERS = 16; -WorldGenerator::WorldGenerator(const GeneratorDef& def, const Content* content) - : def(def), content(content) { +WorldGenerator::WorldGenerator( + const GeneratorDef& def, const Content* content, uint64_t seed +) + : def(def), content(content), seed(seed) { + voxel voxels[CHUNK_VOL]; } static inline void generate_pole( const BlocksLayers& layers, - int top, - int bottom, + int top, int bottom, int seaLevel, voxel* voxels, - int x, - int z + int x, int z ) { uint y = top; uint layerExtension = 0; @@ -75,11 +76,8 @@ static inline const Biome* choose_biome( return chosenBiome; } -#include "util/timeutil.hpp" -void WorldGenerator::generate( - voxel* voxels, int chunkX, int chunkZ, uint64_t seed -) { - timeutil::ScopeLogTimer log(555); +void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { + // timeutil::ScopeLogTimer log(555); auto heightmap = def.script->generateHeightmap( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed ); diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 93127d10..6b001eea 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -1,8 +1,10 @@ #pragma once #include +#include #include "typedefs.hpp" +#include "voxels/voxel.hpp" struct voxel; class Content; @@ -12,10 +14,15 @@ struct GeneratorDef; class WorldGenerator { const GeneratorDef& def; const Content* content; + uint64_t seed; public: + /// @param def generator definition + /// @param content world content + /// @param seed world seed WorldGenerator( const GeneratorDef& def, - const Content* content + const Content* content, + uint64_t seed ); virtual ~WorldGenerator() = default; @@ -23,8 +30,7 @@ public: /// @param voxels destinatiopn chunk voxels buffer /// @param x chunk position X divided by CHUNK_W /// @param z chunk position Y divided by CHUNK_D - /// @param seed world seed - virtual void generate(voxel* voxels, int x, int z, uint64_t seed); + virtual void generate(voxel* voxels, int x, int z); inline static std::string DEFAULT = "core:default"; }; From f413a6f0ace408febc19f5764783462472bb6eb2 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 23 Aug 2024 12:07:38 +0300 Subject: [PATCH 046/139] add ChunkPrototype --- src/world/generator/WorldGenerator.cpp | 34 ++++++++++++++++++-------- src/world/generator/WorldGenerator.hpp | 20 ++++++++++++++- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 5fdfb131..5d3534e3 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -16,7 +16,6 @@ WorldGenerator::WorldGenerator( const GeneratorDef& def, const Content* content, uint64_t seed ) : def(def), content(content), seed(seed) { - voxel voxels[CHUNK_VOL]; } static inline void generate_pole( @@ -76,16 +75,31 @@ static inline const Biome* choose_biome( return chosenBiome; } +std::unique_ptr WorldGenerator::generatePrototype( + int chunkX, int chunkZ +) { + timeutil::ScopeLogTimer log(666); + auto heightmap = def.script->generateHeightmap( + {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed); + auto biomeParams = def.script->generateParameterMaps( + {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed); + const auto& biomes = def.script->getBiomes(); + + std::vector chunkBiomes(CHUNK_W*CHUNK_D); + for (uint z = 0; z < CHUNK_D; z++) { + for (uint x = 0; x < CHUNK_W; x++) { + chunkBiomes[z * CHUNK_W + x] = choose_biome(biomes, biomeParams, x, z); + } + } + return std::make_unique( + std::move(heightmap), std::move(chunkBiomes)); +} + void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { // timeutil::ScopeLogTimer log(555); - auto heightmap = def.script->generateHeightmap( - {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed - ); - auto biomeParams = def.script->generateParameterMaps( - {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed - ); - auto values = heightmap->getValues(); - const auto& biomes = def.script->getBiomes(); + + auto prototype = generatePrototype(chunkX, chunkZ); + const auto values = prototype->heightmap->getValues(); uint seaLevel = def.script->getSeaLevel(); @@ -96,7 +110,7 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { - const Biome* biome = choose_biome(biomes, biomeParams, x, z); + const Biome* biome = prototype->biomes[z * CHUNK_W + x]; int height = values[z * CHUNK_W + x] * CHUNK_H; height = std::max(0, height); diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 6b001eea..a0cd4fe0 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -2,19 +2,37 @@ #include #include +#include +#include "constants.hpp" #include "typedefs.hpp" #include "voxels/voxel.hpp" -struct voxel; class Content; struct GeneratorDef; +class Heightmap; +struct Biome; + +struct ChunkPrototype { + /// @brief chunk heightmap + std::shared_ptr heightmap; + /// @brief chunk biomes matrix + std::vector biomes; + + ChunkPrototype( + std::shared_ptr heightmap, std::vector biomes + ) : heightmap(std::move(heightmap)), biomes(std::move(biomes)) {}; +}; /// @brief High-level world generation controller class WorldGenerator { const GeneratorDef& def; const Content* content; uint64_t seed; + + std::vector> chunks; + + std::unique_ptr generatePrototype(int x, int z); public: /// @param def generator definition /// @param content world content From 54067d0ab23495d7bff1e7735b5ebacde4625e0a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 23 Aug 2024 23:29:40 +0300 Subject: [PATCH 047/139] fix layers generation --- src/world/generator/WorldGenerator.cpp | 2 +- src/world/generator/WorldGenerator.hpp | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 5d3534e3..53649bf7 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -40,7 +40,7 @@ static inline void generate_pole( } else { layerHeight += layerExtension; } - layerHeight = std::min(static_cast(layerHeight), y); + layerHeight = std::min(static_cast(layerHeight), y+1); for (uint i = 0; i < layerHeight; i++, y--) { voxels[vox_index(x, y, z)].id = layer.rt.id; diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index a0cd4fe0..d8a32b16 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -26,17 +26,18 @@ struct ChunkPrototype { /// @brief High-level world generation controller class WorldGenerator { + /// @param def generator definition const GeneratorDef& def; + /// @param content world content const Content* content; + /// @param seed world seed uint64_t seed; - std::vector> chunks; - + /// @brief Generate chunk prototype (see ChunkPrototype) + /// @param x chunk position X divided by CHUNK_W + /// @param z chunk position Y divided by CHUNK_D std::unique_ptr generatePrototype(int x, int z); public: - /// @param def generator definition - /// @param content world content - /// @param seed world seed WorldGenerator( const GeneratorDef& def, const Content* content, From 4c697c0f4697487fb9186da990be931b7d4e0aa1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 24 Aug 2024 03:20:16 +0300 Subject: [PATCH 048/139] add structure block names (for indexing) --- src/world/generator/Structure.cpp | 31 +++++++++++++++++++++++++++++-- src/world/generator/Structure.hpp | 15 +++++++++++++-- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/world/generator/Structure.cpp b/src/world/generator/Structure.cpp index 1d4837b7..bf5426c7 100644 --- a/src/world/generator/Structure.cpp +++ b/src/world/generator/Structure.cpp @@ -1,9 +1,12 @@ #include "Structure.hpp" +#include #include #include "data/dynamic.hpp" #include "data/dynamic_util.hpp" +#include "content/Content.hpp" +#include "voxels/Block.hpp" #include "voxels/ChunksStorage.hpp" #include "voxels/VoxelsVolume.hpp" #include "world/Level.hpp" @@ -15,13 +18,33 @@ std::unique_ptr Structure::create( auto size = glm::abs(a - b); VoxelsVolume volume(size.x, size.y, size.z); + volume.setPosition(start.x, start.y, start.z); level->chunksStorage->getVoxels(&volume); auto volVoxels = volume.getVoxels(); std::vector voxels(size.x*size.y*size.z); - std::memcpy(voxels.data(), volVoxels, sizeof(voxel) * voxels.size()); - return std::make_unique(size, std::move(voxels)); + std::vector blockNames; + std::unordered_map blocksRegistered; + auto contentIndices = level->content->getIndices(); + for (size_t i = 0 ; i < voxels.size(); i++) { + blockid_t id = volVoxels[i].id; + blockid_t index; + + auto found = blocksRegistered.find(id); + if (found == blocksRegistered.end()) { + const auto& def = contentIndices->blocks.require(id); + index = blockNames.size(); + blockNames.push_back(def.name); + blocksRegistered[id] = index; + } else { + index = found->second; + } + voxels[i].id = index; + } + + return std::make_unique( + size, std::move(voxels), std::move(blockNames)); } std::unique_ptr Structure::serialize() const { @@ -29,6 +52,10 @@ std::unique_ptr Structure::serialize() const { root->put("version", STRUCTURE_FORMAT_VERSION); root->put("size", dynamic::to_value(size)); + auto& blockNamesArr = root->putList("block-names"); + for (const auto& name : blockNames) { + blockNamesArr.put(name); + } auto& voxelsArr = root->putList("voxels"); for (size_t i = 0; i < voxels.size(); i++) { voxelsArr.put(static_cast(voxels[i].id)); diff --git a/src/world/generator/Structure.hpp b/src/world/generator/Structure.hpp index 93d93a4d..2e932c61 100644 --- a/src/world/generator/Structure.hpp +++ b/src/world/generator/Structure.hpp @@ -9,15 +9,26 @@ inline constexpr int STRUCTURE_FORMAT_VERSION = 1; class Level; +class Content; struct Structure : public Serializable { glm::ivec3 size; + + /// @brief Structure voxels indexed different to world content std::vector voxels; + /// @brief Block names are used for indexing + std::vector blockNames; Structure() : size() {} - Structure(glm::ivec3 size, std::vector voxels) - : size(size), voxels(std::move(voxels)) {} + Structure( + glm::ivec3 size, + std::vector voxels, + std::vector blockNames + ): size(size), + voxels(std::move(voxels)), + blockNames(std::move(blockNames)) + {} std::unique_ptr serialize() const override; void deserialize(dynamic::Map* src) override; From a1129ccf380613fcb8480e80d1c237ee2316f2b2 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 24 Aug 2024 07:56:54 +0300 Subject: [PATCH 049/139] split WorldGenerator::generatePrototype --- res/generators/default.lua | 6 ++++-- src/world/generator/WorldGenerator.cpp | 20 +++++++++++++++----- src/world/generator/WorldGenerator.hpp | 16 ++++++++++++++-- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 75bfb868..e96ea22e 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -102,9 +102,11 @@ end function generate_biome_parameters(x, y, w, h, seed) local tempmap = Heightmap(w, h) tempmap.noiseSeed = seed + 5324 - tempmap:noise({x, y}, 0.8, 3) + tempmap:noise({x, y}, 0.4, 4) local hummap = Heightmap(w, h) hummap.noiseSeed = seed + 953 - hummap:noise({x, y}, 0.2, 2) + hummap:noise({x, y}, 0.16, 4) + tempmap:pow(2) + hummap:pow(2) return tempmap, hummap end diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 53649bf7..1347a10f 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -78,9 +78,7 @@ static inline const Biome* choose_biome( std::unique_ptr WorldGenerator::generatePrototype( int chunkX, int chunkZ ) { - timeutil::ScopeLogTimer log(666); - auto heightmap = def.script->generateHeightmap( - {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed); + // timeutil::ScopeLogTimer log(666); auto biomeParams = def.script->generateParameterMaps( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed); const auto& biomes = def.script->getBiomes(); @@ -92,13 +90,25 @@ std::unique_ptr WorldGenerator::generatePrototype( } } return std::make_unique( - std::move(heightmap), std::move(chunkBiomes)); + ChunkPrototypeLevel::HEIGHTMAP, + nullptr, + std::move(chunkBiomes)); +} + +void WorldGenerator::generateHeightmap( + ChunkPrototype* prototype, int chunkX, int chunkZ +) { + prototype->heightmap = def.script->generateHeightmap( + {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed); + prototype->level = ChunkPrototypeLevel::HEIGHTMAP; } void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { - // timeutil::ScopeLogTimer log(555); + timeutil::ScopeLogTimer log(555); auto prototype = generatePrototype(chunkX, chunkZ); + generateHeightmap(prototype.get(), chunkX, chunkZ); + const auto values = prototype->heightmap->getValues(); uint seaLevel = def.script->getSeaLevel(); diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index d8a32b16..84c1022a 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -13,15 +13,25 @@ struct GeneratorDef; class Heightmap; struct Biome; +enum class ChunkPrototypeLevel { + BIOME, HEIGHTMAP +}; + struct ChunkPrototype { + ChunkPrototypeLevel level; + /// @brief chunk heightmap std::shared_ptr heightmap; /// @brief chunk biomes matrix std::vector biomes; ChunkPrototype( - std::shared_ptr heightmap, std::vector biomes - ) : heightmap(std::move(heightmap)), biomes(std::move(biomes)) {}; + ChunkPrototypeLevel level, + std::shared_ptr heightmap, + std::vector biomes + ) : level(level), + heightmap(std::move(heightmap)), + biomes(std::move(biomes)) {}; }; /// @brief High-level world generation controller @@ -37,6 +47,8 @@ class WorldGenerator { /// @param x chunk position X divided by CHUNK_W /// @param z chunk position Y divided by CHUNK_D std::unique_ptr generatePrototype(int x, int z); + + void generateHeightmap(ChunkPrototype* prototype, int x, int z); public: WorldGenerator( const GeneratorDef& def, From 0c4f96d620089823800e6aca80191e79ed2d8513 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 24 Aug 2024 07:57:29 +0300 Subject: [PATCH 050/139] add SurroundMap class header --- src/world/generator/SurroundMap.hpp | 38 ++++++++++++++++++++++++++++ test/world/generator/SurroundMap.cpp | 7 +++++ 2 files changed, 45 insertions(+) create mode 100644 src/world/generator/SurroundMap.hpp create mode 100644 test/world/generator/SurroundMap.cpp diff --git a/src/world/generator/SurroundMap.hpp b/src/world/generator/SurroundMap.hpp new file mode 100644 index 00000000..45713870 --- /dev/null +++ b/src/world/generator/SurroundMap.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +#define GLM_ENABLE_EXPERIMENTAL +#include + +class SurroundMap { + struct Entry { + /// @brief Level is increased when all surrounding (8) entries having + // greather or equal confirmed level + int level = 0; + /// @brief number of surrounding entries having greather or equal + /// confirmed level + int surrounding = 0; + /// @brief level confirmed status (entry is ready to expand) + bool confirmed = true; + /// @brief mark used on sweep event (extra isles garbage collection) + bool marked = false; + }; + std::unordered_map entries; +public: + /// @brief Reset all isles marks + void resetMarks(); + + /// @brief Mark all connected entries + /// @param point origin point + void markIsle(const glm::ivec2& point); + + /// @brief Erase all non-marked isles + /// @return erased entries positions + std::vector sweep(); + + /// @brief Expand all confirmed entries with specified level + /// @param level target entries level + /// @return All upgraded entries positions + std::vector expand(int level); +}; diff --git a/test/world/generator/SurroundMap.cpp b/test/world/generator/SurroundMap.cpp new file mode 100644 index 00000000..ede9217f --- /dev/null +++ b/test/world/generator/SurroundMap.cpp @@ -0,0 +1,7 @@ +#include + +#include "world/generator/SurroundMap.hpp" + +TEST(SurroundMap, InitTest) { + SurroundMap map; +} From c2ac6ac54ae91ef411f93d0b157723e3ddf34974 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 24 Aug 2024 08:15:14 +0300 Subject: [PATCH 051/139] fix --- src/world/generator/WorldGenerator.cpp | 2 +- src/world/generator/WorldGenerator.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 1347a10f..5fef618b 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -90,7 +90,7 @@ std::unique_ptr WorldGenerator::generatePrototype( } } return std::make_unique( - ChunkPrototypeLevel::HEIGHTMAP, + ChunkPrototypeLevel::BIOMES, nullptr, std::move(chunkBiomes)); } diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 84c1022a..804cd4e9 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -14,7 +14,7 @@ class Heightmap; struct Biome; enum class ChunkPrototypeLevel { - BIOME, HEIGHTMAP + BIOMES, HEIGHTMAP }; struct ChunkPrototype { From cf3f263f7254eadf9eceaf15a1416807968e200b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 28 Aug 2024 11:41:04 +0300 Subject: [PATCH 052/139] update SurroundMap (WIP) --- src/world/generator/SurroundMap.cpp | 117 +++++++++++++++++++++++++++ src/world/generator/SurroundMap.hpp | 25 ++++-- test/world/generator/SurroundMap.cpp | 6 ++ 3 files changed, 140 insertions(+), 8 deletions(-) create mode 100644 src/world/generator/SurroundMap.cpp diff --git a/src/world/generator/SurroundMap.cpp b/src/world/generator/SurroundMap.cpp new file mode 100644 index 00000000..e54da0e0 --- /dev/null +++ b/src/world/generator/SurroundMap.cpp @@ -0,0 +1,117 @@ +#include "SurroundMap.hpp" + +#include +#include +#include + +void SurroundMap::resetMarks() { + for (auto& [_, entry] : entries) { + entry.marked = false; + } +} + +bool SurroundMap::createEntry(const glm::ivec2& origin) { + auto& entry = entries[origin]; + if (entry.confirmed) { + return false; + } + entry.confirmed = true; + + // calculate initial number of surrounding entries (any level) + int bitOffset = 0; + for (int y = -1; y <= 1; y++) { + for (int x = -1; x <= 1; x++) { + if (x == y && x == 0) { + continue; + } + const auto& found = entries.find(origin + glm::ivec2(x, y)); + if (found != entries.end()) { + entry.surrounding |= (1 << bitOffset); + if (found->second.level == 0) { + found->second.surrounding |= (1 << (7 - bitOffset)); + } + } + bitOffset++; + } + } + return true; +} + +std::vector SurroundMap::upgrade() { + std::vector expansion; + std::queue expanding; + for (const auto& [pos, entry] : entries) { + if (entry.confirmed && entry.surrounding != 0xFF) { + expanding.push(pos); + } + } + while (!expanding.empty()) { + assert(expanding.size() < 64); + + glm::ivec2 pos = expanding.front(); + expanding.pop(); + + const auto& found = entries.find(pos); + assert(found != entries.end() && "concurrent modification"); + + auto& entry = found->second; + int uplevelSurrounding = 0; + int bitOffset = 0; + for (int y = -1; y <= 1; y++) { + for (int x = -1; x <= 1; x++) { + if (x == y && x == 0) { + continue; + } + glm::ivec2 npos = {pos.x+x, pos.y+y}; + const auto& nfound = entries.find(npos); + + if (entry.surrounding & (1 << bitOffset)) { + auto& nentry = nfound->second; + if (nentry.level > entry.level) { + uplevelSurrounding |= (1 << bitOffset); + } + bitOffset++; + continue; + } + if (entry.level == 0) { + // neighbour entry does not exist + createEntry(npos); + expansion.push_back(npos); + } else{ + assert(nfound != entries.end() && "invalid map state"); + if (nfound->second.level == entry.level + 1) { + nfound->second.surrounding |= (1 << (7 - bitOffset)); + expanding.push(npos); + } + } + bitOffset++; + } + } + // level up + entry.surrounding = uplevelSurrounding; + entry.level++; + } + return expansion; +} + +void SurroundMap::getLevels(unsigned char* out, int width, int height, int ox, int oy) const { + std::memset(out, 0, width * height); + for (const auto& [pos, entry] : entries) { + int x = pos.x - ox; + int y = pos.y - oy; + if (x < 0 || x >= width || y < 0 || y >= height) { + continue; + } + + int surroundNum = 0; + for (int i = 0; i < 8; i++) { + if (entry.surrounding & (1 << i)) { + surroundNum++; + } + } + if (surroundNum) { + out[y * width + x] = surroundNum + 1; + } + out[y * width + x] = entry.level + 1; + } +} diff --git a/src/world/generator/SurroundMap.hpp b/src/world/generator/SurroundMap.hpp index 45713870..f8bca041 100644 --- a/src/world/generator/SurroundMap.hpp +++ b/src/world/generator/SurroundMap.hpp @@ -10,11 +10,13 @@ class SurroundMap { /// @brief Level is increased when all surrounding (8) entries having // greather or equal confirmed level int level = 0; - /// @brief number of surrounding entries having greather or equal - /// confirmed level - int surrounding = 0; + /// @brief bits storing surrounding entries having greather or equal + /// confirmed level. + /// 0 - is x=-1,y=-1 offset, 1 is x=0,y=-1, ... 7 is x=1,y=1 + /// (Prevents extra access to the entries hashmap) + uint8_t surrounding = 0x0; /// @brief level confirmed status (entry is ready to expand) - bool confirmed = true; + bool confirmed = false; /// @brief mark used on sweep event (extra isles garbage collection) bool marked = false; }; @@ -24,15 +26,22 @@ public: void resetMarks(); /// @brief Mark all connected entries - /// @param point origin point - void markIsle(const glm::ivec2& point); + /// @param origin origin point + void markIsle(const glm::ivec2& origin); /// @brief Erase all non-marked isles /// @return erased entries positions std::vector sweep(); - /// @brief Expand all confirmed entries with specified level + /// @brief Attempt to upgrade all confirmed entries with specified level /// @param level target entries level /// @return All upgraded entries positions - std::vector expand(int level); + std::vector upgrade(); + + /// @brief Create entry if does not exist + /// @param origin entry position + /// @return true if new entry has been created + bool createEntry(const glm::ivec2& origin); + + void getLevels(unsigned char* out, int width, int height, int ox, int oy) const; }; diff --git a/test/world/generator/SurroundMap.cpp b/test/world/generator/SurroundMap.cpp index ede9217f..b9e0bbb2 100644 --- a/test/world/generator/SurroundMap.cpp +++ b/test/world/generator/SurroundMap.cpp @@ -3,5 +3,11 @@ #include "world/generator/SurroundMap.hpp" TEST(SurroundMap, InitTest) { + int w = 8; + int h = 8; + SurroundMap map; + map.createEntry({w/2, h/2}); + map.createEntry({w/2+1, h/2+1}); + EXPECT_EQ(map.upgrade().size(), 12); } From c3569b5dd4a5bda79a3e1ffbde2f60d7f00b0034 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 9 Sep 2024 18:02:08 +0300 Subject: [PATCH 053/139] add AreaMap2D --- src/frontend/debug_panel.cpp | 2 +- src/graphics/render/WorldRenderer.cpp | 11 +- src/lighting/Lighting.cpp | 5 +- src/logic/BlocksController.cpp | 11 +- src/logic/ChunksController.cpp | 25 ++-- src/util/AreaMap2D.hpp | 163 ++++++++++++++++++++ src/voxels/Chunks.cpp | 200 ++++++++----------------- src/voxels/Chunks.hpp | 33 ++-- src/world/Level.cpp | 2 +- src/world/generator/WorldGenerator.cpp | 2 +- test/util/AreaMap2D.cpp | 7 + 11 files changed, 282 insertions(+), 179 deletions(-) create mode 100644 src/util/AreaMap2D.hpp create mode 100644 test/util/AreaMap2D.cpp diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index fa827724..c16dd5dd 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -84,7 +84,7 @@ std::shared_ptr create_debug_panel( return L"frustum-culling: "+std::wstring(culling ? L"on" : L"off"); })); panel->add(create_label([=]() { - return L"chunks: "+std::to_wstring(level->chunks->chunksCount)+ + return L"chunks: "+std::to_wstring(level->chunks->getChunksCount())+ L" visible: "+std::to_wstring(level->chunks->visible); })); panel->add(create_label([=]() { diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index a293ae2f..21cb56a5 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -79,7 +79,7 @@ WorldRenderer::~WorldRenderer() = default; bool WorldRenderer::drawChunk( size_t index, Camera* camera, Shader* shader, bool culling ) { - auto chunk = level->chunks->chunks[index]; + auto chunk = level->chunks->getChunks()[index]; if (!chunk->flags.lighted) { return false; } @@ -122,15 +122,16 @@ void WorldRenderer::drawChunks(Chunks* chunks, Camera* camera, Shader* shader) { // [warning] this whole method is not thread-safe for chunks std::vector indices; - for (size_t i = 0; i < chunks->volume; i++) { - if (chunks->chunks[i] == nullptr) continue; + for (size_t i = 0; i < chunks->getVolume(); i++) { + if (chunks->getChunks()[i] == nullptr) continue; indices.emplace_back(i); } float px = camera->position.x / static_cast(CHUNK_W) - 0.5f; float pz = camera->position.z / static_cast(CHUNK_D) - 0.5f; std::sort(indices.begin(), indices.end(), [chunks, px, pz](auto i, auto j) { - const auto a = chunks->chunks[i].get(); - const auto b = chunks->chunks[j].get(); + const auto& chunksBuffer = chunks->getChunks(); + const auto a = chunksBuffer[i].get(); + const auto b = chunksBuffer[j].get(); auto adx = (a->x - px); auto adz = (a->z - pz); auto bdx = (b->x - px); diff --git a/src/lighting/Lighting.cpp b/src/lighting/Lighting.cpp index df1e358d..6aad3049 100644 --- a/src/lighting/Lighting.cpp +++ b/src/lighting/Lighting.cpp @@ -23,8 +23,9 @@ Lighting::Lighting(const Content* content, Chunks* chunks) Lighting::~Lighting() = default; void Lighting::clear(){ - for (size_t index = 0; index < chunks->volume; index++){ - auto chunk = chunks->chunks[index]; + const auto& chunks = this->chunks->getChunks(); + for (size_t index = 0; index < chunks.size(); index++){ + auto chunk = chunks[index]; if (chunk == nullptr) continue; Lightmap& lightmap = chunk->lightmap; diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp index 570c1f46..81cb592a 100644 --- a/src/logic/BlocksController.cpp +++ b/src/logic/BlocksController.cpp @@ -124,17 +124,16 @@ void BlocksController::randomTick( void BlocksController::randomTick(int tickid, int parts) { auto indices = level->content->getIndices(); - const int w = chunks->w; - const int d = chunks->d; + const auto& size = chunks->getSize(); int segments = 4; - for (uint z = padding; z < d - padding; z++) { - for (uint x = padding; x < w - padding; x++) { - int index = z * w + x; + for (uint z = padding; z < size.y - padding; z++) { + for (uint x = padding; x < size.x - padding; x++) { + int index = z * size.x + x; if ((index + tickid) % parts != 0) { continue; } - auto& chunk = chunks->chunks[index]; + auto& chunk = chunks->getChunks()[index]; if (chunk == nullptr || !chunk->flags.lighted) { continue; } diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index a1382df5..bd9e36c4 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -52,16 +52,15 @@ void ChunksController::update(int64_t maxDuration) { } bool ChunksController::loadVisible() { - const int w = chunks->w; - const int d = chunks->d; + const auto& size = chunks->getSize(); int nearX = 0; int nearZ = 0; - int minDistance = ((w - padding * 2) / 2) * ((w - padding * 2) / 2); - for (uint z = padding; z < d - padding; z++) { - for (uint x = padding; x < w - padding; x++) { - int index = z * w + x; - auto& chunk = chunks->chunks[index]; + int minDistance = ((size.x - padding * 2) / 2) * ((size.y - padding * 2) / 2); + for (uint z = padding; z < size.y - padding; z++) { + for (uint x = padding; x < size.x - padding; x++) { + int index = z * size.x + x; + auto& chunk = chunks->getChunks()[index]; if (chunk != nullptr) { if (chunk->flags.loaded && !chunk->flags.lighted) { if (buildLights(chunk)) { @@ -70,8 +69,8 @@ bool ChunksController::loadVisible() { } continue; } - int lx = x - w / 2; - int lz = z - d / 2; + int lx = x - size.x / 2; + int lz = z - size.y / 2; int distance = (lx * lx + lz * lz); if (distance < minDistance) { minDistance = distance; @@ -81,14 +80,12 @@ bool ChunksController::loadVisible() { } } - const auto& chunk = chunks->chunks[nearZ * w + nearX]; + const auto& chunk = chunks->getChunks()[nearZ * size.x + nearX]; if (chunk != nullptr) { return false; } - - const int ox = chunks->ox; - const int oz = chunks->oz; - createChunk(nearX + ox, nearZ + oz); + const auto& offset = chunks->getOffset(); + createChunk(nearX + offset.x, nearZ + offset.y); return true; } diff --git a/src/util/AreaMap2D.hpp b/src/util/AreaMap2D.hpp new file mode 100644 index 00000000..cbf90f5b --- /dev/null +++ b/src/util/AreaMap2D.hpp @@ -0,0 +1,163 @@ +#pragma once + +#include +#include +#include +#include + +namespace util { + template + using OutCallback = std::function; + + template + class AreaMap2D { + glm::vec<2, TCoord> offset; + glm::vec<2, TCoord> size; + std::vector firstBuffer; + std::vector secondBuffer; + OutCallback outCallback; + + size_t valuesCount = 0; + + void translate(const glm::vec<2, TCoord>& delta) { + if (delta.x == 0 && delta.y == 0) { + return; + } + std::fill(secondBuffer.begin(), secondBuffer.end(), T{}); + for (TCoord y = 0; y < size.y; y++) { + for (TCoord x = 0; x < size.x; x++) { + auto& value = firstBuffer[y * size.x + x]; + auto nx = x - delta.x; + auto ny = y - delta.y; + if (value == T{}) { + continue; + } + if (nx < 0 || ny < 0 || nx >= size.x || ny >= size.y) { + if (outCallback) { + outCallback(value); + } + valuesCount--; + continue; + } + secondBuffer[ny * size.x + nx] = value; + } + } + std::swap(firstBuffer, secondBuffer); + offset += delta; + } + public: + AreaMap2D(glm::vec<2, TCoord> size) + : size(size), + firstBuffer(size.x * size.y), secondBuffer(size.x * size.y) { + } + + const T* getIf(const glm::vec<2, TCoord>& pos) const { + auto localPos = pos - offset; + if (localPos.x < 0 || localPos.y < 0 || localPos.x >= size.x || + localPos.y >= size.y) { + return nullptr; + } + return &firstBuffer[localPos.y * size.x + localPos.x]; + } + + T get(const glm::vec<2, TCoord>& pos) { + auto localPos = pos - offset; + if (localPos.x < 0 || localPos.y < 0 || localPos.x >= size.x || + localPos.y >= size.y) { + return T{}; + } + return firstBuffer[localPos.y * size.x + localPos.x]; + } + + const T& require(const glm::vec<2, TCoord>& pos) const { + auto localPos = pos - offset; + if (localPos.x < 0 || localPos.y < 0 || localPos.x >= size.x || + localPos.y >= size.y) { + throw std::invalid_argument("position is out of window"); + } + return firstBuffer[localPos.y * size.x + localPos.x]; + } + + bool set(const glm::vec<2, TCoord>& pos, T value) { + auto localPos = pos - offset; + if (localPos.x < 0 || localPos.y < 0 || localPos.x >= size.x || + localPos.y >= size.y) { + return false; + } + auto& element = firstBuffer[localPos.y * size.x + localPos.x]; + if (!element) { + valuesCount++; + } + element = std::move(value); + return true; + } + + void setOutCallback(const OutCallback& callback) { + outCallback = callback; + } + + void resize(const glm::vec<2, TCoord>& newSize) { + if (newSize.x < size.x) { + TCoord delta = size.x - newSize.x; + translate({delta / 2, 0}); + translate({-delta, 0}); + translate({delta, 0}); + } + if (newSize.y < size.y) { + TCoord delta = size.y - newSize.y; + translate({0, delta / 2}); + translate({0, -delta}); + translate({0, delta}); + } + const TCoord newVolume = newSize.x * newSize.y; + std::vector newFirstBuffer(newVolume); + std::vector newSecondBuffer(newVolume); + for (TCoord y = 0; y < size.y && y < newSize.y; y++) { + for (TCoord x = 0; x < size.x && x < newSize.x; x++) { + newFirstBuffer[y * newSize.x + x] = firstBuffer[y * size.x + x]; + } + } + size = newSize; + firstBuffer = std::move(newFirstBuffer); + secondBuffer = std::move(newSecondBuffer); + } + + void setCenter(const glm::vec<2, TCoord>& center) { + auto delta = center - (offset + size / 2); + if (delta.x | delta.y) { + translate({delta.x, delta.y}); + } + } + + void clear() { + for (TCoord i = 0; i < size.x * size.y; i++) { + auto value = firstBuffer[i]; + firstBuffer[i] = {}; + if (outCallback) { + outCallback(value); + } + } + valuesCount = 0; + } + + const glm::vec<2, TCoord>& getOffset() const { + return offset; + } + + const glm::vec<2, TCoord>& getSize() const { + return size; + } + + const std::vector& getBuffer() const { + return firstBuffer; + } + + size_t count() const { + return valuesCount; + } + + TCoord area() const { + return size.x * size.y; + } + }; +} diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index f8b1b19d..be5c3d25 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -32,34 +32,31 @@ Chunks::Chunks( ) : level(level), indices(level->content->getIndices()), - chunks(w * d), - chunksSecond(w * d), - w(w), - d(d), - ox(ox), - oz(oz), + areaMap({w, d}), worldFiles(wfile) { - volume = static_cast(w) * static_cast(d); - chunksCount = 0; + areaMap.setCenter({ox-w/2, oz-d/2}); + areaMap.setOutCallback([this](const auto& chunk) { + save(chunk.get()); + }); } voxel* Chunks::get(int32_t x, int32_t y, int32_t z) const { - x -= ox * CHUNK_W; - z -= oz * CHUNK_D; - int cx = floordiv(x, CHUNK_W); - int cy = floordiv(y, CHUNK_H); - int cz = floordiv(z, CHUNK_D); - if (cx < 0 || cy < 0 || cz < 0 || cx >= int(w) || cy >= 1 || cz >= int(d)) { + if (y < 0 || y >= CHUNK_H) { return nullptr; } - auto& chunk = chunks[cz * w + cx]; // not thread safe + int cx = floordiv(x, CHUNK_W); + int cz = floordiv(z, CHUNK_D); + auto ptr = areaMap.getIf({cx, cz}); + if (ptr == nullptr) { + return nullptr; + } + Chunk* chunk = ptr->get(); // not thread safe if (chunk == nullptr) { return nullptr; } int lx = x - cx * CHUNK_W; - int ly = y - cy * CHUNK_H; int lz = z - cz * CHUNK_D; - return &chunk->voxels[(ly * CHUNK_D + lz) * CHUNK_W + lx]; + return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; } const AABB* Chunks::isObstacleAt(float x, float y, float z) { @@ -114,61 +111,62 @@ bool Chunks::isObstacleBlock(int32_t x, int32_t y, int32_t z) { } ubyte Chunks::getLight(int32_t x, int32_t y, int32_t z, int channel) { - x -= ox * CHUNK_W; - z -= oz * CHUNK_D; - int cx = floordiv(x, CHUNK_W); - int cy = floordiv(y, CHUNK_H); - int cz = floordiv(z, CHUNK_D); - if (cx < 0 || cy < 0 || cz < 0 || cx >= int(w) || cy >= 1 || cz >= int(d)) { + if (y < 0 || y >= CHUNK_H) { return 0; } - const auto& chunk = chunks[(cy * d + cz) * w + cx]; + int cx = floordiv(x, CHUNK_W); + int cz = floordiv(z, CHUNK_D); + + auto ptr = areaMap.getIf({cx, cz}); + if (ptr == nullptr) { + return 0; + } + Chunk* chunk = ptr->get(); if (chunk == nullptr) { return 0; } int lx = x - cx * CHUNK_W; - int ly = y - cy * CHUNK_H; int lz = z - cz * CHUNK_D; - return chunk->lightmap.get(lx, ly, lz, channel); + return chunk->lightmap.get(lx, y, lz, channel); } light_t Chunks::getLight(int32_t x, int32_t y, int32_t z) { - x -= ox * CHUNK_W; - z -= oz * CHUNK_D; - int cx = floordiv(x, CHUNK_W); - int cy = floordiv(y, CHUNK_H); - int cz = floordiv(z, CHUNK_D); - if (cx < 0 || cy < 0 || cz < 0 || cx >= int(w) || cy >= 1 || cz >= int(d)) { + if (y < 0 || y >= CHUNK_H) { return 0; } - const auto& chunk = chunks[(cy * d + cz) * w + cx]; + int cx = floordiv(x, CHUNK_W); + int cz = floordiv(z, CHUNK_D); + + auto ptr = areaMap.getIf({cx, cz}); + if (ptr == nullptr) { + return 0; + } + Chunk* chunk = ptr->get(); if (chunk == nullptr) { return 0; } int lx = x - cx * CHUNK_W; - int ly = y - cy * CHUNK_H; int lz = z - cz * CHUNK_D; - return chunk->lightmap.get(lx, ly, lz); + return chunk->lightmap.get(lx, y, lz); } Chunk* Chunks::getChunkByVoxel(int32_t x, int32_t y, int32_t z) { - if (y < 0 || y >= CHUNK_H) return nullptr; - x -= ox * CHUNK_W; - z -= oz * CHUNK_D; + if (y < 0 || y >= CHUNK_H) { + return nullptr; + } int cx = floordiv(x, CHUNK_W); int cz = floordiv(z, CHUNK_D); - if (cx < 0 || cz < 0 || cx >= int(w) || cz >= int(d)) return nullptr; - return chunks[cz * w + cx].get(); + if (auto ptr = areaMap.getIf({cx, cz})) { + return ptr->get(); + } + return nullptr; } Chunk* Chunks::getChunk(int x, int z) { - x -= ox; - z -= oz; - if (x < 0 || z < 0 || x >= static_cast(w) || - z >= static_cast(d)) { - return nullptr; + if (auto ptr = areaMap.getIf({x, z})) { + return ptr->get(); } - return chunks[z * w + x].get(); + return nullptr; } glm::ivec3 Chunks::seekOrigin( @@ -355,15 +353,13 @@ void Chunks::set( } int32_t gx = x; int32_t gz = z; - x -= ox * CHUNK_W; - z -= oz * CHUNK_D; int cx = floordiv(x, CHUNK_W); int cz = floordiv(z, CHUNK_D); - if (cx < 0 || cz < 0 || cx >= static_cast(w) || - cz >= static_cast(d)) { + auto ptr = areaMap.getIf({cx, cz}); + if (ptr == nullptr) { return; } - Chunk* chunk = chunks[cz * w + cx].get(); + Chunk* chunk = ptr->get(); if (chunk == nullptr) { return; } @@ -396,15 +392,18 @@ void Chunks::set( else if (id == 0) chunk->updateHeights(); - if (lx == 0 && (chunk = getChunk(cx + ox - 1, cz + oz))) + if (lx == 0 && (chunk = getChunk(cx - 1, cz))) { chunk->flags.modified = true; - if (lz == 0 && (chunk = getChunk(cx + ox, cz + oz - 1))) + } + if (lz == 0 && (chunk = getChunk(cx, cz - 1))) { chunk->flags.modified = true; - - if (lx == CHUNK_W - 1 && (chunk = getChunk(cx + ox + 1, cz + oz))) + } + if (lx == CHUNK_W - 1 && (chunk = getChunk(cx, cz))) { chunk->flags.modified = true; - if (lz == CHUNK_D - 1 && (chunk = getChunk(cx + ox, cz + oz + 1))) + } + if (lz == CHUNK_D - 1 && (chunk = getChunk(cx, cz + 1))) { chunk->flags.modified = true; + } } voxel* Chunks::rayCast( @@ -643,97 +642,19 @@ glm::vec3 Chunks::rayCastToObstacle( } void Chunks::setCenter(int32_t x, int32_t z) { - int cx = floordiv(x, CHUNK_W); - int cz = floordiv(z, CHUNK_D); - cx -= ox + w / 2; - cz -= oz + d / 2; - if (cx | cz) { - translate(cx, cz); - } -} - -void Chunks::translate(int32_t dx, int32_t dz) { - for (uint i = 0; i < volume; i++) { - chunksSecond[i] = nullptr; - } - for (uint32_t z = 0; z < d; z++) { - for (uint32_t x = 0; x < w; x++) { - auto& chunk = chunks[z * w + x]; - int nx = x - dx; - int nz = z - dz; - if (chunk == nullptr) continue; - if (nx < 0 || nz < 0 || nx >= static_cast(w) || - nz >= static_cast(d)) { - level->events->trigger(EVT_CHUNK_HIDDEN, chunk.get()); - save(chunk.get()); - chunksCount--; - continue; - } - chunksSecond[nz * w + nx] = chunk; - } - } - std::swap(chunks, chunksSecond); - - ox += dx; - oz += dz; + areaMap.setCenter({floordiv(x, CHUNK_W), floordiv(z, CHUNK_D)}); } void Chunks::resize(uint32_t newW, uint32_t newD) { - if (newW < w) { - int delta = w - newW; - translate(delta / 2, 0); - translate(-delta, 0); - translate(delta, 0); - } - if (newD < d) { - int delta = d - newD; - translate(0, delta / 2); - translate(0, -delta); - translate(0, delta); - } - const int newVolume = newW * newD; - std::vector> newChunks(newVolume); - std::vector> newChunksSecond(newVolume); - for (int z = 0; z < static_cast(d) && z < static_cast(newD); - z++) { - for (int x = 0; x < static_cast(w) && x < static_cast(newW); - x++) { - newChunks[z * newW + x] = chunks[z * w + x]; - } - } - w = newW; - d = newD; - volume = newVolume; - chunks = std::move(newChunks); - chunksSecond = std::move(newChunksSecond); -} - -void Chunks::_setOffset(int32_t x, int32_t z) { - ox = x; - oz = z; + areaMap.resize({newW, newD}); } bool Chunks::putChunk(const std::shared_ptr& chunk) { - int x = chunk->x; - int z = chunk->z; - x -= ox; - z -= oz; - if (x < 0 || z < 0 || x >= static_cast(w) || - z >= static_cast(d)) { - return false; - } - chunks[z * w + x] = chunk; - chunksCount++; - return true; + return areaMap.set({chunk->x, chunk->z}, chunk); } void Chunks::saveAndClear() { - for (size_t i = 0; i < volume; i++) { - auto chunk = chunks[i].get(); - chunks[i] = nullptr; - save(chunk); - } - chunksCount = 0; + areaMap.clear(); } void Chunks::save(Chunk* chunk) { @@ -760,7 +681,8 @@ void Chunks::save(Chunk* chunk) { } void Chunks::saveAll() { - for (size_t i = 0; i < volume; i++) { + const auto& chunks = areaMap.getBuffer(); + for (size_t i = 0; i < areaMap.area(); i++) { if (auto& chunk = chunks[i]) { save(chunk.get()); } diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index 0d3e07e4..5214f81e 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -9,6 +9,7 @@ #include "typedefs.hpp" #include "voxel.hpp" +#include "util/AreaMap2D.hpp" class VoxelRenderer; @@ -33,14 +34,10 @@ class Chunks { void setRotationExtended( const Block& def, blockstate state, glm::ivec3 origin, uint8_t rotation ); + + util::AreaMap2D> areaMap; public: - std::vector> chunks; - std::vector> chunksSecond; - size_t volume; - size_t chunksCount; size_t visible = 0; - uint32_t w, d; - int32_t ox, oz; WorldFiles* worldFiles; Chunks( @@ -105,14 +102,30 @@ public: bool isReplaceableBlock(int32_t x, int32_t y, int32_t z); bool isObstacleBlock(int32_t x, int32_t y, int32_t z); - // does not move chunks inside - void _setOffset(int32_t x, int32_t z); - void setCenter(int32_t x, int32_t z); - void translate(int32_t x, int32_t z); void resize(uint32_t newW, uint32_t newD); void saveAndClear(); void save(Chunk* chunk); void saveAll(); + + const std::vector>& getChunks() const { + return areaMap.getBuffer(); + } + + const glm::ivec2& getSize() const { + return areaMap.getSize(); + } + + const glm::ivec2& getOffset() const { + return areaMap.getOffset(); + } + + size_t getChunksCount() const { + return areaMap.count(); + } + + size_t getVolume() const { + return areaMap.area(); + } }; diff --git a/src/world/Level.cpp b/src/world/Level.cpp index 6d9b1f03..190559d7 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -86,7 +86,7 @@ void Level::loadMatrix(int32_t x, int32_t z, uint32_t radius) { (settings.chunks.loadDistance.get() + settings.chunks.padding.get()) * 2LL ); - if (chunks->w != diameter) { + if (chunks->getSize().x != diameter) { chunks->resize(diameter, diameter); } } diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 5fef618b..f699957d 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -104,7 +104,7 @@ void WorldGenerator::generateHeightmap( } void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { - timeutil::ScopeLogTimer log(555); + //timeutil::ScopeLogTimer log(555); auto prototype = generatePrototype(chunkX, chunkZ); generateHeightmap(prototype.get(), chunkX, chunkZ); diff --git a/test/util/AreaMap2D.cpp b/test/util/AreaMap2D.cpp new file mode 100644 index 00000000..4fdd78fe --- /dev/null +++ b/test/util/AreaMap2D.cpp @@ -0,0 +1,7 @@ +#include + +#include "util/AreaMap2D.hpp" + +TEST(AreaMap2D, BaseTest) { + util::AreaMap2D window({6, 6}); +} From 7c0c268508c9c6cd4061fb19d4d3e17a4ed18f82 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 9 Sep 2024 20:37:42 +0300 Subject: [PATCH 054/139] optimize AreaMap2D --- src/logic/BlocksController.cpp | 9 ++- src/logic/ChunksController.cpp | 22 +++--- src/util/AreaMap2D.hpp | 133 ++++++++++++++++++--------------- src/voxels/Chunks.cpp | 54 +++++++------ src/voxels/Chunks.hpp | 22 ++++-- src/world/Level.cpp | 2 +- 6 files changed, 131 insertions(+), 111 deletions(-) diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp index 81cb592a..32802780 100644 --- a/src/logic/BlocksController.cpp +++ b/src/logic/BlocksController.cpp @@ -124,12 +124,13 @@ void BlocksController::randomTick( void BlocksController::randomTick(int tickid, int parts) { auto indices = level->content->getIndices(); - const auto& size = chunks->getSize(); + int width = chunks->getWidth(); + int height = chunks->getHeight(); int segments = 4; - for (uint z = padding; z < size.y - padding; z++) { - for (uint x = padding; x < size.x - padding; x++) { - int index = z * size.x + x; + for (uint z = padding; z < height - padding; z++) { + for (uint x = padding; x < width - padding; x++) { + int index = z * width + x; if ((index + tickid) % parts != 0) { continue; } diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index bd9e36c4..226acd48 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -52,14 +52,15 @@ void ChunksController::update(int64_t maxDuration) { } bool ChunksController::loadVisible() { - const auto& size = chunks->getSize(); + int sizeX = chunks->getWidth(); + int sizeY = chunks->getHeight(); int nearX = 0; int nearZ = 0; - int minDistance = ((size.x - padding * 2) / 2) * ((size.y - padding * 2) / 2); - for (uint z = padding; z < size.y - padding; z++) { - for (uint x = padding; x < size.x - padding; x++) { - int index = z * size.x + x; + int minDistance = ((sizeX - padding * 2) / 2) * ((sizeY - padding * 2) / 2); + for (uint z = padding; z < sizeY - padding; z++) { + for (uint x = padding; x < sizeX - padding; x++) { + int index = z * sizeX + x; auto& chunk = chunks->getChunks()[index]; if (chunk != nullptr) { if (chunk->flags.loaded && !chunk->flags.lighted) { @@ -69,8 +70,8 @@ bool ChunksController::loadVisible() { } continue; } - int lx = x - size.x / 2; - int lz = z - size.y / 2; + int lx = x - sizeX / 2; + int lz = z - sizeY / 2; int distance = (lx * lx + lz * lz); if (distance < minDistance) { minDistance = distance; @@ -80,12 +81,13 @@ bool ChunksController::loadVisible() { } } - const auto& chunk = chunks->getChunks()[nearZ * size.x + nearX]; + const auto& chunk = chunks->getChunks()[nearZ * sizeX + nearX]; if (chunk != nullptr) { return false; } - const auto& offset = chunks->getOffset(); - createChunk(nearX + offset.x, nearZ + offset.y); + int offsetX = chunks->getOffsetX(); + int offsetY = chunks->getOffsetY(); + createChunk(nearX + offsetX, nearZ + offsetY); return true; } diff --git a/src/util/AreaMap2D.hpp b/src/util/AreaMap2D.hpp index cbf90f5b..547b05b4 100644 --- a/src/util/AreaMap2D.hpp +++ b/src/util/AreaMap2D.hpp @@ -11,80 +11,81 @@ namespace util { template class AreaMap2D { - glm::vec<2, TCoord> offset; - glm::vec<2, TCoord> size; + TCoord offsetX, offsetY; + TCoord sizeX, sizeY; std::vector firstBuffer; std::vector secondBuffer; OutCallback outCallback; size_t valuesCount = 0; - void translate(const glm::vec<2, TCoord>& delta) { - if (delta.x == 0 && delta.y == 0) { + void translate(TCoord dx, TCoord dy) { + if (dx == 0 && dy == 0) { return; } std::fill(secondBuffer.begin(), secondBuffer.end(), T{}); - for (TCoord y = 0; y < size.y; y++) { - for (TCoord x = 0; x < size.x; x++) { - auto& value = firstBuffer[y * size.x + x]; - auto nx = x - delta.x; - auto ny = y - delta.y; + for (TCoord y = 0; y < sizeY; y++) { + for (TCoord x = 0; x < sizeX; x++) { + auto& value = firstBuffer[y * sizeX + x]; + auto nx = x - dx; + auto ny = y - dy; if (value == T{}) { continue; } - if (nx < 0 || ny < 0 || nx >= size.x || ny >= size.y) { + if (nx < 0 || ny < 0 || nx >= sizeX || ny >= sizeY) { if (outCallback) { outCallback(value); } valuesCount--; continue; } - secondBuffer[ny * size.x + nx] = value; + secondBuffer[ny * sizeX + nx] = value; } } std::swap(firstBuffer, secondBuffer); - offset += delta; + offsetX += dx; + offsetY += dy; } public: - AreaMap2D(glm::vec<2, TCoord> size) - : size(size), - firstBuffer(size.x * size.y), secondBuffer(size.x * size.y) { + AreaMap2D(TCoord width, TCoord height) + : sizeX(width), sizeY(height), + firstBuffer(width * height), secondBuffer(width * height) { } - const T* getIf(const glm::vec<2, TCoord>& pos) const { - auto localPos = pos - offset; - if (localPos.x < 0 || localPos.y < 0 || localPos.x >= size.x || - localPos.y >= size.y) { + const T* getIf(TCoord x, TCoord y) const { + auto lx = x - offsetX; + auto ly = y - offsetY; + if (lx < 0 || ly < 0 || lx >= sizeX || ly >= sizeY) { return nullptr; } - return &firstBuffer[localPos.y * size.x + localPos.x]; + return &firstBuffer[ly * sizeX + lx]; } - T get(const glm::vec<2, TCoord>& pos) { - auto localPos = pos - offset; - if (localPos.x < 0 || localPos.y < 0 || localPos.x >= size.x || - localPos.y >= size.y) { + T get(TCoord x, TCoord y) { + auto lx = x - offsetX; + auto ly = y - offsetY; + if (lx < 0 || ly < 0 || lx >= sizeX || ly >= sizeY) { return T{}; } - return firstBuffer[localPos.y * size.x + localPos.x]; + return firstBuffer[ly * sizeX + lx]; } - const T& require(const glm::vec<2, TCoord>& pos) const { - auto localPos = pos - offset; - if (localPos.x < 0 || localPos.y < 0 || localPos.x >= size.x || - localPos.y >= size.y) { + const T& require(TCoord x, TCoord y) const { + auto lx = x - offsetX; + auto ly = y - offsetY; + if (lx < 0 || ly < 0 || lx >= sizeX || ly >= sizeY) { throw std::invalid_argument("position is out of window"); } - return firstBuffer[localPos.y * size.x + localPos.x]; + return firstBuffer[ly * sizeX + lx]; } - bool set(const glm::vec<2, TCoord>& pos, T value) { - auto localPos = pos - offset; - if (localPos.x < 0 || localPos.y < 0 || localPos.x >= size.x || - localPos.y >= size.y) { + bool set(TCoord x, TCoord y, T value) { + auto lx = x - offsetX; + auto ly = y - offsetY; + if (lx < 0 || ly < 0 || lx >= sizeX || ly >= sizeY) { return false; } - auto& element = firstBuffer[localPos.y * size.x + localPos.x]; + auto& element = firstBuffer[ly * sizeX + lx]; if (!element) { valuesCount++; } @@ -96,41 +97,43 @@ namespace util { outCallback = callback; } - void resize(const glm::vec<2, TCoord>& newSize) { - if (newSize.x < size.x) { - TCoord delta = size.x - newSize.x; - translate({delta / 2, 0}); - translate({-delta, 0}); - translate({delta, 0}); + void resize(TCoord newSizeX, TCoord newSizeY) { + if (newSizeX < sizeX) { + TCoord delta = sizeX - newSizeX; + translate(delta / 2, 0); + translate(-delta, 0); + translate(delta, 0); } - if (newSize.y < size.y) { - TCoord delta = size.y - newSize.y; - translate({0, delta / 2}); - translate({0, -delta}); - translate({0, delta}); + if (newSizeY < sizeY) { + TCoord delta = sizeY - newSizeY; + translate(0, delta / 2); + translate(0, -delta); + translate(0, delta); } - const TCoord newVolume = newSize.x * newSize.y; + const TCoord newVolume = newSizeX * newSizeY; std::vector newFirstBuffer(newVolume); std::vector newSecondBuffer(newVolume); - for (TCoord y = 0; y < size.y && y < newSize.y; y++) { - for (TCoord x = 0; x < size.x && x < newSize.x; x++) { - newFirstBuffer[y * newSize.x + x] = firstBuffer[y * size.x + x]; + for (TCoord y = 0; y < sizeY && y < newSizeY; y++) { + for (TCoord x = 0; x < sizeX && x < newSizeX; x++) { + newFirstBuffer[y * newSizeX + x] = firstBuffer[y * sizeX + x]; } } - size = newSize; + sizeX = newSizeX; + sizeY = newSizeY; firstBuffer = std::move(newFirstBuffer); secondBuffer = std::move(newSecondBuffer); } - void setCenter(const glm::vec<2, TCoord>& center) { - auto delta = center - (offset + size / 2); - if (delta.x | delta.y) { - translate({delta.x, delta.y}); + void setCenter(TCoord centerX, TCoord centerY) { + auto deltaX = centerX - (offsetX + sizeX / 2); + auto deltaY = centerY - (offsetY + sizeY / 2); + if (deltaX | deltaY) { + translate(deltaX, deltaY); } } void clear() { - for (TCoord i = 0; i < size.x * size.y; i++) { + for (TCoord i = 0; i < sizeX * sizeY; i++) { auto value = firstBuffer[i]; firstBuffer[i] = {}; if (outCallback) { @@ -140,12 +143,20 @@ namespace util { valuesCount = 0; } - const glm::vec<2, TCoord>& getOffset() const { - return offset; + TCoord getOffsetX() const { + return offsetX; } - const glm::vec<2, TCoord>& getSize() const { - return size; + TCoord getOffsetY() const { + return offsetY; + } + + TCoord getWidth() const { + return sizeX; + } + + TCoord getHeight() const { + return sizeX; } const std::vector& getBuffer() const { @@ -157,7 +168,7 @@ namespace util { } TCoord area() const { - return size.x * size.y; + return sizeX * sizeY; } }; } diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index be5c3d25..ae8eee1a 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -23,8 +23,8 @@ #include "voxel.hpp" Chunks::Chunks( - uint32_t w, - uint32_t d, + int32_t w, + int32_t d, int32_t ox, int32_t oz, WorldFiles* wfile, @@ -32,9 +32,9 @@ Chunks::Chunks( ) : level(level), indices(level->content->getIndices()), - areaMap({w, d}), + areaMap(w, d), worldFiles(wfile) { - areaMap.setCenter({ox-w/2, oz-d/2}); + areaMap.setCenter(ox-w/2, oz-d/2); areaMap.setOutCallback([this](const auto& chunk) { save(chunk.get()); }); @@ -46,7 +46,7 @@ voxel* Chunks::get(int32_t x, int32_t y, int32_t z) const { } int cx = floordiv(x, CHUNK_W); int cz = floordiv(z, CHUNK_D); - auto ptr = areaMap.getIf({cx, cz}); + auto ptr = areaMap.getIf(cx, cz); if (ptr == nullptr) { return nullptr; } @@ -83,8 +83,8 @@ const AABB* Chunks::isObstacleAt(float x, float y, float z) { def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes; for (const auto& hitbox : boxes) { if (hitbox.contains( - {x - ix - offset.x, y - iy - offset.y, z - iz - offset.z} - )) { + {x - ix - offset.x, y - iy - offset.y, z - iz - offset.z} + )) { return &hitbox; } } @@ -117,7 +117,7 @@ ubyte Chunks::getLight(int32_t x, int32_t y, int32_t z, int channel) { int cx = floordiv(x, CHUNK_W); int cz = floordiv(z, CHUNK_D); - auto ptr = areaMap.getIf({cx, cz}); + auto ptr = areaMap.getIf(cx, cz); if (ptr == nullptr) { return 0; } @@ -137,7 +137,7 @@ light_t Chunks::getLight(int32_t x, int32_t y, int32_t z) { int cx = floordiv(x, CHUNK_W); int cz = floordiv(z, CHUNK_D); - auto ptr = areaMap.getIf({cx, cz}); + auto ptr = areaMap.getIf(cx, cz); if (ptr == nullptr) { return 0; } @@ -156,14 +156,14 @@ Chunk* Chunks::getChunkByVoxel(int32_t x, int32_t y, int32_t z) { } int cx = floordiv(x, CHUNK_W); int cz = floordiv(z, CHUNK_D); - if (auto ptr = areaMap.getIf({cx, cz})) { + if (auto ptr = areaMap.getIf(cx, cz)) { return ptr->get(); } return nullptr; } Chunk* Chunks::getChunk(int x, int z) { - if (auto ptr = areaMap.getIf({x, z})) { + if (auto ptr = areaMap.getIf(x, z)) { return ptr->get(); } return nullptr; @@ -351,11 +351,9 @@ void Chunks::set( if (y < 0 || y >= CHUNK_H) { return; } - int32_t gx = x; - int32_t gz = z; int cx = floordiv(x, CHUNK_W); int cz = floordiv(z, CHUNK_D); - auto ptr = areaMap.getIf({cx, cz}); + auto ptr = areaMap.getIf(cx, cz); if (ptr == nullptr) { return; } @@ -373,7 +371,7 @@ void Chunks::set( chunk->removeBlockInventory(lx, y, lz); } if (prevdef.rt.extended && !vox.state.segment) { - eraseSegments(prevdef, vox.state, gx, y, gz); + eraseSegments(prevdef, vox.state, x, y, z); } // block initialization @@ -382,7 +380,7 @@ void Chunks::set( vox.state = state; chunk->setModifiedAndUnsaved(); if (!state.segment && newdef.rt.extended) { - repairSegments(newdef, state, gx, y, gz); + repairSegments(newdef, state, x, y, z); } if (y < chunk->bottom) @@ -424,9 +422,9 @@ voxel* Chunks::rayCast( float dz = dir.z; float t = 0.0f; - int ix = floor(px); - int iy = floor(py); - int iz = floor(pz); + int ix = std::floor(px); + int iy = std::floor(py); + int iz = std::floor(pz); int stepx = (dx > 0.0f) ? 1 : -1; int stepy = (dy > 0.0f) ? 1 : -1; @@ -434,9 +432,9 @@ voxel* Chunks::rayCast( constexpr float infinity = std::numeric_limits::infinity(); constexpr float epsilon = 1e-6f; // 0.000001 - float txDelta = (fabs(dx) < epsilon) ? infinity : abs(1.0f / dx); - float tyDelta = (fabs(dy) < epsilon) ? infinity : abs(1.0f / dy); - float tzDelta = (fabs(dz) < epsilon) ? infinity : abs(1.0f / dz); + float txDelta = (std::fabs(dx) < epsilon) ? infinity : std::fabs(1.0f / dx); + float tyDelta = (std::fabs(dy) < epsilon) ? infinity : std::fabs(1.0f / dy); + float tzDelta = (std::fabs(dz) < epsilon) ? infinity : std::fabs(1.0f / dz); float xdist = (stepx > 0) ? (ix + 1 - px) : (px - ix); float ydist = (stepy > 0) ? (iy + 1 - py) : (py - iy); @@ -567,9 +565,9 @@ glm::vec3 Chunks::rayCastToObstacle( constexpr float infinity = std::numeric_limits::infinity(); constexpr float epsilon = 1e-6f; // 0.000001 - float txDelta = (fabs(dx) < epsilon) ? infinity : abs(1.0f / dx); - float tyDelta = (fabs(dy) < epsilon) ? infinity : abs(1.0f / dy); - float tzDelta = (fabs(dz) < epsilon) ? infinity : abs(1.0f / dz); + float txDelta = (std::fabs(dx) < epsilon) ? infinity : std::fabs(1.0f / dx); + float tyDelta = (std::fabs(dy) < epsilon) ? infinity : std::fabs(1.0f / dy); + float tzDelta = (std::fabs(dz) < epsilon) ? infinity : std::fabs(1.0f / dz); float xdist = (stepx > 0) ? (ix + 1 - px) : (px - ix); float ydist = (stepy > 0) ? (iy + 1 - py) : (py - iy); @@ -642,15 +640,15 @@ glm::vec3 Chunks::rayCastToObstacle( } void Chunks::setCenter(int32_t x, int32_t z) { - areaMap.setCenter({floordiv(x, CHUNK_W), floordiv(z, CHUNK_D)}); + areaMap.setCenter(floordiv(x, CHUNK_W), floordiv(z, CHUNK_D)); } void Chunks::resize(uint32_t newW, uint32_t newD) { - areaMap.resize({newW, newD}); + areaMap.resize(newW, newD); } bool Chunks::putChunk(const std::shared_ptr& chunk) { - return areaMap.set({chunk->x, chunk->z}, chunk); + return areaMap.set(chunk->x, chunk->z, chunk); } void Chunks::saveAndClear() { diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index 5214f81e..6e73193a 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -35,14 +35,14 @@ class Chunks { const Block& def, blockstate state, glm::ivec3 origin, uint8_t rotation ); - util::AreaMap2D> areaMap; + util::AreaMap2D, int32_t> areaMap; public: size_t visible = 0; WorldFiles* worldFiles; Chunks( - uint32_t w, - uint32_t d, + int32_t w, + int32_t d, int32_t ox, int32_t oz, WorldFiles* worldFiles, @@ -113,12 +113,20 @@ public: return areaMap.getBuffer(); } - const glm::ivec2& getSize() const { - return areaMap.getSize(); + int getWidth() const { + return areaMap.getWidth(); } - const glm::ivec2& getOffset() const { - return areaMap.getOffset(); + int getHeight() const { + return areaMap.getHeight(); + } + + int getOffsetX() const { + return areaMap.getOffsetX(); + } + + int getOffsetY() const { + return areaMap.getOffsetY(); } size_t getChunksCount() const { diff --git a/src/world/Level.cpp b/src/world/Level.cpp index 190559d7..39384b4a 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -86,7 +86,7 @@ void Level::loadMatrix(int32_t x, int32_t z, uint32_t radius) { (settings.chunks.loadDistance.get() + settings.chunks.padding.get()) * 2LL ); - if (chunks->getSize().x != diameter) { + if (chunks->getWidth() != diameter) { chunks->resize(diameter, diameter); } } From 2a4dbe3ac4c2b2ef774c5eaa1fdbc16826b89e89 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 10 Sep 2024 14:50:57 +0300 Subject: [PATCH 055/139] rebuild SurroundMap (WIP) --- src/util/AreaMap2D.hpp | 9 +- src/world/generator/SurroundMap.cpp | 119 ++++----------------------- src/world/generator/SurroundMap.hpp | 48 ++++------- test/world/generator/SurroundMap.cpp | 8 +- 4 files changed, 35 insertions(+), 149 deletions(-) diff --git a/src/util/AreaMap2D.hpp b/src/util/AreaMap2D.hpp index 547b05b4..1c3041be 100644 --- a/src/util/AreaMap2D.hpp +++ b/src/util/AreaMap2D.hpp @@ -6,16 +6,17 @@ #include namespace util { - template - using OutCallback = std::function; template class AreaMap2D { + public: + using OutCallback = std::function; + private: TCoord offsetX, offsetY; TCoord sizeX, sizeY; std::vector firstBuffer; std::vector secondBuffer; - OutCallback outCallback; + OutCallback outCallback; size_t valuesCount = 0; @@ -93,7 +94,7 @@ namespace util { return true; } - void setOutCallback(const OutCallback& callback) { + void setOutCallback(const OutCallback& callback) { outCallback = callback; } diff --git a/src/world/generator/SurroundMap.cpp b/src/world/generator/SurroundMap.cpp index e54da0e0..fd31d3e7 100644 --- a/src/world/generator/SurroundMap.cpp +++ b/src/world/generator/SurroundMap.cpp @@ -4,114 +4,25 @@ #include #include -void SurroundMap::resetMarks() { - for (auto& [_, entry] : entries) { - entry.marked = false; - } +SurroundMap::SurroundMap(int loadDistance, ubyte maxLevel) + : areaMap((loadDistance + maxLevel) * 2 + 1, + (loadDistance + maxLevel) * 2 + 1), + levelCallbacks(maxLevel), + maxLevel(maxLevel) +{} + +void SurroundMap::setLevelCallback(int level, LevelCallback callback) { + levelCallbacks.at(level) = callback; } -bool SurroundMap::createEntry(const glm::ivec2& origin) { - auto& entry = entries[origin]; - if (entry.confirmed) { - return false; - } - entry.confirmed = true; - - // calculate initial number of surrounding entries (any level) - int bitOffset = 0; - for (int y = -1; y <= 1; y++) { - for (int x = -1; x <= 1; x++) { - if (x == y && x == 0) { - continue; - } - const auto& found = entries.find(origin + glm::ivec2(x, y)); - if (found != entries.end()) { - entry.surrounding |= (1 << bitOffset); - if (found->second.level == 0) { - found->second.surrounding |= (1 << (7 - bitOffset)); - } - } - bitOffset++; - } - } - return true; +void SurroundMap::setOutCallback(util::AreaMap2D::OutCallback callback) { + areaMap.setOutCallback(callback); } -std::vector SurroundMap::upgrade() { - std::vector expansion; - std::queue expanding; - for (const auto& [pos, entry] : entries) { - if (entry.confirmed && entry.surrounding != 0xFF) { - expanding.push(pos); - } - } - while (!expanding.empty()) { - assert(expanding.size() < 64); - - glm::ivec2 pos = expanding.front(); - expanding.pop(); - - const auto& found = entries.find(pos); - assert(found != entries.end() && "concurrent modification"); - - auto& entry = found->second; - int uplevelSurrounding = 0; - int bitOffset = 0; - for (int y = -1; y <= 1; y++) { - for (int x = -1; x <= 1; x++) { - if (x == y && x == 0) { - continue; - } - glm::ivec2 npos = {pos.x+x, pos.y+y}; - const auto& nfound = entries.find(npos); - - if (entry.surrounding & (1 << bitOffset)) { - auto& nentry = nfound->second; - if (nentry.level > entry.level) { - uplevelSurrounding |= (1 << bitOffset); - } - bitOffset++; - continue; - } - if (entry.level == 0) { - // neighbour entry does not exist - createEntry(npos); - expansion.push_back(npos); - } else{ - assert(nfound != entries.end() && "invalid map state"); - if (nfound->second.level == entry.level + 1) { - nfound->second.surrounding |= (1 << (7 - bitOffset)); - expanding.push(npos); - } - } - bitOffset++; - } - } - // level up - entry.surrounding = uplevelSurrounding; - entry.level++; - } - return expansion; +void SurroundMap::completeAt(int x, int y) { + // TODO } -void SurroundMap::getLevels(unsigned char* out, int width, int height, int ox, int oy) const { - std::memset(out, 0, width * height); - for (const auto& [pos, entry] : entries) { - int x = pos.x - ox; - int y = pos.y - oy; - if (x < 0 || x >= width || y < 0 || y >= height) { - continue; - } - - int surroundNum = 0; - for (int i = 0; i < 8; i++) { - if (entry.surrounding & (1 << i)) { - surroundNum++; - } - } - if (surroundNum) { - out[y * width + x] = surroundNum + 1; - } - out[y * width + x] = entry.level + 1; - } +void SurroundMap::setCenter(int x, int y) { + areaMap.setCenter(x, y); } diff --git a/src/world/generator/SurroundMap.hpp b/src/world/generator/SurroundMap.hpp index f8bca041..3a99817e 100644 --- a/src/world/generator/SurroundMap.hpp +++ b/src/world/generator/SurroundMap.hpp @@ -5,43 +5,23 @@ #define GLM_ENABLE_EXPERIMENTAL #include +#include "typedefs.hpp" +#include "util/AreaMap2D.hpp" + class SurroundMap { - struct Entry { - /// @brief Level is increased when all surrounding (8) entries having - // greather or equal confirmed level - int level = 0; - /// @brief bits storing surrounding entries having greather or equal - /// confirmed level. - /// 0 - is x=-1,y=-1 offset, 1 is x=0,y=-1, ... 7 is x=1,y=1 - /// (Prevents extra access to the entries hashmap) - uint8_t surrounding = 0x0; - /// @brief level confirmed status (entry is ready to expand) - bool confirmed = false; - /// @brief mark used on sweep event (extra isles garbage collection) - bool marked = false; - }; - std::unordered_map entries; public: - /// @brief Reset all isles marks - void resetMarks(); + using LevelCallback = std::function; +private: + util::AreaMap2D areaMap; + std::vector levelCallbacks; + ubyte maxLevel; +public: + SurroundMap(int loadDistance, ubyte maxLevel); - /// @brief Mark all connected entries - /// @param origin origin point - void markIsle(const glm::ivec2& origin); + void setLevelCallback(int level, LevelCallback callback); + void setOutCallback(util::AreaMap2D::OutCallback callback); - /// @brief Erase all non-marked isles - /// @return erased entries positions - std::vector sweep(); + void completeAt(int x, int y); - /// @brief Attempt to upgrade all confirmed entries with specified level - /// @param level target entries level - /// @return All upgraded entries positions - std::vector upgrade(); - - /// @brief Create entry if does not exist - /// @param origin entry position - /// @return true if new entry has been created - bool createEntry(const glm::ivec2& origin); - - void getLevels(unsigned char* out, int width, int height, int ox, int oy) const; + void setCenter(int x, int y); }; diff --git a/test/world/generator/SurroundMap.cpp b/test/world/generator/SurroundMap.cpp index b9e0bbb2..dc29ea03 100644 --- a/test/world/generator/SurroundMap.cpp +++ b/test/world/generator/SurroundMap.cpp @@ -3,11 +3,5 @@ #include "world/generator/SurroundMap.hpp" TEST(SurroundMap, InitTest) { - int w = 8; - int h = 8; - - SurroundMap map; - map.createEntry({w/2, h/2}); - map.createEntry({w/2+1, h/2+1}); - EXPECT_EQ(map.upgrade().size(), 12); + SurroundMap map(50, 8); } From fdefbda49fe3fe17aa5deb981b32a1e0de622ea3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 10 Sep 2024 20:34:26 +0300 Subject: [PATCH 056/139] implement SurroundMap & update AreaMap2D --- src/util/AreaMap2D.hpp | 36 ++++++++++++++++----- src/voxels/Chunks.cpp | 2 +- src/world/generator/SurroundMap.cpp | 48 +++++++++++++++++++++++++--- src/world/generator/SurroundMap.hpp | 31 ++++++++++++++---- test/world/generator/SurroundMap.cpp | 5 ++- 5 files changed, 100 insertions(+), 22 deletions(-) diff --git a/src/util/AreaMap2D.hpp b/src/util/AreaMap2D.hpp index 1c3041be..54c10657 100644 --- a/src/util/AreaMap2D.hpp +++ b/src/util/AreaMap2D.hpp @@ -10,9 +10,9 @@ namespace util { template class AreaMap2D { public: - using OutCallback = std::function; + using OutCallback = std::function; private: - TCoord offsetX, offsetY; + TCoord offsetX = 0, offsetY = 0; TCoord sizeX, sizeY; std::vector firstBuffer; std::vector secondBuffer; @@ -35,7 +35,7 @@ namespace util { } if (nx < 0 || ny < 0 || nx >= sizeX || ny >= sizeY) { if (outCallback) { - outCallback(value); + outCallback(x + offsetX, y + offsetY, value); } valuesCount--; continue; @@ -71,6 +71,23 @@ namespace util { return firstBuffer[ly * sizeX + lx]; } + T get(TCoord x, TCoord y, const T& def) const { + if (auto ptr = getIf(x, y)) { + const auto& value = *ptr; + if (value == T{}) { + return def; + } + return value; + } + return def; + } + + bool isInside(TCoord x, TCoord y) const { + auto lx = x - offsetX; + auto ly = y - offsetY; + return !(lx < 0 || ly < 0 || lx >= sizeX || ly >= sizeY); + } + const T& require(TCoord x, TCoord y) const { auto lx = x - offsetX; auto ly = y - offsetY; @@ -134,11 +151,14 @@ namespace util { } void clear() { - for (TCoord i = 0; i < sizeX * sizeY; i++) { - auto value = firstBuffer[i]; - firstBuffer[i] = {}; - if (outCallback) { - outCallback(value); + for (TCoord y = 0; y < sizeY; y++) { + for (TCoord x = 0; x < sizeX; x++) { + auto i = y * sizeX + x; + auto value = firstBuffer[i]; + firstBuffer[i] = {}; + if (outCallback) { + outCallback(x + offsetX, y + offsetY, value); + } } } valuesCount = 0; diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index ae8eee1a..c408190b 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -35,7 +35,7 @@ Chunks::Chunks( areaMap(w, d), worldFiles(wfile) { areaMap.setCenter(ox-w/2, oz-d/2); - areaMap.setOutCallback([this](const auto& chunk) { + areaMap.setOutCallback([this](int, int, const auto& chunk) { save(chunk.get()); }); } diff --git a/src/world/generator/SurroundMap.cpp b/src/world/generator/SurroundMap.cpp index fd31d3e7..8017758f 100644 --- a/src/world/generator/SurroundMap.cpp +++ b/src/world/generator/SurroundMap.cpp @@ -4,25 +4,63 @@ #include #include -SurroundMap::SurroundMap(int loadDistance, ubyte maxLevel) +SurroundMap::SurroundMap(int loadDistance, int8_t maxLevel) : areaMap((loadDistance + maxLevel) * 2 + 1, (loadDistance + maxLevel) * 2 + 1), levelCallbacks(maxLevel), maxLevel(maxLevel) {} -void SurroundMap::setLevelCallback(int level, LevelCallback callback) { - levelCallbacks.at(level) = callback; +void SurroundMap::setLevelCallback(int8_t level, LevelCallback callback) { + auto& wrapper = levelCallbacks.at(level); + wrapper.callback = callback; + wrapper.active = callback != nullptr; } -void SurroundMap::setOutCallback(util::AreaMap2D::OutCallback callback) { +void SurroundMap::setOutCallback(util::AreaMap2D::OutCallback callback) { areaMap.setOutCallback(callback); } +void SurroundMap::upgrade(int x, int y, int8_t level) { + auto& callback = levelCallbacks[level]; + int size = maxLevel - level + 1; + for (int ly = -size+1; ly < size; ly++) { + for (int lx = -size+1; lx < size; lx++) { + int posX = lx + x; + int posY = ly + y; + int8_t sourceLevel = areaMap.get(posX, posY, 0); + if (sourceLevel < level-1) { + throw std::runtime_error("invalid map state"); + } + if (sourceLevel >= level) { + continue; + } + areaMap.set(posX, posY, level); + if (callback.active) { + callback.callback(posX, posY); + } + } + } +} + void SurroundMap::completeAt(int x, int y) { - // TODO + if (!areaMap.isInside(x - maxLevel + 1, y - maxLevel + 1) || + !areaMap.isInside(x + maxLevel - 1, y + maxLevel - 1)) { + throw std::invalid_argument( + "upgrade square is not fully inside of area"); + } + for (int8_t level = 1; level <= maxLevel; level++) { + upgrade(x, y, level); + } } void SurroundMap::setCenter(int x, int y) { areaMap.setCenter(x, y); } + +int8_t SurroundMap::at(int x, int y) { + if (auto ptr = areaMap.getIf(x, y)) { + return *ptr; + } + throw std::invalid_argument("position is out of area"); +} diff --git a/src/world/generator/SurroundMap.hpp b/src/world/generator/SurroundMap.hpp index 3a99817e..a7af02dd 100644 --- a/src/world/generator/SurroundMap.hpp +++ b/src/world/generator/SurroundMap.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #define GLM_ENABLE_EXPERIMENTAL #include @@ -10,18 +11,34 @@ class SurroundMap { public: - using LevelCallback = std::function; + using LevelCallback = std::function; + struct LevelCallbackWrapper { + LevelCallback callback; + bool active; + }; private: - util::AreaMap2D areaMap; - std::vector levelCallbacks; - ubyte maxLevel; + util::AreaMap2D areaMap; + std::vector levelCallbacks; + int8_t maxLevel; + + void upgrade(int x, int y, int8_t level); public: - SurroundMap(int loadDistance, ubyte maxLevel); + SurroundMap(int loadDistance, int8_t maxLevel); - void setLevelCallback(int level, LevelCallback callback); - void setOutCallback(util::AreaMap2D::OutCallback callback); + /// @brief Callback called on point level increments + void setLevelCallback(int8_t level, LevelCallback callback); + /// @brief Callback called when non-zero value moves out of area + void setOutCallback(util::AreaMap2D::OutCallback callback); + + /// @brief Upgrade point to maxLevel + /// @throws std::invalid_argument - upgrade square is not fully inside void completeAt(int x, int y); + /// @brief Set map area center void setCenter(int x, int y); + + /// @brief Get level at position + /// @throws std::invalid_argument - position is out of area + int8_t at(int x, int y); }; diff --git a/test/world/generator/SurroundMap.cpp b/test/world/generator/SurroundMap.cpp index dc29ea03..d9ce8542 100644 --- a/test/world/generator/SurroundMap.cpp +++ b/test/world/generator/SurroundMap.cpp @@ -3,5 +3,8 @@ #include "world/generator/SurroundMap.hpp" TEST(SurroundMap, InitTest) { - SurroundMap map(50, 8); + int8_t maxLevel = 2; + SurroundMap map(50, maxLevel); + map.completeAt(25, 25); + EXPECT_EQ(map.at(25, 25), maxLevel); } From 4248917aab1fa31b1f48538762ade6440763bfd3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 10 Sep 2024 20:46:13 +0300 Subject: [PATCH 057/139] fix SurroundMap --- src/world/generator/SurroundMap.cpp | 4 ++-- src/world/generator/SurroundMap.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/world/generator/SurroundMap.cpp b/src/world/generator/SurroundMap.cpp index 8017758f..7fed03f2 100644 --- a/src/world/generator/SurroundMap.cpp +++ b/src/world/generator/SurroundMap.cpp @@ -12,7 +12,7 @@ SurroundMap::SurroundMap(int loadDistance, int8_t maxLevel) {} void SurroundMap::setLevelCallback(int8_t level, LevelCallback callback) { - auto& wrapper = levelCallbacks.at(level); + auto& wrapper = levelCallbacks.at(level - 1); wrapper.callback = callback; wrapper.active = callback != nullptr; } @@ -22,7 +22,7 @@ void SurroundMap::setOutCallback(util::AreaMap2D::OutCallback callback) } void SurroundMap::upgrade(int x, int y, int8_t level) { - auto& callback = levelCallbacks[level]; + auto& callback = levelCallbacks[level - 1]; int size = maxLevel - level + 1; for (int ly = -size+1; ly < size; ly++) { for (int lx = -size+1; lx < size; lx++) { diff --git a/src/world/generator/SurroundMap.hpp b/src/world/generator/SurroundMap.hpp index a7af02dd..31bafc52 100644 --- a/src/world/generator/SurroundMap.hpp +++ b/src/world/generator/SurroundMap.hpp @@ -14,7 +14,7 @@ public: using LevelCallback = std::function; struct LevelCallbackWrapper { LevelCallback callback; - bool active; + bool active = false; }; private: util::AreaMap2D areaMap; From 6fdea11e2ec5c76f5c13f8edc52b825bcc98ff73 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 10 Sep 2024 21:10:04 +0300 Subject: [PATCH 058/139] advanced SurroundMap test --- test/world/generator/SurroundMap.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/test/world/generator/SurroundMap.cpp b/test/world/generator/SurroundMap.cpp index d9ce8542..b61db149 100644 --- a/test/world/generator/SurroundMap.cpp +++ b/test/world/generator/SurroundMap.cpp @@ -1,10 +1,26 @@ #include +#include #include "world/generator/SurroundMap.hpp" TEST(SurroundMap, InitTest) { - int8_t maxLevel = 2; - SurroundMap map(50, maxLevel); - map.completeAt(25, 25); - EXPECT_EQ(map.at(25, 25), maxLevel); + int maxLevelZone = 50; + int x = 20; + int y = 30; + int8_t maxLevel = 5; + std::atomic_int affected = 0; + + SurroundMap map(maxLevelZone, maxLevel); + map.setLevelCallback(1, [&affected](auto, auto) { + affected++; + }); + map.completeAt(x, y); + EXPECT_EQ(affected, (maxLevel * 2 - 1)*(maxLevel * 2 - 1)); + + for (int ly = -maxLevel+1; ly < maxLevel; ly++) { + for (int lx = -maxLevel+1; lx < maxLevel; lx++) { + int levelExpected = maxLevel-std::max(std::abs(lx), std::abs(ly)); + EXPECT_EQ(map.at(x+lx, y+ly), levelExpected); + } + } } From ab110ab8bf22d73b8c41aad3ec566da1fd1fa428 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 10 Sep 2024 22:19:54 +0300 Subject: [PATCH 059/139] add AreaMap2D tests --- src/util/AreaMap2D.hpp | 5 +- test/util/AreaMap2D.cpp | 73 +++++++++++++++++++++++++++- test/world/generator/SurroundMap.cpp | 22 ++++++--- 3 files changed, 90 insertions(+), 10 deletions(-) diff --git a/src/util/AreaMap2D.hpp b/src/util/AreaMap2D.hpp index 54c10657..3b550ae2 100644 --- a/src/util/AreaMap2D.hpp +++ b/src/util/AreaMap2D.hpp @@ -104,9 +104,12 @@ namespace util { return false; } auto& element = firstBuffer[ly * sizeX + lx]; - if (!element) { + if (value && !element) { valuesCount++; } + if (element && !value) { + valuesCount--; + } element = std::move(value); return true; } diff --git a/test/util/AreaMap2D.cpp b/test/util/AreaMap2D.cpp index 4fdd78fe..a4535926 100644 --- a/test/util/AreaMap2D.cpp +++ b/test/util/AreaMap2D.cpp @@ -1,7 +1,78 @@ #include +#include #include "util/AreaMap2D.hpp" TEST(AreaMap2D, BaseTest) { - util::AreaMap2D window({6, 6}); + util::AreaMap2D window({7, 5}); + window.setCenter(0, 0); + { + int i = 1; + for (int y = -2; y <= 2; y++) { + for (int x = -3; x <= 3; x++, i++) { + window.set(x, y, i); + } + } + } + EXPECT_EQ(window.count(), 7 * 5); + { + int i = 1; + for (int y = -2; y <= 2; y++) { + for (int x = -3; x <= 3; x++, i++) { + EXPECT_EQ(window.require(x, y), i); + } + } + } + window.set(0, 0, 0); + EXPECT_EQ(window.count(), 7 * 5 - 1); +} + + +TEST(AreaMap2D, ResizeTest) { + util::AreaMap2D window({7, 5}); + window.setCenter(0, 0); + { + int i = 1; + for (int y = -2; y <= 2; y++) { + for (int x = -3; x <= 3; x++, i++) { + window.set(x, y, i); + } + } + } + EXPECT_EQ(window.count(), 7 * 5); + window.resize(9, 7); + window.setCenter(0, 0); + EXPECT_EQ(window.count(), 7 * 5); + window.resize(7, 5); + + EXPECT_EQ(window.count(), 7 * 5); + { + int i = 1; + for (int y = -2; y <= 2; y++) { + for (int x = -3; x <= 3; x++, i++) { + EXPECT_EQ(window.require(x, y), i); + } + } + } +} + +TEST(AreaMap2D, TranslateWithOut) { + util::AreaMap2D window({7, 5}); + window.setCenter(0, 0); + { + int i = 1; + for (int y = -2; y <= 2; y++) { + for (int x = -3; x <= 3; x++, i++) { + window.set(x, y, i); + } + } + } + std::atomic_int outside = 0; + window.setOutCallback([&outside](auto, auto, auto) { + outside++; + }); + window.setCenter(-2, -1); + EXPECT_EQ(window.require(-3, -2), 1); + EXPECT_EQ(outside, 15); + EXPECT_EQ(window.count(), 20); } diff --git a/test/world/generator/SurroundMap.cpp b/test/world/generator/SurroundMap.cpp index b61db149..7131ff94 100644 --- a/test/world/generator/SurroundMap.cpp +++ b/test/world/generator/SurroundMap.cpp @@ -5,22 +5,28 @@ TEST(SurroundMap, InitTest) { int maxLevelZone = 50; - int x = 20; - int y = 30; + int x = 0; + int y = 0; int8_t maxLevel = 5; - std::atomic_int affected = 0; SurroundMap map(maxLevelZone, maxLevel); + std::atomic_int affected = 0; + map.setLevelCallback(1, [&affected](auto, auto) { affected++; }); + map.setCenter(0, 0); map.completeAt(x, y); - EXPECT_EQ(affected, (maxLevel * 2 - 1)*(maxLevel * 2 - 1)); + EXPECT_EQ(affected, (maxLevel * 2 - 1) * (maxLevel * 2 - 1)); - for (int ly = -maxLevel+1; ly < maxLevel; ly++) { - for (int lx = -maxLevel+1; lx < maxLevel; lx++) { - int levelExpected = maxLevel-std::max(std::abs(lx), std::abs(ly)); - EXPECT_EQ(map.at(x+lx, y+ly), levelExpected); + for (int ly = -maxLevel + 1; ly < maxLevel; ly++) { + for (int lx = -maxLevel + 1; lx < maxLevel; lx++) { + int levelExpected = maxLevel - std::max(std::abs(lx), std::abs(ly)); + EXPECT_EQ(map.at(x + lx, y + ly), levelExpected); } } + + affected = 0; + map.completeAt(x - 1, y); + EXPECT_EQ(affected, maxLevel * 2 - 1); } From 80d8a6738bda6529a65c3f805232babe427625b9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 10 Sep 2024 23:02:15 +0300 Subject: [PATCH 060/139] add SurroundMap.resize(...) --- src/world/generator/SurroundMap.cpp | 11 ++++++++--- src/world/generator/SurroundMap.hpp | 8 +++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/world/generator/SurroundMap.cpp b/src/world/generator/SurroundMap.cpp index 7fed03f2..07fa7087 100644 --- a/src/world/generator/SurroundMap.cpp +++ b/src/world/generator/SurroundMap.cpp @@ -4,9 +4,9 @@ #include #include -SurroundMap::SurroundMap(int loadDistance, int8_t maxLevel) - : areaMap((loadDistance + maxLevel) * 2 + 1, - (loadDistance + maxLevel) * 2 + 1), +SurroundMap::SurroundMap(int maxLevelRadius, int8_t maxLevel) + : areaMap((maxLevelRadius + maxLevel) * 2 + 1, + (maxLevelRadius + maxLevel) * 2 + 1), levelCallbacks(maxLevel), maxLevel(maxLevel) {} @@ -43,6 +43,11 @@ void SurroundMap::upgrade(int x, int y, int8_t level) { } } +void SurroundMap::resize(int maxLevelRadius) { + areaMap.resize((maxLevelRadius + maxLevel) * 2 + 1, + (maxLevelRadius + maxLevel) * 2 + 1); +} + void SurroundMap::completeAt(int x, int y) { if (!areaMap.isInside(x - maxLevel + 1, y - maxLevel + 1) || !areaMap.isInside(x + maxLevel - 1, y + maxLevel - 1)) { diff --git a/src/world/generator/SurroundMap.hpp b/src/world/generator/SurroundMap.hpp index 31bafc52..4f04d63e 100644 --- a/src/world/generator/SurroundMap.hpp +++ b/src/world/generator/SurroundMap.hpp @@ -23,7 +23,7 @@ private: void upgrade(int x, int y, int8_t level); public: - SurroundMap(int loadDistance, int8_t maxLevel); + SurroundMap(int maxLevelRadius, int8_t maxLevel); /// @brief Callback called on point level increments void setLevelCallback(int8_t level, LevelCallback callback); @@ -38,7 +38,13 @@ public: /// @brief Set map area center void setCenter(int x, int y); + void resize(int maxLevelRadius); + /// @brief Get level at position /// @throws std::invalid_argument - position is out of area int8_t at(int x, int y); + + const util::AreaMap2D& getArea() const { + return areaMap; + } }; From 826817652701850f345cef6f165608650ec40442 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Sep 2024 11:35:56 +0300 Subject: [PATCH 061/139] add SurroundMap intergration to world generator (WIP) --- res/generators/default.lua | 19 ++++++++++-- src/logic/ChunksController.cpp | 6 +++- src/logic/ChunksController.hpp | 6 +++- src/logic/LevelController.cpp | 6 +++- src/world/generator/WorldGenerator.cpp | 41 +++++++++++++++++++++++--- src/world/generator/WorldGenerator.hpp | 8 +++++ 6 files changed, 76 insertions(+), 10 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index e96ea22e..10f3b08c 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -99,14 +99,27 @@ function generate_heightmap(x, y, w, h, seed) return map end -function generate_biome_parameters(x, y, w, h, seed) +local function _generate_biome_parameters(x, y, w, h, seed, s) local tempmap = Heightmap(w, h) tempmap.noiseSeed = seed + 5324 - tempmap:noise({x, y}, 0.4, 4) + tempmap:noise({x, y}, 0.4*s, 4) local hummap = Heightmap(w, h) hummap.noiseSeed = seed + 953 - hummap:noise({x, y}, 0.16, 4) + hummap:noise({x, y}, 0.16*s, 4) tempmap:pow(2) hummap:pow(2) return tempmap, hummap end + +function generate_biome_parameters(x, y, w, h, seed) + local bpd = 4 + local tmap, hmap = _generate_biome_parameters( + math.floor(x/bpd), math.floor(y/bpd), + math.floor(w/bpd)+1, math.floor(h/bpd)+1, seed, bpd) + tmap:resize(w+bpd, h+bpd, 'linear') + tmap:crop(0, 0, w, h) + + hmap:resize(w+bpd, h+bpd, 'linear') + hmap:crop(0, 0, w, h) + return tmap, hmap +end diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 226acd48..b1db58fe 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -35,7 +35,11 @@ ChunksController::ChunksController(Level* level, uint padding) ChunksController::~ChunksController() = default; -void ChunksController::update(int64_t maxDuration) { +void ChunksController::update( + int64_t maxDuration, int loadDistance, int centerX, int centerY +) { + generator->update(centerX, centerY, loadDistance); + int64_t mcstotal = 0; for (uint i = 0; i < MAX_WORK_PER_FRAME; i++) { diff --git a/src/logic/ChunksController.hpp b/src/logic/ChunksController.hpp index 5e607f51..85b00e8a 100644 --- a/src/logic/ChunksController.hpp +++ b/src/logic/ChunksController.hpp @@ -28,5 +28,9 @@ public: ~ChunksController(); /// @param maxDuration milliseconds reserved for chunks loading - void update(int64_t maxDuration); + void update( + int64_t maxDuration, + int loadDistance, + int centerX, + int centerY); }; diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 1badb0dc..1e947b42 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -10,6 +10,7 @@ #include "settings.hpp" #include "world/Level.hpp" #include "world/World.hpp" +#include "maths/voxmaths.hpp" #include "scripting/scripting.hpp" static debug::Logger logger("level-control"); @@ -38,7 +39,10 @@ void LevelController::update(float delta, bool input, bool pause) { position.z, settings.chunks.loadDistance.get() + settings.chunks.padding.get() * 2 ); - chunks->update(settings.chunks.loadSpeed.get()); + chunks->update( + settings.chunks.loadSpeed.get(), settings.chunks.loadDistance.get(), + floordiv(position.x, CHUNK_W), floordiv(position.z, CHUNK_D) + ); if (!pause) { // update all objects that needed diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index f699957d..c1e568fe 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -9,13 +9,35 @@ #include "voxels/Chunk.hpp" #include "world/generator/GeneratorDef.hpp" #include "util/timeutil.hpp" +#include "debug/Logger.hpp" + +static debug::Logger logger("world-generator"); static inline constexpr uint MAX_PARAMETERS = 16; +static inline constexpr uint MAX_CHUNK_PROTOTYPE_LEVELS = 8; WorldGenerator::WorldGenerator( const GeneratorDef& def, const Content* content, uint64_t seed ) - : def(def), content(content), seed(seed) { + : def(def), + content(content), + seed(seed), + surroundMap(0, MAX_CHUNK_PROTOTYPE_LEVELS) +{ + surroundMap.setOutCallback([this](int const x, int const z, int8_t) { + const auto& found = prototypes.find({x, z}); + if (found == prototypes.end()) { + logger.warning() << "unable to remove non-existing chunk prototype"; + return; + } + prototypes.erase({x, z}); + }); + surroundMap.setLevelCallback(1, [this](int const x, int const z) { + if (prototypes.find({x, z}) != prototypes.end()) { + return; + } + prototypes[{x, z}] = generatePrototype(x, z); + }); } static inline void generate_pole( @@ -78,7 +100,6 @@ static inline const Biome* choose_biome( std::unique_ptr WorldGenerator::generatePrototype( int chunkX, int chunkZ ) { - // timeutil::ScopeLogTimer log(666); auto biomeParams = def.script->generateParameterMaps( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed); const auto& biomes = def.script->getBiomes(); @@ -103,11 +124,23 @@ void WorldGenerator::generateHeightmap( prototype->level = ChunkPrototypeLevel::HEIGHTMAP; } +void WorldGenerator::update(int centerX, int centerY, int loadDistance) { + surroundMap.setCenter(centerX, centerY); + surroundMap.resize(loadDistance); + surroundMap.setCenter(centerX, centerY); +} + void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { //timeutil::ScopeLogTimer log(555); + surroundMap.completeAt(chunkX, chunkZ); - auto prototype = generatePrototype(chunkX, chunkZ); - generateHeightmap(prototype.get(), chunkX, chunkZ); + const auto& found = prototypes.find({chunkX, chunkZ}); + if (found == prototypes.end()) { + throw std::runtime_error("no prototype found"); + } + + auto prototype = found->second.get(); + generateHeightmap(prototype, chunkX, chunkZ); const auto values = prototype->heightmap->getValues(); diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 804cd4e9..2802baa6 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -3,10 +3,12 @@ #include #include #include +#include #include "constants.hpp" #include "typedefs.hpp" #include "voxels/voxel.hpp" +#include "SurroundMap.hpp" class Content; struct GeneratorDef; @@ -42,6 +44,10 @@ class WorldGenerator { const Content* content; /// @param seed world seed uint64_t seed; + /// @brief Chunk prototypes main storage + std::unordered_map> prototypes; + /// @brief Chunk prototypes loading surround map + SurroundMap surroundMap; /// @brief Generate chunk prototype (see ChunkPrototype) /// @param x chunk position X divided by CHUNK_W @@ -57,6 +63,8 @@ public: ); virtual ~WorldGenerator() = default; + virtual void update(int centerX, int centerY, int loadDistance); + /// @brief Generate complete chunk voxels /// @param voxels destinatiopn chunk voxels buffer /// @param x chunk position X divided by CHUNK_W From 8516cf4f7f7ef3e3a7c7a31cf6e4af4e6900ebc6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Sep 2024 15:23:09 +0300 Subject: [PATCH 062/139] add world generator level 2 (heightmaps) --- res/generators/default.lua | 4 ++-- src/maths/Heightmap.cpp | 8 ++++---- src/world/generator/WorldGenerator.cpp | 15 +++++++++++---- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 10f3b08c..3641db57 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -112,10 +112,10 @@ local function _generate_biome_parameters(x, y, w, h, seed, s) end function generate_biome_parameters(x, y, w, h, seed) - local bpd = 4 + local bpd = 8 local tmap, hmap = _generate_biome_parameters( math.floor(x/bpd), math.floor(y/bpd), - math.floor(w/bpd)+1, math.floor(h/bpd)+1, seed, bpd) + math.floor(w/bpd)+1, math.floor(h/bpd)+1, seed, bpd) tmap:resize(w+bpd, h+bpd, 'linear') tmap:crop(0, 0, w, h) diff --git a/src/maths/Heightmap.cpp b/src/maths/Heightmap.cpp index 165c0040..d3b048de 100644 --- a/src/maths/Heightmap.cpp +++ b/src/maths/Heightmap.cpp @@ -11,7 +11,7 @@ static inline float smootherstep(float x) { static inline float sample_at( const float* buffer, - uint width, uint height, + uint width, uint x, uint y ) { return buffer[y*width+x]; @@ -36,11 +36,11 @@ static inline float sample_at( switch (interp) { case InterpolationType::LINEAR: { float s00 = val; - float s10 = sample_at(buffer, width, height, + float s10 = sample_at(buffer, width, ix + 1 < width ? ix + 1 : ix, iy); - float s01 = sample_at(buffer, width, height, ix, + float s01 = sample_at(buffer, width, ix, iy + 1 < height ? iy + 1 : iy); - float s11 = sample_at(buffer, width, height, + float s11 = sample_at(buffer, width, ix + 1 < width ? ix + 1 : ix, iy + 1 < height ? iy + 1 : iy); float a00 = s00; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index c1e568fe..3e0216a4 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -38,6 +38,13 @@ WorldGenerator::WorldGenerator( } prototypes[{x, z}] = generatePrototype(x, z); }); + surroundMap.setLevelCallback(2, [this](int const x, int const z) { + const auto& found = prototypes.find({x, z}); + if (found == prototypes.end()) { + throw std::runtime_error("prototype not found"); + } + generateHeightmap(found->second.get(), x, z); + }); } static inline void generate_pole( @@ -119,6 +126,9 @@ std::unique_ptr WorldGenerator::generatePrototype( void WorldGenerator::generateHeightmap( ChunkPrototype* prototype, int chunkX, int chunkZ ) { + if (prototype->level >= ChunkPrototypeLevel::HEIGHTMAP) { + return; + } prototype->heightmap = def.script->generateHeightmap( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed); prototype->level = ChunkPrototypeLevel::HEIGHTMAP; @@ -126,12 +136,11 @@ void WorldGenerator::generateHeightmap( void WorldGenerator::update(int centerX, int centerY, int loadDistance) { surroundMap.setCenter(centerX, centerY); - surroundMap.resize(loadDistance); + surroundMap.resize(loadDistance + 1 /* additional safety padding */); surroundMap.setCenter(centerX, centerY); } void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { - //timeutil::ScopeLogTimer log(555); surroundMap.completeAt(chunkX, chunkZ); const auto& found = prototypes.find({chunkX, chunkZ}); @@ -140,8 +149,6 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { } auto prototype = found->second.get(); - generateHeightmap(prototype, chunkX, chunkZ); - const auto values = prototype->heightmap->getValues(); uint seaLevel = def.script->getSeaLevel(); From 390a16ace16b9cf8dfb3c98e45904800fddde017 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 12 Sep 2024 11:01:19 +0300 Subject: [PATCH 063/139] rename generator/Structure to generator/VoxelStructure --- src/logic/scripting/lua/libgeneration.cpp | 4 ++-- .../generator/{Structure.cpp => VoxelStructure.cpp} | 10 +++++----- .../generator/{Structure.hpp => VoxelStructure.hpp} | 8 ++++---- src/world/generator/WorldGenerator.cpp | 5 +++-- 4 files changed, 14 insertions(+), 13 deletions(-) rename src/world/generator/{Structure.cpp => VoxelStructure.cpp} (90%) rename src/world/generator/{Structure.hpp => VoxelStructure.hpp} (84%) diff --git a/src/logic/scripting/lua/libgeneration.cpp b/src/logic/scripting/lua/libgeneration.cpp index 106ddbb1..b7c6eff7 100644 --- a/src/logic/scripting/lua/libgeneration.cpp +++ b/src/logic/scripting/lua/libgeneration.cpp @@ -4,7 +4,7 @@ #include "files/util.hpp" #include "coders/binary_json.hpp" #include "world/Level.hpp" -#include "world/generator/Structure.hpp" +#include "world/generator/VoxelStructure.hpp" using namespace scripting; @@ -17,7 +17,7 @@ static int l_save_structure(lua::State* L) { } bool saveEntities = lua::toboolean(L, 4); - auto structure = Structure::create(level, pointA, pointB, saveEntities); + auto structure = VoxelStructure::create(level, pointA, pointB, saveEntities); auto map = structure->serialize(); auto bytes = json::to_binary(map.get()); diff --git a/src/world/generator/Structure.cpp b/src/world/generator/VoxelStructure.cpp similarity index 90% rename from src/world/generator/Structure.cpp rename to src/world/generator/VoxelStructure.cpp index bf5426c7..64b22621 100644 --- a/src/world/generator/Structure.cpp +++ b/src/world/generator/VoxelStructure.cpp @@ -1,4 +1,4 @@ -#include "Structure.hpp" +#include "VoxelStructure.hpp" #include #include @@ -11,7 +11,7 @@ #include "voxels/VoxelsVolume.hpp" #include "world/Level.hpp" -std::unique_ptr Structure::create( +std::unique_ptr VoxelStructure::create( Level* level, const glm::ivec3& a, const glm::ivec3& b, bool entities ) { auto start = glm::min(a, b); @@ -43,11 +43,11 @@ std::unique_ptr Structure::create( voxels[i].id = index; } - return std::make_unique( + return std::make_unique( size, std::move(voxels), std::move(blockNames)); } -std::unique_ptr Structure::serialize() const { +std::unique_ptr VoxelStructure::serialize() const { auto root = std::make_unique(); root->put("version", STRUCTURE_FORMAT_VERSION); root->put("size", dynamic::to_value(size)); @@ -64,7 +64,7 @@ std::unique_ptr Structure::serialize() const { return root; } -void Structure::deserialize(dynamic::Map* src) { +void VoxelStructure::deserialize(dynamic::Map* src) { size = glm::ivec3(); dynamic::get_vec(src, "size", size); voxels.resize(size.x*size.y*size.z); diff --git a/src/world/generator/Structure.hpp b/src/world/generator/VoxelStructure.hpp similarity index 84% rename from src/world/generator/Structure.hpp rename to src/world/generator/VoxelStructure.hpp index 2e932c61..635175f2 100644 --- a/src/world/generator/Structure.hpp +++ b/src/world/generator/VoxelStructure.hpp @@ -11,7 +11,7 @@ inline constexpr int STRUCTURE_FORMAT_VERSION = 1; class Level; class Content; -struct Structure : public Serializable { +struct VoxelStructure : public Serializable { glm::ivec3 size; /// @brief Structure voxels indexed different to world content @@ -19,9 +19,9 @@ struct Structure : public Serializable { /// @brief Block names are used for indexing std::vector blockNames; - Structure() : size() {} + VoxelStructure() : size() {} - Structure( + VoxelStructure( glm::ivec3 size, std::vector voxels, std::vector blockNames @@ -33,6 +33,6 @@ struct Structure : public Serializable { std::unique_ptr serialize() const override; void deserialize(dynamic::Map* src) override; - static std::unique_ptr create( + static std::unique_ptr create( Level* level, const glm::ivec3& a, const glm::ivec3& b, bool entities); }; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 3e0216a4..a6a1fe7e 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -13,7 +13,7 @@ static debug::Logger logger("world-generator"); -static inline constexpr uint MAX_PARAMETERS = 16; +static inline constexpr uint MAX_PARAMETERS = 8; static inline constexpr uint MAX_CHUNK_PROTOTYPE_LEVELS = 8; WorldGenerator::WorldGenerator( @@ -136,7 +136,8 @@ void WorldGenerator::generateHeightmap( void WorldGenerator::update(int centerX, int centerY, int loadDistance) { surroundMap.setCenter(centerX, centerY); - surroundMap.resize(loadDistance + 1 /* additional safety padding */); + // 1 is safety padding preventing ChunksController rounding problem + surroundMap.resize(loadDistance + 1); surroundMap.setCenter(centerX, centerY); } From 5241c91cc8eb54c6b17ce8c0752dd4da3e6f1f1c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 12 Sep 2024 12:35:24 +0300 Subject: [PATCH 064/139] add new json::stringify overloads --- src/coders/json.cpp | 71 +++++++++++++++++++++++++++++++------------- src/coders/json.hpp | 4 +++ src/data/dynamic.cpp | 11 +++++++ src/data/dynamic.hpp | 2 ++ 4 files changed, 67 insertions(+), 21 deletions(-) diff --git a/src/coders/json.cpp b/src/coders/json.cpp index ac18eb3c..ea2d33c9 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -44,6 +44,14 @@ void stringifyObj( bool nice ); +void stringifyArr( + const List* list, + std::stringstream& ss, + int indent, + const std::string& indentstr, + bool nice +); + void stringifyValue( const Value& value, std::stringstream& ss, @@ -54,26 +62,7 @@ void stringifyValue( if (auto map = std::get_if(&value)) { stringifyObj(map->get(), ss, indent, indentstr, nice); } else if (auto listptr = std::get_if(&value)) { - auto list = *listptr; - if (list->size() == 0) { - ss << "[]"; - return; - } - ss << '['; - for (uint i = 0; i < list->size(); i++) { - Value& value = list->get(i); - if (i > 0 || nice) { - newline(ss, nice, indent, indentstr); - } - stringifyValue(value, ss, indent + 1, indentstr, nice); - if (i + 1 < list->size()) { - ss << ','; - } - } - if (nice) { - newline(ss, true, indent - 1, indentstr); - } - ss << ']'; + stringifyArr(listptr->get(), ss, indent, indentstr, nice); } else if (auto flag = std::get_if(&value)) { ss << (*flag ? "true" : "false"); } else if (auto num = std::get_if(&value)) { @@ -87,6 +76,38 @@ void stringifyValue( } } +void stringifyArr( + const List* list, + std::stringstream& ss, + int indent, + const std::string& indentstr, + bool nice +) { + if (list == nullptr) { + ss << "nullptr"; + return; + } + if (list->values.empty()) { + ss << "[]"; + return; + } + ss << "["; + for (size_t i = 0; i < list->size(); i++) { + if (i > 0 || nice) { + newline(ss, nice, indent, indentstr); + } + const Value& value = list->values[i]; + stringifyValue(value, ss, indent + 1, indentstr, nice); + if (i + 1 < list->size()) { + ss << ','; + } + } + if (nice) { + newline(ss, true, indent - 1, indentstr); + } + ss << ']'; +} + void stringifyObj( const Map* obj, std::stringstream& ss, @@ -103,7 +124,7 @@ void stringifyObj( return; } ss << "{"; - uint index = 0; + size_t index = 0; for (auto& entry : obj->values) { const std::string& key = entry.first; if (index > 0 || nice) { @@ -131,6 +152,14 @@ std::string json::stringify( return ss.str(); } +std::string json::stringify( + const dynamic::List* arr, bool nice, const std::string& indent +) { + std::stringstream ss; + stringifyArr(arr, ss, 1, indent, nice); + return ss.str(); +} + std::string json::stringify( const dynamic::Value& value, bool nice, const std::string& indent ) { diff --git a/src/coders/json.hpp b/src/coders/json.hpp index ef48ac37..1491be70 100644 --- a/src/coders/json.hpp +++ b/src/coders/json.hpp @@ -14,6 +14,10 @@ namespace json { const dynamic::Map* obj, bool nice, const std::string& indent ); + std::string stringify( + const dynamic::List* arr, bool nice, const std::string& indent + ); + std::string stringify( const dynamic::Value& value, bool nice, const std::string& indent ); diff --git a/src/data/dynamic.cpp b/src/data/dynamic.cpp index f0d60bf1..f49a0aee 100644 --- a/src/data/dynamic.cpp +++ b/src/data/dynamic.cpp @@ -9,11 +9,22 @@ std::ostream& operator<<(std::ostream& stream, const dynamic::Value& value) { return stream; } +std::ostream& operator<<(std::ostream& stream, const dynamic::Map& value) { + stream << json::stringify(&value, false, " "); + return stream; +} + std::ostream& operator<<(std::ostream& stream, const dynamic::Map_sptr& value) { stream << json::stringify(value, false, " "); return stream; } +std::ostream& operator<<(std::ostream& stream, const dynamic::List& value) { + stream << json::stringify(&value, false, " "); + return stream; +} + + std::ostream& operator<<( std::ostream& stream, const dynamic::List_sptr& value ) { diff --git a/src/data/dynamic.hpp b/src/data/dynamic.hpp index 65320fbb..9525a898 100644 --- a/src/data/dynamic.hpp +++ b/src/data/dynamic.hpp @@ -160,5 +160,7 @@ namespace dynamic { } std::ostream& operator<<(std::ostream& stream, const dynamic::Value& value); +std::ostream& operator<<(std::ostream& stream, const dynamic::Map& value); std::ostream& operator<<(std::ostream& stream, const dynamic::Map_sptr& value); +std::ostream& operator<<(std::ostream& stream, const dynamic::List& value); std::ostream& operator<<(std::ostream& stream, const dynamic::List_sptr& value); From 88b0f8e3d6ac5951fe6d7033831152654480fa67 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 19 Sep 2024 18:41:34 +0300 Subject: [PATCH 065/139] add VoxelStructure lua usertype --- res/generators/default.files/tree0.vox | Bin 0 -> 16479 bytes res/scripts/world.lua | 1 + src/logic/scripting/lua/libfile.cpp | 2 +- src/logic/scripting/lua/libgeneration.cpp | 17 ++++++ src/logic/scripting/lua/lua_custom_types.hpp | 49 ++++++++++++------ src/logic/scripting/lua/lua_engine.cpp | 5 +- src/logic/scripting/lua/lua_util.hpp | 8 +-- .../{ => usertypes}/lua_type_bytearray.cpp | 36 ++++++------- .../{ => usertypes}/lua_type_heightmap.cpp | 21 +++++++- .../lua/usertypes/lua_type_voxelstructure.cpp | 26 ++++++++++ src/maths/Heightmap.hpp | 1 + src/util/stringutil.cpp | 8 ++- src/util/stringutil.hpp | 2 + src/world/generator/VoxelStructure.cpp | 16 +++++- src/world/generator/VoxelStructure.hpp | 17 +++++- src/world/generator/WorldGenerator.cpp | 9 ++-- src/world/generator/WorldGenerator.hpp | 22 ++++---- 17 files changed, 180 insertions(+), 60 deletions(-) create mode 100644 res/generators/default.files/tree0.vox rename src/logic/scripting/lua/{ => usertypes}/lua_type_bytearray.cpp (81%) rename src/logic/scripting/lua/{ => usertypes}/lua_type_heightmap.cpp (95%) create mode 100644 src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp diff --git a/res/generators/default.files/tree0.vox b/res/generators/default.files/tree0.vox new file mode 100644 index 0000000000000000000000000000000000000000..3bcff6939b0f4c9ff3aefcdf1375869974caf324 GIT binary patch literal 16479 zcmeI)!3u&v5XSMbE=HGLq!;kin}pRKBy&N*qNi`?Fw{wKH>8$7A|*PzJ3spL5*EjU z$mk!g9b~l@wyXt;th?jaivR*s0;&1u2{EP1IRt)JK(m{Yf2&Wq2c*>gt}H7Skf z{Aiu-n*Z1_v_l(bnQoZ{0R&9IvKNzItF`>6j1g1&*@J!O69EL~30RI}GN?70f4c|F zoB93hw>8YI*Xv`}9WxI#2;?Y`njg#VH^#})m!1&FT_834x%i=IO%w19rN}==!1YUHGf1ya1~wcpLx# literal 0 HcmV?d00001 diff --git a/res/scripts/world.lua b/res/scripts/world.lua index 964100e7..d2726162 100644 --- a/res/scripts/world.lua +++ b/res/scripts/world.lua @@ -1,3 +1,4 @@ -- use for engine development tests -- must be empty in release -- must not be modified by content-packs +print(generation.load_structure("core:generators/default.files/tree0.vox")) diff --git a/src/logic/scripting/lua/libfile.cpp b/src/logic/scripting/lua/libfile.cpp index 5da4358d..30c0e204 100644 --- a/src/logic/scripting/lua/libfile.cpp +++ b/src/logic/scripting/lua/libfile.cpp @@ -158,7 +158,7 @@ static int l_file_write_bytes(lua::State* L) { fs::path path = resolve_path(lua::require_string(L, pathIndex)); - if (auto bytearray = lua::touserdata(L, -1)) { + if (auto bytearray = lua::touserdata(L, -1)) { auto& bytes = bytearray->data(); return lua::pushboolean( L, files::write_bytes(path, bytes.data(), bytes.size()) diff --git a/src/logic/scripting/lua/libgeneration.cpp b/src/logic/scripting/lua/libgeneration.cpp index 956c28e5..c38716e9 100644 --- a/src/logic/scripting/lua/libgeneration.cpp +++ b/src/logic/scripting/lua/libgeneration.cpp @@ -5,6 +5,8 @@ #include "coders/binary_json.hpp" #include "world/Level.hpp" #include "world/generator/VoxelStructure.hpp" +#include "engine.hpp" +#include "lua_custom_types.hpp" using namespace scripting; @@ -25,6 +27,21 @@ static int l_save_structure(lua::State* L) { return 0; } +static int l_load_structure(lua::State* L) { + auto paths = engine->getPaths(); + auto filename = lua::require_string(L, 1); + auto path = paths->resolve(filename); + if (!std::filesystem::exists(path)) { + throw std::runtime_error("file "+path.u8string()+" does not exist"); + } + auto map = files::read_binary_json(path); + + auto structure = std::make_shared(); + structure->deserialize(map); + return lua::newuserdata(L, std::move(structure)); +} + const luaL_Reg generationlib[] = { {"save_structure", lua::wrap}, + {"load_structure", lua::wrap}, {NULL, NULL}}; diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index d6a7ba0b..67faeef4 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -4,9 +4,10 @@ #include #include "lua_commons.hpp" -#include "maths/Heightmap.hpp" struct fnl_state; +class Heightmap; +class VoxelStructure; namespace lua { class Userdata { @@ -15,12 +16,12 @@ namespace lua { virtual const std::string& getTypeName() const = 0; }; - class Bytearray : public Userdata { + class LuaBytearray : public Userdata { std::vector buffer; public: - Bytearray(size_t capacity); - Bytearray(std::vector buffer); - virtual ~Bytearray(); + LuaBytearray(size_t capacity); + LuaBytearray(std::vector buffer); + virtual ~LuaBytearray(); const std::string& getTypeName() const override { return TYPENAME; @@ -32,6 +33,7 @@ namespace lua { static int createMetatable(lua::State*); inline static std::string TYPENAME = "Bytearray"; }; + static_assert(!std::is_abstract()); class LuaHeightmap : public Userdata { std::shared_ptr map; @@ -41,21 +43,13 @@ namespace lua { virtual ~LuaHeightmap(); - uint getWidth() const { - return map->getWidth(); - } + uint getWidth() const; - uint getHeight() const { - return map->getHeight(); - } + uint getHeight() const; - float* getValues() { - return map->getValues(); - } + float* getValues(); - const float* getValues() const { - return map->getValues(); - } + const float* getValues() const; const std::string& getTypeName() const override { return TYPENAME; @@ -74,4 +68,25 @@ namespace lua { static int createMetatable(lua::State*); inline static std::string TYPENAME = "Heightmap"; }; + static_assert(!std::is_abstract()); + + class LuaVoxelStructure : public Userdata { + std::shared_ptr structure; + public: + LuaVoxelStructure(std::shared_ptr structure); + + virtual ~LuaVoxelStructure(); + + std::shared_ptr getStructure() const { + return structure; + } + + const std::string& getTypeName() const override { + return TYPENAME; + } + + static int createMetatable(lua::State*); + inline static std::string TYPENAME = "VoxelStructure"; + }; + static_assert(!std::is_abstract()); } diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 879ec4c6..6ae427e3 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -96,8 +96,9 @@ void lua::initialize() { initialize_libs_extends(L); - newusertype(L, "Bytearray"); - newusertype(L, "Heightmap"); + newusertype(L); + newusertype(L); + newusertype(L); } void lua::finalize() { diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index fc202555..f365cdba 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -248,10 +248,12 @@ namespace lua { return 1; } - template - inline void newusertype(lua::State* L, const std::string& name) { + template + inline std::enable_if_t> + newusertype(lua::State* L) { + const std::string& name = T::TYPENAME; usertypeNames[typeid(T)] = name; - func(L); + T::createMetatable(L); pushcfunction(L, userdata_destructor); setfield(L, "__gc"); diff --git a/src/logic/scripting/lua/lua_type_bytearray.cpp b/src/logic/scripting/lua/usertypes/lua_type_bytearray.cpp similarity index 81% rename from src/logic/scripting/lua/lua_type_bytearray.cpp rename to src/logic/scripting/lua/usertypes/lua_type_bytearray.cpp index cf41f318..7be3fc10 100644 --- a/src/logic/scripting/lua/lua_type_bytearray.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_bytearray.cpp @@ -1,23 +1,23 @@ -#include "lua_custom_types.hpp" +#include "../lua_custom_types.hpp" #include -#include "lua_util.hpp" +#include "../lua_util.hpp" using namespace lua; -Bytearray::Bytearray(size_t capacity) : buffer(capacity) { +LuaBytearray::LuaBytearray(size_t capacity) : buffer(capacity) { buffer.resize(capacity); } -Bytearray::Bytearray(std::vector buffer) : buffer(std::move(buffer)) { +LuaBytearray::LuaBytearray(std::vector buffer) : buffer(std::move(buffer)) { } -Bytearray::~Bytearray() { +LuaBytearray::~LuaBytearray() { } static int l_append(lua::State* L) { - if (auto buffer = touserdata(L, 1)) { + if (auto buffer = touserdata(L, 1)) { auto value = tointeger(L, 2); buffer->data().push_back(static_cast(value)); } @@ -25,7 +25,7 @@ static int l_append(lua::State* L) { } static int l_insert(lua::State* L) { - auto buffer = touserdata(L, 1); + auto buffer = touserdata(L, 1); if (buffer == nullptr) { return 0; } @@ -40,7 +40,7 @@ static int l_insert(lua::State* L) { } static int l_remove(lua::State* L) { - auto buffer = touserdata(L, 1); + auto buffer = touserdata(L, 1); if (buffer == nullptr) { return 0; } @@ -69,17 +69,17 @@ static int l_meta_meta_call(lua::State* L) { buffer[i] = static_cast(tointeger(L, -1)); pop(L); } - return newuserdata(L, std::move(buffer)); + return newuserdata(L, std::move(buffer)); } auto size = tointeger(L, 2); if (size < 0) { throw std::runtime_error("size can not be less than 0"); } - return newuserdata(L, static_cast(size)); + return newuserdata(L, static_cast(size)); } static int l_meta_index(lua::State* L) { - auto buffer = touserdata(L, 1); + auto buffer = touserdata(L, 1); if (buffer == nullptr) { return 0; } @@ -98,7 +98,7 @@ static int l_meta_index(lua::State* L) { } static int l_meta_newindex(lua::State* L) { - auto buffer = touserdata(L, 1); + auto buffer = touserdata(L, 1); if (buffer == nullptr) { return 0; } @@ -116,14 +116,14 @@ static int l_meta_newindex(lua::State* L) { } static int l_meta_len(lua::State* L) { - if (auto buffer = touserdata(L, 1)) { + if (auto buffer = touserdata(L, 1)) { return pushinteger(L, buffer->data().size()); } return 0; } static int l_meta_tostring(lua::State* L) { - auto buffer = touserdata(L, 1); + auto buffer = touserdata(L, 1); if (buffer == nullptr) { return 0; } @@ -147,8 +147,8 @@ static int l_meta_tostring(lua::State* L) { } static int l_meta_add(lua::State* L) { - auto bufferA = touserdata(L, 1); - auto bufferB = touserdata(L, 2); + auto bufferA = touserdata(L, 1); + auto bufferB = touserdata(L, 2); if (bufferA == nullptr || bufferB == nullptr) { return 0; } @@ -159,10 +159,10 @@ static int l_meta_add(lua::State* L) { ab.reserve(dataA.size() + dataB.size()); ab.insert(ab.end(), dataA.begin(), dataA.end()); ab.insert(ab.end(), dataB.begin(), dataB.end()); - return newuserdata(L, std::move(ab)); + return newuserdata(L, std::move(ab)); } -int Bytearray::createMetatable(lua::State* L) { +int LuaBytearray::createMetatable(lua::State* L) { createtable(L, 0, 6); pushcfunction(L, lua::wrap); setfield(L, "__index"); diff --git a/src/logic/scripting/lua/lua_type_heightmap.cpp b/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp similarity index 95% rename from src/logic/scripting/lua/lua_type_heightmap.cpp rename to src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp index f467dc35..fc766c53 100644 --- a/src/logic/scripting/lua/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp @@ -1,4 +1,4 @@ -#include "lua_custom_types.hpp" +#include "../lua_custom_types.hpp" #include #include @@ -11,7 +11,8 @@ #include "coders/png.hpp" #include "files/util.hpp" #include "graphics/core/ImageData.hpp" -#include "lua_util.hpp" +#include "maths/Heightmap.hpp" +#include "../lua_util.hpp" using namespace lua; @@ -27,6 +28,22 @@ void LuaHeightmap::setSeed(int64_t seed) { noise->seed = seed; } +uint LuaHeightmap::getWidth() const { + return map->getWidth(); +} + +uint LuaHeightmap::getHeight() const { + return map->getHeight(); +} + +float* LuaHeightmap::getValues() { + return map->getValues(); +} + +const float* LuaHeightmap::getValues() const { + return map->getValues(); +} + static int l_dump(lua::State* L) { if (auto heightmap = touserdata(L, 1)) { auto filename = require_string(L, 2); diff --git a/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp b/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp new file mode 100644 index 00000000..b35ed517 --- /dev/null +++ b/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp @@ -0,0 +1,26 @@ +#include "../lua_custom_types.hpp" + +#include "../lua_util.hpp" + +#include "world/generator/VoxelStructure.hpp" +#include "util/stringutil.hpp" + +using namespace lua; + +LuaVoxelStructure::LuaVoxelStructure(std::shared_ptr structure) + : structure(std::move(structure)) {} + +LuaVoxelStructure::~LuaVoxelStructure() { +} + +static int l_meta_tostring(lua::State* L) { + return pushstring(L, "VoxelStructure(0x" + util::tohex( + reinterpret_cast(topointer(L, 1)))+")"); +} + +int LuaVoxelStructure::createMetatable(lua::State* L) { + createtable(L, 0, 1); + pushcfunction(L, lua::wrap); + setfield(L, "__tostring"); + return 1; +} diff --git a/src/maths/Heightmap.hpp b/src/maths/Heightmap.hpp index 52a3d394..8e7e5594 100644 --- a/src/maths/Heightmap.hpp +++ b/src/maths/Heightmap.hpp @@ -4,6 +4,7 @@ #include #include "typedefs.hpp" +#include "maths/Heightmap.hpp" enum class InterpolationType { NEAREST, diff --git a/src/util/stringutil.cpp b/src/util/stringutil.cpp index 4452301b..d1e5ef9f 100644 --- a/src/util/stringutil.cpp +++ b/src/util/stringutil.cpp @@ -312,13 +312,17 @@ std::string util::base64_encode(const ubyte* data, size_t size) { return ss.str(); } -std::string util::mangleid(uint64_t value) { - // todo: use base64 +std::string util::tohex(uint64_t value) { std::stringstream ss; ss << std::hex << value; return ss.str(); } +std::string util::mangleid(uint64_t value) { + // todo: use base64 + return tohex(value); +} + util::Buffer util::base64_decode(const char* str, size_t size) { util::Buffer bytes((size / 4) * 3); ubyte* dst = bytes.data(); diff --git a/src/util/stringutil.hpp b/src/util/stringutil.hpp index 21d58e5e..cb251e83 100644 --- a/src/util/stringutil.hpp +++ b/src/util/stringutil.hpp @@ -60,6 +60,8 @@ namespace util { util::Buffer base64_decode(const char* str, size_t size); util::Buffer base64_decode(const std::string& str); + std::string tohex(uint64_t value); + std::string mangleid(uint64_t value); int replaceAll( diff --git a/src/world/generator/VoxelStructure.cpp b/src/world/generator/VoxelStructure.cpp index 1b7abe4d..b7595064 100644 --- a/src/world/generator/VoxelStructure.cpp +++ b/src/world/generator/VoxelStructure.cpp @@ -66,11 +66,23 @@ dv::value VoxelStructure::serialize() const { void VoxelStructure::deserialize(const dv::value& src) { size = glm::ivec3(); dv::get_vec(src, "size", size); - voxels.resize(size.x*size.y*size.z); + + auto volume = size.x*size.y*size.z; + voxels.resize(volume); const auto& voxelsArr = src["voxels"]; - for (size_t i = 0; i < size.x*size.y*size.z; i++) { + for (size_t i = 0; i < volume; i++) { voxels[i].id = voxelsArr[i * 2].asInteger(); voxels[i].state = int2blockstate(voxelsArr[i * 2 + 1].asInteger()); } } + +void VoxelStructure::prepare(const Content& content) { + auto volume = size.x*size.y*size.z; + voxelsRuntime.resize(volume); + for (size_t i = 0; i < volume; i++) { + const auto& name = blockNames[voxels[i].id]; + voxelsRuntime[i].id = content.blocks.require(name).rt.id; + voxelsRuntime[i].state = voxels[i].state; + } +} diff --git a/src/world/generator/VoxelStructure.hpp b/src/world/generator/VoxelStructure.hpp index 68819653..444a1f1a 100644 --- a/src/world/generator/VoxelStructure.hpp +++ b/src/world/generator/VoxelStructure.hpp @@ -11,7 +11,7 @@ inline constexpr int STRUCTURE_FORMAT_VERSION = 1; class Level; class Content; -struct VoxelStructure : public Serializable { +class VoxelStructure : public Serializable { glm::ivec3 size; /// @brief Structure voxels indexed different to world content @@ -19,6 +19,9 @@ struct VoxelStructure : public Serializable { /// @brief Block names are used for indexing std::vector blockNames; + /// @brief Structure voxels built on prepare(...) call + std::vector voxelsRuntime; +public: VoxelStructure() : size() {} VoxelStructure( @@ -33,6 +36,18 @@ struct VoxelStructure : public Serializable { dv::value serialize() const override; void deserialize(const dv::value& src) override; + /// @brief Build runtime voxel indices + /// @param content world content + void prepare(const Content& content); + static std::unique_ptr create( Level* level, const glm::ivec3& a, const glm::ivec3& b, bool entities); + + const glm::ivec3& getSize() const { + return size; + } + + const std::vector& getRuntimeVoxels() { + return voxelsRuntime; + } }; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index a6a1fe7e..726467a0 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -7,7 +7,8 @@ #include "content/Content.hpp" #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" -#include "world/generator/GeneratorDef.hpp" +#include "GeneratorDef.hpp" +#include "VoxelStructure.hpp" #include "util/timeutil.hpp" #include "debug/Logger.hpp" @@ -47,6 +48,8 @@ WorldGenerator::WorldGenerator( }); } +WorldGenerator::~WorldGenerator() {} + static inline void generate_pole( const BlocksLayers& layers, int top, int bottom, @@ -119,8 +122,8 @@ std::unique_ptr WorldGenerator::generatePrototype( } return std::make_unique( ChunkPrototypeLevel::BIOMES, - nullptr, - std::move(chunkBiomes)); + std::move(chunkBiomes), + nullptr); } void WorldGenerator::generateHeightmap( diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 2802baa6..d30d88f9 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -14,6 +14,7 @@ class Content; struct GeneratorDef; class Heightmap; struct Biome; +class VoxelStructure; enum class ChunkPrototypeLevel { BIOMES, HEIGHTMAP @@ -22,18 +23,19 @@ enum class ChunkPrototypeLevel { struct ChunkPrototype { ChunkPrototypeLevel level; - /// @brief chunk heightmap - std::shared_ptr heightmap; /// @brief chunk biomes matrix std::vector biomes; + /// @brief chunk heightmap + std::shared_ptr heightmap; + ChunkPrototype( ChunkPrototypeLevel level, - std::shared_ptr heightmap, - std::vector biomes + std::vector biomes, + std::shared_ptr heightmap ) : level(level), - heightmap(std::move(heightmap)), - biomes(std::move(biomes)) {}; + biomes(std::move(biomes)), + heightmap(std::move(heightmap)) {}; }; /// @brief High-level world generation controller @@ -49,6 +51,8 @@ class WorldGenerator { /// @brief Chunk prototypes loading surround map SurroundMap surroundMap; + std::vector> structures; + /// @brief Generate chunk prototype (see ChunkPrototype) /// @param x chunk position X divided by CHUNK_W /// @param z chunk position Y divided by CHUNK_D @@ -61,15 +65,15 @@ public: const Content* content, uint64_t seed ); - virtual ~WorldGenerator() = default; + ~WorldGenerator(); - virtual void update(int centerX, int centerY, int loadDistance); + void update(int centerX, int centerY, int loadDistance); /// @brief Generate complete chunk voxels /// @param voxels destinatiopn chunk voxels buffer /// @param x chunk position X divided by CHUNK_W /// @param z chunk position Y divided by CHUNK_D - virtual void generate(voxel* voxels, int x, int z); + void generate(voxel* voxels, int x, int z); inline static std::string DEFAULT = "core:default"; }; From 4f882a3ca3e2eef345a0b11bbc923a18e6d38ced Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 20 Sep 2024 23:43:54 +0300 Subject: [PATCH 066/139] add StructurePlacement & add SurroundMap visualization test --- src/util/AreaMap2D.hpp | 2 +- src/world/generator/GeneratorDef.hpp | 6 +++- src/world/generator/WorldGenerator.cpp | 12 ++++--- src/world/generator/WorldGenerator.hpp | 15 +++++++-- test/world/generator/SurroundMap.cpp | 46 ++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 9 deletions(-) diff --git a/src/util/AreaMap2D.hpp b/src/util/AreaMap2D.hpp index 3b550ae2..a14a0a5f 100644 --- a/src/util/AreaMap2D.hpp +++ b/src/util/AreaMap2D.hpp @@ -62,7 +62,7 @@ namespace util { return &firstBuffer[ly * sizeX + lx]; } - T get(TCoord x, TCoord y) { + T get(TCoord x, TCoord y) const { auto lx = x - offsetX; auto ly = y - offsetY; if (lx < 0 || ly < 0 || lx >= sizeX || ly >= sizeY) { diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 4824a39e..3b5df181 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -7,6 +7,7 @@ #include "maths/Heightmap.hpp" class Content; +class VoxelStructure; struct BlocksLayer { /// @brief Layer block @@ -99,8 +100,11 @@ class GeneratorScript { public: virtual ~GeneratorScript() = default; + /// @brief Load all structures + //virtual std::vector> loadStructures() = 0; + /// @brief Generates a heightmap with values in range 0..1 - /// @param offset position of the heightmap top left corner in the world + /// @param offset position of the heightmap in the world /// @param size size of the heightmap /// @param seed world seed /// @return generated heightmap of given size (can't be nullptr) diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 726467a0..25899272 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -14,8 +14,8 @@ static debug::Logger logger("world-generator"); -static inline constexpr uint MAX_PARAMETERS = 8; -static inline constexpr uint MAX_CHUNK_PROTOTYPE_LEVELS = 8; +static inline constexpr uint MAX_PARAMETERS = 4; +static inline constexpr uint MAX_CHUNK_PROTOTYPE_LEVELS = 3; WorldGenerator::WorldGenerator( const GeneratorDef& def, const Content* content, uint64_t seed @@ -114,10 +114,11 @@ std::unique_ptr WorldGenerator::generatePrototype( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed); const auto& biomes = def.script->getBiomes(); - std::vector chunkBiomes(CHUNK_W*CHUNK_D); + auto chunkBiomes = std::make_unique(CHUNK_W*CHUNK_D); for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { - chunkBiomes[z * CHUNK_W + x] = choose_biome(biomes, biomeParams, x, z); + chunkBiomes.get()[z * CHUNK_W + x] = + choose_biome(biomes, biomeParams, x, z); } } return std::make_unique( @@ -162,9 +163,10 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { PseudoRandom plantsRand; plantsRand.setSeed(chunkX, chunkZ); + const auto& biomes = prototype->biomes.get(); for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { - const Biome* biome = prototype->biomes[z * CHUNK_W + x]; + const Biome* biome = biomes[z * CHUNK_W + x]; int height = values[z * CHUNK_W + x] * CHUNK_H; height = std::max(0, height); diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index d30d88f9..4241433e 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -20,18 +20,29 @@ enum class ChunkPrototypeLevel { BIOMES, HEIGHTMAP }; +struct StructurePlacement { + VoxelStructure& structure; + + glm::ivec3 position; + + StructurePlacement(VoxelStructure& structure, glm::ivec3 position) + : structure(structure), position(std::move(position)) {} +}; + struct ChunkPrototype { ChunkPrototypeLevel level; /// @brief chunk biomes matrix - std::vector biomes; + std::unique_ptr biomes; /// @brief chunk heightmap std::shared_ptr heightmap; + std::vector structures; + ChunkPrototype( ChunkPrototypeLevel level, - std::vector biomes, + std::unique_ptr biomes, std::shared_ptr heightmap ) : level(level), biomes(std::move(biomes)), diff --git a/test/world/generator/SurroundMap.cpp b/test/world/generator/SurroundMap.cpp index 7131ff94..6fb5d7ae 100644 --- a/test/world/generator/SurroundMap.cpp +++ b/test/world/generator/SurroundMap.cpp @@ -30,3 +30,49 @@ TEST(SurroundMap, InitTest) { map.completeAt(x - 1, y); EXPECT_EQ(affected, maxLevel * 2 - 1); } + +#define VISUAL_TEST +#ifdef VISUAL_TEST + +#include + +#include "coders/png.hpp" +#include "graphics/core/ImageData.hpp" + +void visualize(const SurroundMap& map, int mul, int max) { + const auto& areaMap = map.getArea(); + int w = areaMap.getWidth(); + int h = areaMap.getHeight(); + int ox = areaMap.getOffsetX(); + int oy = areaMap.getOffsetY(); + + ImageData image(ImageFormat::rgb888, w, h); + ubyte* bytes = image.getData(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int val = areaMap.get(x + ox, y + oy) * mul; + if (val && val / mul < max) { + val = val / 4 + 50; + } + bytes[(y * w + x) * 3] = val; + bytes[(y * w + x) * 3 + 1] = val; + bytes[(y * w + x) * 3 + 2] = val; + } + } + png::write_image("test.png", &image); +} + +TEST(SurroundMap, Visualize) { + int levels = 3; + SurroundMap map(50, levels); + map.setCenter(0, 0); + + for (int i = 0; i < 1000; i++) { + float x = glm::gaussRand(0.0f, 2.0f); + float y = glm::gaussRand(0.0f, 2.0f); + map.completeAt(x, y); + } + visualize(map, 30, levels); +} + +#endif From aa1fb26ea5c4b8242d8be701dc78ff87a9d141fd Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 21 Sep 2024 01:24:38 +0300 Subject: [PATCH 067/139] add EnginePaths::parsePath & update generation.load_structure --- res/scripts/world.lua | 2 +- src/files/engine_paths.cpp | 16 ++++++++++++---- src/files/engine_paths.hpp | 2 ++ src/logic/scripting/lua/libgeneration.cpp | 5 +++-- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/res/scripts/world.lua b/res/scripts/world.lua index d2726162..18ecea91 100644 --- a/res/scripts/world.lua +++ b/res/scripts/world.lua @@ -1,4 +1,4 @@ -- use for engine development tests -- must be empty in release -- must not be modified by content-packs -print(generation.load_structure("core:generators/default.files/tree0.vox")) +print(generation.load_structure("core:default.files/tree0")) diff --git a/src/files/engine_paths.cpp b/src/files/engine_paths.cpp index 1be3ca20..d665603e 100644 --- a/src/files/engine_paths.cpp +++ b/src/files/engine_paths.cpp @@ -162,15 +162,23 @@ void EnginePaths::setContentPacks(std::vector* contentPacks) { this->contentPacks = contentPacks; } +std::tuple EnginePaths::parsePath(std::string_view path) { + size_t separator = path.find(':'); + if (separator == std::string::npos) { + return {"", std::string(path)}; + } + auto prefix = std::string(path.substr(0, separator)); + auto filename = std::string(path.substr(separator + 1)); + return {prefix, filename}; +} + std::filesystem::path EnginePaths::resolve( const std::string& path, bool throwErr ) { - size_t separator = path.find(':'); - if (separator == std::string::npos) { + auto [prefix, filename] = EnginePaths::parsePath(path); + if (prefix.empty()) { throw files_access_error("no entry point specified"); } - std::string prefix = path.substr(0, separator); - std::string filename = path.substr(separator + 1); filename = toCanonic(fs::u8path(filename)).u8string(); if (prefix == "res" || prefix == "core") { diff --git a/src/files/engine_paths.hpp b/src/files/engine_paths.hpp index ab966791..ceb2f613 100644 --- a/src/files/engine_paths.hpp +++ b/src/files/engine_paths.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "content/ContentPack.hpp" @@ -40,6 +41,7 @@ public: std::filesystem::path resolve(const std::string& path, bool throwErr = true); + static std::tuple parsePath(std::string_view view); private: std::filesystem::path userFilesFolder {"."}; std::filesystem::path resourcesFolder {"res"}; diff --git a/src/logic/scripting/lua/libgeneration.cpp b/src/logic/scripting/lua/libgeneration.cpp index c38716e9..b5e90515 100644 --- a/src/logic/scripting/lua/libgeneration.cpp +++ b/src/logic/scripting/lua/libgeneration.cpp @@ -29,8 +29,9 @@ static int l_save_structure(lua::State* L) { static int l_load_structure(lua::State* L) { auto paths = engine->getPaths(); - auto filename = lua::require_string(L, 1); - auto path = paths->resolve(filename); + auto [prefix, filename] = EnginePaths::parsePath(lua::require_string(L, 1)); + + auto path = paths->resolve(prefix+":generators/"+filename+".vox"); if (!std::filesystem::exists(path)) { throw std::runtime_error("file "+path.u8string()+" does not exist"); } From cba1a5c23e8edada84062c9e9c80bd6ef4403a81 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 21 Sep 2024 01:32:38 +0300 Subject: [PATCH 068/139] add LuaGeneratorScript::loadStructures --- res/generators/default.lua | 6 ++++++ res/scripts/world.lua | 1 - .../scripting/scripting_world_generation.cpp | 20 +++++++++++++++++++ src/world/generator/GeneratorDef.hpp | 2 +- src/world/generator/WorldGenerator.cpp | 2 ++ src/world/generator/WorldGenerator.hpp | 2 +- 6 files changed, 30 insertions(+), 3 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 3641db57..89b03707 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -55,6 +55,12 @@ biomes = { } } +function load_structures() + local structures = {} + table.insert(structures, generation.load_structure("core:default.files/tree0")) + return structures +end + local function _generate_heightmap(x, y, w, h, seed, s) local umap = Heightmap(w, h) local vmap = Heightmap(w, h) diff --git a/res/scripts/world.lua b/res/scripts/world.lua index 18ecea91..964100e7 100644 --- a/res/scripts/world.lua +++ b/res/scripts/world.lua @@ -1,4 +1,3 @@ -- use for engine development tests -- must be empty in release -- must not be modified by content-packs -print(generation.load_structure("core:default.files/tree0")) diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index a296de22..b291d600 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -30,6 +30,26 @@ public: seaLevel(seaLevel) {} + std::vector> loadStructures() override { + std::vector> structures; + + auto L = lua::get_main_thread(); + lua::pushenv(L, *env); + if (lua::getfield(L, "load_structures")) { + if (lua::call_nothrow(L, 0, 1)) { + for (int i = 1; i <= lua::objlen(L, -1); i++) { + lua::rawgeti(L, i); + if (auto lstruct = + lua::touserdata(L, -1)) { + structures.push_back(lstruct->getStructure()); + } + } + } + } + lua::pop(L); + return structures; + } + std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed ) override { diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 3b5df181..f219e6d4 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -101,7 +101,7 @@ public: virtual ~GeneratorScript() = default; /// @brief Load all structures - //virtual std::vector> loadStructures() = 0; + virtual std::vector> loadStructures() = 0; /// @brief Generates a heightmap with values in range 0..1 /// @param offset position of the heightmap in the world diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 25899272..5991a699 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -46,6 +46,8 @@ WorldGenerator::WorldGenerator( } generateHeightmap(found->second.get(), x, z); }); + + structures = def.script->loadStructures(); } WorldGenerator::~WorldGenerator() {} diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 4241433e..8d6306c9 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -62,7 +62,7 @@ class WorldGenerator { /// @brief Chunk prototypes loading surround map SurroundMap surroundMap; - std::vector> structures; + std::vector> structures; /// @brief Generate chunk prototype (see ChunkPrototype) /// @param x chunk position X divided by CHUNK_W From 03ba7c9539dc20c92dac0dfc4ad851c681fa64f1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 21 Sep 2024 17:21:47 +0300 Subject: [PATCH 069/139] test voxel structures generation --- res/generators/default.files/tree0.vox | Bin 16479 -> 16479 bytes res/generators/default.lua | 16 ++- .../scripting/scripting_world_generation.cpp | 35 +++++++ src/world/generator/GeneratorDef.hpp | 5 + src/world/generator/StructurePlacement.hpp | 12 +++ src/world/generator/VoxelStructure.cpp | 8 +- src/world/generator/VoxelStructure.hpp | 1 + src/world/generator/WorldGenerator.cpp | 97 ++++++++++++++---- src/world/generator/WorldGenerator.hpp | 30 ++---- 9 files changed, 164 insertions(+), 40 deletions(-) create mode 100644 src/world/generator/StructurePlacement.hpp diff --git a/res/generators/default.files/tree0.vox b/res/generators/default.files/tree0.vox index 3bcff6939b0f4c9ff3aefcdf1375869974caf324..c01219d8ee27132f1431e07cbc1f80617f9d88be 100644 GIT binary patch delta 59 zcmccLz<9rbal;2jMwZPV8UHhbC{_r?388o)l;D46Ru*PPX6DHo1#LGQiRo$q0K3E# A-2eap delta 59 zcmccLz<9rbal;43%}jFtm>C%-3fgbhV>`q=QOIhu7S}OmMuyEFdH;hc!T-$649tv^ M*8`OpiRo$q0DwgksQ>@~ diff --git a/res/generators/default.lua b/res/generators/default.lua index 89b03707..d6058b4b 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -57,10 +57,24 @@ biomes = { function load_structures() local structures = {} - table.insert(structures, generation.load_structure("core:default.files/tree0")) + local names = {"tree0"} + for i, name in ipairs(names) do + local filename = "core:default.files/"..name + debug.log("loading structure "..filename) + table.insert(structures, generation.load_structure(filename)) + end return structures end +function place_structures(x, z, w, d, seed) + local placements = {} + local px = math.random() * w + local py = 200; + local pz = math.random() * d + table.insert(placements, {0, {px, py, pz}}) + return placements +end + local function _generate_heightmap(x, y, w, h, seed, s) local umap = Heightmap(w, h) local vmap = Heightmap(w, h) diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index b291d600..4551a0c9 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -43,6 +43,7 @@ public: lua::touserdata(L, -1)) { structures.push_back(lstruct->getStructure()); } + lua::pop(L); } } } @@ -97,6 +98,40 @@ public: return maps; } + std::vector placeStructures( + const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed + ) override { + std::vector placements; + + auto L = lua::get_main_thread(); + lua::stackguard _(L); + lua::pushenv(L, *env); + if (lua::getfield(L, "place_structures")) { + lua::pushivec_stack(L, offset); + lua::pushivec_stack(L, size); + lua::pushinteger(L, seed); + if (lua::call_nothrow(L, 5, 1)) { + int len = lua::objlen(L, -1); + for (int i = 1; i <= len; i++) { + lua::rawgeti(L, i); + + lua::rawgeti(L, 1); + int structIndex = lua::tointeger(L, -1); + lua::pop(L); + + lua::rawgeti(L, 2); + glm::ivec3 pos = lua::tovec3(L, -1); + + lua::pop(L); + + placements.emplace_back(structIndex, pos); + } + lua::pop(L); + } + } + return placements; + } + void prepare(const Content* content) override { for (auto& biome : biomes) { for (auto& layer : biome.groundLayers.layers) { diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index f219e6d4..d038c986 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -5,6 +5,7 @@ #include "typedefs.hpp" #include "maths/Heightmap.hpp" +#include "StructurePlacement.hpp" class Content; class VoxelStructure; @@ -114,6 +115,10 @@ public: virtual std::vector> generateParameterMaps( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; + virtual std::vector placeStructures( + const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; + + /// @brief Get generator biomes virtual const std::vector& getBiomes() const = 0; /// @return Number of biome parameters, that biome choosing depending on diff --git a/src/world/generator/StructurePlacement.hpp b/src/world/generator/StructurePlacement.hpp new file mode 100644 index 00000000..584ec1dd --- /dev/null +++ b/src/world/generator/StructurePlacement.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +struct StructurePlacement { + int structure; + + glm::ivec3 position; + + StructurePlacement(int structure, glm::ivec3 position) + : structure(structure), position(std::move(position)) {} +}; diff --git a/src/world/generator/VoxelStructure.cpp b/src/world/generator/VoxelStructure.cpp index b7595064..6e27e086 100644 --- a/src/world/generator/VoxelStructure.cpp +++ b/src/world/generator/VoxelStructure.cpp @@ -40,6 +40,7 @@ std::unique_ptr VoxelStructure::create( index = found->second; } voxels[i].id = index; + voxels[i].state = volVoxels[i].state; } return std::make_unique( @@ -67,6 +68,11 @@ void VoxelStructure::deserialize(const dv::value& src) { size = glm::ivec3(); dv::get_vec(src, "size", size); + const auto& namesArr = src["block-names"]; + for (const auto& elem : namesArr) { + blockNames.push_back(elem.asString()); + } + auto volume = size.x*size.y*size.z; voxels.resize(volume); @@ -81,7 +87,7 @@ void VoxelStructure::prepare(const Content& content) { auto volume = size.x*size.y*size.z; voxelsRuntime.resize(volume); for (size_t i = 0; i < volume; i++) { - const auto& name = blockNames[voxels[i].id]; + const auto& name = blockNames.at(voxels[i].id); voxelsRuntime[i].id = content.blocks.require(name).rt.id; voxelsRuntime[i].state = voxels[i].state; } diff --git a/src/world/generator/VoxelStructure.hpp b/src/world/generator/VoxelStructure.hpp index 444a1f1a..938df3cd 100644 --- a/src/world/generator/VoxelStructure.hpp +++ b/src/world/generator/VoxelStructure.hpp @@ -48,6 +48,7 @@ public: } const std::vector& getRuntimeVoxels() { + assert(!voxelsRuntime.empty()); return voxelsRuntime; } }; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 5991a699..2e6462e8 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -15,7 +15,7 @@ static debug::Logger logger("world-generator"); static inline constexpr uint MAX_PARAMETERS = 4; -static inline constexpr uint MAX_CHUNK_PROTOTYPE_LEVELS = 3; +static inline constexpr uint MAX_CHUNK_PROTOTYPE_LEVELS = 5; WorldGenerator::WorldGenerator( const GeneratorDef& def, const Content* content, uint64_t seed @@ -44,14 +44,31 @@ WorldGenerator::WorldGenerator( if (found == prototypes.end()) { throw std::runtime_error("prototype not found"); } - generateHeightmap(found->second.get(), x, z); + generateStructures(requirePrototype(x, z), x, z); + }); + surroundMap.setLevelCallback(3, [this](int const x, int const z) { + generateBiomes(requirePrototype(x, z), x, z); + }); + surroundMap.setLevelCallback(4, [this](int const x, int const z) { + generateHeightmap(requirePrototype(x, z), x, z); }); structures = def.script->loadStructures(); + for (auto& structure : structures) { + structure->prepare(*content); + } } WorldGenerator::~WorldGenerator() {} +ChunkPrototype& WorldGenerator::requirePrototype(int x, int z) { + const auto& found = prototypes.find({x, z}); + if (found == prototypes.end()) { + throw std::runtime_error("prototype not found"); + } + return *found->second; +} + static inline void generate_pole( const BlocksLayers& layers, int top, int bottom, @@ -112,6 +129,27 @@ static inline const Biome* choose_biome( std::unique_ptr WorldGenerator::generatePrototype( int chunkX, int chunkZ ) { + return std::make_unique(); +} + +void WorldGenerator::generateStructures( + ChunkPrototype& prototype, int chunkX, int chunkZ +) { + if (prototype.level >= ChunkPrototypeLevel::STRUCTURES) { + return; + } + prototype.structures = def.script->placeStructures( + {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed + ); + prototype.level = ChunkPrototypeLevel::STRUCTURES; +} + +void WorldGenerator::generateBiomes( + ChunkPrototype& prototype, int chunkX, int chunkZ +) { + if (prototype.level >= ChunkPrototypeLevel::BIOMES) { + return; + } auto biomeParams = def.script->generateParameterMaps( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed); const auto& biomes = def.script->getBiomes(); @@ -123,21 +161,19 @@ std::unique_ptr WorldGenerator::generatePrototype( choose_biome(biomes, biomeParams, x, z); } } - return std::make_unique( - ChunkPrototypeLevel::BIOMES, - std::move(chunkBiomes), - nullptr); + prototype.biomes = std::move(chunkBiomes); + prototype.level = ChunkPrototypeLevel::BIOMES; } void WorldGenerator::generateHeightmap( - ChunkPrototype* prototype, int chunkX, int chunkZ + ChunkPrototype& prototype, int chunkX, int chunkZ ) { - if (prototype->level >= ChunkPrototypeLevel::HEIGHTMAP) { + if (prototype.level >= ChunkPrototypeLevel::HEIGHTMAP) { return; } - prototype->heightmap = def.script->generateHeightmap( + prototype.heightmap = def.script->generateHeightmap( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed); - prototype->level = ChunkPrototypeLevel::HEIGHTMAP; + prototype.level = ChunkPrototypeLevel::HEIGHTMAP; } void WorldGenerator::update(int centerX, int centerY, int loadDistance) { @@ -150,13 +186,8 @@ void WorldGenerator::update(int centerX, int centerY, int loadDistance) { void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { surroundMap.completeAt(chunkX, chunkZ); - const auto& found = prototypes.find({chunkX, chunkZ}); - if (found == prototypes.end()) { - throw std::runtime_error("no prototype found"); - } - - auto prototype = found->second.get(); - const auto values = prototype->heightmap->getValues(); + const auto& prototype = requirePrototype(chunkX, chunkZ); + const auto values = prototype.heightmap->getValues(); uint seaLevel = def.script->getSeaLevel(); @@ -165,7 +196,7 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { PseudoRandom plantsRand; plantsRand.setSeed(chunkX, chunkZ); - const auto& biomes = prototype->biomes.get(); + const auto& biomes = prototype.biomes.get(); for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { const Biome* biome = biomes[z * CHUNK_W + x]; @@ -189,4 +220,34 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { } } } + + for (const auto& placement : prototype.structures) { + if (placement.structure < 0 || placement.structure >= structures.size()) { + logger.error() << "invalid structure index " << placement.structure; + } + auto& structure = *structures[placement.structure]; + auto& structVoxels = structure.getRuntimeVoxels(); + const auto& offset = placement.position; + const auto& size = structure.getSize(); + 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; + if (sz < 0 || sz >= CHUNK_D) { + continue; + } + for (int x = 0; x < size.x; x++) { + int sx = x + offset.x; + if (sx < 0 || sx >= CHUNK_W) { + continue; + } + voxels[vox_index(sx, sy, sz)] = + structVoxels[vox_index(x, y, z, size.x, size.z)]; + } + } + } + } } diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 8d6306c9..3b3fc12e 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -9,6 +9,7 @@ #include "typedefs.hpp" #include "voxels/voxel.hpp" #include "SurroundMap.hpp" +#include "StructurePlacement.hpp" class Content; struct GeneratorDef; @@ -17,20 +18,11 @@ struct Biome; class VoxelStructure; enum class ChunkPrototypeLevel { - BIOMES, HEIGHTMAP -}; - -struct StructurePlacement { - VoxelStructure& structure; - - glm::ivec3 position; - - StructurePlacement(VoxelStructure& structure, glm::ivec3 position) - : structure(structure), position(std::move(position)) {} + VOID=0, STRUCTURES, BIOMES, HEIGHTMAP }; struct ChunkPrototype { - ChunkPrototypeLevel level; + ChunkPrototypeLevel level = ChunkPrototypeLevel::VOID; /// @brief chunk biomes matrix std::unique_ptr biomes; @@ -39,14 +31,6 @@ struct ChunkPrototype { std::shared_ptr heightmap; std::vector structures; - - ChunkPrototype( - ChunkPrototypeLevel level, - std::unique_ptr biomes, - std::shared_ptr heightmap - ) : level(level), - biomes(std::move(biomes)), - heightmap(std::move(heightmap)) {}; }; /// @brief High-level world generation controller @@ -69,7 +53,13 @@ class WorldGenerator { /// @param z chunk position Y divided by CHUNK_D std::unique_ptr generatePrototype(int x, int z); - void generateHeightmap(ChunkPrototype* prototype, int x, int z); + ChunkPrototype& requirePrototype(int x, int z); + + void generateStructures(ChunkPrototype& prototype, int x, int z); + + void generateBiomes(ChunkPrototype& prototype, int x, int z); + + void generateHeightmap(ChunkPrototype& prototype, int x, int z); public: WorldGenerator( const GeneratorDef& def, From a413c5d09ea07771e1296ab582cb49c4052c33fc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 21 Sep 2024 20:50:12 +0300 Subject: [PATCH 070/139] implement inter-chunk structure generation --- res/generators/default.lua | 11 +++-- .../lua/usertypes/lua_type_heightmap.cpp | 10 +++++ .../scripting/scripting_world_generation.cpp | 1 + src/util/listutil.hpp | 9 +++- src/world/generator/WorldGenerator.cpp | 45 +++++++++++++++++-- 5 files changed, 67 insertions(+), 9 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index d6058b4b..254820cd 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -68,10 +68,13 @@ end function place_structures(x, z, w, d, seed) local placements = {} - local px = math.random() * w - local py = 200; - local pz = math.random() * d - table.insert(placements, {0, {px, py, pz}}) + local hmap = generate_heightmap(x, z, w, d, seed) + for i=0,math.floor(math.random()*3)+5 do + local px = math.random() * w + local pz = math.random() * d + local py = hmap:at(px, pz) * 256 + table.insert(placements, {0, {px-8, py, pz-8}}) + end return placements end diff --git a/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp b/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp index fc766c53..3982a5be 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp @@ -70,6 +70,15 @@ static int l_dump(lua::State* L) { return 0; } +static int l_at(lua::State* L) { + if (auto heightmap = touserdata(L, 1)) { + int x = lua::tointeger(L, 2); + int y = lua::tointeger(L, 3); + return lua::pushnumber(L, heightmap->getHeightmap()->get(x, y)); + } + return 0; +} + template static int l_noise(lua::State* L) { if (auto heightmap = touserdata(L, 1)) { @@ -208,6 +217,7 @@ static std::unordered_map methods { {"abs", lua::wrap>}, {"resize", lua::wrap}, {"crop", lua::wrap}, + {"at", lua::wrap}, }; static int l_meta_meta_call(lua::State* L) { diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 4551a0c9..2eb4fd39 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -121,6 +121,7 @@ public: lua::rawgeti(L, 2); glm::ivec3 pos = lua::tovec3(L, -1); + lua::pop(L); lua::pop(L); diff --git a/src/util/listutil.hpp b/src/util/listutil.hpp index dec9499b..b3df6deb 100644 --- a/src/util/listutil.hpp +++ b/src/util/listutil.hpp @@ -1,15 +1,20 @@ #pragma once #include -#include #include #include namespace util { template - bool contains(const std::vector& vec, const T& value) { + inline bool contains(const std::vector& vec, const T& value) { return std::find(vec.begin(), vec.end(), value) != vec.end(); } + template + inline void concat(std::vector& a, const std::vector& b) { + a.reserve(a.size() + b.size()); + a.insert(a.end(), b.begin(), b.end()); + } + std::string to_string(const std::vector& vec); } diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 2e6462e8..d22512b4 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -10,6 +10,7 @@ #include "GeneratorDef.hpp" #include "VoxelStructure.hpp" #include "util/timeutil.hpp" +#include "util/listutil.hpp" #include "debug/Logger.hpp" static debug::Logger logger("world-generator"); @@ -132,15 +133,49 @@ std::unique_ptr WorldGenerator::generatePrototype( return std::make_unique(); } +inline AABB gen_chunk_aabb(int chunkX, int chunkZ) { + return AABB({chunkX * CHUNK_W, 0, chunkZ * CHUNK_D}, + {(chunkX + 1)*CHUNK_W, 256, (chunkZ + 1) * CHUNK_D}); +} + void WorldGenerator::generateStructures( ChunkPrototype& prototype, int chunkX, int chunkZ ) { if (prototype.level >= ChunkPrototypeLevel::STRUCTURES) { return; } - prototype.structures = def.script->placeStructures( + util::concat(prototype.structures, def.script->placeStructures( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed - ); + )); + for (const auto& placement : prototype.structures) { + const auto& offset = placement.position; + if (placement.structure < 0 || placement.structure >= structures.size()) { + logger.error() << "invalid structure index " << placement.structure; + continue; + } + auto& structure = *structures[placement.structure]; + auto position = glm::ivec3(chunkX * CHUNK_W, 0, chunkZ * CHUNK_D)+offset; + auto size = structure.getSize() + glm::ivec3(0, CHUNK_H, 0); + AABB aabb(position, position + size); + for (int lcz = -1; lcz <= 1; lcz++) { + for (int lcx = -1; lcx <= 1; lcx++) { + if (lcx == 0 && lcz == 0) { + continue; + } + auto& otherPrototype = requirePrototype( + chunkX + lcx, chunkZ + lcz + ); + auto chunkAABB = gen_chunk_aabb(chunkX + lcx, chunkZ + lcz); + if (chunkAABB.intersect(aabb)) { + otherPrototype.structures.emplace_back( + placement.structure, + placement.position - + glm::ivec3(lcx * CHUNK_W, 0, lcz * CHUNK_D) + ); + } + } + } + } prototype.level = ChunkPrototypeLevel::STRUCTURES; } @@ -224,6 +259,7 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { for (const auto& placement : prototype.structures) { if (placement.structure < 0 || placement.structure >= structures.size()) { logger.error() << "invalid structure index " << placement.structure; + continue; } auto& structure = *structures[placement.structure]; auto& structVoxels = structure.getRuntimeVoxels(); @@ -244,8 +280,11 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { if (sx < 0 || sx >= CHUNK_W) { continue; } - voxels[vox_index(sx, sy, sz)] = + const auto& structVoxel = structVoxels[vox_index(x, y, z, size.x, size.z)]; + if (structVoxel.id) { + voxels[vox_index(sx, sy, sz)] = structVoxel; + } } } } From f4793ac9dbc0956e922125cfaf798a8f8dd6ca6e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 21 Sep 2024 21:08:25 +0300 Subject: [PATCH 071/139] advanced structures test --- res/generators/default.files/tower.vox | Bin 0 -> 343 bytes res/generators/default.files/tree0.vox | Bin 16479 -> 262 bytes res/generators/default.lua | 19 +++++++++++++++---- 3 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 res/generators/default.files/tower.vox diff --git a/res/generators/default.files/tower.vox b/res/generators/default.files/tower.vox new file mode 100644 index 0000000000000000000000000000000000000000..c992fe45175e0c29a4ced15147f0b5aff93f0957 GIT binary patch literal 343 zcmb2|=3oE==C?N-^9~sZv_34n|?@&A1Ozx7YfRsEaV z_sg;Q&wT!${dXdf<+s(_Q*YO_XoSXgpGcE9yzAw$UmvS4nHjD*UVG=Nzy0QY)wQ>`{@yqB fMlIXQKd0<}Pn9_&u{%-w-bwMoD2=8V919Wm_WiT#wZ+HOh*UhTJFc5~}N#ddhH{OUh;L1CZs#z4P zfd;C0^`{jS1UF`a@#p2CErrfBgnlV8tEEUEPQHy|o#3k(Hz_^_0A-MW;CGRKpd9TE zOIuI=fpnVujkRVtr}oHGKe=2rJv8;AtOYc*-)*Vv*at^S{g{8_+|xbmT@ShSNE?HG z9)J|@kbmvFbFCk#`Oj7V>Ny0oO77ott{eyI08imfUU*@Vt7>ZYsyhMRRA{o)wHwwhk=T3`3W!?8Jgv2He!-gmJH2XRLJ M0gAB5;$J`j0G3IA9smFU literal 16479 zcmeI)K?;IE6vpvq)52)eMY@2iZW3m*kgNd(i%#D>V#tjPXCfs3NTlR@e1`O!LkY5rq-==?&TU(A&;5J2D~5V9AO-%uO!pVA|yHq0WWHuT^ffu#zB9LMBH zYc&6vF;V{khGWN!ujeebZmH*+c_T0sNX^eM!^6H)*He#U>Uivr00Q3;AiLjjqzeKF zAbi&RzYq{`mnrU8J{dSG$|P-Bex`DtI>l diff --git a/res/generators/default.lua b/res/generators/default.lua index 254820cd..cb6be030 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -57,7 +57,7 @@ biomes = { function load_structures() local structures = {} - local names = {"tree0"} + local names = {"tree0", "tower"} for i, name in ipairs(names) do local filename = "core:default.files/"..name debug.log("loading structure "..filename) @@ -69,11 +69,22 @@ end function place_structures(x, z, w, d, seed) local placements = {} local hmap = generate_heightmap(x, z, w, d, seed) - for i=0,math.floor(math.random()*3)+5 do + for i=0,math.floor(math.random()*3) do local px = math.random() * w local pz = math.random() * d local py = hmap:at(px, pz) * 256 + if py <= sea_level then + goto continue + end table.insert(placements, {0, {px-8, py, pz-8}}) + ::continue:: + end + + if math.random() < 0.03 then + local px = math.random() * w + local pz = math.random() * d + local py = hmap:at(px, pz) * 256 + table.insert(placements, {1, {px-8, py, pz-8}}) end return placements end @@ -125,10 +136,10 @@ end local function _generate_biome_parameters(x, y, w, h, seed, s) local tempmap = Heightmap(w, h) tempmap.noiseSeed = seed + 5324 - tempmap:noise({x, y}, 0.4*s, 4) + tempmap:noise({x, y}, 0.04*s, 4) local hummap = Heightmap(w, h) hummap.noiseSeed = seed + 953 - hummap:noise({x, y}, 0.16*s, 4) + hummap:noise({x, y}, 0.016*s, 4) tempmap:pow(2) hummap:pow(2) return tempmap, hummap From 001b9302124f7253723b7d4783cdb3bf83e1df94 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 21 Sep 2024 23:29:19 +0300 Subject: [PATCH 072/139] update world generation pipeline --- res/generators/default.files/tree1.vox | Bin 0 -> 299 bytes res/generators/default.files/tree2.vox | Bin 0 -> 291 bytes res/generators/default.lua | 15 ++++++++------- src/logic/scripting/lua/lua_custom_types.hpp | 1 + .../lua/usertypes/lua_type_heightmap.cpp | 4 ++++ .../scripting/scripting_world_generation.cpp | 8 ++++++-- src/world/generator/GeneratorDef.hpp | 3 ++- src/world/generator/WorldGenerator.cpp | 15 ++++++--------- src/world/generator/WorldGenerator.hpp | 2 +- 9 files changed, 28 insertions(+), 20 deletions(-) create mode 100644 res/generators/default.files/tree1.vox create mode 100644 res/generators/default.files/tree2.vox diff --git a/res/generators/default.files/tree1.vox b/res/generators/default.files/tree1.vox new file mode 100644 index 0000000000000000000000000000000000000000..7dda751dfcc3095bd7f6523019ef26af1fc59f9e GIT binary patch literal 299 zcmV+`0o48tg2TNb}q1WgI_7uHAakqJp+6@@6rKew8 z5SFrqHWQqRe|{(kPE_!lMBUS)$e7+^pT!3Oe&E85`8@!zLH>d5BLBd4k$+&lG=F#e z#a4>`0oc&w*DLGiF+TIpA7*S-jrmtoTQbIFotyTW{xRoY`y9a1^mCtA&A;x8Hh+J= zhwr^Z(?2ZNAEK_ciQG)El z+JEUEOJBcceJS#fwX=Nxt?RYV4*&oF0000000000uo80U)A8lu)V{_nzG+0{n1;A- xyP>(A4i0UO`?r*yo4fg_kL@^}oV$-GKJ3DM*vS}&+@+HQCua}2>!n{%0092&n@9iv literal 0 HcmV?d00001 diff --git a/res/generators/default.files/tree2.vox b/res/generators/default.files/tree2.vox new file mode 100644 index 0000000000000000000000000000000000000000..98fc3bef1ffdf3fc90a02b808229d7e6cb64fe3e GIT binary patch literal 291 zcmV+;0o?u{iwFP!000001MS(tYJ)%!#_`#m9xT1&*q7)FRfD4*-0Sf1q9DA7~f(2iiscfp%H* z&(~$ZSJb*3E;UR3YwH2nS+W!V3vi{cdthJW@Z+Y)*^$cpp z4%~yS`hIO~_*1w3zn*tk#{#s-t9M@Ze~)_yVB?*C0KTmAhjsn$aeV;wwpcq@=l}dW z1pIh?L)_x}0DO^O00000000000001hyC;V+A3t7B{bwo?yG}%oc}n|!n7YUF&Y>@9 p|CRH5_q6Vfsh`gWm*FQ#)SW)-PUbX~A)iEp`~r;2=8Rup003mslwbe= literal 0 HcmV?d00001 diff --git a/res/generators/default.lua b/res/generators/default.lua index cb6be030..41f7f500 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -45,10 +45,12 @@ biomes = { {value=0.2, weight=0.5}, }, sea_layers = { + {block="base:brick", height=1}, {block="base:water", height=-1}, }, layers = { - {block="base:stone", height=6}, + {block="base:grass_block", height=1, below_sea_level=false}, + {block="base:dirt", height=3, below_sea_level=false}, {block="base:stone", height=-1}, {block="base:bazalt", height=1}, } @@ -57,7 +59,7 @@ biomes = { function load_structures() local structures = {} - local names = {"tree0", "tower"} + local names = {"tree0", "tree1", "tree2", "tower"} for i, name in ipairs(names) do local filename = "core:default.files/"..name debug.log("loading structure "..filename) @@ -66,9 +68,8 @@ function load_structures() return structures end -function place_structures(x, z, w, d, seed) +function place_structures(x, z, w, d, seed, hmap) local placements = {} - local hmap = generate_heightmap(x, z, w, d, seed) for i=0,math.floor(math.random()*3) do local px = math.random() * w local pz = math.random() * d @@ -76,15 +77,15 @@ function place_structures(x, z, w, d, seed) if py <= sea_level then goto continue end - table.insert(placements, {0, {px-8, py, pz-8}}) + table.insert(placements, {math.floor(math.random() * 3), {px-8, py, pz-8}}) ::continue:: end - if math.random() < 0.03 then + if math.random() < 0.01 then local px = math.random() * w local pz = math.random() * d local py = hmap:at(px, pz) * 256 - table.insert(placements, {1, {px-8, py, pz-8}}) + table.insert(placements, {3, {px-8, py, pz-8}}) end return placements end diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index 67faeef4..44fcd511 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -39,6 +39,7 @@ namespace lua { std::shared_ptr map; std::unique_ptr noise; public: + LuaHeightmap(const std::shared_ptr& map); LuaHeightmap(uint width, uint height); virtual ~LuaHeightmap(); diff --git a/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp b/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp index 3982a5be..8a58e099 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp @@ -16,6 +16,10 @@ using namespace lua; +LuaHeightmap::LuaHeightmap(const std::shared_ptr& map) + : map(map), noise(std::make_unique(fnlCreateState())) { +} + LuaHeightmap::LuaHeightmap(uint width, uint height) : map(std::make_shared(width, height)), noise(std::make_unique(fnlCreateState())) diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 2eb4fd39..99605da6 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -13,6 +13,8 @@ #include "data/dv.hpp" #include "world/generator/GeneratorDef.hpp" +#include "util/timeutil.hpp" + class LuaGeneratorScript : public GeneratorScript { scriptenv env; std::vector biomes; @@ -99,7 +101,8 @@ public: } std::vector placeStructures( - const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed + const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, + const std::shared_ptr& heightmap ) override { std::vector placements; @@ -110,7 +113,8 @@ public: lua::pushivec_stack(L, offset); lua::pushivec_stack(L, size); lua::pushinteger(L, seed); - if (lua::call_nothrow(L, 5, 1)) { + lua::newuserdata(L, heightmap); + if (lua::call_nothrow(L, 6, 1)) { int len = lua::objlen(L, -1); for (int i = 1; i <= len; i++) { lua::rawgeti(L, i); diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index d038c986..8f3c3aea 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -116,7 +116,8 @@ public: const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; virtual std::vector placeStructures( - const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; + const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, + const std::shared_ptr& heightmap) = 0; /// @brief Get generator biomes virtual const std::vector& getBiomes() const = 0; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index d22512b4..12078ae5 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -41,18 +41,14 @@ WorldGenerator::WorldGenerator( prototypes[{x, z}] = generatePrototype(x, z); }); surroundMap.setLevelCallback(2, [this](int const x, int const z) { - const auto& found = prototypes.find({x, z}); - if (found == prototypes.end()) { - throw std::runtime_error("prototype not found"); - } - generateStructures(requirePrototype(x, z), x, z); - }); - surroundMap.setLevelCallback(3, [this](int const x, int const z) { generateBiomes(requirePrototype(x, z), x, z); }); - surroundMap.setLevelCallback(4, [this](int const x, int const z) { + surroundMap.setLevelCallback(3, [this](int const x, int const z) { generateHeightmap(requirePrototype(x, z), x, z); }); + surroundMap.setLevelCallback(4, [this](int const x, int const z) { + generateStructures(requirePrototype(x, z), x, z); + }); structures = def.script->loadStructures(); for (auto& structure : structures) { @@ -145,7 +141,8 @@ void WorldGenerator::generateStructures( return; } util::concat(prototype.structures, def.script->placeStructures( - {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed + {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, + prototype.heightmap )); for (const auto& placement : prototype.structures) { const auto& offset = placement.position; diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 3b3fc12e..b8685599 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -18,7 +18,7 @@ struct Biome; class VoxelStructure; enum class ChunkPrototypeLevel { - VOID=0, STRUCTURES, BIOMES, HEIGHTMAP + VOID=0, BIOMES, HEIGHTMAP, STRUCTURES }; struct ChunkPrototype { From 74ded5a052358dd59031e873aa78dbf9ce0b999e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 22 Sep 2024 01:58:59 +0300 Subject: [PATCH 073/139] implement structures rotation --- res/generators/default.lua | 5 +-- .../scripting/scripting_world_generation.cpp | 6 +++- src/world/generator/StructurePlacement.hpp | 10 ++++-- src/world/generator/VoxelStructure.cpp | 32 +++++++++++++++++++ src/world/generator/VoxelStructure.hpp | 4 +++ src/world/generator/WorldGenerator.cpp | 21 ++++++++---- src/world/generator/WorldGenerator.hpp | 3 +- 7 files changed, 67 insertions(+), 14 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 41f7f500..cea18b91 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -77,7 +77,8 @@ function place_structures(x, z, w, d, seed, hmap) if py <= sea_level then goto continue end - table.insert(placements, {math.floor(math.random() * 3), {px-8, py, pz-8}}) + table.insert(placements, + {math.floor(math.random() * 3), {px-8, py, pz-8}, math.floor(math.random()*4)}) ::continue:: end @@ -85,7 +86,7 @@ function place_structures(x, z, w, d, seed, hmap) local px = math.random() * w local pz = math.random() * d local py = hmap:at(px, pz) * 256 - table.insert(placements, {3, {px-8, py, pz-8}}) + table.insert(placements, {3, {px-8, py, pz-8}, 0}) end return placements end diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 99605da6..01a8e56e 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -127,9 +127,13 @@ public: glm::ivec3 pos = lua::tovec3(L, -1); lua::pop(L); + lua::rawgeti(L, 3); + int rotation = lua::tointeger(L, -1) & 0b11; lua::pop(L); - placements.emplace_back(structIndex, pos); + lua::pop(L); + + placements.emplace_back(structIndex, pos, rotation); } lua::pop(L); } diff --git a/src/world/generator/StructurePlacement.hpp b/src/world/generator/StructurePlacement.hpp index 584ec1dd..ba688ddb 100644 --- a/src/world/generator/StructurePlacement.hpp +++ b/src/world/generator/StructurePlacement.hpp @@ -1,12 +1,16 @@ #pragma once +#include #include struct StructurePlacement { int structure; - glm::ivec3 position; + uint8_t rotation; - StructurePlacement(int structure, glm::ivec3 position) - : structure(structure), position(std::move(position)) {} + StructurePlacement(int structure, glm::ivec3 position, uint8_t rotation) + : structure(structure), + position(std::move(position)), + rotation(rotation) { + } }; diff --git a/src/world/generator/VoxelStructure.cpp b/src/world/generator/VoxelStructure.cpp index 6e27e086..38732363 100644 --- a/src/world/generator/VoxelStructure.cpp +++ b/src/world/generator/VoxelStructure.cpp @@ -92,3 +92,35 @@ void VoxelStructure::prepare(const Content& content) { voxelsRuntime[i].state = voxels[i].state; } } + +std::unique_ptr VoxelStructure::rotated(const Content& content) const { + std::vector newVoxels(voxels.size()); + + for (int y = 0; y < size.y; y++) { + for (int z = 0; z < size.z; z++) { + for (int x = 0; x < size.x; x++) { + auto& voxel = newVoxels[vox_index(x, y, z, size.x, size.z)]; + voxel = voxels[vox_index(size.z-z-1, y, x, size.z, size.x)]; + // swap X and Z segment bits + voxel.state.segment = ((voxel.state.segment & 0b001) << 2) + | (voxel.state.segment & 0b010) + | ((voxel.state.segment & 0b100) >> 2); + auto& def = content.blocks.require(blockNames[voxel.id]); + if (def.rotations.name == BlockRotProfile::PANE_NAME || + def.rotations.name == BlockRotProfile::PIPE_NAME){ + if (voxel.state.rotation < 4) { + voxel.state.rotation = (voxel.state.rotation + 3) & 0b11; + } + } + } + } + } + auto newStructure = std::make_unique( + // swap X and Z on 90 deg. rotation + glm::ivec3(size.z, size.y, size.x), + std::move(newVoxels), + blockNames + ); + newStructure->prepare(content); + return newStructure; +} diff --git a/src/world/generator/VoxelStructure.hpp b/src/world/generator/VoxelStructure.hpp index 938df3cd..ef70c229 100644 --- a/src/world/generator/VoxelStructure.hpp +++ b/src/world/generator/VoxelStructure.hpp @@ -40,6 +40,9 @@ public: /// @param content world content void prepare(const Content& content); + /// @brief Create structure copy rotated 90 deg. clockwise + std::unique_ptr rotated(const Content& content) const; + static std::unique_ptr create( Level* level, const glm::ivec3& a, const glm::ivec3& b, bool entities); @@ -47,6 +50,7 @@ public: return size; } + /// @return Voxels with indices valid to current world content const std::vector& getRuntimeVoxels() { assert(!voxelsRuntime.empty()); return voxelsRuntime; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 12078ae5..933f2901 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -1,7 +1,6 @@ #include "WorldGenerator.hpp" #include -#include #include "maths/util.hpp" #include "content/Content.hpp" @@ -50,9 +49,16 @@ WorldGenerator::WorldGenerator( generateStructures(requirePrototype(x, z), x, z); }); - structures = def.script->loadStructures(); - for (auto& structure : structures) { - structure->prepare(*content); + auto rawStructures = def.script->loadStructures(); + structures.resize(rawStructures.size()); + + for (int i = 0; i < rawStructures.size(); i++) { + structures[i][0] = std::move(rawStructures[i]); + structures[i][0]->prepare(*content); + // pre-calculate rotated structure variants + for (int j = 1; j < 4; j++) { + structures[i][j] = structures[i][j-1]->rotated(*content); + } } } @@ -150,7 +156,7 @@ void WorldGenerator::generateStructures( logger.error() << "invalid structure index " << placement.structure; continue; } - auto& structure = *structures[placement.structure]; + auto& structure = *structures[placement.structure][placement.rotation]; auto position = glm::ivec3(chunkX * CHUNK_W, 0, chunkZ * CHUNK_D)+offset; auto size = structure.getSize() + glm::ivec3(0, CHUNK_H, 0); AABB aabb(position, position + size); @@ -167,7 +173,8 @@ void WorldGenerator::generateStructures( otherPrototype.structures.emplace_back( placement.structure, placement.position - - glm::ivec3(lcx * CHUNK_W, 0, lcz * CHUNK_D) + glm::ivec3(lcx * CHUNK_W, 0, lcz * CHUNK_D), + placement.rotation ); } } @@ -258,7 +265,7 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { logger.error() << "invalid structure index " << placement.structure; continue; } - auto& structure = *structures[placement.structure]; + auto& structure = *structures[placement.structure][placement.rotation]; auto& structVoxels = structure.getRuntimeVoxels(); const auto& offset = placement.position; const auto& size = structure.getSize(); diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index b8685599..8783bc15 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -46,7 +47,7 @@ class WorldGenerator { /// @brief Chunk prototypes loading surround map SurroundMap surroundMap; - std::vector> structures; + std::vector, 4>> structures; /// @brief Generate chunk prototype (see ChunkPrototype) /// @param x chunk position X divided by CHUNK_W From 9dd4e9640dd47e787a5c0eaa137e6efb4ee4e9b7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 22 Sep 2024 02:12:35 +0300 Subject: [PATCH 074/139] fix structures rotation --- src/world/generator/VoxelStructure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/world/generator/VoxelStructure.cpp b/src/world/generator/VoxelStructure.cpp index 38732363..5c36176b 100644 --- a/src/world/generator/VoxelStructure.cpp +++ b/src/world/generator/VoxelStructure.cpp @@ -109,7 +109,7 @@ std::unique_ptr VoxelStructure::rotated(const Content& content) if (def.rotations.name == BlockRotProfile::PANE_NAME || def.rotations.name == BlockRotProfile::PIPE_NAME){ if (voxel.state.rotation < 4) { - voxel.state.rotation = (voxel.state.rotation + 3) & 0b11; + voxel.state.rotation = (voxel.state.rotation + 1) & 0b11; } } } From 519ebc05f183797a2e95280e34bb904096fcb4d2 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 23 Sep 2024 23:35:39 +0300 Subject: [PATCH 075/139] optimize test generator --- res/generators/default.lua | 15 ++++++--------- src/logic/scripting/lua/lua_custom_types.hpp | 2 +- .../scripting/scripting_world_generation.cpp | 1 - 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index cea18b91..d12fd70c 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -39,21 +39,20 @@ biomes = { {block="base:bazalt", height=1}, } }, - mountains = { + forest = { parameters = { {value=1.0, weight=1.0}, {value=0.2, weight=0.5}, }, sea_layers = { - {block="base:brick", height=1}, {block="base:water", height=-1}, }, layers = { {block="base:grass_block", height=1, below_sea_level=false}, - {block="base:dirt", height=3, below_sea_level=false}, + {block="base:dirt", height=7, below_sea_level=false}, {block="base:stone", height=-1}, {block="base:bazalt", height=1}, - } + }, } } @@ -70,7 +69,7 @@ end function place_structures(x, z, w, d, seed, hmap) local placements = {} - for i=0,math.floor(math.random()*3) do + for i=0,math.floor(math.random()*3)+5 do local px = math.random() * w local pz = math.random() * d local py = hmap:at(px, pz) * 256 @@ -102,8 +101,7 @@ local function _generate_heightmap(x, y, w, h, seed, s) local map = Heightmap(w, h) map.noiseSeed = seed map:noise({x, y}, 0.8*s, 4, 0.04) - map:cellnoise({x, y}, 0.1*s, 3, 0.7, umap, vmap) - map:mul(0.5) + map:cellnoise({x, y}, 0.1*s, 3, 0.5, umap, vmap) map:add(0.5) local rivermap = Heightmap(w, h) @@ -111,11 +109,10 @@ local function _generate_heightmap(x, y, w, h, seed, s) rivermap:noise({x+21, y+12}, 0.1*s, 4) rivermap:abs() rivermap:mul(2.0) - rivermap:pow(0.3) + rivermap:pow(0.2) rivermap:max(0.6) map:add(0.4) map:mul(rivermap) - map:add(-0.15) return map end diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index 44fcd511..79ea4492 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -56,7 +56,7 @@ namespace lua { return TYPENAME; } - std::shared_ptr getHeightmap() const { + const std::shared_ptr& getHeightmap() const { return map; } diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 01a8e56e..12b4d1d5 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -87,7 +87,6 @@ public: for (int i = biomeParameters-1; i >= 0; i--) { maps.push_back( lua::touserdata(L, -1-i)->getHeightmap()); - } lua::pop(L, 1+biomeParameters); return maps; From 323c2f29350538773eda5da89cb37af67b47a7d4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 24 Sep 2024 04:05:13 +0300 Subject: [PATCH 076/139] refactor & add structures.json --- res/generators/default.lua | 7 +- res/generators/default/structures.json | 6 ++ .../{default.files => default}/tower.vox | Bin .../{default.files => default}/tree0.vox | Bin .../{default.files => default}/tree1.vox | Bin .../{default.files => default}/tree2.vox | Bin src/content/Content.cpp | 1 + src/content/ContentBuilder.hpp | 1 + src/content/ContentLoader.cpp | 15 +--- src/content/loading/GeneratorLoader.cpp | 74 ++++++++++++++++++ src/logic/scripting/lua/libgeneration.cpp | 6 +- src/logic/scripting/lua/lua_custom_types.hpp | 8 +- .../lua/usertypes/lua_type_voxelstructure.cpp | 4 +- .../scripting/scripting_world_generation.cpp | 4 +- src/world/generator/GeneratorDef.cpp | 11 +++ src/world/generator/GeneratorDef.hpp | 25 +++++- .../{VoxelStructure.cpp => VoxelFragment.cpp} | 16 ++-- .../{VoxelStructure.hpp => VoxelFragment.hpp} | 10 +-- src/world/generator/WorldGenerator.cpp | 4 +- src/world/generator/WorldGenerator.hpp | 4 +- 20 files changed, 153 insertions(+), 43 deletions(-) create mode 100644 res/generators/default/structures.json rename res/generators/{default.files => default}/tower.vox (100%) rename res/generators/{default.files => default}/tree0.vox (100%) rename res/generators/{default.files => default}/tree1.vox (100%) rename res/generators/{default.files => default}/tree2.vox (100%) create mode 100644 src/content/loading/GeneratorLoader.cpp create mode 100644 src/world/generator/GeneratorDef.cpp rename src/world/generator/{VoxelStructure.cpp => VoxelFragment.cpp} (89%) rename src/world/generator/{VoxelStructure.hpp => VoxelFragment.hpp} (85%) diff --git a/res/generators/default.lua b/res/generators/default.lua index d12fd70c..41790163 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -53,6 +53,11 @@ biomes = { {block="base:stone", height=-1}, {block="base:bazalt", height=1}, }, + structures = { + "tree0", + "tree1", + "tree2" + } } } @@ -60,7 +65,7 @@ function load_structures() local structures = {} local names = {"tree0", "tree1", "tree2", "tower"} for i, name in ipairs(names) do - local filename = "core:default.files/"..name + local filename = "core:default/"..name debug.log("loading structure "..filename) table.insert(structures, generation.load_structure(filename)) end diff --git a/res/generators/default/structures.json b/res/generators/default/structures.json new file mode 100644 index 00000000..109ca95f --- /dev/null +++ b/res/generators/default/structures.json @@ -0,0 +1,6 @@ +{ + "tree0": {}, + "tree1": {}, + "tree2": {}, + "tower": {} +} diff --git a/res/generators/default.files/tower.vox b/res/generators/default/tower.vox similarity index 100% rename from res/generators/default.files/tower.vox rename to res/generators/default/tower.vox diff --git a/res/generators/default.files/tree0.vox b/res/generators/default/tree0.vox similarity index 100% rename from res/generators/default.files/tree0.vox rename to res/generators/default/tree0.vox diff --git a/res/generators/default.files/tree1.vox b/res/generators/default/tree1.vox similarity index 100% rename from res/generators/default.files/tree1.vox rename to res/generators/default/tree1.vox diff --git a/res/generators/default.files/tree2.vox b/res/generators/default/tree2.vox similarity index 100% rename from res/generators/default.files/tree2.vox rename to res/generators/default/tree2.vox diff --git a/src/content/Content.cpp b/src/content/Content.cpp index 98affbbf..68e9cec3 100644 --- a/src/content/Content.cpp +++ b/src/content/Content.cpp @@ -10,6 +10,7 @@ #include "objects/EntityDef.hpp" #include "objects/rigging.hpp" #include "voxels/Block.hpp" +#include "world/generator/VoxelFragment.hpp" #include "world/generator/GeneratorDef.hpp" #include "ContentPack.hpp" diff --git a/src/content/ContentBuilder.hpp b/src/content/ContentBuilder.hpp index a0daa9ac..44099304 100644 --- a/src/content/ContentBuilder.hpp +++ b/src/content/ContentBuilder.hpp @@ -8,6 +8,7 @@ #include "ContentPack.hpp" #include "items/ItemDef.hpp" #include "objects/EntityDef.hpp" +#include "world/generator/VoxelFragment.hpp" #include "world/generator/GeneratorDef.hpp" #include "voxels/Block.hpp" diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index ca4be87d..42e83fbf 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -433,18 +433,6 @@ void ContentLoader::loadEntity( if (fs::exists(configFile)) loadEntity(def, full, configFile); } - -void ContentLoader::loadGenerator( - GeneratorDef& def, const std::string& full, const std::string& name -) { - auto folder = pack->folder; - auto generatorFile = folder / fs::path("generators/" + name + ".lua"); - if (!fs::exists(generatorFile)) { - return; - } - def.script = scripting::load_generator(generatorFile); -} - void ContentLoader::loadBlock( Block& def, const std::string& full, const std::string& name ) { @@ -523,6 +511,9 @@ void ContentLoader::load() { if (fs::is_directory(generatorsDir)) { for (const auto& entry : fs::directory_iterator(generatorsDir)) { const auto& file = entry.path(); + if (fs::is_directory(file)) { + continue; + } std::string name = file.stem().u8string(); auto [packid, full, filename] = diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp new file mode 100644 index 00000000..a0f8ba49 --- /dev/null +++ b/src/content/loading/GeneratorLoader.cpp @@ -0,0 +1,74 @@ +#include "../ContentLoader.hpp" + +#include "../ContentPack.hpp" + +#include "files/files.hpp" +#include "logic/scripting/scripting.hpp" +#include "world/generator/GeneratorDef.hpp" +#include "world/generator/VoxelFragment.hpp" +#include "debug/Logger.hpp" + +static debug::Logger logger("generator-loader"); + +static VoxelStructureMeta load_structure_meta( + const std::string& name, const dv::value& config +) { + VoxelStructureMeta meta; + meta.name = name; + + return meta; +} + +static std::vector> load_structures( + const fs::path& structuresFile +) { + auto structuresDir = structuresFile.parent_path(); + auto map = files::read_json(structuresFile); + + std::vector> structures; + for (auto& [name, config] : map.asObject()) { + auto structFile = structuresDir / fs::u8path(name + ".vox"); + logger.debug() << "loading voxel fragment " << structFile.u8string(); + if (!fs::exists(structFile)) { + throw std::runtime_error("structure file does not exist (" + + structFile.u8string()); + } + auto fragment = std::make_unique(); + fragment->deserialize(files::read_binary_json(structFile)); + + structures.push_back(std::make_unique( + load_structure_meta(name, config), + std::move(fragment) + )); + } + return structures; +} + +static void load_structures(GeneratorDef& def, const fs::path& structuresFile) { + def.structures = load_structures(structuresFile); + // build indices map + for (size_t i = 0; i < def.structures.size(); i++) { + auto& structure = def.structures[i]; + def.structuresIndices[structure->meta.name] = i; + } +} + +static inline const auto STRUCTURES_FILE = fs::u8path("structures.json"); +static inline const auto GENERATORS_DIR = fs::u8path("generators"); + +void ContentLoader::loadGenerator( + GeneratorDef& def, const std::string& full, const std::string& name +) { + auto packDir = pack->folder; + auto generatorsDir = packDir / GENERATORS_DIR; + auto folder = generatorsDir / fs::u8path(name); + auto generatorFile = generatorsDir / fs::u8path(name + ".lua"); + if (!fs::exists(generatorFile)) { + return; + } + auto structuresFile = folder / STRUCTURES_FILE; + if (fs::exists(structuresFile)) { + load_structures(def, structuresFile); + } + def.script = scripting::load_generator(generatorFile); +} diff --git a/src/logic/scripting/lua/libgeneration.cpp b/src/logic/scripting/lua/libgeneration.cpp index b5e90515..2f5e4851 100644 --- a/src/logic/scripting/lua/libgeneration.cpp +++ b/src/logic/scripting/lua/libgeneration.cpp @@ -4,7 +4,7 @@ #include "files/util.hpp" #include "coders/binary_json.hpp" #include "world/Level.hpp" -#include "world/generator/VoxelStructure.hpp" +#include "world/generator/VoxelFragment.hpp" #include "engine.hpp" #include "lua_custom_types.hpp" @@ -19,7 +19,7 @@ static int l_save_structure(lua::State* L) { } bool saveEntities = lua::toboolean(L, 4); - auto structure = VoxelStructure::create(level, pointA, pointB, saveEntities); + auto structure = VoxelFragment::create(level, pointA, pointB, saveEntities); auto map = structure->serialize(); auto bytes = json::to_binary(map); @@ -37,7 +37,7 @@ static int l_load_structure(lua::State* L) { } auto map = files::read_binary_json(path); - auto structure = std::make_shared(); + auto structure = std::make_shared(); structure->deserialize(map); return lua::newuserdata(L, std::move(structure)); } diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index 79ea4492..dcd78800 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -7,7 +7,7 @@ struct fnl_state; class Heightmap; -class VoxelStructure; +class VoxelFragment; namespace lua { class Userdata { @@ -72,13 +72,13 @@ namespace lua { static_assert(!std::is_abstract()); class LuaVoxelStructure : public Userdata { - std::shared_ptr structure; + std::shared_ptr structure; public: - LuaVoxelStructure(std::shared_ptr structure); + LuaVoxelStructure(std::shared_ptr structure); virtual ~LuaVoxelStructure(); - std::shared_ptr getStructure() const { + std::shared_ptr getStructure() const { return structure; } diff --git a/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp b/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp index b35ed517..08a3ef84 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp @@ -2,12 +2,12 @@ #include "../lua_util.hpp" -#include "world/generator/VoxelStructure.hpp" +#include "world/generator/VoxelFragment.hpp" #include "util/stringutil.hpp" using namespace lua; -LuaVoxelStructure::LuaVoxelStructure(std::shared_ptr structure) +LuaVoxelStructure::LuaVoxelStructure(std::shared_ptr structure) : structure(std::move(structure)) {} LuaVoxelStructure::~LuaVoxelStructure() { diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 12b4d1d5..59712dff 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -32,8 +32,8 @@ public: seaLevel(seaLevel) {} - std::vector> loadStructures() override { - std::vector> structures; + std::vector> loadStructures() override { + std::vector> structures; auto L = lua::get_main_thread(); lua::pushenv(L, *env); diff --git a/src/world/generator/GeneratorDef.cpp b/src/world/generator/GeneratorDef.cpp new file mode 100644 index 00000000..32abee49 --- /dev/null +++ b/src/world/generator/GeneratorDef.cpp @@ -0,0 +1,11 @@ +#include "GeneratorDef.hpp" + +#include "VoxelFragment.hpp" + +GeneratingVoxelStructure::GeneratingVoxelStructure( + VoxelStructureMeta meta, + std::unique_ptr structure +) : structure(std::move(structure)), meta(std::move(meta)) {} + + +GeneratorDef::GeneratorDef(std::string name) : name(std::move(name)) {} diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 8f3c3aea..33432b09 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -1,14 +1,20 @@ #pragma once #include +#include #include +#include #include "typedefs.hpp" #include "maths/Heightmap.hpp" #include "StructurePlacement.hpp" class Content; -class VoxelStructure; +class VoxelFragment; + +struct VoxelStructureMeta { + std::string name; +}; struct BlocksLayer { /// @brief Layer block @@ -102,7 +108,7 @@ public: virtual ~GeneratorScript() = default; /// @brief Load all structures - virtual std::vector> loadStructures() = 0; + virtual std::vector> loadStructures() = 0; /// @brief Generates a heightmap with values in range 0..1 /// @param offset position of the heightmap in the world @@ -133,12 +139,25 @@ public: virtual void prepare(const Content* content) = 0; }; +struct GeneratingVoxelStructure { + VoxelStructureMeta meta; + std::unique_ptr structure; + + GeneratingVoxelStructure( + VoxelStructureMeta meta, + std::unique_ptr structure + ); +}; + /// @brief Generator information struct GeneratorDef { /// @brief Generator full name - packid:name std::string name; std::unique_ptr script; - GeneratorDef(std::string name) : name(std::move(name)) {} + std::unordered_map structuresIndices; + std::vector> structures; + + GeneratorDef(std::string name); GeneratorDef(const GeneratorDef&) = delete; }; diff --git a/src/world/generator/VoxelStructure.cpp b/src/world/generator/VoxelFragment.cpp similarity index 89% rename from src/world/generator/VoxelStructure.cpp rename to src/world/generator/VoxelFragment.cpp index 5c36176b..2dd49fb7 100644 --- a/src/world/generator/VoxelStructure.cpp +++ b/src/world/generator/VoxelFragment.cpp @@ -1,4 +1,4 @@ -#include "VoxelStructure.hpp" +#include "VoxelFragment.hpp" #include #include @@ -10,7 +10,7 @@ #include "voxels/VoxelsVolume.hpp" #include "world/Level.hpp" -std::unique_ptr VoxelStructure::create( +std::unique_ptr VoxelFragment::create( Level* level, const glm::ivec3& a, const glm::ivec3& b, bool entities ) { auto start = glm::min(a, b); @@ -43,11 +43,11 @@ std::unique_ptr VoxelStructure::create( voxels[i].state = volVoxels[i].state; } - return std::make_unique( + return std::make_unique( size, std::move(voxels), std::move(blockNames)); } -dv::value VoxelStructure::serialize() const { +dv::value VoxelFragment::serialize() const { auto root = dv::object(); root["version"] = STRUCTURE_FORMAT_VERSION; root["size"] = dv::to_value(size); @@ -64,7 +64,7 @@ dv::value VoxelStructure::serialize() const { return root; } -void VoxelStructure::deserialize(const dv::value& src) { +void VoxelFragment::deserialize(const dv::value& src) { size = glm::ivec3(); dv::get_vec(src, "size", size); @@ -83,7 +83,7 @@ void VoxelStructure::deserialize(const dv::value& src) { } } -void VoxelStructure::prepare(const Content& content) { +void VoxelFragment::prepare(const Content& content) { auto volume = size.x*size.y*size.z; voxelsRuntime.resize(volume); for (size_t i = 0; i < volume; i++) { @@ -93,7 +93,7 @@ void VoxelStructure::prepare(const Content& content) { } } -std::unique_ptr VoxelStructure::rotated(const Content& content) const { +std::unique_ptr VoxelFragment::rotated(const Content& content) const { std::vector newVoxels(voxels.size()); for (int y = 0; y < size.y; y++) { @@ -115,7 +115,7 @@ std::unique_ptr VoxelStructure::rotated(const Content& content) } } } - auto newStructure = std::make_unique( + auto newStructure = std::make_unique( // swap X and Z on 90 deg. rotation glm::ivec3(size.z, size.y, size.x), std::move(newVoxels), diff --git a/src/world/generator/VoxelStructure.hpp b/src/world/generator/VoxelFragment.hpp similarity index 85% rename from src/world/generator/VoxelStructure.hpp rename to src/world/generator/VoxelFragment.hpp index ef70c229..0a75359e 100644 --- a/src/world/generator/VoxelStructure.hpp +++ b/src/world/generator/VoxelFragment.hpp @@ -11,7 +11,7 @@ inline constexpr int STRUCTURE_FORMAT_VERSION = 1; class Level; class Content; -class VoxelStructure : public Serializable { +class VoxelFragment : public Serializable { glm::ivec3 size; /// @brief Structure voxels indexed different to world content @@ -22,9 +22,9 @@ class VoxelStructure : public Serializable { /// @brief Structure voxels built on prepare(...) call std::vector voxelsRuntime; public: - VoxelStructure() : size() {} + VoxelFragment() : size() {} - VoxelStructure( + VoxelFragment( glm::ivec3 size, std::vector voxels, std::vector blockNames @@ -41,9 +41,9 @@ public: void prepare(const Content& content); /// @brief Create structure copy rotated 90 deg. clockwise - std::unique_ptr rotated(const Content& content) const; + std::unique_ptr rotated(const Content& content) const; - static std::unique_ptr create( + static std::unique_ptr create( Level* level, const glm::ivec3& a, const glm::ivec3& b, bool entities); const glm::ivec3& getSize() const { diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 933f2901..90778870 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -7,7 +7,7 @@ #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" #include "GeneratorDef.hpp" -#include "VoxelStructure.hpp" +#include "VoxelFragment.hpp" #include "util/timeutil.hpp" #include "util/listutil.hpp" #include "debug/Logger.hpp" @@ -259,6 +259,8 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { } } } + + // TODO: put automatic placement here for (const auto& placement : prototype.structures) { if (placement.structure < 0 || placement.structure >= structures.size()) { diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 8783bc15..29de778b 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -16,7 +16,7 @@ class Content; struct GeneratorDef; class Heightmap; struct Biome; -class VoxelStructure; +class VoxelFragment; enum class ChunkPrototypeLevel { VOID=0, BIOMES, HEIGHTMAP, STRUCTURES @@ -47,7 +47,7 @@ class WorldGenerator { /// @brief Chunk prototypes loading surround map SurroundMap surroundMap; - std::vector, 4>> structures; + std::vector, 4>> structures; /// @brief Generate chunk prototype (see ChunkPrototype) /// @param x chunk position X divided by CHUNK_W From 806ed4b155115feb62e535a43e2c687a9854f5b6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 24 Sep 2024 17:08:25 +0300 Subject: [PATCH 077/139] add automatic biome-based structures placement --- res/generators/default.lua | 25 +--- src/content/ContentBuilder.cpp | 2 +- src/content/loading/GeneratorLoader.cpp | 7 +- .../scripting/scripting_world_generation.cpp | 67 +++++++---- src/world/generator/GeneratorDef.cpp | 2 +- src/world/generator/GeneratorDef.hpp | 62 ++++++---- src/world/generator/WorldGenerator.cpp | 108 ++++++++++++------ src/world/generator/WorldGenerator.hpp | 7 +- 8 files changed, 174 insertions(+), 106 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 41790163..921bfa06 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -53,10 +53,11 @@ biomes = { {block="base:stone", height=-1}, {block="base:bazalt", height=1}, }, + structure_chance = 0.032, structures = { - "tree0", - "tree1", - "tree2" + {name="tree0", weight=1}, + {name="tree1", weight=1}, + {name="tree2", weight=1}, } } } @@ -74,24 +75,6 @@ end function place_structures(x, z, w, d, seed, hmap) local placements = {} - for i=0,math.floor(math.random()*3)+5 do - local px = math.random() * w - local pz = math.random() * d - local py = hmap:at(px, pz) * 256 - if py <= sea_level then - goto continue - end - table.insert(placements, - {math.floor(math.random() * 3), {px-8, py, pz-8}, math.floor(math.random()*4)}) - ::continue:: - end - - if math.random() < 0.01 then - local px = math.random() * w - local pz = math.random() * d - local py = hmap:at(px, pz) * 256 - table.insert(placements, {3, {px-8, py, pz-8}, 0}) - end return placements end diff --git a/src/content/ContentBuilder.cpp b/src/content/ContentBuilder.cpp index 46d4f909..2ce7a963 100644 --- a/src/content/ContentBuilder.cpp +++ b/src/content/ContentBuilder.cpp @@ -89,7 +89,7 @@ std::unique_ptr ContentBuilder::build() { } for (auto& [name, def] : content->generators.getDefs()) { - def->script->prepare(content.get()); + def->script->prepare(*def, content.get()); } return content; diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index a0f8ba49..256a1b28 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -45,7 +45,12 @@ static std::vector> load_structures( } static void load_structures(GeneratorDef& def, const fs::path& structuresFile) { - def.structures = load_structures(structuresFile); + auto rawStructures = load_structures(structuresFile); + def.structures.resize(rawStructures.size()); + + for (int i = 0; i < rawStructures.size(); i++) { + def.structures[i] = std::move(rawStructures[i]); + } // build indices map for (size_t i = 0; i < def.structures.size(); i++) { auto& structure = def.structures[i]; diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 59712dff..16ffaee2 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -12,7 +12,7 @@ #include "voxels/Chunk.hpp" #include "data/dv.hpp" #include "world/generator/GeneratorDef.hpp" - +#include "util/stringutil.hpp" #include "util/timeutil.hpp" class LuaGeneratorScript : public GeneratorScript { @@ -140,7 +140,7 @@ public: return placements; } - void prepare(const Content* content) override { + void prepare(const GeneratorDef& def, const Content* content) override { for (auto& biome : biomes) { for (auto& layer : biome.groundLayers.layers) { layer.rt.id = content->blocks.require(layer.block).rt.id; @@ -148,8 +148,16 @@ public: for (auto& layer : biome.seaLayers.layers) { layer.rt.id = content->blocks.require(layer.block).rt.id; } - for (auto& plant : biome.plants.plants) { - plant.rt.id = content->blocks.require(plant.block).rt.id; + for (auto& plant : biome.plants.entries) { + plant.rt.id = content->blocks.require(plant.name).rt.id; + } + for (auto& structure : biome.structures.entries) { + const auto& found = def.structuresIndices.find(structure.name); + if (found == def.structuresIndices.end()) { + throw std::runtime_error( + "no structure "+util::quote(structure.name)+" found"); + } + structure.rt.id = found->second; } } } @@ -207,29 +215,36 @@ static inline BlocksLayers load_layers( return BlocksLayers {std::move(layers), lastLayersHeight}; } -static inline BiomePlants load_plants( - const dv::value& biomeMap +static inline BiomeElementList load_biome_element_list( + const dv::value map, + const std::string& chanceName, + const std::string& arrName, + const std::string& nameName ) { - float plantChance = 0.0f; - biomeMap.at("plant_chance").get(plantChance); - float plantsWeightSum = 0.0f; - - std::vector plants; - if (biomeMap.has("plants")) { - const auto& plantsArr = biomeMap["plants"]; - for (const auto& entry : plantsArr) { - const auto& block = entry["block"].asString(); + float chance = 0.0f; + map.at(chanceName).get(chance); + std::vector entries; + if (map.has(arrName)) { + const auto& arr = map[arrName]; + for (const auto& entry : arr) { + const auto& name = entry[nameName].asString(); float weight = entry["weight"].asNumber(); if (weight <= 0.0f) { throw std::runtime_error("weight must be positive"); } - plantsWeightSum += weight; - plants.push_back(PlantEntry {block, weight, {}}); + entries.push_back(WeightedEntry {name, weight, {}}); } } - std::sort(plants.begin(), plants.end(), std::greater()); - return BiomePlants { - std::move(plants), plantsWeightSum, plantChance}; + std::sort(entries.begin(), entries.end(), std::greater()); + return BiomeElementList(std::move(entries), chance); +} + +static inline BiomeElementList load_plants(const dv::value& biomeMap) { + return load_biome_element_list(biomeMap, "plant_chance", "plants", "block"); +} + +static inline BiomeElementList load_structures(const dv::value map) { + return load_biome_element_list(map, "structure_chance", "structures", "name"); } static inline Biome load_biome( @@ -252,13 +267,19 @@ static inline Biome load_biome( parameters.push_back(BiomeParameter {value, weight}); } - BiomePlants plants = load_plants(biomeMap); - BlocksLayers groundLayers = load_layers(biomeMap["layers"], "layers"); - BlocksLayers seaLayers = load_layers(biomeMap["sea_layers"], "sea_layers"); + auto plants = load_plants(biomeMap); + auto groundLayers = load_layers(biomeMap["layers"], "layers"); + auto seaLayers = load_layers(biomeMap["sea_layers"], "sea_layers"); + + BiomeElementList structures; + if (biomeMap.has("structures")) { + structures = load_structures(biomeMap); + } return Biome { name, std::move(parameters), std::move(plants), + std::move(structures), std::move(groundLayers), std::move(seaLayers)}; } diff --git a/src/world/generator/GeneratorDef.cpp b/src/world/generator/GeneratorDef.cpp index 32abee49..123f5f98 100644 --- a/src/world/generator/GeneratorDef.cpp +++ b/src/world/generator/GeneratorDef.cpp @@ -5,7 +5,7 @@ GeneratingVoxelStructure::GeneratingVoxelStructure( VoxelStructureMeta meta, std::unique_ptr structure -) : structure(std::move(structure)), meta(std::move(meta)) {} +) : fragments({std::move(structure)}), meta(std::move(meta)) {} GeneratorDef::GeneratorDef(std::string name) : name(std::move(name)) {} diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 33432b09..acf8f61d 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -11,6 +12,7 @@ class Content; class VoxelFragment; +struct GeneratorDef; struct VoxelStructureMeta { std::string name; @@ -49,56 +51,70 @@ struct BiomeParameter { float weight; }; -/// @brief Plant is a single-block structure randomly generating in world -struct PlantEntry { - /// @brief Plant block id - std::string block; - /// @brief Plant weight +struct WeightedEntry { + std::string name; float weight; struct { - blockid_t id; + size_t id; } rt; - bool operator>(const PlantEntry& other) const { + bool operator>(const WeightedEntry& other) const { return weight > other.weight; } }; -struct BiomePlants { +struct BiomeElementList { static inline float MIN_CHANCE = 0.000001f; - /// @brief Plant entries sorted by weight descending. - std::vector plants; + /// @brief Entries sorted by weight descending. + std::vector entries; /// @brief Sum of weight values float weightsSum; - /// @brief Plant generation chance + /// @brief Value generation chance float chance; - /// @brief Choose plant based on weight + BiomeElementList() {} + + BiomeElementList(std::vector entries, float chance) + : entries(entries), chance(chance) { + for (const auto& entry : entries) { + weightsSum += entry.weight; + } + } + + /// @brief Choose value based on weight /// @param rand some random value in range [0, 1) - /// @return index of chosen plant block - inline blockid_t choose(float rand) const { - if (plants.empty() || rand > chance || chance < MIN_CHANCE) { - return 0; + /// @return *.index of chosen value + inline size_t choose(float rand, size_t def=0) const { + if (entries.empty() || rand > chance || chance < MIN_CHANCE) { + return def; } rand = rand / chance; rand *= weightsSum; - for (const auto& plant : plants) { - rand -= plant.weight; + for (const auto& entry : entries) { + rand -= entry.weight; if (rand <= 0.0f) { - return plant.rt.id; + return entry.rt.id; } } - return plants[plants.size()-1].rt.id; + return entries[entries.size()-1].rt.id; } }; struct Biome { + /// @brief Biome name std::string name; + std::vector parameters; - BiomePlants plants; + + /// @brief Plant is a single-block structure randomly generating in world + BiomeElementList plants; + + BiomeElementList structures; + BlocksLayers groundLayers; + BlocksLayers seaLayers; }; @@ -136,12 +152,12 @@ public: /// @brief Build the runtime cache /// @param content built content - virtual void prepare(const Content* content) = 0; + virtual void prepare(const GeneratorDef& def, const Content* content) = 0; }; struct GeneratingVoxelStructure { VoxelStructureMeta meta; - std::unique_ptr structure; + std::array, 4> fragments; GeneratingVoxelStructure( VoxelStructureMeta meta, diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 90778870..1f297068 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -49,15 +49,12 @@ WorldGenerator::WorldGenerator( generateStructures(requirePrototype(x, z), x, z); }); - auto rawStructures = def.script->loadStructures(); - structures.resize(rawStructures.size()); - - for (int i = 0; i < rawStructures.size(); i++) { - structures[i][0] = std::move(rawStructures[i]); - structures[i][0]->prepare(*content); + for (int i = 0; i < def.structures.size(); i++) { // pre-calculate rotated structure variants + def.structures[i]->fragments[0]->prepare(*content); for (int j = 1; j < 4; j++) { - structures[i][j] = structures[i][j-1]->rotated(*content); + def.structures[i]->fragments[j] = + def.structures[i]->fragments[j-1]->rotated(*content); } } } @@ -140,44 +137,89 @@ inline AABB gen_chunk_aabb(int chunkX, int chunkZ) { {(chunkX + 1)*CHUNK_W, 256, (chunkZ + 1) * CHUNK_D}); } +void WorldGenerator::placeStructure( + const glm::ivec3 offset, size_t structureId, uint8_t rotation, + int chunkX, int chunkZ +) { + auto& structure = *def.structures[structureId]->fragments[rotation]; + auto position = glm::ivec3(chunkX * CHUNK_W, 0, chunkZ * CHUNK_D)+offset; + auto size = structure.getSize() + glm::ivec3(0, CHUNK_H, 0); + AABB aabb(position, position + size); + for (int lcz = -1; lcz <= 1; lcz++) { + for (int lcx = -1; lcx <= 1; lcx++) { + if (lcx == 0 && lcz == 0) { + continue; + } + auto& otherPrototype = requirePrototype( + chunkX + lcx, chunkZ + lcz + ); + auto chunkAABB = gen_chunk_aabb(chunkX + lcx, chunkZ + lcz); + if (chunkAABB.intersect(aabb)) { + otherPrototype.structures.emplace_back( + structureId, + offset - + glm::ivec3(lcx * CHUNK_W, 0, lcz * CHUNK_D), + rotation + ); + } + } + } +} + void WorldGenerator::generateStructures( ChunkPrototype& prototype, int chunkX, int chunkZ ) { if (prototype.level >= ChunkPrototypeLevel::STRUCTURES) { return; } + const auto& biomes = prototype.biomes; + const auto& heightmap = prototype.heightmap; + const auto& heights = heightmap->getValues(); + util::concat(prototype.structures, def.script->placeStructures( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, - prototype.heightmap + heightmap )); for (const auto& placement : prototype.structures) { const auto& offset = placement.position; - if (placement.structure < 0 || placement.structure >= structures.size()) { + if (placement.structure < 0 || placement.structure >= def.structures.size()) { logger.error() << "invalid structure index " << placement.structure; continue; } - auto& structure = *structures[placement.structure][placement.rotation]; - auto position = glm::ivec3(chunkX * CHUNK_W, 0, chunkZ * CHUNK_D)+offset; - auto size = structure.getSize() + glm::ivec3(0, CHUNK_H, 0); - AABB aabb(position, position + size); - for (int lcz = -1; lcz <= 1; lcz++) { - for (int lcx = -1; lcx <= 1; lcx++) { - if (lcx == 0 && lcz == 0) { - continue; - } - auto& otherPrototype = requirePrototype( - chunkX + lcx, chunkZ + lcz - ); - auto chunkAABB = gen_chunk_aabb(chunkX + lcx, chunkZ + lcz); - if (chunkAABB.intersect(aabb)) { - otherPrototype.structures.emplace_back( - placement.structure, - placement.position - - glm::ivec3(lcx * CHUNK_W, 0, lcz * CHUNK_D), - placement.rotation - ); - } + placeStructure( + offset, placement.structure, placement.rotation, chunkX, chunkZ); + } + + PseudoRandom structsRand; + structsRand.setSeed(chunkX, chunkZ); + + for (uint z = 0; z < CHUNK_D; z++) { + for (uint x = 0; x < CHUNK_W; x++) { + float rand = (structsRand.randU32() % RAND_MAX) / + static_cast(RAND_MAX); + const Biome* biome = biomes[z * CHUNK_W + x]; + size_t structureId = biome->structures.choose(rand, -1); + if (structureId == -1) { + continue; } + uint8_t rotation = structsRand.randU32() % 4; + int height = heights[z * CHUNK_W + x] * CHUNK_H; + if (height < def.script->getSeaLevel()) { + continue; + } + auto& structure = *def.structures[structureId]->fragments[rotation]; + glm::ivec3 position {x, height, z}; + position.x -= structure.getSize().x / 2; + position.z -= structure.getSize().z / 2; + prototype.structures.push_back({ + static_cast(structureId), position, rotation}); + placeStructure( + position, + structureId, + rotation, + chunkX, + chunkZ + ); } } prototype.level = ChunkPrototypeLevel::STRUCTURES; @@ -259,15 +301,13 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { } } } - - // TODO: put automatic placement here for (const auto& placement : prototype.structures) { - if (placement.structure < 0 || placement.structure >= structures.size()) { + if (placement.structure < 0 || placement.structure >= def.structures.size()) { logger.error() << "invalid structure index " << placement.structure; continue; } - auto& structure = *structures[placement.structure][placement.rotation]; + auto& structure = *def.structures[placement.structure]->fragments[placement.rotation]; auto& structVoxels = structure.getRuntimeVoxels(); const auto& offset = placement.position; const auto& size = structure.getSize(); diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 29de778b..9b90cb3f 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -47,8 +47,6 @@ class WorldGenerator { /// @brief Chunk prototypes loading surround map SurroundMap surroundMap; - std::vector, 4>> structures; - /// @brief Generate chunk prototype (see ChunkPrototype) /// @param x chunk position X divided by CHUNK_W /// @param z chunk position Y divided by CHUNK_D @@ -61,6 +59,11 @@ class WorldGenerator { void generateBiomes(ChunkPrototype& prototype, int x, int z); void generateHeightmap(ChunkPrototype& prototype, int x, int z); + + void placeStructure( + const glm::ivec3 offset, size_t structure, uint8_t rotation, + int chunkX, int chunkZ + ); public: WorldGenerator( const GeneratorDef& def, From 983a24bd058ea42e4b547c1e5e84f16141af5391 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 24 Sep 2024 17:16:52 +0300 Subject: [PATCH 078/139] remove generator script load_structures function --- res/generators/default.lua | 11 ---------- .../scripting/scripting_world_generation.cpp | 21 ------------------- src/world/generator/GeneratorDef.hpp | 3 --- src/world/generator/WorldGenerator.cpp | 1 - 4 files changed, 36 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 921bfa06..713af232 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -62,17 +62,6 @@ biomes = { } } -function load_structures() - local structures = {} - local names = {"tree0", "tree1", "tree2", "tower"} - for i, name in ipairs(names) do - local filename = "core:default/"..name - debug.log("loading structure "..filename) - table.insert(structures, generation.load_structure(filename)) - end - return structures -end - function place_structures(x, z, w, d, seed, hmap) local placements = {} return placements diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 16ffaee2..a05dade8 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -32,27 +32,6 @@ public: seaLevel(seaLevel) {} - std::vector> loadStructures() override { - std::vector> structures; - - auto L = lua::get_main_thread(); - lua::pushenv(L, *env); - if (lua::getfield(L, "load_structures")) { - if (lua::call_nothrow(L, 0, 1)) { - for (int i = 1; i <= lua::objlen(L, -1); i++) { - lua::rawgeti(L, i); - if (auto lstruct = - lua::touserdata(L, -1)) { - structures.push_back(lstruct->getStructure()); - } - lua::pop(L); - } - } - } - lua::pop(L); - return structures; - } - std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed ) override { diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index acf8f61d..39539c53 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -123,9 +123,6 @@ class GeneratorScript { public: virtual ~GeneratorScript() = default; - /// @brief Load all structures - virtual std::vector> loadStructures() = 0; - /// @brief Generates a heightmap with values in range 0..1 /// @param offset position of the heightmap in the world /// @param size size of the heightmap diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 1f297068..11602236 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -48,7 +48,6 @@ WorldGenerator::WorldGenerator( surroundMap.setLevelCallback(4, [this](int const x, int const z) { generateStructures(requirePrototype(x, z), x, z); }); - for (int i = 0; i < def.structures.size(); i++) { // pre-calculate rotated structure variants def.structures[i]->fragments[0]->prepare(*content); From 09e9c570c015840933c08c7154f046b2755de1d5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 24 Sep 2024 17:25:20 +0300 Subject: [PATCH 079/139] add plants to test forest biome & update biome params noise --- res/generators/default.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 713af232..8198fc49 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -53,6 +53,11 @@ biomes = { {block="base:stone", height=-1}, {block="base:bazalt", height=1}, }, + plant_chance = 0.4, + plants = { + {block="base:grass", weight=1}, + {block="base:flower", weight=0.03}, + }, structure_chance = 0.032, structures = { {name="tree0", weight=1}, @@ -112,10 +117,10 @@ end local function _generate_biome_parameters(x, y, w, h, seed, s) local tempmap = Heightmap(w, h) tempmap.noiseSeed = seed + 5324 - tempmap:noise({x, y}, 0.04*s, 4) + tempmap:noise({x, y}, 0.04*s, 5) local hummap = Heightmap(w, h) hummap.noiseSeed = seed + 953 - hummap:noise({x, y}, 0.016*s, 4) + hummap:noise({x, y}, 0.016*s, 5) tempmap:pow(2) hummap:pow(2) return tempmap, hummap From dbaea71398a77a6bac316dbde967b3f8d68d8df2 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 24 Sep 2024 18:31:15 +0300 Subject: [PATCH 080/139] fix structures choosing --- res/generators/default.lua | 12 ++++++------ src/content/ContentLoader.cpp | 1 - src/world/generator/GeneratorDef.hpp | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 8198fc49..4ebbbe6c 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -7,7 +7,7 @@ biome_parameters = 2 biomes = { plains = { parameters = { - {value=0.5, weight=1.0}, + {value=0.5, weight=0.3}, {value=0.5, weight=0.3}, }, sea_layers = { @@ -27,8 +27,8 @@ biomes = { }, desert = { parameters = { - {value=0.0, weight=0.36}, - {value=0.0, weight=0.4}, + {value=0.0, weight=0.1}, + {value=0.0, weight=0.1}, }, sea_layers = { {block="base:water", height=-1}, @@ -120,9 +120,9 @@ local function _generate_biome_parameters(x, y, w, h, seed, s) tempmap:noise({x, y}, 0.04*s, 5) local hummap = Heightmap(w, h) hummap.noiseSeed = seed + 953 - hummap:noise({x, y}, 0.016*s, 5) - tempmap:pow(2) - hummap:pow(2) + hummap:noise({x, y}, 0.04*s, 5) + tempmap:pow(3) + hummap:pow(3) return tempmap, hummap end diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 42e83fbf..0844503d 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -520,7 +520,6 @@ void ContentLoader::load() { create_unit_id(pack->id, file.stem().u8string()); auto& def = builder.generators.create(full); - try { loadGenerator(def, full, name); } catch (const std::runtime_error& err) { diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 39539c53..c695a5a6 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -70,7 +70,7 @@ struct BiomeElementList { /// @brief Entries sorted by weight descending. std::vector entries; /// @brief Sum of weight values - float weightsSum; + float weightsSum = 0.0f; /// @brief Value generation chance float chance; From bca222afe5b41a2c6189969e34b7cd7de305803f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 24 Sep 2024 20:46:50 +0300 Subject: [PATCH 081/139] update test generator script --- res/generators/default.lua | 27 ++++++++++++++++----------- src/world/generator/GeneratorDef.hpp | 2 +- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/res/generators/default.lua b/res/generators/default.lua index 4ebbbe6c..1d658b34 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -7,8 +7,8 @@ biome_parameters = 2 biomes = { plains = { parameters = { - {value=0.5, weight=0.3}, - {value=0.5, weight=0.3}, + {value=0.5, weight=0.6}, + {value=0.5, weight=0.6}, }, sea_layers = { {block="base:water", height=-1}, @@ -23,6 +23,11 @@ biomes = { plants = { {block="base:grass", weight=1}, {block="base:flower", weight=0.03}, + }, + structure_chance = 0.0001, + structures = { + {name="tree0", weight=1}, + {name="tree1", weight=1}, } }, desert = { @@ -63,6 +68,7 @@ biomes = { {name="tree0", weight=1}, {name="tree1", weight=1}, {name="tree2", weight=1}, + {name="tower", weight=0.002}, } } } @@ -82,20 +88,19 @@ local function _generate_heightmap(x, y, w, h, seed, s) local map = Heightmap(w, h) map.noiseSeed = seed - map:noise({x, y}, 0.8*s, 4, 0.04) - map:cellnoise({x, y}, 0.1*s, 3, 0.5, umap, vmap) - map:add(0.5) + map:noise({x, y}, 0.8*s, 4, 0.02) + map:cellnoise({x, y}, 0.1*s, 3, 0.3, umap, vmap) + map:add(0.4) local rivermap = Heightmap(w, h) rivermap.noiseSeed = seed rivermap:noise({x+21, y+12}, 0.1*s, 4) rivermap:abs() rivermap:mul(2.0) - rivermap:pow(0.2) - rivermap:max(0.6) - map:add(0.4) + rivermap:pow(0.15) + rivermap:max(0.3) + map:add(0.3) map:mul(rivermap) - return map end @@ -117,10 +122,10 @@ end local function _generate_biome_parameters(x, y, w, h, seed, s) local tempmap = Heightmap(w, h) tempmap.noiseSeed = seed + 5324 - tempmap:noise({x, y}, 0.04*s, 5) + tempmap:noise({x, y}, 0.04*s, 6) local hummap = Heightmap(w, h) hummap.noiseSeed = seed + 953 - hummap:noise({x, y}, 0.04*s, 5) + hummap:noise({x, y}, 0.04*s, 6) tempmap:pow(3) hummap:pow(3) return tempmap, hummap diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index c695a5a6..be429173 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -65,7 +65,7 @@ struct WeightedEntry { }; struct BiomeElementList { - static inline float MIN_CHANCE = 0.000001f; + static inline float MIN_CHANCE = 1e-6f; /// @brief Entries sorted by weight descending. std::vector entries; From c987bbd34fd9ccf3571c272d198821ad774e3c68 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 24 Sep 2024 22:06:37 +0300 Subject: [PATCH 082/139] add biomes.json --- res/generators/default.lua | 69 +----------------------------- res/generators/default/biomes.json | 68 +++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 68 deletions(-) create mode 100644 res/generators/default/biomes.json diff --git a/res/generators/default.lua b/res/generators/default.lua index 1d658b34..6eeee14a 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -4,74 +4,7 @@ sea_level = 64 -- 2 - humidity biome_parameters = 2 -biomes = { - plains = { - parameters = { - {value=0.5, weight=0.6}, - {value=0.5, weight=0.6}, - }, - sea_layers = { - {block="base:water", height=-1}, - }, - layers = { - {block="base:grass_block", height=1, below_sea_level=false}, - {block="base:dirt", height=5, below_sea_level=false}, - {block="base:stone", height=-1}, - {block="base:bazalt", height=1}, - }, - plant_chance = 0.3, - plants = { - {block="base:grass", weight=1}, - {block="base:flower", weight=0.03}, - }, - structure_chance = 0.0001, - structures = { - {name="tree0", weight=1}, - {name="tree1", weight=1}, - } - }, - desert = { - parameters = { - {value=0.0, weight=0.1}, - {value=0.0, weight=0.1}, - }, - sea_layers = { - {block="base:water", height=-1}, - }, - layers = { - {block="base:sand", height=6}, - {block="base:stone", height=-1}, - {block="base:bazalt", height=1}, - } - }, - forest = { - parameters = { - {value=1.0, weight=1.0}, - {value=0.2, weight=0.5}, - }, - sea_layers = { - {block="base:water", height=-1}, - }, - layers = { - {block="base:grass_block", height=1, below_sea_level=false}, - {block="base:dirt", height=7, below_sea_level=false}, - {block="base:stone", height=-1}, - {block="base:bazalt", height=1}, - }, - plant_chance = 0.4, - plants = { - {block="base:grass", weight=1}, - {block="base:flower", weight=0.03}, - }, - structure_chance = 0.032, - structures = { - {name="tree0", weight=1}, - {name="tree1", weight=1}, - {name="tree2", weight=1}, - {name="tower", weight=0.002}, - } - } -} +biomes = json.parse(file.read("core:generators/default/biomes.json")) function place_structures(x, z, w, d, seed, hmap) local placements = {} diff --git a/res/generators/default/biomes.json b/res/generators/default/biomes.json new file mode 100644 index 00000000..52b39eae --- /dev/null +++ b/res/generators/default/biomes.json @@ -0,0 +1,68 @@ +{ + "forest": { + "parameters": [ + {"weight": 1, "value": 1}, + {"weight": 0.5, "value": 0.2} + ], + "layers": [ + {"below_sea_level": false, "height": 1, "block": "base:grass_block"}, + {"below_sea_level": false, "height": 7, "block": "base:dirt"}, + {"height": -1, "block": "base:stone"}, + {"height": 1, "block": "base:bazalt"} + ], + "sea_layers": [ + {"height": -1, "block": "base:water"} + ], + "plant_chance": 0.4, + "plants": [ + {"weight": 1, "block": "base:grass"}, + {"weight": 0.03, "block": "base:flower"} + ], + "structure_chance": 0.032, + "structures": [ + {"name": "tree0", "weight": 1}, + {"name": "tree1", "weight": 1}, + {"name": "tree2", "weight": 1}, + {"name": "tower", "weight": 0.002} + ] + }, + "desert": { + "parameters": [ + {"weight": 0.1, "value": 0}, + {"weight": 0.1, "value": 0} + ], + "layers": [ + {"height": 6, "block": "base:sand"}, + {"height": -1, "block": "base:stone"}, + {"height": 1, "block": "base:bazalt"} + ], + "sea_layers": [ + {"height": -1, "block": "base:water"} + ] + }, + "plains": { + "parameters": [ + {"weight": 0.6, "value": 0.5}, + {"weight": 0.6, "value": 0.5} + ], + "layers": [ + {"below_sea_level": false, "height": 1, "block": "base:grass_block"}, + {"below_sea_level": false, "height": 5, "block": "base:dirt"}, + {"height": -1, "block": "base:stone"}, + {"height": 1, "block": "base:bazalt"} + ], + "sea_layers": [ + {"height": -1, "block": "base:water"} + ], + "plant_chance": 0.3, + "plants": [ + {"weight": 1, "block": "base:grass"}, + {"weight": 0.03, "block": "base:flower"} + ], + "structure_chance": 0.0001, + "structures": [ + {"name": "tree0", "weight": 1}, + {"name": "tree1", "weight": 1} + ] + } +} From 36eed38b4c23bac5bc596020d47065239b479660 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 24 Sep 2024 22:37:31 +0300 Subject: [PATCH 083/139] add new methods to PseudoRandom & move this class to util:: --- src/items/Inventories.hpp | 2 +- src/maths/util.hpp | 94 +++++++++++++++----------- src/world/generator/WorldGenerator.cpp | 15 ++-- 3 files changed, 62 insertions(+), 49 deletions(-) diff --git a/src/items/Inventories.hpp b/src/items/Inventories.hpp index aed3e090..1af4ec8a 100644 --- a/src/items/Inventories.hpp +++ b/src/items/Inventories.hpp @@ -15,7 +15,7 @@ using inventories_map = std::unordered_map>; class Inventories { Level& level; inventories_map map; - PseudoRandom random; + util::PseudoRandom random; public: Inventories(Level& level); ~Inventories(); diff --git a/src/maths/util.hpp b/src/maths/util.hpp index 5e671082..432fe0bb 100644 --- a/src/maths/util.hpp +++ b/src/maths/util.hpp @@ -4,49 +4,63 @@ #include -class PseudoRandom { - unsigned short seed; -public: - PseudoRandom() { - seed = (unsigned short)time(0); - } +namespace util { + class PseudoRandom { + unsigned short seed; + public: + PseudoRandom() { + seed = static_cast(time(0)); + } - 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)); + 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)); - return (int)seed; - } + return static_cast(seed); + } - int32_t rand32() { - return (rand() << 16) | rand(); - } + int32_t rand32() { + return (rand() << 16) | rand(); + } - uint32_t randU32() { - return (rand() << 16) | rand(); - } + uint32_t randU32() { + return (rand() << 16) | rand(); + } - int64_t rand64() { - uint64_t x = randU32(); - uint64_t y = randU32(); - return (x << 32ULL) | y; - } + int64_t rand64() { + uint64_t x = randU32(); + uint64_t y = randU32(); + return (x << 32ULL) | y; + } - void setSeed(int number) { - seed = - ((unsigned short)(number * 23729) ^ (unsigned short)(number + 16786) - ); - rand(); - } - void setSeed(int number1, int number2) { - seed = - (((unsigned short)(number1 * 23729) | - (unsigned short)(number2 % 16786)) ^ - (unsigned short)(number2 * number1)); - rand(); - } -}; + uint64_t randU64() { + uint64_t x = randU32(); + uint64_t y = randU32(); + return (x << 32ULL) | y; + } + + float randFloat() { + return randU32() / static_cast(UINT32_MAX); + } + + double randDouble() { + return randU64() / static_cast(UINT64_MAX); + } + + void setSeed(int number) { + seed = (static_cast(number * 23729) ^ + static_cast(number + 16786)); + rand(); + } + void setSeed(int number1, int number2) { + seed = ((static_cast(number1 * 23729) | + static_cast(number2 % 16786)) ^ + static_cast(number2 * number1)); + rand(); + } + }; +} diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 11602236..27c6753c 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -173,7 +173,6 @@ void WorldGenerator::generateStructures( } const auto& biomes = prototype.biomes; const auto& heightmap = prototype.heightmap; - const auto& heights = heightmap->getValues(); util::concat(prototype.structures, def.script->placeStructures( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, @@ -189,13 +188,13 @@ void WorldGenerator::generateStructures( offset, placement.structure, placement.rotation, chunkX, chunkZ); } - PseudoRandom structsRand; + util::PseudoRandom structsRand; structsRand.setSeed(chunkX, chunkZ); + auto heights = heightmap->getValues(); for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { - float rand = (structsRand.randU32() % RAND_MAX) / - static_cast(RAND_MAX); + float rand = structsRand.randFloat(); const Biome* biome = biomes[z * CHUNK_W + x]; size_t structureId = biome->structures.choose(rand, -1); if (structureId == -1) { @@ -273,7 +272,7 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { std::memset(voxels, 0, sizeof(voxel) * CHUNK_VOL); - PseudoRandom plantsRand; + util::PseudoRandom plantsRand; plantsRand.setSeed(chunkX, chunkZ); const auto& biomes = prototype.biomes.get(); @@ -291,8 +290,7 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { generate_pole(groundLayers, height, 0, seaLevel, voxels, x, z); if (height+1 > seaLevel) { - float rand = (plantsRand.randU32() % RAND_MAX) / - static_cast(RAND_MAX); + float rand = plantsRand.randFloat(); blockid_t plant = biome->plants.choose(rand); if (plant) { voxels[vox_index(x, height+1, z)].id = plant; @@ -306,7 +304,8 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { logger.error() << "invalid structure index " << placement.structure; continue; } - auto& structure = *def.structures[placement.structure]->fragments[placement.rotation]; + auto& generatingStructure = def.structures[placement.structure]; + auto& structure = *generatingStructure->fragments[placement.rotation]; auto& structVoxels = structure.getRuntimeVoxels(); const auto& offset = placement.position; const auto& size = structure.getSize(); From d534207debdd0a81f8e1ef5a1afd234abe9bcde9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 25 Sep 2024 00:13:59 +0300 Subject: [PATCH 084/139] add test ore (coal) --- res/content/base/blocks/coal_ore.json | 3 +++ res/content/base/content.json | 15 ++++++++------- res/content/base/textures/blocks/coal_ore.png | Bin 0 -> 7430 bytes res/generators/default.lua | 6 ++++++ res/generators/default/coal_ore0.vox | Bin 0 -> 339 bytes res/generators/default/structures.json | 3 ++- .../scripting/scripting_world_generation.cpp | 11 ++++++++++- src/world/generator/GeneratorDef.hpp | 1 + src/world/generator/WorldGenerator.cpp | 2 +- 9 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 res/content/base/blocks/coal_ore.json create mode 100644 res/content/base/textures/blocks/coal_ore.png create mode 100644 res/generators/default/coal_ore0.vox diff --git a/res/content/base/blocks/coal_ore.json b/res/content/base/blocks/coal_ore.json new file mode 100644 index 00000000..23c050c6 --- /dev/null +++ b/res/content/base/blocks/coal_ore.json @@ -0,0 +1,3 @@ +{ + "texture": "coal_ore" +} diff --git a/res/content/base/content.json b/res/content/base/content.json index 9b696dfd..cec825a8 100644 --- a/res/content/base/content.json +++ b/res/content/base/content.json @@ -1,8 +1,6 @@ { - "entities": [ - "drop", - "player", - "falling_block" + "items": [ + "bazalt_breaker" ], "blocks": [ "dirt", @@ -28,9 +26,12 @@ "pipe", "lightbulb", "torch", - "wooden_door" + "wooden_door", + "coal_ore" ], - "items": [ - "bazalt_breaker" + "entities": [ + "drop", + "player", + "falling_block" ] } \ No newline at end of file diff --git a/res/content/base/textures/blocks/coal_ore.png b/res/content/base/textures/blocks/coal_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..98f36abeaaff372d515aae486e3b1eb19ae9bd2e GIT binary patch literal 7430 zcmeHLX;>54(vB>GED9177sePDKxxw5=_H*DD6&aF7&axY*hzOFvLqy75pY8kL`6{) zcQz46#0^mt6hV~nDvP4vm1PtcL>N)If+*x9Y8`Q+3iC zJltpN>x|W*P$>F*X9q9x+d%c!9!h>r==y+BD4K>*J_4l|7D1EArD91CK~pZ55i}xF zBBoFx?`8WgJag1suiu|)qoE#GA&dNNfz8^VR!URKryWge$jd?2@2Yo?t=RXW&*p|7 zYRQ#aS5W2JTEo*;oY8dPJ4l^6rdTj0758h98jkBL} ze(wlaUgXfY^HF+FT8w?)xE<49PuRI)K}!FV35KnYZ|>~x9ha38dS#P&;t1z3!7RS{ zXw7pvMzu;<2H5)B>Swz~J3ND*%8~TCE#W@8TMMaMtH%rh! zxrt959ehULIdjz${$;21dyCZ)|A=k+Irm}>!JbgLsBWZXMC{=y=2^Mg?_VIR-j^r;zdW(}z?t&3D^3AI}td&Ve#QgTl?odf-R%F*}4y6x6bJ7TCF z`+oBEwHiN*qz-2C54s$q{?Q@bC3OF(%2f%CD_KGC6aZ9 zxh5I1i`M@d8EoB{`JXFv^Yp}?zWsYjmvelFmW%3H)1T2?k+#P-ayFeaMn+jiH`Ff3 znp)U#E`nda>E|l_XNIx(DQQNOrXRY`F?npbFhp?6d45*pj)~n14DIW)-kqJiE<)cW zIk5Cx_u+?fX65Whd~ezFlNRD-Inz${KGS3RdixZ%H!Iz@m0+B`mW zQA1si)#!~YgTPB!7vA!5L4{NDkxebGT?>;1ibR7Mg6{JdO?O*8PL+qH2W$+;*xBUw z`d!T2h(^D%=FS3cZyU7vP-5Di7Mkl4l;wALMP{^flyTBU1rpD@SLkQ-N})Y-yY1Wx z-v_do(rww2YtQqJWwiOUoO+G-KReryb?DZ#D+|lQqCF_RxqX$bPM!~c+Et%?|5;#h zz{~2RdyhXm+xzR0m&?ZKEW2ql?w-8Sl3v^(FBxHW=go~1hRPHFzR7tCbzE`B*X?R{ zq0Z2v-8fQRcvfqeF?GY+=J7@0`RjM~H2pC~tJC_!aJP(ASM@$bX0@9+d?;Ae97bId zcG#l6U*d7sw4Jx;@|{3mvlP?$QLOfMkyD8v@pkgARWsJyp8f7#cazq-(rhC&$KhY?y6rFmp%3#@!u9?sg^4h%J*MybcpUs^Kf?2TL-8CAVrPPkTA3e#g|3P^&`A7dN)< zcCR#wPq<>IHQn?^;vpw%$MK%5{jb)|**>>w;l2X-j)Ldc{7impn|^&!4mVa~)JctD zbgGZ1-57S}>1PK8&No*!UY^yTI7+FimVI^>z!xKq^tuS0FB&;HXW#llUP^@Q0$}mwNp`-~3e@Tc9y|bu6^x-A5D^DB|MunKN{(BY7l1Frma~k-9FwbOr{B82eZ z??@5mU?>wr5P*qb8~|a%Ab@c>A^-=;JwgyTgmL~(iX0yh;*t4-pzoJOCYyy292_Mn z5(I!CLI@C|Ocp?nD<%SQQ6>%zwt~;w;4fv7>jQCl2#W{7-;W|!EJj3RzX5QVY;*|B zW&8;`{XFqn%2Y>0ykQ51tPocLQPA^->hAXh~Z zWRr#Y{U{R7$WX8XzQeFx=wrIRr?iJpO-2UXKfJ47mN zdmTlw9Hn~@T3^E7ra{T3dbz|I1WBbv8bdZaspqjODk>)P%bJ^ix@v;&In>zLczbK< zQjZCtGtv}cVUBwanP)${5rCAL*yrA{d;hLy%aY@>w@n^zYC19Ql8OEJihw-QK=FkO zp%D=kot=z7!uQV0b)-6)hzwd=Tki-@UgFL+&`|PBj~0+0&AUZfXqUj|OT4)0S40zF*}T`Yb*l4zcIq7L3$`P(TyifE30Fne!# zsNQOWSTyNm9X+qWGS&6Sk(T4PuHj`k{OcdeDo?6rYHDgsObocUQgFP!Jhb@b{npm_ z#s&gcO)|}oxS0-*$LF89 z+27G|R82dU(%HX$?0RR5A-gKY0eKdQYOUj|xZ&a9FPifgS@n*v=KWG>p@?uKi0*@p|sZ3 literal 0 HcmV?d00001 diff --git a/res/generators/default.lua b/res/generators/default.lua index 6eeee14a..a5bd50bf 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -8,6 +8,12 @@ biomes = json.parse(file.read("core:generators/default/biomes.json")) function place_structures(x, z, w, d, seed, hmap) local placements = {} + for i=1,10 do + local sx = math.random() * w + local sz = math.random() * d + local sy = math.random() * hmap:at(sx, sz)*256 - 6 + table.insert(placements, {"coal_ore0", {sx, sy, sz}, math.random()*4}) + end return placements end diff --git a/res/generators/default/coal_ore0.vox b/res/generators/default/coal_ore0.vox new file mode 100644 index 0000000000000000000000000000000000000000..882afcb7e9288b7f55c4cf1281fc2d989f876fcd GIT binary patch literal 339 zcmZP!W@KO}%dbeyDP~||X21d&fnp#GgXnyiI6fL#4~z{n6Gr3Hk1j`!9&|IX@EMYF z@{_Z5^AdAYfuX^{!N9 placeStructures( + const GeneratorDef& def, const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap ) override { @@ -98,7 +99,15 @@ public: lua::rawgeti(L, i); lua::rawgeti(L, 1); - int structIndex = lua::tointeger(L, -1); + int structIndex = 0; + if (lua::isstring(L, -1)) { + const auto& found = def.structuresIndices.find(lua::require_string(L, -1)); + if (found != def.structuresIndices.end()) { + structIndex = found->second; + } + } else { + structIndex = lua::tointeger(L, -1); + } lua::pop(L); lua::rawgeti(L, 2); diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index be429173..bf6934b0 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -135,6 +135,7 @@ public: const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; virtual std::vector placeStructures( + const GeneratorDef& def, const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap) = 0; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 27c6753c..596a66fc 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -175,7 +175,7 @@ void WorldGenerator::generateStructures( const auto& heightmap = prototype.heightmap; util::concat(prototype.structures, def.script->placeStructures( - {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, + def, {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, heightmap )); for (const auto& placement : prototype.structures) { From 788ad1f6a65b81150e41e6f5b4a39d81e1b1e6bb Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 25 Sep 2024 15:45:45 +0300 Subject: [PATCH 085/139] add core:obstacle block & move cameras from base to core & add new generator --- res/content/base/generators/demo.lua | 86 +++++++++++++++++ .../base/generators/demo}/biomes.json | 0 .../base/generators/demo}/coal_ore0.vox | Bin .../base/generators/demo}/structures.json | 0 .../base/generators/demo}/tower.vox | Bin .../base/generators/demo}/tree0.vox | Bin .../base/generators/demo}/tree1.vox | Bin .../base/generators/demo}/tree2.vox | Bin res/content/base/preload.json | 7 -- res/generators/default.lua | 91 +++--------------- res/preload.json | 7 +- res/{content/base => }/resources.json | 0 res/textures/blocks/obstacle.png | Bin 0 -> 6849 bytes src/constants.hpp | 1 + src/content/ContentLoader.cpp | 24 ++--- src/core_defs.cpp | 35 +++++-- src/core_defs.hpp | 1 + .../scripting/scripting_world_generation.cpp | 3 +- src/objects/Player.cpp | 6 +- 19 files changed, 147 insertions(+), 114 deletions(-) create mode 100644 res/content/base/generators/demo.lua rename res/{generators/default => content/base/generators/demo}/biomes.json (100%) rename res/{generators/default => content/base/generators/demo}/coal_ore0.vox (100%) rename res/{generators/default => content/base/generators/demo}/structures.json (100%) rename res/{generators/default => content/base/generators/demo}/tower.vox (100%) rename res/{generators/default => content/base/generators/demo}/tree0.vox (100%) rename res/{generators/default => content/base/generators/demo}/tree1.vox (100%) rename res/{generators/default => content/base/generators/demo}/tree2.vox (100%) rename res/{content/base => }/resources.json (100%) create mode 100644 res/textures/blocks/obstacle.png diff --git a/res/content/base/generators/demo.lua b/res/content/base/generators/demo.lua new file mode 100644 index 00000000..a8e0a365 --- /dev/null +++ b/res/content/base/generators/demo.lua @@ -0,0 +1,86 @@ +sea_level = 64 + +-- 1 - temperature +-- 2 - humidity +biome_parameters = 2 + +biomes = json.parse(file.read("base:generators/demo/biomes.json")) + +function place_structures(x, z, w, d, seed, hmap) + local placements = {} + for i=1,10 do + local sx = math.random() * w + local sz = math.random() * d + local sy = math.random() * 128 + if sy < hmap:at(sx, sz) * 256 - 6 then + table.insert(placements, {"coal_ore0", {sx, sy, sz}, math.random()*4}) + end + end + return placements +end + +local function _generate_heightmap(x, y, w, h, seed, s) + local umap = Heightmap(w, h) + local vmap = Heightmap(w, h) + umap.noiseSeed = seed + vmap.noiseSeed = seed + vmap:noise({x+521, y+70}, 0.1*s, 3, 25.8) + vmap:noise({x+95, y+246}, 0.15*s, 3, 25.8) + + local map = Heightmap(w, h) + map.noiseSeed = seed + map:noise({x, y}, 0.8*s, 4, 0.02) + map:cellnoise({x, y}, 0.1*s, 3, 0.3, umap, vmap) + map:add(0.4) + + local rivermap = Heightmap(w, h) + rivermap.noiseSeed = seed + rivermap:noise({x+21, y+12}, 0.1*s, 4) + rivermap:abs() + rivermap:mul(2.0) + rivermap:pow(0.15) + rivermap:max(0.3) + map:add(0.3) + map:mul(rivermap) + return map +end + +function generate_heightmap(x, y, w, h, seed) + -- blocks per dot + -- 8 - linear interpolation is visible, but not so much + -- 4 - high quality, but slower + -- 2 - you really don't need it + -- 1 - please have mercy on your CPU + local bpd = 4 + local map = _generate_heightmap( + math.floor(x/bpd), math.floor(y/bpd), + math.floor(w/bpd)+1, math.floor(h/bpd)+1, seed, bpd) + map:resize(w+bpd, h+bpd, 'linear') + map:crop(0, 0, w, h) + return map +end + +local function _generate_biome_parameters(x, y, w, h, seed, s) + local tempmap = Heightmap(w, h) + tempmap.noiseSeed = seed + 5324 + tempmap:noise({x, y}, 0.04*s, 6) + local hummap = Heightmap(w, h) + hummap.noiseSeed = seed + 953 + hummap:noise({x, y}, 0.04*s, 6) + tempmap:pow(3) + hummap:pow(3) + return tempmap, hummap +end + +function generate_biome_parameters(x, y, w, h, seed) + local bpd = 8 + local tmap, hmap = _generate_biome_parameters( + math.floor(x/bpd), math.floor(y/bpd), + math.floor(w/bpd)+1, math.floor(h/bpd)+1, seed, bpd) + tmap:resize(w+bpd, h+bpd, 'linear') + tmap:crop(0, 0, w, h) + + hmap:resize(w+bpd, h+bpd, 'linear') + hmap:crop(0, 0, w, h) + return tmap, hmap +end diff --git a/res/generators/default/biomes.json b/res/content/base/generators/demo/biomes.json similarity index 100% rename from res/generators/default/biomes.json rename to res/content/base/generators/demo/biomes.json diff --git a/res/generators/default/coal_ore0.vox b/res/content/base/generators/demo/coal_ore0.vox similarity index 100% rename from res/generators/default/coal_ore0.vox rename to res/content/base/generators/demo/coal_ore0.vox diff --git a/res/generators/default/structures.json b/res/content/base/generators/demo/structures.json similarity index 100% rename from res/generators/default/structures.json rename to res/content/base/generators/demo/structures.json diff --git a/res/generators/default/tower.vox b/res/content/base/generators/demo/tower.vox similarity index 100% rename from res/generators/default/tower.vox rename to res/content/base/generators/demo/tower.vox diff --git a/res/generators/default/tree0.vox b/res/content/base/generators/demo/tree0.vox similarity index 100% rename from res/generators/default/tree0.vox rename to res/content/base/generators/demo/tree0.vox diff --git a/res/generators/default/tree1.vox b/res/content/base/generators/demo/tree1.vox similarity index 100% rename from res/generators/default/tree1.vox rename to res/content/base/generators/demo/tree1.vox diff --git a/res/generators/default/tree2.vox b/res/content/base/generators/demo/tree2.vox similarity index 100% rename from res/generators/default/tree2.vox rename to res/content/base/generators/demo/tree2.vox diff --git a/res/content/base/preload.json b/res/content/base/preload.json index 1e519ca2..33f38f36 100644 --- a/res/content/base/preload.json +++ b/res/content/base/preload.json @@ -7,13 +7,6 @@ "models": [ "drop-item" ], - "shaders": [ - "ui3d", - "entity", - "screen", - "background", - "skybox_gen" - ], "textures": [ "misc/moon", "misc/sun", diff --git a/res/generators/default.lua b/res/generators/default.lua index a5bd50bf..c88a6135 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -1,84 +1,15 @@ -sea_level = 64 +biome_parameters = 0 --- 1 - temperature --- 2 - humidity -biome_parameters = 2 - -biomes = json.parse(file.read("core:generators/default/biomes.json")) - -function place_structures(x, z, w, d, seed, hmap) - local placements = {} - for i=1,10 do - local sx = math.random() * w - local sz = math.random() * d - local sy = math.random() * hmap:at(sx, sz)*256 - 6 - table.insert(placements, {"coal_ore0", {sx, sy, sz}, math.random()*4}) - end - return placements -end - -local function _generate_heightmap(x, y, w, h, seed, s) - local umap = Heightmap(w, h) - local vmap = Heightmap(w, h) - umap.noiseSeed = seed - vmap.noiseSeed = seed - vmap:noise({x+521, y+70}, 0.1*s, 3, 25.8) - vmap:noise({x+95, y+246}, 0.15*s, 3, 25.8) - - local map = Heightmap(w, h) - map.noiseSeed = seed - map:noise({x, y}, 0.8*s, 4, 0.02) - map:cellnoise({x, y}, 0.1*s, 3, 0.3, umap, vmap) - map:add(0.4) - - local rivermap = Heightmap(w, h) - rivermap.noiseSeed = seed - rivermap:noise({x+21, y+12}, 0.1*s, 4) - rivermap:abs() - rivermap:mul(2.0) - rivermap:pow(0.15) - rivermap:max(0.3) - map:add(0.3) - map:mul(rivermap) - return map -end +biomes = {flat = { + parameters = {}, + layers = { + {height=-1, block="core:obstacle"}, + }, + sea_layers = { + {height=-1, block="core:obstacle"} + } +}} function generate_heightmap(x, y, w, h, seed) - -- blocks per dot - -- 8 - linear interpolation is visible, but not so much - -- 4 - high quality, but slower - -- 2 - you really don't need it - -- 1 - please have mercy on your CPU - local bpd = 4 - local map = _generate_heightmap( - math.floor(x/bpd), math.floor(y/bpd), - math.floor(w/bpd)+1, math.floor(h/bpd)+1, seed, bpd) - map:resize(w+bpd, h+bpd, 'linear') - map:crop(0, 0, w, h) - return map -end - -local function _generate_biome_parameters(x, y, w, h, seed, s) - local tempmap = Heightmap(w, h) - tempmap.noiseSeed = seed + 5324 - tempmap:noise({x, y}, 0.04*s, 6) - local hummap = Heightmap(w, h) - hummap.noiseSeed = seed + 953 - hummap:noise({x, y}, 0.04*s, 6) - tempmap:pow(3) - hummap:pow(3) - return tempmap, hummap -end - -function generate_biome_parameters(x, y, w, h, seed) - local bpd = 8 - local tmap, hmap = _generate_biome_parameters( - math.floor(x/bpd), math.floor(y/bpd), - math.floor(w/bpd)+1, math.floor(h/bpd)+1, seed, bpd) - tmap:resize(w+bpd, h+bpd, 'linear') - tmap:crop(0, 0, w, h) - - hmap:resize(w+bpd, h+bpd, 'linear') - hmap:crop(0, 0, w, h) - return tmap, hmap + return Heightmap(w, h) end diff --git a/res/preload.json b/res/preload.json index 92d371cf..72f96c6b 100644 --- a/res/preload.json +++ b/res/preload.json @@ -1,8 +1,13 @@ { "shaders": [ "ui", + "ui3d", "main", - "lines" + "lines", + "entity", + "screen", + "background", + "skybox_gen" ], "textures": [ "gui/menubg", diff --git a/res/content/base/resources.json b/res/resources.json similarity index 100% rename from res/content/base/resources.json rename to res/resources.json diff --git a/res/textures/blocks/obstacle.png b/res/textures/blocks/obstacle.png new file mode 100644 index 0000000000000000000000000000000000000000..a15d3bde42d12b763ad4ed84ffbf1639e9ffe502 GIT binary patch literal 6849 zcmeHMc~nzZ8V{%-2ve(4aV19DO7SIc&09)T3Xw%a8Aw$WmA55C$CHWsQ@tC8B-A2I;)s^v-Ew#T{MruPRsCMmnW~Iw8(Xpqqu6_U%62u6Jatl~ zzUZ#V^b%54R6&4Haq;w+LLx7Bao3nh*$v+7t*Q%tg^6`mWKuIT@6zWfGi&#k1T2ZJ zTb$*YvA4M%e=Z~Y^=*#8*p!F z@vPI=7ah8BrzLI1hI%peo4!jg8PD(Ry`V{3)HrqYzjuaYn9TRR)i>3@-}!lO!W!Fw z;W_W;F(px|B2?A*I=tWiRQEgo8uM$ncO!QQeX5E}3cpKC^^C5sbx&NaBIRd(TeZ`w z&Y2RE-!)==&XLh|feFP^T4Q&OaX(SyQIg6vy_PNyS@3nqw*$Y1t-RfDG<+aaI*^ zFyryV^m5CzU`gPY$J`EXnyE5X8nU1l(d^GH4$2MN9;hjZ zi$pdEK^wVg=jN!#=sw&c9Lx#BF0Y8+xylygze8%RJl$nkfHTiOwRh(ZI;ghiwfK?I_ZNMX8J+8~Cm_G&$c;f|$`N6yy~@*m?0F(6ZR-1x z2XY2h7YF1nyK{2Vhf@ZOD4XT2z108g`11|*`l>TuHe~JCn4TP=oaJqAruNSU(Qe=%6rcEv$*S0*Q?%P+Aw`PyX*H1hpJ^I{Lw~o3$rNCgA z8xlXrH56J9w9L#XVoI;CPYv+=GN5p5n|s7(_1p9niH9T-w|5O; zVX;wRVGj}%q~)rtrK-rPQ3I=|##e96nLDVM5;ny^3v;MxemSv2j(cxmRwOoe)AY~p z(>J2B?81c_?nGsE!g$Z|p*>qAZ>L$}>`QyKecxx|euw4E`{_-C({D-Ej^kFIK>n7z z$N0)61X-QnS3c#NxkDO<%*m@A&d22Ux;=1e)9pL=w<26VFHsF!hZp+zTB}~T?^#h^X8lKRQ=HVp{sncUX4tW^9>+h$}412gP@X)Cp56e5+)`aB4=2| z_!%);e#Ug37Gxtwc!xL`0ALjDtkhvlG1(Z0O6J04z_oK&A(Og9?9)}U37S}GnAs{y zaXBuBA+^JhhRQ~GOGB)Jj)@B&-2nl9sbtA^yM<9G($mxB>6qMX)hiI1rWG)%Kv4)t zK(-8%opnGa+fXM&J4U!@qD~v|P<11`-bs7Na*w7!YuxY`$qlgo2=2R;$s?$W1edyy50{?KmCDoeZq9gDM zF-0^2Q5(>TJfSioN)!8Vh0_FjgVEwz1&sZKq}`x z3@XZGv$G~%bV31exdGrPbudB*Ivqq~90B1h$w4eF3J}e*Btg?SOAyFo5N}!yAS&6E z$9Ls~5&#rR5j=roBE*VH8p2_MgjkLvAxdCj7)MzVq1tyN@XTnl)yM*O8jP%7R9H-U zmky`Fnb6oMl?;``k49osSi24^P|3y`OsS4X6Iz2&jJLB+Ya%2{qDovz(gcDkY2_oK zaiY}*BGHM7z;dh|*Xb4pkO78eopA~PTq^(FhmPV4AQ|EDzbtOhCw-aLb1)Pv!}CGF;owj0yZFkTx_Jn zT&4_qQaU|ZblM3p zz<&tRd0xbn1TK;S1t^LlgyS3naTG>C;JCtYgr+b7X)gs2)8LuBNNW+A!3hRIJC7o* z)8Ra+gdqV1g$_rRN{C{CD?$9SFvY@3K>FDOwe$F3I*~wW5r=SREaC(L>Nw5ukdDVh zlA}qQr?_Y5^Psk%h!#biE`#aLqsWT{3zG-|u`HqliAPZohY2F2qeucH1d*f&{MjjX z9v_tFcm&pAkSO99I1VUq9N>Tt!YfIhAW5B)!~UQ6fUKc-n4}?6NwJ`TKt+fnL7yp# zIst)k5vK{_+4;1GAPT{tYETS_LX_$}m*JqL!f0@^p(1G8a2QrXtdip(B?&4e=wXyN z@#oZj9PoosaDw9ujx*SQWm$;A*_S`)Swa;5)77*KJl4pAlfQio+#rDa4aLJ7j*gbV z*$DlGua2hXFVp~_pDTGP`#v|e4V@NR2ij?2`_mgY0iHDfP`fk literal 0 HcmV?d00001 diff --git a/src/constants.hpp b/src/constants.hpp index 15b3e732..63aa840b 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -17,6 +17,7 @@ inline constexpr bool ENGINE_DEBUG_BUILD = true; inline const std::string ENGINE_VERSION_STRING = "0.23"; inline constexpr blockid_t BLOCK_AIR = 0; +inline constexpr blockid_t BLOCK_OBSTACLE = 1; inline constexpr itemid_t ITEM_EMPTY = 0; inline constexpr entityid_t ENTITY_NONE = 0; diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 0844503d..907e8388 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -528,6 +528,18 @@ void ContentLoader::load() { } } + fs::path resourcesFile = folder / fs::u8path("resources.json"); + if (fs::exists(resourcesFile)) { + auto resRoot = files::read_json(resourcesFile); + for (const auto& [key, arr] : resRoot.asObject()) { + if (auto resType = ResourceType_from(key)) { + loadResources(*resType, arr); + } else { + logger.warning() << "unknown resource type: " << key; + } + } + } + if (!fs::is_regular_file(pack->getContentFile())) return; auto root = files::read_json(pack->getContentFile()); @@ -723,18 +735,6 @@ void ContentLoader::load() { } } } - - fs::path resourcesFile = folder / fs::u8path("resources.json"); - if (fs::exists(resourcesFile)) { - auto resRoot = files::read_json(resourcesFile); - for (const auto& [key, arr] : resRoot.asObject()) { - if (auto resType = ResourceType_from(key)) { - loadResources(*resType, arr); - } else { - logger.warning() << "unknown resource type: " << key; - } - } - } } void ContentLoader::loadResources(ResourceType type, const dv::value& list) { diff --git a/src/core_defs.cpp b/src/core_defs.cpp index cf8dc1f4..b56b3cc8 100644 --- a/src/core_defs.cpp +++ b/src/core_defs.cpp @@ -12,17 +12,19 @@ // All in-game definitions (blocks, items, etc..) void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) { - Block& block = builder->blocks.create("core:air"); - block.replaceable = true; - block.drawGroup = 1; - block.lightPassing = true; - block.skyLightPassing = true; - block.obstacle = false; - block.selectable = false; - block.model = BlockModel::none; - block.pickingItem = "core:empty"; + { + Block& block = builder->blocks.create(CORE_AIR); + block.replaceable = true; + block.drawGroup = 1; + block.lightPassing = true; + block.skyLightPassing = true; + block.obstacle = false; + block.selectable = false; + block.model = BlockModel::none; + block.pickingItem = CORE_EMPTY; + } - ItemDef& item = builder->items.create("core:empty"); + ItemDef& item = builder->items.create(CORE_EMPTY); item.iconType = item_icon_type::none; auto bindsFile = paths->getResourcesFolder()/fs::path("bindings.toml"); @@ -31,4 +33,17 @@ void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) { bindsFile.u8string(), files::read_string(bindsFile) ); } + + { + Block& block = builder->blocks.create(CORE_OBSTACLE); + for (uint i = 0; i < 6; i++) { + block.textureFaces[i] = "obstacle"; + } + block.hitboxes = {AABB()}; + ItemDef& item = builder->items.create(CORE_OBSTACLE+".item"); + item.iconType = item_icon_type::block; + item.icon = CORE_OBSTACLE; + item.placingBlock = CORE_OBSTACLE; + item.caption = block.caption; + } } diff --git a/src/core_defs.hpp b/src/core_defs.hpp index 32b2424c..ccfa2e69 100644 --- a/src/core_defs.hpp +++ b/src/core_defs.hpp @@ -3,6 +3,7 @@ #include inline const std::string CORE_EMPTY = "core:empty"; +inline const std::string CORE_OBSTACLE = "core:obstacle"; inline const std::string CORE_AIR = "core:air"; inline const std::string TEXTURE_NOTFOUND = "notfound"; diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index eeeeb1c5..da6e63a4 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -286,7 +286,8 @@ std::unique_ptr scripting::load_generator( lua::pop(L); uint biomeParameters = root["biome_parameters"].asInteger(); - uint seaLevel = root["sea_level"].asInteger(); + uint seaLevel = 0; + root.at("sea_level").get(seaLevel); std::vector biomes; diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index 2acbdf46..a2f854ee 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -37,9 +37,9 @@ Player::Player( position(position), inventory(std::move(inv)), eid(eid), - camera(level->getCamera("base:first-person")), - spCamera(level->getCamera("base:third-person-front")), - tpCamera(level->getCamera("base:third-person-back")), + camera(level->getCamera("core:first-person")), + spCamera(level->getCamera("core:third-person-front")), + tpCamera(level->getCamera("core:third-person-back")), currentCamera(camera) { camera->setFov(glm::radians(90.0f)); spCamera->setFov(glm::radians(90.0f)); From d839da7dab6837d7dd705b506780b50d16530f26 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 26 Sep 2024 01:30:55 +0300 Subject: [PATCH 086/139] collect generator voxel fragments to directory --- .../generators/demo/{ => fragments}/coal_ore0.vox | Bin .../base/generators/demo/{ => fragments}/tower.vox | Bin .../base/generators/demo/{ => fragments}/tree0.vox | Bin .../base/generators/demo/{ => fragments}/tree1.vox | Bin .../base/generators/demo/{ => fragments}/tree2.vox | Bin src/content/ContentLoader.hpp | 2 +- src/content/loading/GeneratorLoader.cpp | 2 +- src/logic/scripting/lua/libcore.cpp | 4 +++- 8 files changed, 5 insertions(+), 3 deletions(-) rename res/content/base/generators/demo/{ => fragments}/coal_ore0.vox (100%) rename res/content/base/generators/demo/{ => fragments}/tower.vox (100%) rename res/content/base/generators/demo/{ => fragments}/tree0.vox (100%) rename res/content/base/generators/demo/{ => fragments}/tree1.vox (100%) rename res/content/base/generators/demo/{ => fragments}/tree2.vox (100%) diff --git a/res/content/base/generators/demo/coal_ore0.vox b/res/content/base/generators/demo/fragments/coal_ore0.vox similarity index 100% rename from res/content/base/generators/demo/coal_ore0.vox rename to res/content/base/generators/demo/fragments/coal_ore0.vox diff --git a/res/content/base/generators/demo/tower.vox b/res/content/base/generators/demo/fragments/tower.vox similarity index 100% rename from res/content/base/generators/demo/tower.vox rename to res/content/base/generators/demo/fragments/tower.vox diff --git a/res/content/base/generators/demo/tree0.vox b/res/content/base/generators/demo/fragments/tree0.vox similarity index 100% rename from res/content/base/generators/demo/tree0.vox rename to res/content/base/generators/demo/fragments/tree0.vox diff --git a/res/content/base/generators/demo/tree1.vox b/res/content/base/generators/demo/fragments/tree1.vox similarity index 100% rename from res/content/base/generators/demo/tree1.vox rename to res/content/base/generators/demo/fragments/tree1.vox diff --git a/res/content/base/generators/demo/tree2.vox b/res/content/base/generators/demo/fragments/tree2.vox similarity index 100% rename from res/content/base/generators/demo/tree2.vox rename to res/content/base/generators/demo/fragments/tree2.vox diff --git a/src/content/ContentLoader.hpp b/src/content/ContentLoader.hpp index fdd93ef8..ae01a955 100644 --- a/src/content/ContentLoader.hpp +++ b/src/content/ContentLoader.hpp @@ -55,7 +55,7 @@ class ContentLoader { public: ContentLoader(ContentPack* pack, ContentBuilder& builder); - bool fixPackIndices( + static bool fixPackIndices( const fs::path& folder, dv::value& indicesRoot, const std::string& contentSection diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index 256a1b28..fd6fdf38 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -22,7 +22,7 @@ static VoxelStructureMeta load_structure_meta( static std::vector> load_structures( const fs::path& structuresFile ) { - auto structuresDir = structuresFile.parent_path(); + auto structuresDir = structuresFile.parent_path() / fs::path("fragments"); auto map = files::read_json(structuresFile); std::vector> structures; diff --git a/src/logic/scripting/lua/libcore.cpp b/src/logic/scripting/lua/libcore.cpp index 3ab665c2..38267e4d 100644 --- a/src/logic/scripting/lua/libcore.cpp +++ b/src/logic/scripting/lua/libcore.cpp @@ -180,12 +180,14 @@ static int l_get_default_generator(lua::State* L) { /// @brief Get a list of all world generators /// @return A table with the IDs of all world generators static int l_get_generators(lua::State* L) { + if (content == nullptr) { + throw std::runtime_error("content is not initialized"); + } const auto& generators = content->generators.getDefs(); lua::createtable(L, generators.size(), 0); int i = 0; for (auto& [name, _] : generators) { - std::cout << name << std::endl; lua::pushstring(L, name); lua::rawseti(L, i + 1); i++; From 3d478aef085aa99d184b182fe23e3b0bf288753d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 26 Sep 2024 22:26:01 +0300 Subject: [PATCH 087/139] add generator definition file (.toml) --- res/content/base/generators/demo.toml | 4 ++ .../generators/{demo.lua => demo/script.lua} | 6 --- res/generators/default.toml | 1 + .../{default.lua => default/script.lua} | 6 --- src/content/ContentLoader.cpp | 2 +- src/content/loading/GeneratorLoader.cpp | 12 ++++-- src/files/files.cpp | 33 +++++++++++++++ src/files/files.hpp | 4 ++ src/logic/scripting/scripting.hpp | 2 + .../scripting/scripting_world_generation.cpp | 41 ++++++------------- src/world/generator/GeneratorDef.hpp | 13 +++--- src/world/generator/WorldGenerator.cpp | 6 +-- 12 files changed, 76 insertions(+), 54 deletions(-) create mode 100644 res/content/base/generators/demo.toml rename res/content/base/generators/{demo.lua => demo/script.lua} (97%) create mode 100644 res/generators/default.toml rename res/generators/{default.lua => default/script.lua} (63%) diff --git a/res/content/base/generators/demo.toml b/res/content/base/generators/demo.toml new file mode 100644 index 00000000..a5cad573 --- /dev/null +++ b/res/content/base/generators/demo.toml @@ -0,0 +1,4 @@ +# 1 - temperature +# 2 - humidity +biome_parameters = 2 +sea_level = 64 diff --git a/res/content/base/generators/demo.lua b/res/content/base/generators/demo/script.lua similarity index 97% rename from res/content/base/generators/demo.lua rename to res/content/base/generators/demo/script.lua index a8e0a365..7744882f 100644 --- a/res/content/base/generators/demo.lua +++ b/res/content/base/generators/demo/script.lua @@ -1,9 +1,3 @@ -sea_level = 64 - --- 1 - temperature --- 2 - humidity -biome_parameters = 2 - biomes = json.parse(file.read("base:generators/demo/biomes.json")) function place_structures(x, z, w, d, seed, hmap) diff --git a/res/generators/default.toml b/res/generators/default.toml new file mode 100644 index 00000000..b5e19313 --- /dev/null +++ b/res/generators/default.toml @@ -0,0 +1 @@ +biome_parameters = 0 diff --git a/res/generators/default.lua b/res/generators/default/script.lua similarity index 63% rename from res/generators/default.lua rename to res/generators/default/script.lua index c88a6135..e949a610 100644 --- a/res/generators/default.lua +++ b/res/generators/default/script.lua @@ -1,5 +1,3 @@ -biome_parameters = 0 - biomes = {flat = { parameters = {}, layers = { @@ -9,7 +7,3 @@ biomes = {flat = { {height=-1, block="core:obstacle"} } }} - -function generate_heightmap(x, y, w, h, seed) - return Heightmap(w, h) -end diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 907e8388..7cdb3a96 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -49,7 +49,7 @@ static void detect_defs( if (name[0] == '_') { continue; } - if (fs::is_regular_file(file) && file.extension() == ".json") { + if (fs::is_regular_file(file) && files::is_data_file(file)) { detected.push_back(prefix.empty() ? name : prefix + ":" + name); } else if (fs::is_directory(file)) { detect_defs(file, name, detected); diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index fd6fdf38..5342c081 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -66,14 +66,20 @@ void ContentLoader::loadGenerator( ) { auto packDir = pack->folder; auto generatorsDir = packDir / GENERATORS_DIR; - auto folder = generatorsDir / fs::u8path(name); - auto generatorFile = generatorsDir / fs::u8path(name + ".lua"); + auto generatorFile = generatorsDir / fs::u8path(name + ".toml"); if (!fs::exists(generatorFile)) { return; } + auto map = files::read_toml(generatorsDir / fs::u8path(name + ".toml")); + map.at("biome_parameters").get(def.biomeParameters); + map.at("sea_level").get(def.seaLevel); + + auto folder = generatorsDir / fs::u8path(name); + auto scriptFile = folder / fs::u8path("script.lua"); + auto structuresFile = folder / STRUCTURES_FILE; if (fs::exists(structuresFile)) { load_structures(def, structuresFile); } - def.script = scripting::load_generator(generatorFile); + def.script = scripting::load_generator(def, scriptFile); } diff --git a/src/files/files.cpp b/src/files/files.cpp index c1f74c63..31eff94e 100644 --- a/src/files/files.cpp +++ b/src/files/files.cpp @@ -159,3 +159,36 @@ std::vector files::read_list(const fs::path& filename) { } return lines; } + +#include + +#include "coders/json.hpp" +#include "coders/toml.hpp" + +using DecodeFunc = dv::value(*)(std::string_view, std::string_view); + +static std::map data_decoders { + {fs::u8path(".json"), json::parse}, + {fs::u8path(".toml"), toml::parse}, +}; + +bool files::is_data_file(const fs::path& file) { + return is_data_interchange_format(file.extension()); +} + +bool files::is_data_interchange_format(const fs::path& ext) { + return data_decoders.find(ext) != data_decoders.end(); +} + +dv::value files::read_object(const fs::path& file) { + const auto& found = data_decoders.find(file.extension()); + if (found == data_decoders.end()) { + throw std::runtime_error("unknown file format"); + } + auto text = read_string(file); + try { + return found->second(file.u8string(), text); + } catch (const parsing_error& err) { + throw std::runtime_error(err.errorLog()); + } +} diff --git a/src/files/files.hpp b/src/files/files.hpp index d6827dae..3c602b66 100644 --- a/src/files/files.hpp +++ b/src/files/files.hpp @@ -66,4 +66,8 @@ namespace files { dv::value read_binary_json(const fs::path& file); dv::value read_toml(const fs::path& file); std::vector read_list(const fs::path& file); + + bool is_data_file(const fs::path& file); + bool is_data_interchange_format(const fs::path& ext); + dv::value read_object(const fs::path& file); } diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index 3f4ada53..aad1bd9e 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -33,6 +33,7 @@ class LevelController; class Entity; struct EntityDef; class GeneratorScript; +struct GeneratorDef; namespace scripting { extern Engine* engine; @@ -147,6 +148,7 @@ namespace scripting { void load_entity_component(const std::string& name, const fs::path& file); std::unique_ptr load_generator( + const GeneratorDef& def, const fs::path& file ); diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index da6e63a4..fe0bd8de 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -16,20 +16,17 @@ #include "util/timeutil.hpp" class LuaGeneratorScript : public GeneratorScript { + const GeneratorDef& def; scriptenv env; std::vector biomes; - uint biomeParameters; - uint seaLevel; public: LuaGeneratorScript( - scriptenv env, - std::vector biomes, - uint biomeParameters, - uint seaLevel) - : env(std::move(env)), - biomes(std::move(biomes)), - biomeParameters(biomeParameters), - seaLevel(seaLevel) + const GeneratorDef& def, + scriptenv env, + std::vector biomes) + : def(def), + env(std::move(env)), + biomes(std::move(biomes)) {} std::shared_ptr generateHeightmap( @@ -56,6 +53,7 @@ public: ) override { std::vector> maps; + uint biomeParameters = def.biomeParameters; auto L = lua::get_main_thread(); lua::pushenv(L, *env); if (lua::getfield(L, "generate_biome_parameters")) { @@ -79,7 +77,6 @@ public: } std::vector placeStructures( - const GeneratorDef& def, const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap ) override { @@ -153,14 +150,6 @@ public: const std::vector& getBiomes() const override { return biomes; } - - uint getBiomeParameters() const override { - return biomeParameters; - } - - uint getSeaLevel() const override { - return seaLevel; - } }; static BlocksLayer load_layer( @@ -273,7 +262,7 @@ static inline Biome load_biome( } std::unique_ptr scripting::load_generator( - const fs::path& file + const GeneratorDef& def, const fs::path& file ) { auto env = create_environment(); auto L = lua::get_main_thread(); @@ -285,24 +274,20 @@ std::unique_ptr scripting::load_generator( auto root = lua::tovalue(L, -1); lua::pop(L); - uint biomeParameters = root["biome_parameters"].asInteger(); - uint seaLevel = 0; - root.at("sea_level").get(seaLevel); - std::vector biomes; const auto& biomesMap = root["biomes"]; for (const auto& [biomeName, biomeMap] : biomesMap.asObject()) { try { biomes.push_back( - load_biome(biomeMap, biomeName, biomeParameters, -2)); + load_biome(biomeMap, biomeName, def.biomeParameters, -2)); } catch (const std::runtime_error& err) { throw std::runtime_error("biome "+biomeName+": "+err.what()); } } return std::make_unique( + def, std::move(env), - std::move(biomes), - biomeParameters, - seaLevel); + std::move(biomes) + ); } diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index bf6934b0..8311e15f 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -135,19 +135,12 @@ public: const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; virtual std::vector placeStructures( - const GeneratorDef& def, const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap) = 0; /// @brief Get generator biomes virtual const std::vector& getBiomes() const = 0; - /// @return Number of biome parameters, that biome choosing depending on - virtual uint getBiomeParameters() const = 0; - - /// @return Sea level (top of seaLayers) - virtual uint getSeaLevel() const = 0; - /// @brief Build the runtime cache /// @param content built content virtual void prepare(const GeneratorDef& def, const Content* content) = 0; @@ -169,6 +162,12 @@ struct GeneratorDef { std::string name; std::unique_ptr script; + /// @brief Sea level (top of seaLayers) + uint seaLevel = 0; + + /// @brief Number of biome parameters, that biome choosing depending on + uint biomeParameters = 0; + std::unordered_map structuresIndices; std::vector> structures; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 596a66fc..e1c32c65 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -175,7 +175,7 @@ void WorldGenerator::generateStructures( const auto& heightmap = prototype.heightmap; util::concat(prototype.structures, def.script->placeStructures( - def, {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, + {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, heightmap )); for (const auto& placement : prototype.structures) { @@ -202,7 +202,7 @@ void WorldGenerator::generateStructures( } uint8_t rotation = structsRand.randU32() % 4; int height = heights[z * CHUNK_W + x] * CHUNK_H; - if (height < def.script->getSeaLevel()) { + if (height < def.seaLevel) { continue; } auto& structure = *def.structures[structureId]->fragments[rotation]; @@ -268,7 +268,7 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { const auto& prototype = requirePrototype(chunkX, chunkZ); const auto values = prototype.heightmap->getValues(); - uint seaLevel = def.script->getSeaLevel(); + uint seaLevel = def.seaLevel; std::memset(voxels, 0, sizeof(voxel) * CHUNK_VOL); From 39030efd742224dc96d876239440329452bec4ab Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 27 Sep 2024 04:21:35 +0300 Subject: [PATCH 088/139] refactor ContentLoader --- src/content/ContentLoader.cpp | 157 ++++++++++++++++++---------------- src/content/ContentLoader.hpp | 3 + 2 files changed, 87 insertions(+), 73 deletions(-) diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 7cdb3a96..93a3aa6b 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -93,14 +93,14 @@ bool ContentLoader::fixPackIndices( void ContentLoader::fixPackIndices() { auto folder = pack->folder; - auto indexFile = pack->getContentFile(); + auto contentFile = pack->getContentFile(); auto blocksFolder = folder / ContentPack::BLOCKS_FOLDER; auto itemsFolder = folder / ContentPack::ITEMS_FOLDER; auto entitiesFolder = folder / ContentPack::ENTITIES_FOLDER; dv::value root; - if (fs::is_regular_file(indexFile)) { - root = files::read_json(indexFile); + if (fs::is_regular_file(contentFile)) { + root = files::read_json(contentFile); } else { root = dv::object(); } @@ -112,7 +112,7 @@ void ContentLoader::fixPackIndices() { if (modified) { // rewrite modified json - files::write_json(indexFile, root); + files::write_json(contentFile, root); } } @@ -493,56 +493,7 @@ void ContentLoader::loadBlockMaterial( root.at("break-sound").get(def.breakSound); } -void ContentLoader::load() { - logger.info() << "loading pack [" << pack->id << "]"; - - fixPackIndices(); - - auto folder = pack->folder; - - fs::path scriptFile = folder / fs::path("scripts/world.lua"); - if (fs::is_regular_file(scriptFile)) { - scripting::load_world_script( - env, pack->id, scriptFile, runtime->worldfuncsset - ); - } - - fs::path generatorsDir = folder / fs::u8path("generators"); - if (fs::is_directory(generatorsDir)) { - for (const auto& entry : fs::directory_iterator(generatorsDir)) { - const auto& file = entry.path(); - if (fs::is_directory(file)) { - continue; - } - - std::string name = file.stem().u8string(); - auto [packid, full, filename] = - create_unit_id(pack->id, file.stem().u8string()); - - auto& def = builder.generators.create(full); - try { - loadGenerator(def, full, name); - } catch (const std::runtime_error& err) { - throw std::runtime_error("generator '"+full+"': "+err.what()); - } - } - } - - fs::path resourcesFile = folder / fs::u8path("resources.json"); - if (fs::exists(resourcesFile)) { - auto resRoot = files::read_json(resourcesFile); - for (const auto& [key, arr] : resRoot.asObject()) { - if (auto resType = ResourceType_from(key)) { - loadResources(*resType, arr); - } else { - logger.warning() << "unknown resource type: " << key; - } - } - } - - if (!fs::is_regular_file(pack->getContentFile())) return; - - auto root = files::read_json(pack->getContentFile()); +void ContentLoader::loadContent(const dv::value& root) { std::vector> pendingDefs; auto getJsonParent = [this](const std::string& prefix, const std::string& name) { auto configFile = pack->folder / fs::path(prefix + "/" + name + ".json"); @@ -699,8 +650,68 @@ void ContentLoader::load() { ); } } +} - fs::path materialsDir = folder / fs::u8path("block_materials"); +static inline void foreach_file( + const fs::path& dir, std::function handler +) { + if (fs::is_directory(dir)) { + for (const auto& entry : fs::directory_iterator(dir)) { + const auto& path = entry.path(); + if (fs::is_directory(path)) { + continue; + } + handler(path); + } + } +} + +void ContentLoader::load() { + logger.info() << "loading pack [" << pack->id << "]"; + + fixPackIndices(); + + auto folder = pack->folder; + + // Load main world script + fs::path scriptFile = folder / fs::path("scripts/world.lua"); + if (fs::is_regular_file(scriptFile)) { + scripting::load_world_script( + env, pack->id, scriptFile, runtime->worldfuncsset + ); + } + + // Load world generators + fs::path generatorsDir = folder / fs::u8path("generators"); + foreach_file(generatorsDir, [this](const fs::path& file) { + std::string name = file.stem().u8string(); + auto [packid, full, filename] = + create_unit_id(pack->id, file.stem().u8string()); + + auto& def = builder.generators.create(full); + try { + loadGenerator(def, full, name); + } catch (const std::runtime_error& err) { + throw std::runtime_error("generator '"+full+"': "+err.what()); + } + }); + + // Load pack resources.json + fs::path resourcesFile = folder / fs::u8path("resources.json"); + if (fs::exists(resourcesFile)) { + auto resRoot = files::read_json(resourcesFile); + for (const auto& [key, arr] : resRoot.asObject()) { + if (auto resType = ResourceType_from(key)) { + loadResources(*resType, arr); + } else { + // Ignore unknown resources + logger.warning() << "unknown resource type: " << key; + } + } + } + + // Load block materials + fs::path materialsDir = folder / fs::u8path("block_materials"); if (fs::is_directory(materialsDir)) { for (const auto& entry : fs::directory_iterator(materialsDir)) { const auto& file = entry.path(); @@ -713,27 +724,27 @@ void ContentLoader::load() { } } + // Load skeletons fs::path skeletonsDir = folder / fs::u8path("skeletons"); - if (fs::is_directory(skeletonsDir)) { - for (const auto& entry : fs::directory_iterator(skeletonsDir)) { - const auto& file = entry.path(); - std::string name = pack->id + ":" + file.stem().u8string(); - std::string text = files::read_string(file); - builder.add( - rigging::SkeletonConfig::parse(text, file.u8string(), name) - ); - } - } + foreach_file(skeletonsDir, [this](const fs::path& file) { + std::string name = pack->id + ":" + file.stem().u8string(); + std::string text = files::read_string(file); + builder.add( + rigging::SkeletonConfig::parse(text, file.u8string(), name) + ); + }); + // Load entity components fs::path componentsDir = folder / fs::u8path("scripts/components"); - if (fs::is_directory(componentsDir)) { - for (const auto& entry : fs::directory_iterator(componentsDir)) { - fs::path scriptfile = entry.path(); - if (fs::is_regular_file(scriptfile)) { - auto name = pack->id + ":" + scriptfile.stem().u8string(); - scripting::load_entity_component(name, scriptfile); - } - } + foreach_file(componentsDir, [this](const fs::path& file) { + auto name = pack->id + ":" + file.stem().u8string(); + scripting::load_entity_component(name, file); + }); + + // Process content.json and load defined content units + auto contentFile = pack->getContentFile(); + if (fs::exists(contentFile)) { + loadContent(files::read_json(contentFile)); } } diff --git a/src/content/ContentLoader.hpp b/src/content/ContentLoader.hpp index ae01a955..b252d2a8 100644 --- a/src/content/ContentLoader.hpp +++ b/src/content/ContentLoader.hpp @@ -52,9 +52,12 @@ class ContentLoader { EntityDef& def, const std::string& name, const fs::path& file ); void loadResources(ResourceType type, const dv::value& list); + + void loadContent(const dv::value& map); public: ContentLoader(ContentPack* pack, ContentBuilder& builder); + // Refresh pack content.json static bool fixPackIndices( const fs::path& folder, dv::value& indicesRoot, From c46090f881e1c7d85d9c9b864ff38bdf1eb1aac6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 27 Sep 2024 23:55:14 +0300 Subject: [PATCH 089/139] update generators scanning --- .../{demo => demo.files}/biomes.json | 0 .../fragments/coal_ore0.vox | Bin .../{demo => demo.files}/fragments/tower.vox | Bin .../{demo => demo.files}/fragments/tree0.vox | Bin .../{demo => demo.files}/fragments/tree1.vox | Bin .../{demo => demo.files}/fragments/tree2.vox | Bin .../{demo => demo.files}/script.lua | 2 +- .../{demo => demo.files}/structures.json | 0 .../{default => default.files}/script.lua | 0 src/content/ContentLoader.cpp | 11 ++++++++- src/content/ContentLoader.hpp | 5 ++++ src/content/ContentPack.hpp | 12 +++++++++ src/content/loading/GeneratorLoader.cpp | 2 +- src/logic/scripting/lua/libcore.cpp | 23 +++++++++++++----- 14 files changed, 46 insertions(+), 9 deletions(-) rename res/content/base/generators/{demo => demo.files}/biomes.json (100%) rename res/content/base/generators/{demo => demo.files}/fragments/coal_ore0.vox (100%) rename res/content/base/generators/{demo => demo.files}/fragments/tower.vox (100%) rename res/content/base/generators/{demo => demo.files}/fragments/tree0.vox (100%) rename res/content/base/generators/{demo => demo.files}/fragments/tree1.vox (100%) rename res/content/base/generators/{demo => demo.files}/fragments/tree2.vox (100%) rename res/content/base/generators/{demo => demo.files}/script.lua (96%) rename res/content/base/generators/{demo => demo.files}/structures.json (100%) rename res/generators/{default => default.files}/script.lua (100%) diff --git a/res/content/base/generators/demo/biomes.json b/res/content/base/generators/demo.files/biomes.json similarity index 100% rename from res/content/base/generators/demo/biomes.json rename to res/content/base/generators/demo.files/biomes.json diff --git a/res/content/base/generators/demo/fragments/coal_ore0.vox b/res/content/base/generators/demo.files/fragments/coal_ore0.vox similarity index 100% rename from res/content/base/generators/demo/fragments/coal_ore0.vox rename to res/content/base/generators/demo.files/fragments/coal_ore0.vox diff --git a/res/content/base/generators/demo/fragments/tower.vox b/res/content/base/generators/demo.files/fragments/tower.vox similarity index 100% rename from res/content/base/generators/demo/fragments/tower.vox rename to res/content/base/generators/demo.files/fragments/tower.vox diff --git a/res/content/base/generators/demo/fragments/tree0.vox b/res/content/base/generators/demo.files/fragments/tree0.vox similarity index 100% rename from res/content/base/generators/demo/fragments/tree0.vox rename to res/content/base/generators/demo.files/fragments/tree0.vox diff --git a/res/content/base/generators/demo/fragments/tree1.vox b/res/content/base/generators/demo.files/fragments/tree1.vox similarity index 100% rename from res/content/base/generators/demo/fragments/tree1.vox rename to res/content/base/generators/demo.files/fragments/tree1.vox diff --git a/res/content/base/generators/demo/fragments/tree2.vox b/res/content/base/generators/demo.files/fragments/tree2.vox similarity index 100% rename from res/content/base/generators/demo/fragments/tree2.vox rename to res/content/base/generators/demo.files/fragments/tree2.vox diff --git a/res/content/base/generators/demo/script.lua b/res/content/base/generators/demo.files/script.lua similarity index 96% rename from res/content/base/generators/demo/script.lua rename to res/content/base/generators/demo.files/script.lua index 7744882f..a254c664 100644 --- a/res/content/base/generators/demo/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -1,4 +1,4 @@ -biomes = json.parse(file.read("base:generators/demo/biomes.json")) +biomes = json.parse(file.read("base:generators/demo.files/biomes.json")) function place_structures(x, z, w, d, seed, hmap) local placements = {} diff --git a/res/content/base/generators/demo/structures.json b/res/content/base/generators/demo.files/structures.json similarity index 100% rename from res/content/base/generators/demo/structures.json rename to res/content/base/generators/demo.files/structures.json diff --git a/res/generators/default/script.lua b/res/generators/default.files/script.lua similarity index 100% rename from res/generators/default/script.lua rename to res/generators/default.files/script.lua diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 93a3aa6b..bd811074 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -51,13 +51,22 @@ static void detect_defs( } if (fs::is_regular_file(file) && files::is_data_file(file)) { detected.push_back(prefix.empty() ? name : prefix + ":" + name); - } else if (fs::is_directory(file)) { + } else if (fs::is_directory(file) && + file.extension() != fs::u8path(".files")) { detect_defs(file, name, detected); } } } } +std::vector ContentLoader::scanContent( + const ContentPack& pack, ContentType type +) { + std::vector detected; + detect_defs(pack.folder / ContentPack::getFolderFor(type), pack.id, detected); + return detected; +} + bool ContentLoader::fixPackIndices( const fs::path& folder, dv::value& indicesRoot, diff --git a/src/content/ContentLoader.hpp b/src/content/ContentLoader.hpp index b252d2a8..762b3fbe 100644 --- a/src/content/ContentLoader.hpp +++ b/src/content/ContentLoader.hpp @@ -63,6 +63,11 @@ public: dv::value& indicesRoot, const std::string& contentSection ); + + static std::vector scanContent( + const ContentPack& pack, ContentType type + ); + void fixPackIndices(); void load(); }; diff --git a/src/content/ContentPack.hpp b/src/content/ContentPack.hpp index 7815d009..7b7b19e8 100644 --- a/src/content/ContentPack.hpp +++ b/src/content/ContentPack.hpp @@ -6,6 +6,7 @@ #include #include "typedefs.hpp" +#include "content_fwd.hpp" class EnginePaths; @@ -51,6 +52,7 @@ struct ContentPack { static inline const fs::path BLOCKS_FOLDER = "blocks"; static inline const fs::path ITEMS_FOLDER = "items"; static inline const fs::path ENTITIES_FOLDER = "entities"; + static inline const fs::path GENERATORS_FOLDER = "generators"; static const std::vector RESERVED_NAMES; static bool is_pack(const fs::path& folder); @@ -69,6 +71,16 @@ struct ContentPack { ); static ContentPack createCore(const EnginePaths*); + + static inline fs::path getFolderFor(ContentType type) { + switch (type) { + case ContentType::BLOCK: return ContentPack::BLOCKS_FOLDER; + case ContentType::ITEM: return ContentPack::ITEMS_FOLDER; + case ContentType::ENTITY: return ContentPack::ENTITIES_FOLDER; + case ContentType::GENERATOR: return ContentPack::GENERATORS_FOLDER; + case ContentType::NONE: return fs::u8path(""); + } + } }; struct ContentPackStats { diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index 5342c081..28ff2f30 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -74,7 +74,7 @@ void ContentLoader::loadGenerator( map.at("biome_parameters").get(def.biomeParameters); map.at("sea_level").get(def.seaLevel); - auto folder = generatorsDir / fs::u8path(name); + auto folder = generatorsDir / fs::u8path(name + ".files"); auto scriptFile = folder / fs::u8path("script.lua"); auto structuresFile = folder / STRUCTURES_FILE; diff --git a/src/logic/scripting/lua/libcore.cpp b/src/logic/scripting/lua/libcore.cpp index 38267e4d..5f04ff64 100644 --- a/src/logic/scripting/lua/libcore.cpp +++ b/src/logic/scripting/lua/libcore.cpp @@ -4,6 +4,7 @@ #include "constants.hpp" #include "engine.hpp" #include "content/Content.hpp" +#include "content/ContentLoader.hpp" #include "files/engine_paths.hpp" #include "files/settings_io.hpp" #include "frontend/menu.hpp" @@ -14,6 +15,8 @@ #include "window/Window.hpp" #include "world/generator/WorldGenerator.hpp" #include "world/Level.hpp" +#include "util/listutil.hpp" + #include "api_lua.hpp" using namespace scripting; @@ -180,18 +183,26 @@ static int l_get_default_generator(lua::State* L) { /// @brief Get a list of all world generators /// @return A table with the IDs of all world generators static int l_get_generators(lua::State* L) { - if (content == nullptr) { - throw std::runtime_error("content is not initialized"); - } - const auto& generators = content->generators.getDefs(); - lua::createtable(L, generators.size(), 0); + const auto& packs = engine->getContentPacks(); + + lua::createtable(L, 0, 0); int i = 0; - for (auto& [name, _] : generators) { + auto names = ContentLoader::scanContent( + ContentPack::createCore(engine->getPaths()), ContentType::GENERATOR); + for (const auto& name : names) { lua::pushstring(L, name); lua::rawseti(L, i + 1); i++; } + for (const auto& pack : packs) { + auto names = ContentLoader::scanContent(pack, ContentType::GENERATOR); + for (const auto& name : names) { + lua::pushstring(L, name); + lua::rawseti(L, i + 1); + i++; + } + } return 1; } From 50c73082119192c0bfef77b345258269514fb4f9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 28 Sep 2024 14:23:20 +0300 Subject: [PATCH 090/139] move core.get_generators to generation.get_generators --- res/layouts/pages/generators.xml.lua | 2 +- src/engine.cpp | 6 +++++ src/engine.hpp | 2 ++ src/logic/scripting/lua/libcore.cpp | 28 ----------------------- src/logic/scripting/lua/libgeneration.cpp | 21 +++++++++++++++++ 5 files changed, 30 insertions(+), 29 deletions(-) diff --git a/res/layouts/pages/generators.xml.lua b/res/layouts/pages/generators.xml.lua index dd9b6743..04c1d8bb 100644 --- a/res/layouts/pages/generators.xml.lua +++ b/res/layouts/pages/generators.xml.lua @@ -1,7 +1,7 @@ settings = session.get_entry('new_world') function on_open() - local names = core.get_generators() + local names = generation.get_generators() table.sort(names) local panel = document.root diff --git a/src/engine.cpp b/src/engine.cpp index 6a98e1d3..7bb847d9 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -398,6 +398,12 @@ const Content* Engine::getContent() const { return content.get(); } +std::vector Engine::getAllContentPacks() { + auto packs = getContentPacks(); + packs.insert(packs.begin(), ContentPack::createCore(paths)); + return packs; +} + std::vector& Engine::getContentPacks() { return contentPacks; } diff --git a/src/engine.hpp b/src/engine.hpp index de25c220..75b9a1b7 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -130,6 +130,8 @@ public: /// @brief Get selected content packs std::vector& getContentPacks(); + std::vector getAllContentPacks(); + std::vector& getBasePacks(); /// @brief Get current screen diff --git a/src/logic/scripting/lua/libcore.cpp b/src/logic/scripting/lua/libcore.cpp index 5f04ff64..84033656 100644 --- a/src/logic/scripting/lua/libcore.cpp +++ b/src/logic/scripting/lua/libcore.cpp @@ -4,7 +4,6 @@ #include "constants.hpp" #include "engine.hpp" #include "content/Content.hpp" -#include "content/ContentLoader.hpp" #include "files/engine_paths.hpp" #include "files/settings_io.hpp" #include "frontend/menu.hpp" @@ -180,32 +179,6 @@ static int l_get_default_generator(lua::State* L) { return lua::pushstring(L, WorldGenerator::DEFAULT); } -/// @brief Get a list of all world generators -/// @return A table with the IDs of all world generators -static int l_get_generators(lua::State* L) { - const auto& packs = engine->getContentPacks(); - - lua::createtable(L, 0, 0); - - int i = 0; - auto names = ContentLoader::scanContent( - ContentPack::createCore(engine->getPaths()), ContentType::GENERATOR); - for (const auto& name : names) { - lua::pushstring(L, name); - lua::rawseti(L, i + 1); - i++; - } - for (const auto& pack : packs) { - auto names = ContentLoader::scanContent(pack, ContentType::GENERATOR); - for (const auto& name : names) { - lua::pushstring(L, name); - lua::rawseti(L, i + 1); - i++; - } - } - return 1; -} - const luaL_Reg corelib[] = { {"new_world", lua::wrap}, {"open_world", lua::wrap}, @@ -219,5 +192,4 @@ const luaL_Reg corelib[] = { {"get_setting_info", lua::wrap}, {"quit", lua::wrap}, {"get_default_generator", lua::wrap}, - {"get_generators", lua::wrap}, {NULL, NULL}}; diff --git a/src/logic/scripting/lua/libgeneration.cpp b/src/logic/scripting/lua/libgeneration.cpp index 2f5e4851..ec425345 100644 --- a/src/logic/scripting/lua/libgeneration.cpp +++ b/src/logic/scripting/lua/libgeneration.cpp @@ -5,6 +5,7 @@ #include "coders/binary_json.hpp" #include "world/Level.hpp" #include "world/generator/VoxelFragment.hpp" +#include "content/ContentLoader.hpp" #include "engine.hpp" #include "lua_custom_types.hpp" @@ -42,7 +43,27 @@ static int l_load_structure(lua::State* L) { return lua::newuserdata(L, std::move(structure)); } +/// @brief Get a list of all world generators +/// @return A table with the IDs of all world generators +static int l_get_generators(lua::State* L) { + auto packs = engine->getAllContentPacks(); + + lua::createtable(L, 0, 0); + + int i = 1; + for (const auto& pack : packs) { + auto names = ContentLoader::scanContent(pack, ContentType::GENERATOR); + for (const auto& name : names) { + lua::pushstring(L, name); + lua::rawseti(L, i); + i++; + } + } + return 1; +} + const luaL_Reg generationlib[] = { {"save_structure", lua::wrap}, {"load_structure", lua::wrap}, + {"get_generators", lua::wrap}, {NULL, NULL}}; From d62d66cf8528eaaa6788ad8f9e0af0e362d18e9c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 29 Sep 2024 21:37:49 +0300 Subject: [PATCH 091/139] make core:obstacle unbreakable --- src/core_defs.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core_defs.cpp b/src/core_defs.cpp index b56b3cc8..5fe46341 100644 --- a/src/core_defs.cpp +++ b/src/core_defs.cpp @@ -40,6 +40,7 @@ void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) { block.textureFaces[i] = "obstacle"; } block.hitboxes = {AABB()}; + block.breakable = false; ItemDef& item = builder->items.create(CORE_OBSTACLE+".item"); item.iconType = item_icon_type::block; item.icon = CORE_OBSTACLE; From 3784b57eeddb6a597ff415fe1ecf815a20274493 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 29 Sep 2024 22:03:37 +0300 Subject: [PATCH 092/139] move biomes definition to a json file --- .../base/generators/demo.files/script.lua | 6 - res/generators/default.files/biomes.json | 11 ++ res/generators/default.files/script.lua | 9 - src/content/ContentBuilder.cpp | 2 +- src/content/loading/GeneratorLoader.cpp | 129 ++++++++++++++ src/logic/scripting/lua/libpack.cpp | 8 +- .../scripting/scripting_world_generation.cpp | 165 ++---------------- src/world/generator/GeneratorDef.cpp | 25 +++ src/world/generator/GeneratorDef.hpp | 10 +- src/world/generator/WorldGenerator.cpp | 2 +- 10 files changed, 183 insertions(+), 184 deletions(-) create mode 100644 res/generators/default.files/biomes.json delete mode 100644 res/generators/default.files/script.lua diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua index a254c664..8a108964 100644 --- a/res/content/base/generators/demo.files/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -1,5 +1,3 @@ -biomes = json.parse(file.read("base:generators/demo.files/biomes.json")) - function place_structures(x, z, w, d, seed, hmap) local placements = {} for i=1,10 do @@ -41,10 +39,6 @@ end function generate_heightmap(x, y, w, h, seed) -- blocks per dot - -- 8 - linear interpolation is visible, but not so much - -- 4 - high quality, but slower - -- 2 - you really don't need it - -- 1 - please have mercy on your CPU local bpd = 4 local map = _generate_heightmap( math.floor(x/bpd), math.floor(y/bpd), diff --git a/res/generators/default.files/biomes.json b/res/generators/default.files/biomes.json new file mode 100644 index 00000000..baf0bf9b --- /dev/null +++ b/res/generators/default.files/biomes.json @@ -0,0 +1,11 @@ +{ + "flat": { + "parameters": [], + "layers": [ + {"height": -1, "block": "core:obstacle"} + ], + "sea_layers": [ + {"height": -1, "block": "core:obstacle"} + ] + } +} diff --git a/res/generators/default.files/script.lua b/res/generators/default.files/script.lua deleted file mode 100644 index e949a610..00000000 --- a/res/generators/default.files/script.lua +++ /dev/null @@ -1,9 +0,0 @@ -biomes = {flat = { - parameters = {}, - layers = { - {height=-1, block="core:obstacle"}, - }, - sea_layers = { - {height=-1, block="core:obstacle"} - } -}} diff --git a/src/content/ContentBuilder.cpp b/src/content/ContentBuilder.cpp index 2ce7a963..60dbf36e 100644 --- a/src/content/ContentBuilder.cpp +++ b/src/content/ContentBuilder.cpp @@ -89,7 +89,7 @@ std::unique_ptr ContentBuilder::build() { } for (auto& [name, def] : content->generators.getDefs()) { - def->script->prepare(*def, content.get()); + def->prepare(content.get()); } return content; diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index 28ff2f30..90cbc4c1 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -8,8 +8,116 @@ #include "world/generator/VoxelFragment.hpp" #include "debug/Logger.hpp" +static BlocksLayer load_layer( + const dv::value& map, uint& lastLayersHeight, bool& hasResizeableLayer +) { + const auto& name = map["block"].asString(); + int height = map["height"].asInteger(); + bool belowSeaLevel = true; + map.at("below_sea_level").get(belowSeaLevel); + + if (hasResizeableLayer) { + lastLayersHeight += height; + } + if (height == -1) { + if (hasResizeableLayer) { + throw std::runtime_error("only one resizeable layer allowed"); + } + hasResizeableLayer = true; + } + return BlocksLayer {name, height, belowSeaLevel, {}}; +} + +static inline BlocksLayers load_layers( + const dv::value& layersArr, const std::string& fieldname +) { + uint lastLayersHeight = 0; + bool hasResizeableLayer = false; + std::vector layers; + + for (int i = 0; i < layersArr.size(); i++) { + const auto& layerMap = layersArr[i]; + try { + layers.push_back( + load_layer(layerMap, lastLayersHeight, hasResizeableLayer)); + } catch (const std::runtime_error& err) { + throw std::runtime_error( + fieldname+" #"+std::to_string(i)+": "+err.what()); + } + } + return BlocksLayers {std::move(layers), lastLayersHeight}; +} + +static inline BiomeElementList load_biome_element_list( + const dv::value map, + const std::string& chanceName, + const std::string& arrName, + const std::string& nameName +) { + float chance = 0.0f; + map.at(chanceName).get(chance); + std::vector entries; + if (map.has(arrName)) { + const auto& arr = map[arrName]; + for (const auto& entry : arr) { + const auto& name = entry[nameName].asString(); + float weight = entry["weight"].asNumber(); + if (weight <= 0.0f) { + throw std::runtime_error("weight must be positive"); + } + entries.push_back(WeightedEntry {name, weight, {}}); + } + } + std::sort(entries.begin(), entries.end(), std::greater()); + return BiomeElementList(std::move(entries), chance); +} + +static inline BiomeElementList load_plants(const dv::value& biomeMap) { + return load_biome_element_list(biomeMap, "plant_chance", "plants", "block"); +} + +static inline BiomeElementList load_structures(const dv::value map) { + return load_biome_element_list(map, "structure_chance", "structures", "name"); +} + static debug::Logger logger("generator-loader"); +static inline Biome load_biome( + const dv::value& biomeMap, + const std::string& name, + uint parametersCount +) { + std::vector parameters; + + const auto& paramsArr = biomeMap["parameters"]; + if (paramsArr.size() < parametersCount) { + throw std::runtime_error( + std::to_string(parametersCount)+" parameters expected"); + } + for (size_t i = 0; i < parametersCount; i++) { + const auto& paramMap = paramsArr[i]; + float value = paramMap["value"].asNumber(); + float weight = paramMap["weight"].asNumber(); + parameters.push_back(BiomeParameter {value, weight}); + } + + auto plants = load_plants(biomeMap); + auto groundLayers = load_layers(biomeMap["layers"], "layers"); + auto seaLayers = load_layers(biomeMap["sea_layers"], "sea_layers"); + + BiomeElementList structures; + if (biomeMap.has("structures")) { + structures = load_structures(biomeMap); + } + return Biome { + name, + std::move(parameters), + std::move(plants), + std::move(structures), + std::move(groundLayers), + std::move(seaLayers)}; +} + static VoxelStructureMeta load_structure_meta( const std::string& name, const dv::value& config ) { @@ -59,8 +167,21 @@ static void load_structures(GeneratorDef& def, const fs::path& structuresFile) { } static inline const auto STRUCTURES_FILE = fs::u8path("structures.json"); +static inline const auto BIOMES_FILE = fs::u8path("biomes.json"); static inline const auto GENERATORS_DIR = fs::u8path("generators"); +static void load_biomes(GeneratorDef& def, const fs::path& file) { + auto root = files::read_json(file); + for (const auto& [biomeName, biomeMap] : root.asObject()) { + try { + def.biomes.push_back( + load_biome(biomeMap, biomeName, def.biomeParameters)); + } catch (const std::runtime_error& err) { + throw std::runtime_error("biome "+biomeName+": "+err.what()); + } + } +} + void ContentLoader::loadGenerator( GeneratorDef& def, const std::string& full, const std::string& name ) { @@ -81,5 +202,13 @@ void ContentLoader::loadGenerator( if (fs::exists(structuresFile)) { load_structures(def, structuresFile); } + auto biomesFiles = folder / BIOMES_FILE; + if (!fs::exists(biomesFiles)) { + throw std::runtime_error( + BIOMES_FILE.u8string() + + ": file not found (at least one biome required)" + ); + } + load_biomes(def, biomesFiles); def.script = scripting::load_generator(def, scriptFile); } diff --git a/src/logic/scripting/lua/libpack.cpp b/src/logic/scripting/lua/libpack.cpp index bc9bb204..fdd9ae8a 100644 --- a/src/logic/scripting/lua/libpack.cpp +++ b/src/logic/scripting/lua/libpack.cpp @@ -16,11 +16,9 @@ using namespace scripting; static int l_pack_get_folder(lua::State* L) { std::string packName = lua::tostring(L, 1); - if (packName == "core") { - auto folder = engine->getPaths()->getResourcesFolder().u8string() + "/"; - return lua::pushstring(L, folder); - } - for (auto& pack : engine->getContentPacks()) { + auto packs = engine->getAllContentPacks(); + + for (auto& pack : packs) { if (pack.id == packName) { return lua::pushstring(L, pack.folder.u8string() + "/"); } diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index fe0bd8de..28082540 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -12,21 +12,17 @@ #include "voxels/Chunk.hpp" #include "data/dv.hpp" #include "world/generator/GeneratorDef.hpp" -#include "util/stringutil.hpp" #include "util/timeutil.hpp" class LuaGeneratorScript : public GeneratorScript { const GeneratorDef& def; scriptenv env; - std::vector biomes; public: LuaGeneratorScript( const GeneratorDef& def, - scriptenv env, - std::vector biomes) + scriptenv env) : def(def), - env(std::move(env)), - biomes(std::move(biomes)) + env(std::move(env)) {} std::shared_ptr generateHeightmap( @@ -124,143 +120,8 @@ public: } return placements; } - - void prepare(const GeneratorDef& def, const Content* content) override { - for (auto& biome : biomes) { - for (auto& layer : biome.groundLayers.layers) { - layer.rt.id = content->blocks.require(layer.block).rt.id; - } - for (auto& layer : biome.seaLayers.layers) { - layer.rt.id = content->blocks.require(layer.block).rt.id; - } - for (auto& plant : biome.plants.entries) { - plant.rt.id = content->blocks.require(plant.name).rt.id; - } - for (auto& structure : biome.structures.entries) { - const auto& found = def.structuresIndices.find(structure.name); - if (found == def.structuresIndices.end()) { - throw std::runtime_error( - "no structure "+util::quote(structure.name)+" found"); - } - structure.rt.id = found->second; - } - } - } - - const std::vector& getBiomes() const override { - return biomes; - } }; -static BlocksLayer load_layer( - const dv::value& map, uint& lastLayersHeight, bool& hasResizeableLayer -) { - const auto& name = map["block"].asString(); - int height = map["height"].asInteger(); - bool belowSeaLevel = true; - map.at("below_sea_level").get(belowSeaLevel); - - if (hasResizeableLayer) { - lastLayersHeight += height; - } - if (height == -1) { - if (hasResizeableLayer) { - throw std::runtime_error("only one resizeable layer allowed"); - } - hasResizeableLayer = true; - } - return BlocksLayer {name, height, belowSeaLevel, {}}; -} - -static inline BlocksLayers load_layers( - const dv::value& layersArr, const std::string& fieldname -) { - uint lastLayersHeight = 0; - bool hasResizeableLayer = false; - std::vector layers; - - for (int i = 0; i < layersArr.size(); i++) { - const auto& layerMap = layersArr[i]; - try { - layers.push_back( - load_layer(layerMap, lastLayersHeight, hasResizeableLayer)); - } catch (const std::runtime_error& err) { - throw std::runtime_error( - fieldname+" #"+std::to_string(i)+": "+err.what()); - } - } - return BlocksLayers {std::move(layers), lastLayersHeight}; -} - -static inline BiomeElementList load_biome_element_list( - const dv::value map, - const std::string& chanceName, - const std::string& arrName, - const std::string& nameName -) { - float chance = 0.0f; - map.at(chanceName).get(chance); - std::vector entries; - if (map.has(arrName)) { - const auto& arr = map[arrName]; - for (const auto& entry : arr) { - const auto& name = entry[nameName].asString(); - float weight = entry["weight"].asNumber(); - if (weight <= 0.0f) { - throw std::runtime_error("weight must be positive"); - } - entries.push_back(WeightedEntry {name, weight, {}}); - } - } - std::sort(entries.begin(), entries.end(), std::greater()); - return BiomeElementList(std::move(entries), chance); -} - -static inline BiomeElementList load_plants(const dv::value& biomeMap) { - return load_biome_element_list(biomeMap, "plant_chance", "plants", "block"); -} - -static inline BiomeElementList load_structures(const dv::value map) { - return load_biome_element_list(map, "structure_chance", "structures", "name"); -} - -static inline Biome load_biome( - const dv::value& biomeMap, - const std::string& name, - uint parametersCount, - int idx -) { - std::vector parameters; - - const auto& paramsArr = biomeMap["parameters"]; - if (paramsArr.size() < parametersCount) { - throw std::runtime_error( - std::to_string(parametersCount)+" parameters expected"); - } - for (size_t i = 0; i < parametersCount; i++) { - const auto& paramMap = paramsArr[i]; - float value = paramMap["value"].asNumber(); - float weight = paramMap["weight"].asNumber(); - parameters.push_back(BiomeParameter {value, weight}); - } - - auto plants = load_plants(biomeMap); - auto groundLayers = load_layers(biomeMap["layers"], "layers"); - auto seaLayers = load_layers(biomeMap["sea_layers"], "sea_layers"); - - BiomeElementList structures; - if (biomeMap.has("structures")) { - structures = load_structures(biomeMap); - } - return Biome { - name, - std::move(parameters), - std::move(plants), - std::move(structures), - std::move(groundLayers), - std::move(seaLayers)}; -} - std::unique_ptr scripting::load_generator( const GeneratorDef& def, const fs::path& file ) { @@ -268,26 +129,20 @@ std::unique_ptr scripting::load_generator( auto L = lua::get_main_thread(); lua::stackguard _(L); - lua::pop(L, load_script(*env, "generator", file)); + if (fs::exists(file)) { + lua::pop(L, load_script(*env, "generator", file)); + } else { + // Use default (empty) script + lua::pop(L, lua::execute(lua::get_main_thread(), *env, "", "")); + } + lua::pushenv(L, *env); auto root = lua::tovalue(L, -1); lua::pop(L); - std::vector biomes; - - const auto& biomesMap = root["biomes"]; - for (const auto& [biomeName, biomeMap] : biomesMap.asObject()) { - try { - biomes.push_back( - load_biome(biomeMap, biomeName, def.biomeParameters, -2)); - } catch (const std::runtime_error& err) { - throw std::runtime_error("biome "+biomeName+": "+err.what()); - } - } return std::make_unique( def, - std::move(env), - std::move(biomes) + std::move(env) ); } diff --git a/src/world/generator/GeneratorDef.cpp b/src/world/generator/GeneratorDef.cpp index 123f5f98..c372cde4 100644 --- a/src/world/generator/GeneratorDef.cpp +++ b/src/world/generator/GeneratorDef.cpp @@ -1,6 +1,9 @@ #include "GeneratorDef.hpp" #include "VoxelFragment.hpp" +#include "content/Content.hpp" +#include "util/stringutil.hpp" +#include "voxels/Block.hpp" GeneratingVoxelStructure::GeneratingVoxelStructure( VoxelStructureMeta meta, @@ -9,3 +12,25 @@ GeneratingVoxelStructure::GeneratingVoxelStructure( GeneratorDef::GeneratorDef(std::string name) : name(std::move(name)) {} + +void GeneratorDef::prepare(const Content* content) { + for (auto& biome : biomes) { + for (auto& layer : biome.groundLayers.layers) { + layer.rt.id = content->blocks.require(layer.block).rt.id; + } + for (auto& layer : biome.seaLayers.layers) { + layer.rt.id = content->blocks.require(layer.block).rt.id; + } + for (auto& plant : biome.plants.entries) { + plant.rt.id = content->blocks.require(plant.name).rt.id; + } + for (auto& structure : biome.structures.entries) { + const auto& found = structuresIndices.find(structure.name); + if (found == structuresIndices.end()) { + throw std::runtime_error( + "no structure "+util::quote(structure.name)+" found"); + } + structure.rt.id = found->second; + } + } +} diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 8311e15f..7209b4c4 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -137,13 +137,6 @@ public: virtual std::vector placeStructures( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap) = 0; - - /// @brief Get generator biomes - virtual const std::vector& getBiomes() const = 0; - - /// @brief Build the runtime cache - /// @param content built content - virtual void prepare(const GeneratorDef& def, const Content* content) = 0; }; struct GeneratingVoxelStructure { @@ -170,7 +163,10 @@ struct GeneratorDef { std::unordered_map structuresIndices; std::vector> structures; + std::vector biomes; GeneratorDef(std::string name); GeneratorDef(const GeneratorDef&) = delete; + + void prepare(const Content* content); }; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index e1c32c65..6c54cb65 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -231,7 +231,7 @@ void WorldGenerator::generateBiomes( } auto biomeParams = def.script->generateParameterMaps( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed); - const auto& biomes = def.script->getBiomes(); + const auto& biomes = def.biomes; auto chunkBiomes = std::make_unique(CHUNK_W*CHUNK_D); for (uint z = 0; z < CHUNK_D; z++) { From 6c2d10e14b2546e3e70c20691bac2d07e721a6cb Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 29 Sep 2024 22:08:28 +0300 Subject: [PATCH 093/139] cleanup --- src/logic/scripting/scripting_world_generation.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 28082540..a68f4277 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -135,11 +135,6 @@ std::unique_ptr scripting::load_generator( // Use default (empty) script lua::pop(L, lua::execute(lua::get_main_thread(), *env, "", "")); } - - - lua::pushenv(L, *env); - auto root = lua::tovalue(L, -1); - lua::pop(L); return std::make_unique( def, From abc8eccab8afce041528ee715e4d7184f657ed37 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 29 Sep 2024 23:12:45 +0300 Subject: [PATCH 094/139] add biome-bpd and heights-bpd generator def parameters --- .../base/generators/demo.files/script.lua | 28 +----------- src/content/loading/GeneratorLoader.cpp | 8 ++-- .../scripting/scripting_world_generation.cpp | 10 +++-- src/world/generator/GeneratorDef.cpp | 2 +- src/world/generator/GeneratorDef.hpp | 44 ++++++++++++++++--- src/world/generator/WorldGenerator.cpp | 25 ++++++++++- 6 files changed, 74 insertions(+), 43 deletions(-) diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua index 8a108964..73c4698d 100644 --- a/res/content/base/generators/demo.files/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -11,7 +11,7 @@ function place_structures(x, z, w, d, seed, hmap) return placements end -local function _generate_heightmap(x, y, w, h, seed, s) +function generate_heightmap(x, y, w, h, seed, s) local umap = Heightmap(w, h) local vmap = Heightmap(w, h) umap.noiseSeed = seed @@ -37,18 +37,7 @@ local function _generate_heightmap(x, y, w, h, seed, s) return map end -function generate_heightmap(x, y, w, h, seed) - -- blocks per dot - local bpd = 4 - local map = _generate_heightmap( - math.floor(x/bpd), math.floor(y/bpd), - math.floor(w/bpd)+1, math.floor(h/bpd)+1, seed, bpd) - map:resize(w+bpd, h+bpd, 'linear') - map:crop(0, 0, w, h) - return map -end - -local function _generate_biome_parameters(x, y, w, h, seed, s) +function generate_biome_parameters(x, y, w, h, seed, s) local tempmap = Heightmap(w, h) tempmap.noiseSeed = seed + 5324 tempmap:noise({x, y}, 0.04*s, 6) @@ -59,16 +48,3 @@ local function _generate_biome_parameters(x, y, w, h, seed, s) hummap:pow(3) return tempmap, hummap end - -function generate_biome_parameters(x, y, w, h, seed) - local bpd = 8 - local tmap, hmap = _generate_biome_parameters( - math.floor(x/bpd), math.floor(y/bpd), - math.floor(w/bpd)+1, math.floor(h/bpd)+1, seed, bpd) - tmap:resize(w+bpd, h+bpd, 'linear') - tmap:crop(0, 0, w, h) - - hmap:resize(w+bpd, h+bpd, 'linear') - hmap:crop(0, 0, w, h) - return tmap, hmap -end diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index 90cbc4c1..2657ddd1 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -127,13 +127,13 @@ static VoxelStructureMeta load_structure_meta( return meta; } -static std::vector> load_structures( +static std::vector> load_structures( const fs::path& structuresFile ) { auto structuresDir = structuresFile.parent_path() / fs::path("fragments"); auto map = files::read_json(structuresFile); - std::vector> structures; + std::vector> structures; for (auto& [name, config] : map.asObject()) { auto structFile = structuresDir / fs::u8path(name + ".vox"); logger.debug() << "loading voxel fragment " << structFile.u8string(); @@ -144,7 +144,7 @@ static std::vector> load_structures( auto fragment = std::make_unique(); fragment->deserialize(files::read_binary_json(structFile)); - structures.push_back(std::make_unique( + structures.push_back(std::make_unique( load_structure_meta(name, config), std::move(fragment) )); @@ -193,6 +193,8 @@ void ContentLoader::loadGenerator( } auto map = files::read_toml(generatorsDir / fs::u8path(name + ".toml")); map.at("biome_parameters").get(def.biomeParameters); + map.at("biome-bpd").get(def.biomesBPD); + map.at("heights-bpd").get(def.heightsBPD); map.at("sea_level").get(def.seaLevel); auto folder = generatorsDir / fs::u8path(name + ".files"); diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index a68f4277..ff7175a0 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -26,7 +26,7 @@ public: {} std::shared_ptr generateHeightmap( - const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed + const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, uint bpd ) override { auto L = lua::get_main_thread(); lua::pushenv(L, *env); @@ -34,7 +34,8 @@ public: lua::pushivec_stack(L, offset); lua::pushivec_stack(L, size); lua::pushinteger(L, seed); - if (lua::call_nothrow(L, 5)) { + lua::pushinteger(L, bpd); + if (lua::call_nothrow(L, 6)) { auto map = lua::touserdata(L, -1)->getHeightmap(); lua::pop(L, 2); return map; @@ -45,7 +46,7 @@ public: } std::vector> generateParameterMaps( - const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed + const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, uint bpd ) override { std::vector> maps; @@ -56,7 +57,8 @@ public: lua::pushivec_stack(L, offset); lua::pushivec_stack(L, size); lua::pushinteger(L, seed); - if (lua::call_nothrow(L, 5, biomeParameters)) { + lua::pushinteger(L, bpd); + if (lua::call_nothrow(L, 6, biomeParameters)) { for (int i = biomeParameters-1; i >= 0; i--) { maps.push_back( lua::touserdata(L, -1-i)->getHeightmap()); diff --git a/src/world/generator/GeneratorDef.cpp b/src/world/generator/GeneratorDef.cpp index c372cde4..515d27e7 100644 --- a/src/world/generator/GeneratorDef.cpp +++ b/src/world/generator/GeneratorDef.cpp @@ -5,7 +5,7 @@ #include "util/stringutil.hpp" #include "voxels/Block.hpp" -GeneratingVoxelStructure::GeneratingVoxelStructure( +VoxelStructure::VoxelStructure( VoxelStructureMeta meta, std::unique_ptr structure ) : fragments({std::move(structure)}), meta(std::move(meta)) {} diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 7209b4c4..02d9bc69 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -123,27 +123,51 @@ class GeneratorScript { public: virtual ~GeneratorScript() = default; - /// @brief Generates a heightmap with values in range 0..1 + /// @brief Generate a heightmap with values in range 0..1 /// @param offset position of the heightmap in the world /// @param size size of the heightmap /// @param seed world seed - /// @return generated heightmap of given size (can't be nullptr) + /// @param bpd blocks per dot + /// @return generated heightmap (can't be nullptr) virtual std::shared_ptr generateHeightmap( - const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; + const glm::ivec2& offset, + const glm::ivec2& size, + uint64_t seed, + uint bpd + ) = 0; + /// @brief Generate a biomes parameters maps + /// @param offset position of maps in the world + /// @param size maps size + /// @param seed world seed + /// @param bpd blocks per dot + /// @return generated maps (can't be nullptr) virtual std::vector> generateParameterMaps( - const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; + const glm::ivec2& offset, + const glm::ivec2& size, + uint64_t seed, + uint bpd + ) = 0; + /// @brief Generate a list of structures placements. Structures may be + /// placed to nearest chunks also (position of out area). + /// @param offset position of the area + /// @param size size of the area (blocks) + /// @param seed world seed + /// @param heightmap area heightmap + /// @return structure placements virtual std::vector placeStructures( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap) = 0; }; -struct GeneratingVoxelStructure { +/// @brief Structure voxel fragments and metadata +struct VoxelStructure { VoxelStructureMeta meta; + /// @brief voxel fragment and 3 pre-calculated rotated versions std::array, 4> fragments; - GeneratingVoxelStructure( + VoxelStructure( VoxelStructureMeta meta, std::unique_ptr structure ); @@ -161,8 +185,14 @@ struct GeneratorDef { /// @brief Number of biome parameters, that biome choosing depending on uint biomeParameters = 0; + /// @brief Biome parameter map blocks per dot + uint biomesBPD = 8; + + /// @brief Heightmap blocks per dot + uint heightsBPD = 4; + std::unordered_map structuresIndices; - std::vector> structures; + std::vector> structures; std::vector biomes; GeneratorDef(std::string name); diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 6c54cb65..b70fae35 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -10,6 +10,7 @@ #include "VoxelFragment.hpp" #include "util/timeutil.hpp" #include "util/listutil.hpp" +#include "maths/voxmaths.hpp" #include "debug/Logger.hpp" static debug::Logger logger("world-generator"); @@ -229,8 +230,19 @@ void WorldGenerator::generateBiomes( if (prototype.level >= ChunkPrototypeLevel::BIOMES) { return; } + uint bpd = def.biomesBPD; auto biomeParams = def.script->generateParameterMaps( - {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed); + {floordiv(chunkX * CHUNK_W, bpd), floordiv(chunkZ * CHUNK_D, bpd)}, + {floordiv(CHUNK_W, bpd)+1, floordiv(CHUNK_D, bpd)+1}, + seed, + bpd + ); + for (const auto& map : biomeParams) { + map->resize( + CHUNK_W + bpd, CHUNK_D + bpd, InterpolationType::LINEAR + ); + map->crop(0, 0, CHUNK_W, CHUNK_D); + } const auto& biomes = def.biomes; auto chunkBiomes = std::make_unique(CHUNK_W*CHUNK_D); @@ -250,8 +262,17 @@ void WorldGenerator::generateHeightmap( if (prototype.level >= ChunkPrototypeLevel::HEIGHTMAP) { return; } + uint bpd = def.heightsBPD; prototype.heightmap = def.script->generateHeightmap( - {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed); + {floordiv(chunkX * CHUNK_W, bpd), floordiv(chunkZ * CHUNK_D, bpd)}, + {floordiv(CHUNK_W, bpd)+1, floordiv(CHUNK_D, bpd)+1}, + seed, + bpd + ); + prototype.heightmap->resize( + CHUNK_W + bpd, CHUNK_D + bpd, InterpolationType::LINEAR + ); + prototype.heightmap->crop(0, 0, CHUNK_W, CHUNK_D); prototype.level = ChunkPrototypeLevel::HEIGHTMAP; } From 16fac768c82639c2c5be452d10ab9991eba0ec18 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 29 Sep 2024 23:39:42 +0300 Subject: [PATCH 095/139] add chunk_height parameter to place_structures --- res/content/base/generators/demo.files/script.lua | 6 +++--- src/logic/scripting/scripting_world_generation.cpp | 5 +++-- src/world/generator/GeneratorDef.hpp | 3 ++- src/world/generator/WorldGenerator.cpp | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua index 73c4698d..98ccc785 100644 --- a/res/content/base/generators/demo.files/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -1,10 +1,10 @@ -function place_structures(x, z, w, d, seed, hmap) +function place_structures(x, z, w, d, seed, hmap, chunk_height) local placements = {} for i=1,10 do local sx = math.random() * w local sz = math.random() * d - local sy = math.random() * 128 - if sy < hmap:at(sx, sz) * 256 - 6 then + local sy = math.random() * (chunk_height * 0.5) + if sy < hmap:at(sx, sz) * chunk_height - 6 then table.insert(placements, {"coal_ore0", {sx, sy, sz}, math.random()*4}) end end diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index ff7175a0..4ee87116 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -76,7 +76,7 @@ public: std::vector placeStructures( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, - const std::shared_ptr& heightmap + const std::shared_ptr& heightmap, uint chunkHeight ) override { std::vector placements; @@ -88,7 +88,8 @@ public: lua::pushivec_stack(L, size); lua::pushinteger(L, seed); lua::newuserdata(L, heightmap); - if (lua::call_nothrow(L, 6, 1)) { + lua::pushinteger(L, chunkHeight); + if (lua::call_nothrow(L, 7, 1)) { int len = lua::objlen(L, -1); for (int i = 1; i <= len; i++) { lua::rawgeti(L, i); diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 02d9bc69..630f7400 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -155,10 +155,11 @@ public: /// @param size size of the area (blocks) /// @param seed world seed /// @param heightmap area heightmap + /// @param chunkHeight chunk height to use as heights multiplier /// @return structure placements virtual std::vector placeStructures( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, - const std::shared_ptr& heightmap) = 0; + const std::shared_ptr& heightmap, uint chunkHeight) = 0; }; /// @brief Structure voxel fragments and metadata diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index b70fae35..2043b7a7 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -177,7 +177,7 @@ void WorldGenerator::generateStructures( util::concat(prototype.structures, def.script->placeStructures( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, - heightmap + heightmap, CHUNK_H )); for (const auto& placement : prototype.structures) { const auto& offset = placement.position; From 3a4b77dafc24574c59dfafeaf8df3a6016247003 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 30 Sep 2024 00:34:32 +0300 Subject: [PATCH 096/139] add array as root element support to json parser --- src/coders/json.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/coders/json.cpp b/src/coders/json.cpp index 049eeb40..8fb6a1e2 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -162,10 +162,12 @@ Parser::Parser(std::string_view filename, std::string_view source) dv::value Parser::parse() { char next = peek(); - if (next != '{') { - throw error("'{' expected"); + if (next == '{') { + return parseObject(); + } else if (next == '[') { + return parseList(); } - return parseObject(); + throw error("'{' or '[' expected"); } dv::value Parser::parseObject() { From e590d06bb0afac1537289426be6785f540002872 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 30 Sep 2024 01:55:42 +0300 Subject: [PATCH 097/139] add file.read_combined_list(...) --- src/engine.cpp | 23 +++++++++++++++-------- src/files/engine_paths.cpp | 28 ++++++++++++++++++++++++++++ src/files/engine_paths.hpp | 6 ++++++ src/files/files.hpp | 5 +++++ src/logic/scripting/lua/libfile.cpp | 9 +++++++++ 5 files changed, 63 insertions(+), 8 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index 7bb847d9..538721ea 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -303,21 +303,28 @@ void Engine::loadContent() { names = manager.assembly(names); contentPacks = manager.getAll(names); - std::vector resRoots; - { - auto pack = ContentPack::createCore(paths); - resRoots.push_back({"core", pack.folder}); - ContentLoader(&pack, contentBuilder).load(); - load_configs(pack.folder); - } + auto corePack = ContentPack::createCore(paths); + + // Setup filesystem entry points + std::vector resRoots { + {"core", corePack.folder} + }; for (auto& pack : contentPacks) { resRoots.push_back({pack.id, pack.folder}); + } + resPaths = std::make_unique(resdir, resRoots); + + // Load content + { + ContentLoader(&corePack, contentBuilder).load(); + load_configs(corePack.folder); + } + for (auto& pack : contentPacks) { ContentLoader(&pack, contentBuilder).load(); load_configs(pack.folder); } content = contentBuilder.build(); - resPaths = std::make_unique(resdir, resRoots); langs::setup(resdir, langs::current->getId(), contentPacks); loadAssets(); diff --git a/src/files/engine_paths.cpp b/src/files/engine_paths.cpp index d665603e..c3b9585e 100644 --- a/src/files/engine_paths.cpp +++ b/src/files/engine_paths.cpp @@ -10,6 +10,9 @@ #include #include "WorldFiles.hpp" +#include "debug/Logger.hpp" + +static debug::Logger logger("engine-paths"); /// @brief ENUM for accessing folder and file names @@ -258,6 +261,31 @@ std::vector ResPaths::listdir( return entries; } +dv::value ResPaths::readCombinedList(const std::string& filename) { + dv::value list = dv::list(); + for (const auto& root : roots) { + auto path = root.path / fs::u8path(filename); + if (!fs::exists(path)) { + continue; + } + try { + auto value = files::read_object(path); + if (!value.isList()) { + logger.warning() << "reading combined list " << root.name << ":" + << filename << " is not a list (skipped)"; + continue; + } + for (const auto& elem : value) { + list.add(elem); + } + } catch (const std::runtime_error& err) { + logger.warning() << "reading combined list " << root.name << ":" + << filename << ": " << err.what(); + } + } + return list; +} + const std::filesystem::path& ResPaths::getMainRoot() const { return mainRoot; } diff --git a/src/files/engine_paths.hpp b/src/files/engine_paths.hpp index ceb2f613..d9af69a3 100644 --- a/src/files/engine_paths.hpp +++ b/src/files/engine_paths.hpp @@ -6,6 +6,7 @@ #include #include +#include "data/dv.hpp" #include "content/ContentPack.hpp" @@ -63,6 +64,11 @@ public: std::vector listdir(const std::string& folder) const; std::vector listdirRaw(const std::string& folder) const; + /// @brief Read all found list versions from all packs and combine into a + /// single list. Invalid versions will be skipped with logging a warning + /// @param file *.json file path relative to entry point + dv::value readCombinedList(const std::string& file); + const std::filesystem::path& getMainRoot() const; private: diff --git a/src/files/files.hpp b/src/files/files.hpp index 3c602b66..e1ead5c7 100644 --- a/src/files/files.hpp +++ b/src/files/files.hpp @@ -63,8 +63,13 @@ namespace files { /// @brief Read JSON or BJSON file /// @param file *.json or *.bjson file dv::value read_json(const fs::path& file); + dv::value read_binary_json(const fs::path& file); + + /// @brief Read TOML file + /// @param file *.toml file dv::value read_toml(const fs::path& file); + std::vector read_list(const fs::path& file); bool is_data_file(const fs::path& file); diff --git a/src/logic/scripting/lua/libfile.cpp b/src/logic/scripting/lua/libfile.cpp index 30c0e204..52f7e4db 100644 --- a/src/logic/scripting/lua/libfile.cpp +++ b/src/logic/scripting/lua/libfile.cpp @@ -247,6 +247,14 @@ static int l_file_gzip_decompress(lua::State* L) { } } +static int l_file_read_combined_list(lua::State* L) { + std::string path = lua::require_string(L, 1); + if (path.find(':') != std::string::npos) { + throw std::runtime_error("entry point must not be specified"); + } + return lua::pushvalue(L, engine->getResPaths()->readCombinedList(path)); +} + const luaL_Reg filelib[] = { {"exists", lua::wrap}, {"find", lua::wrap}, @@ -265,4 +273,5 @@ const luaL_Reg filelib[] = { {"write", lua::wrap}, {"gzip_compress", lua::wrap}, {"gzip_decompress", lua::wrap}, + {"read_combined_list", lua::wrap}, {NULL, NULL}}; From e30e0083a0a2db9368c2491d845a146bcb8525df Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 30 Sep 2024 13:14:53 +0300 Subject: [PATCH 098/139] add ores --- .../base/generators/demo.files/ores.json | 3 ++ .../base/generators/demo.files/script.lua | 38 ++++++++++++++----- src/content/loading/GeneratorLoader.cpp | 3 +- src/logic/scripting/scripting.hpp | 3 +- .../scripting/scripting_world_generation.cpp | 10 ++++- 5 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 res/content/base/generators/demo.files/ores.json diff --git a/res/content/base/generators/demo.files/ores.json b/res/content/base/generators/demo.files/ores.json new file mode 100644 index 00000000..682bb23a --- /dev/null +++ b/res/content/base/generators/demo.files/ores.json @@ -0,0 +1,3 @@ +[ + {"struct": "coal_ore0", "rarity": 22000} +] diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua index 98ccc785..00a91691 100644 --- a/res/content/base/generators/demo.files/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -1,13 +1,32 @@ -function place_structures(x, z, w, d, seed, hmap, chunk_height) - local placements = {} - for i=1,10 do - local sx = math.random() * w - local sz = math.random() * d - local sy = math.random() * (chunk_height * 0.5) - if sy < hmap:at(sx, sz) * chunk_height - 6 then - table.insert(placements, {"coal_ore0", {sx, sy, sz}, math.random()*4}) +BLOCKS_PER_CHUNK = 65536 + +local _, dir = parse_path(__DIR__) +ores = file.read_combined_list(dir.."/ores.json") + +local function place_ores(placements, x, z, w, d, seed, hmap, chunk_height) + for _, ore in ipairs(ores) do + local count = BLOCKS_PER_CHUNK / ore.rarity + + -- average count is less than 1 + local addchance = math.fmod(count, 1.0) + if math.random() < addchance then + count = count + 1 + end + + for i=1,count do + local sx = math.random() * w + local sz = math.random() * d + local sy = math.random() * (chunk_height * 0.5) + if sy < hmap:at(sx, sz) * chunk_height - 6 then + table.insert(placements, {ore.struct, {sx, sy, sz}, math.random()*4}) + end end end +end + +function place_structures(x, z, w, d, seed, hmap, chunk_height) + local placements = {} + place_ores(placements, x, z, w, d, seed, hmap, chunk_height) return placements end @@ -23,7 +42,7 @@ function generate_heightmap(x, y, w, h, seed, s) map.noiseSeed = seed map:noise({x, y}, 0.8*s, 4, 0.02) map:cellnoise({x, y}, 0.1*s, 3, 0.3, umap, vmap) - map:add(0.4) + map:add(0.7) local rivermap = Heightmap(w, h) rivermap.noiseSeed = seed @@ -32,7 +51,6 @@ function generate_heightmap(x, y, w, h, seed, s) rivermap:mul(2.0) rivermap:pow(0.15) rivermap:max(0.3) - map:add(0.3) map:mul(rivermap) return map end diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index 2657ddd1..cdf0d893 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -212,5 +212,6 @@ void ContentLoader::loadGenerator( ); } load_biomes(def, biomesFiles); - def.script = scripting::load_generator(def, scriptFile); + def.script = scripting::load_generator( + def, scriptFile, pack->id+":generators/"+name+".files"); } diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index aad1bd9e..8a1a39cd 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -149,7 +149,8 @@ namespace scripting { std::unique_ptr load_generator( const GeneratorDef& def, - const fs::path& file + const fs::path& file, + const std::string& dirPath ); /// @brief Load package-specific world script diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 4ee87116..dd4d8ad1 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -126,12 +126,20 @@ public: }; std::unique_ptr scripting::load_generator( - const GeneratorDef& def, const fs::path& file + const GeneratorDef& def, const fs::path& file, const std::string& dirPath ) { auto env = create_environment(); auto L = lua::get_main_thread(); lua::stackguard _(L); + lua::pushenv(L, *env); + lua::pushstring(L, dirPath); + lua::setfield(L, "__DIR__"); + lua::pushstring(L, dirPath + "/script.lua"); + lua::setfield(L, "__FILE__"); + + lua::pop(L); + if (fs::exists(file)) { lua::pop(L, load_script(*env, "generator", file)); } else { From 88752806e6359782d153ebf3683202085a5eebfe Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 30 Sep 2024 14:39:16 +0300 Subject: [PATCH 099/139] fix: extra chunk generation when all area chunks are generated --- src/logic/ChunksController.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index b1db58fe..07492f29 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -61,6 +61,7 @@ bool ChunksController::loadVisible() { int nearX = 0; int nearZ = 0; + bool assigned = false; int minDistance = ((sizeX - padding * 2) / 2) * ((sizeY - padding * 2) / 2); for (uint z = padding; z < sizeY - padding; z++) { for (uint x = padding; x < sizeX - padding; x++) { @@ -81,12 +82,13 @@ bool ChunksController::loadVisible() { minDistance = distance; nearX = x; nearZ = z; + assigned = true; } } } const auto& chunk = chunks->getChunks()[nearZ * sizeX + nearX]; - if (chunk != nullptr) { + if (chunk != nullptr || !assigned) { return false; } int offsetX = chunks->getOffsetX(); From 75d66b644b5691615d846791d4eb3ed971258f23 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 30 Sep 2024 15:14:34 +0300 Subject: [PATCH 100/139] add debug world generator visualization --- .../base/generators/demo.files/script.lua | 3 +- src/frontend/hud.cpp | 65 +++++++++++++++++-- src/frontend/hud.hpp | 7 ++ src/graphics/core/GLTexture.cpp | 8 ++- src/graphics/core/GLTexture.hpp | 2 + src/graphics/core/Texture.hpp | 2 + src/logic/ChunksController.hpp | 4 ++ src/logic/LevelController.cpp | 4 ++ src/logic/LevelController.hpp | 1 + src/world/generator/WorldGenerator.cpp | 18 +++++ src/world/generator/WorldGenerator.hpp | 11 ++++ 11 files changed, 116 insertions(+), 9 deletions(-) diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua index 00a91691..cd443163 100644 --- a/res/content/base/generators/demo.files/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -1,9 +1,8 @@ -BLOCKS_PER_CHUNK = 65536 - local _, dir = parse_path(__DIR__) ores = file.read_combined_list(dir.."/ores.json") local function place_ores(placements, x, z, w, d, seed, hmap, chunk_height) + local BLOCKS_PER_CHUNK = w * d * chunk_height for _, ore in ipairs(ores) do local count = BLOCKS_PER_CHUNK / ore.rarity diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 925627e7..640fc656 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -17,6 +17,7 @@ #include "graphics/core/Mesh.hpp" #include "graphics/core/Shader.hpp" #include "graphics/core/Texture.hpp" +#include "graphics/core/ImageData.hpp" #include "graphics/render/WorldRenderer.hpp" #include "graphics/ui/elements/InventoryView.hpp" #include "graphics/ui/elements/Menu.hpp" @@ -29,6 +30,8 @@ #include "items/Inventory.hpp" #include "items/ItemDef.hpp" #include "logic/scripting/scripting.hpp" +#include "logic/LevelController.hpp" +#include "world/generator/WorldGenerator.hpp" #include "maths/voxmaths.hpp" #include "objects/Player.hpp" #include "physics/Hitbox.hpp" @@ -140,12 +143,16 @@ std::shared_ptr Hud::createHotbar() { return view; } -Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player) - : assets(engine->getAssets()), - gui(engine->getGUI()), - frontend(frontend), - player(player) -{ +static constexpr uint WORLDGEN_IMG_SIZE = 64U; + +Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player) + : assets(engine->getAssets()), + gui(engine->getGUI()), + frontend(frontend), + player(player), + debugImgWorldGen(std::make_unique( + ImageFormat::rgba8888, WORLDGEN_IMG_SIZE, WORLDGEN_IMG_SIZE + )) { contentAccess = createContentAccess(); contentAccess->setId("hud.content-access"); contentAccessPanel = std::make_shared( @@ -177,6 +184,14 @@ Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player) dplotter->setGravity(Gravity::bottom_right); dplotter->setInteractive(false); add(HudElement(hud_element_mode::permanent, nullptr, dplotter, true)); + + assets->store(Texture::from(debugImgWorldGen.get()), DEBUG_WORLDGEN_IMAGE); + + add(HudElement(hud_element_mode::permanent, nullptr, + guiutil::create( + "" + ), true)); } Hud::~Hud() { @@ -250,6 +265,40 @@ void Hud::updateHotbarControl() { } } +void Hud::updateWorldGenDebugVisualization() { + auto level = frontend->getLevel(); + auto generator = + frontend->getController()->getChunksController()->getGenerator(); + auto debugInfo = generator->createDebugInfo(); + uint width = debugImgWorldGen->getWidth(); + uint height = debugImgWorldGen->getHeight(); + ubyte* data = debugImgWorldGen->getData(); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (x >= debugInfo.areaWidth || y >= debugInfo.areaHeight) { + for (int i = 0; i < 4; i++) { + data[(y * width + x) * 4 + i] = 0; + } + continue; + } + int cx = x + debugInfo.areaOffsetX; + int cz = y + debugInfo.areaOffsetY; + auto value = debugInfo.areaLevels[y * debugInfo.areaWidth + x] * 35; + // Chunk is already generated + if (level->chunks->getChunk(cx, cz)) { + value = 255; + } + for (int i = 0; i < 3; i++) { + data[(y * width + x) * 4 + i] = value; + } + data[(y * width + x) * 4 + 3] = 100; + } + } + + auto texture = assets->get(DEBUG_WORLDGEN_IMAGE); + texture->reload(*debugImgWorldGen); +} + void Hud::update(bool visible) { auto level = frontend->getLevel(); auto menu = gui->getMenu(); @@ -296,6 +345,10 @@ void Hud::update(bool visible) { } } cleanup(); + + if (player->debug) { + updateWorldGenDebugVisualization(); + } } /// @brief Show inventory on the screen and turn on inventory mode blocking movement diff --git a/src/frontend/hud.hpp b/src/frontend/hud.hpp index 26d0d9d3..05e93e32 100644 --- a/src/frontend/hud.hpp +++ b/src/frontend/hud.hpp @@ -18,6 +18,7 @@ class LevelFrontend; class UiDocument; class DrawContext; class Viewport; +class ImageData; namespace gui { class GUI; @@ -107,6 +108,8 @@ class Hud : public util::ObjectsKeeper { /// @brief UI element will be dynamicly positioned near to inventory or in screen center std::shared_ptr secondUI = nullptr; + + std::unique_ptr debugImgWorldGen; std::shared_ptr createContentAccess(); std::shared_ptr createHotbar(); @@ -117,6 +120,7 @@ class Hud : public util::ObjectsKeeper { void cleanup(); void showExchangeSlot(); + void updateWorldGenDebugVisualization(); public: Hud(Engine* engine, LevelFrontend* frontend, Player* player); ~Hud(); @@ -167,4 +171,7 @@ public: Player* getPlayer() const; std::shared_ptr getBlockInventory(); + + /// @brief Runtime updating debug visualization texture + inline static std::string DEBUG_WORLDGEN_IMAGE = "#debug.img.worldgen"; }; diff --git a/src/graphics/core/GLTexture.cpp b/src/graphics/core/GLTexture.cpp index 9a47ef22..9572f415 100644 --- a/src/graphics/core/GLTexture.cpp +++ b/src/graphics/core/GLTexture.cpp @@ -41,7 +41,13 @@ void GLTexture::unbind() { glBindTexture(GL_TEXTURE_2D, 0); } -void GLTexture::reload(const ubyte* data){ +void GLTexture::reload(const ImageData& image) { + width = image.getWidth(); + height = image.getHeight(); + reload(image.getData()); +} + +void GLTexture::reload(const ubyte* data) { glBindTexture(GL_TEXTURE_2D, id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, static_cast(data)); diff --git a/src/graphics/core/GLTexture.hpp b/src/graphics/core/GLTexture.hpp index 10e002e9..7f9e8f22 100644 --- a/src/graphics/core/GLTexture.hpp +++ b/src/graphics/core/GLTexture.hpp @@ -16,6 +16,8 @@ public: void setNearestFilter(); + virtual void reload(const ImageData& image) override; + virtual std::unique_ptr readData() override; virtual uint getId() const override; diff --git a/src/graphics/core/Texture.hpp b/src/graphics/core/Texture.hpp index 9e16f670..4b3f1651 100644 --- a/src/graphics/core/Texture.hpp +++ b/src/graphics/core/Texture.hpp @@ -20,6 +20,8 @@ public: virtual void bind() = 0; virtual void unbind() = 0; + virtual void reload(const ImageData& image) = 0; + virtual std::unique_ptr readData() = 0; virtual uint getWidth() const { diff --git a/src/logic/ChunksController.hpp b/src/logic/ChunksController.hpp index 85b00e8a..8566ee1b 100644 --- a/src/logic/ChunksController.hpp +++ b/src/logic/ChunksController.hpp @@ -33,4 +33,8 @@ public: int loadDistance, int centerX, int centerY); + + const WorldGenerator* getGenerator() const { + return generator.get(); + } }; diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 1e947b42..f0bee4c5 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -95,6 +95,10 @@ BlocksController* LevelController::getBlocksController() { return blocks.get(); } +ChunksController* LevelController::getChunksController() { + return chunks.get(); +} + PlayerController* LevelController::getPlayerController() { return player.get(); } diff --git a/src/logic/LevelController.hpp b/src/logic/LevelController.hpp index fe16ed19..a2c5af3e 100644 --- a/src/logic/LevelController.hpp +++ b/src/logic/LevelController.hpp @@ -34,5 +34,6 @@ public: Player* getPlayer(); BlocksController* getBlocksController(); + ChunksController* getChunksController(); PlayerController* getPlayerController(); }; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 2043b7a7..365a63fc 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -355,3 +355,21 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { } } } + +WorldGenDebugInfo WorldGenerator::createDebugInfo() const { + const auto& area = surroundMap.getArea(); + const auto& levels = area.getBuffer(); + auto values = std::make_unique(area.getWidth()*area.getHeight()); + + for (uint i = 0; i < levels.size(); i++) { + values[i] = levels[i]; + } + + return WorldGenDebugInfo { + area.getOffsetX(), + area.getOffsetY(), + static_cast(area.getWidth()), + static_cast(area.getHeight()), + std::move(values) + }; +} diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 9b90cb3f..7490ef4d 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -34,6 +34,14 @@ struct ChunkPrototype { std::vector structures; }; +struct WorldGenDebugInfo { + int areaOffsetX; + int areaOffsetY; + uint areaWidth; + uint areaHeight; + std::unique_ptr areaLevels; +}; + /// @brief High-level world generation controller class WorldGenerator { /// @param def generator definition @@ -80,5 +88,8 @@ public: /// @param z chunk position Y divided by CHUNK_D void generate(voxel* voxels, int x, int z); + WorldGenDebugInfo createDebugInfo() const; + + /// @brief Default generator name // TODO: move to config inline static std::string DEFAULT = "core:default"; }; From 06a046896735430d9c5ef70e8e4ca8f994e5ff15 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 3 Oct 2024 22:27:46 +0300 Subject: [PATCH 101/139] update debug generator visualizer --- src/frontend/hud.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 640fc656..655bd37f 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -40,6 +40,7 @@ #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" +#include "voxels/ChunksStorage.hpp" #include "window/Camera.hpp" #include "window/Events.hpp" #include "window/input.hpp" @@ -143,7 +144,7 @@ std::shared_ptr Hud::createHotbar() { return view; } -static constexpr uint WORLDGEN_IMG_SIZE = 64U; +static constexpr uint WORLDGEN_IMG_SIZE = 128U; Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player) : assets(engine->getAssets()), @@ -275,23 +276,24 @@ void Hud::updateWorldGenDebugVisualization() { ubyte* data = debugImgWorldGen->getData(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - if (x >= debugInfo.areaWidth || y >= debugInfo.areaHeight) { - for (int i = 0; i < 4; i++) { - data[(y * width + x) * 4 + i] = 0; - } - continue; - } int cx = x + debugInfo.areaOffsetX; int cz = y + debugInfo.areaOffsetY; - auto value = debugInfo.areaLevels[y * debugInfo.areaWidth + x] * 35; + + data[(y * width + x) * 4 + 1] = + level->chunks->getChunk(cx, cz) ? 255 : 0; + data[(y * width + x) * 4 + 0] = + level->chunksStorage->get(cx, cz) ? 255 : 0; + + if (x >= debugInfo.areaWidth || y >= debugInfo.areaHeight) { + data[(y * width + x) * 4 + 2] = 0; + data[(y * width + x) * 4 + 3] = 0; + continue; + } + + auto value = debugInfo.areaLevels[y * debugInfo.areaWidth + x] * 25; // Chunk is already generated - if (level->chunks->getChunk(cx, cz)) { - value = 255; - } - for (int i = 0; i < 3; i++) { - data[(y * width + x) * 4 + i] = value; - } - data[(y * width + x) * 4 + 3] = 100; + data[(y * width + x) * 4 + 2] = value; + data[(y * width + x) * 4 + 3] = 150; } } From 0cbc04852346be33a88e57ecf252961b77404518 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 3 Oct 2024 23:02:22 +0300 Subject: [PATCH 102/139] move debug generator visualizer origin to center --- src/frontend/hud.cpp | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 655bd37f..54194dc5 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -271,32 +271,44 @@ void Hud::updateWorldGenDebugVisualization() { auto generator = frontend->getController()->getChunksController()->getGenerator(); auto debugInfo = generator->createDebugInfo(); - uint width = debugImgWorldGen->getWidth(); - uint height = debugImgWorldGen->getHeight(); + + int width = debugImgWorldGen->getWidth(); + int height = debugImgWorldGen->getHeight(); ubyte* data = debugImgWorldGen->getData(); - for (int y = 0; y < height; y++) { + + int ox = debugInfo.areaOffsetX; + int oz = debugInfo.areaOffsetY; + + int areaWidth = debugInfo.areaWidth; + int areaHeight = debugInfo.areaHeight; + + for (int z = 0; z < height; z++) { for (int x = 0; x < width; x++) { - int cx = x + debugInfo.areaOffsetX; - int cz = y + debugInfo.areaOffsetY; + int cx = x + ox; + int cz = z + oz; - data[(y * width + x) * 4 + 1] = - level->chunks->getChunk(cx, cz) ? 255 : 0; - data[(y * width + x) * 4 + 0] = - level->chunksStorage->get(cx, cz) ? 255 : 0; + int ax = x - (width - areaWidth) / 2; + int az = z - (height - areaHeight) / 2; - if (x >= debugInfo.areaWidth || y >= debugInfo.areaHeight) { - data[(y * width + x) * 4 + 2] = 0; - data[(y * width + x) * 4 + 3] = 0; + data[(z * width + x) * 4 + 1] = + level->chunks->getChunk(ax + ox, az + oz) ? 255 : 0; + data[(z * width + x) * 4 + 0] = + level->chunksStorage->get(ax + ox, az + oz) ? 255 : 0; + + if (ax < 0 || az < 0 || + ax >= areaWidth || az >= areaHeight) { + data[(z * width + x) * 4 + 2] = 0; + data[(z * width + x) * 4 + 3] = 0; + data[(z * width + x) * 4 + 3] = 100; continue; } + auto value = debugInfo.areaLevels[az * areaWidth + ax] * 25; - auto value = debugInfo.areaLevels[y * debugInfo.areaWidth + x] * 25; // Chunk is already generated - data[(y * width + x) * 4 + 2] = value; - data[(y * width + x) * 4 + 3] = 150; + data[(z * width + x) * 4 + 2] = value; + data[(z * width + x) * 4 + 3] = 150; } } - auto texture = assets->get(DEBUG_WORLDGEN_IMAGE); texture->reload(*debugImgWorldGen); } From a47b650591b5aef3d7bbe3a9a59d1577d41e9f7a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 4 Oct 2024 01:53:17 +0300 Subject: [PATCH 103/139] add core:struct_air --- res/textures/blocks/struct_air.png | Bin 0 -> 6361 bytes src/constants.hpp | 1 + src/core_defs.cpp | 23 ++++++++++++++++++++--- src/core_defs.hpp | 3 ++- src/world/generator/WorldGenerator.cpp | 6 +++++- 5 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 res/textures/blocks/struct_air.png diff --git a/res/textures/blocks/struct_air.png b/res/textures/blocks/struct_air.png new file mode 100644 index 0000000000000000000000000000000000000000..d5a7de095933be099b152d1b7441abfa0b2d8971 GIT binary patch literal 6361 zcmeHLc~}$I77u7$P_!ttN>#=nR+dR-CX-}l1QA1!RG}1*MWi~JnLvb)m<)^X6xJNbRa#pBIffwoATM_hW|Lp!~wjg-wqi z>241k(O#FEII`oy-YVwvfc&~_P1?=!Urv5>e4y!y=aPnKXh?9y?m2$mmwr3=i)Uo( zXT@oVeXYI6@%e1o!#~xrqBW%tu~ zuT|zX>)*@W`yZrSxM7=P$ukn?1ggMWgQd|s;ym6CHALKdn^ppol z^@H8+jriWFEWBdlQP+-qHeLT)&fv;><)1ZrZ;U^G^~f#aZgh)h>XEtWQ5&`{S#GEN z_lR|OCFN0LN*2%V_hs9piSy&m^xQLG^bceDx~Rk7T^$yH_WR&mwRNat)aIgH?NrMT zZ;7Txs;nuYlJDc} z+oBdVQ4Y%k_FS@=n|+%-uq0>N7SuM&utJsST(#q72S3fB&+g)jfq4t7MJuvv+K;PN z_lT|WU7S?XZ!u*HDI;2{hK50c#)#o*GM`b1bDo`m+d07ch;`4C9Ej56)U{)wk^99^a}8t-kzjz=QWsW_cB?q^=a*676iuul;!MonvWE|MIps zotWXe^Q@u|x+b^8@4e{TX!VegqpJMY6a0~~T=fU8F&n?4NyS)Z!m&?1cAu=NYhCtj zwF|s!#smAPY3(ir5y3~MtPd-4`nfrX36O5tqjukvl(=E~0mtTw-!nHg6%^_=-+yOn z{JM){XAW+PzHcopEVbG!NkJJ>qj@|c!@7LkjDGf0Wep=P*4sXEdmJ(O zQe%4cZ8y0hE9z$H!xfQ~Q{(8hw|sQ!)ZFlN1z%?!)L<*>R|i-BChPfgJt?y-4;$rt z(|N-<`v&rk6FR5l(R)^vjw=Vw9Nch!wG5q8e!af)c4@ZGxWN6Z-&+wrx1`cm`R=tt zJ}pUAeP^^m6O+|lCJwdueMB9u8?LHGjwqi9Z^m;ZtB!)m67C|wxoGO!4zn8F?7yB+?l3hG*>$^L zAwJ4glrulBs%mM?`URhE-#lRReCSWB)~jdNv`WdSUdynE9 zm|-XQUnje?sz+D4CG8b1ggWdFlppnA0`v3#7(V@8pFi%14sP!}{*X*Ot)^D=bCG}N;l!Hr@?&o;d#iXT+OTeRYoDzVu}tZb$hyX? zvaFKzYeV+swntptQxoYP9(k*{bJ4k?ecO&E7ApK_*iFiXCipnG6wo2lUE0<}_}9%n zb;!G|!^P-&!EIh1W91N=8}0q4Q|I-jA3qFCQJo`B+;1hE4vcRWohT=d-P?K|56n0# zF_iA;{DMMmsZuwUDwk%Q(VcDGUFw>)|Kb4T5BBids~`9jP8c_E&#F2(H*a4QRPp(UaL=l%$T%VpU8U&gYvUK5bz8>!So~wARgir+8{&>VG0GuqBxi{=@X3%r%d7!c;^=(Smv3&A<-Cbp~Es_E}qi? zQ4_F=b~8Cq6%h1H!yJMbtSPk$!j&wgVoJ9^Nxkii;e*GXSm&@pK`Yd1F3)j z%2&@Mn%z_RdI`<;DOS&DS<3QANNJYAG*VbfU?_}hP#RXNK~kh7&EOm+p=l0#0ZNr% z;^_p2GeZGzkrv=+Xqv%T84cqELBKe|kg%F1X;>oVNK%5Ka+Y9UfcVg;1yxDMzvz`2 ziUm*@A!Vevgo9~LMgl0Q9H!N3IZUuLg5Vg&w6II$ri#$v-9 zaLOl0*+d0E-6TLMeX()1^N`d^L z=KFggH5j)QSrjlya|B89xjR#s0mP%y= zC+9#rVt?ao!O&0)qi`7*FRzax%Hndh909|Plp=s4V5de9P^2=J1L?#Fj>KO*Wng^b zFp9tk9*Q6oPEeBnX*^G{`BL=s!0;6R-;vTK@WL$$jNPs^a61E+J@K>K-jjK3K860l zpC>2EKWG6!zcKPs@qH7nH{p7z2)vZ=o9cQKu9u3yO9{WJuD>l@HqYM|aS7m`$Yk&~ zX#75J7w{&jmwM7fU&}is!F@$#I(X`9@Q*YJ1pNk^KURXmq9I_hH?Im%_HMK3;ea@e z=FY4HOQ_1%CzPmVCU4n$f7QG}8S#SMv~T;7`h^LCU74$QW(-pxdl1K7B{VXF-tVtX a>Lu7%@be_{(k|4#0u2_ literal 0 HcmV?d00001 diff --git a/src/constants.hpp b/src/constants.hpp index 9766bcc8..e99c1c7f 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -24,6 +24,7 @@ inline constexpr uint MAX_OPEN_REGION_FILES = 32; inline constexpr blockid_t BLOCK_AIR = 0; inline constexpr blockid_t BLOCK_OBSTACLE = 1; +inline constexpr blockid_t BLOCK_STRUCT_AIR = 2; inline constexpr itemid_t ITEM_EMPTY = 0; inline constexpr entityid_t ENTITY_NONE = 0; diff --git a/src/core_defs.cpp b/src/core_defs.cpp index 5fe46341..7d19acd1 100644 --- a/src/core_defs.cpp +++ b/src/core_defs.cpp @@ -23,9 +23,10 @@ void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) { block.model = BlockModel::none; block.pickingItem = CORE_EMPTY; } - - ItemDef& item = builder->items.create(CORE_EMPTY); - item.iconType = item_icon_type::none; + { + ItemDef& item = builder->items.create(CORE_EMPTY); + item.iconType = item_icon_type::none; + } auto bindsFile = paths->getResourcesFolder()/fs::path("bindings.toml"); if (fs::is_regular_file(bindsFile)) { @@ -47,4 +48,20 @@ void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) { item.placingBlock = CORE_OBSTACLE; item.caption = block.caption; } + { + Block& block = builder->blocks.create(CORE_STRUCT_AIR); + for (uint i = 0; i < 6; i++) { + block.textureFaces[i] = "struct_air"; + } + block.drawGroup = -1; + block.skyLightPassing = true; + block.lightPassing = true; + block.hitboxes = {AABB()}; + block.obstacle = false; + ItemDef& item = builder->items.create(CORE_STRUCT_AIR+".item"); + item.iconType = item_icon_type::block; + item.icon = CORE_STRUCT_AIR; + item.placingBlock = CORE_STRUCT_AIR; + item.caption = block.caption; + } } diff --git a/src/core_defs.hpp b/src/core_defs.hpp index ccfa2e69..0f25de3a 100644 --- a/src/core_defs.hpp +++ b/src/core_defs.hpp @@ -3,8 +3,9 @@ #include inline const std::string CORE_EMPTY = "core:empty"; -inline const std::string CORE_OBSTACLE = "core:obstacle"; inline const std::string CORE_AIR = "core:air"; +inline const std::string CORE_OBSTACLE = "core:obstacle"; +inline const std::string CORE_STRUCT_AIR = "core:struct_air"; inline const std::string TEXTURE_NOTFOUND = "notfound"; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 365a63fc..e31f3cb0 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -348,7 +348,11 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { const auto& structVoxel = structVoxels[vox_index(x, y, z, size.x, size.z)]; if (structVoxel.id) { - voxels[vox_index(sx, sy, sz)] = structVoxel; + if (structVoxel.id == BLOCK_STRUCT_AIR) { + voxels[vox_index(sx, sy, sz)] = {0, {}}; + } else { + voxels[vox_index(sx, sy, sz)] = structVoxel; + } } } } From 736fdbf96444db62916197835daee4487b0748ef Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 5 Oct 2024 00:53:41 +0300 Subject: [PATCH 104/139] move lua libs to /logic/scripting/lua/libs --- src/logic/scripting/lua/{ => libs}/api_lua.hpp | 2 +- src/logic/scripting/lua/{ => libs}/lib__rigidbody.cpp | 0 src/logic/scripting/lua/{ => libs}/lib__skeleton.cpp | 0 src/logic/scripting/lua/{ => libs}/lib__transform.cpp | 0 src/logic/scripting/lua/{ => libs}/libaudio.cpp | 0 src/logic/scripting/lua/{ => libs}/libblock.cpp | 0 src/logic/scripting/lua/{ => libs}/libcamera.cpp | 0 src/logic/scripting/lua/{ => libs}/libconsole.cpp | 0 src/logic/scripting/lua/{ => libs}/libcore.cpp | 0 src/logic/scripting/lua/{ => libs}/libentity.cpp | 0 src/logic/scripting/lua/{ => libs}/libentity.hpp | 0 src/logic/scripting/lua/{ => libs}/libfile.cpp | 0 src/logic/scripting/lua/{ => libs}/libgeneration.cpp | 2 +- src/logic/scripting/lua/{ => libs}/libgui.cpp | 0 src/logic/scripting/lua/{ => libs}/libhud.cpp | 0 src/logic/scripting/lua/{ => libs}/libinput.cpp | 0 src/logic/scripting/lua/{ => libs}/libinventory.cpp | 0 src/logic/scripting/lua/{ => libs}/libitem.cpp | 0 src/logic/scripting/lua/{ => libs}/libjson.cpp | 0 src/logic/scripting/lua/{ => libs}/libmat4.cpp | 0 src/logic/scripting/lua/{ => libs}/libpack.cpp | 0 src/logic/scripting/lua/{ => libs}/libplayer.cpp | 0 src/logic/scripting/lua/{ => libs}/libquat.cpp | 0 src/logic/scripting/lua/{ => libs}/libtime.cpp | 0 src/logic/scripting/lua/{ => libs}/libtoml.cpp | 0 src/logic/scripting/lua/{ => libs}/libvecn.cpp | 0 src/logic/scripting/lua/{ => libs}/libworld.cpp | 0 src/logic/scripting/lua/lua_engine.cpp | 2 +- src/logic/scripting/lua/lua_extensions.cpp | 2 +- src/logic/scripting/lua/lua_overrides.cpp | 2 +- src/logic/scripting/scripting_hud.cpp | 2 +- 31 files changed, 6 insertions(+), 6 deletions(-) rename src/logic/scripting/lua/{ => libs}/api_lua.hpp (98%) rename src/logic/scripting/lua/{ => libs}/lib__rigidbody.cpp (100%) rename src/logic/scripting/lua/{ => libs}/lib__skeleton.cpp (100%) rename src/logic/scripting/lua/{ => libs}/lib__transform.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libaudio.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libblock.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libcamera.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libconsole.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libcore.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libentity.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libentity.hpp (100%) rename src/logic/scripting/lua/{ => libs}/libfile.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libgeneration.cpp (98%) rename src/logic/scripting/lua/{ => libs}/libgui.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libhud.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libinput.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libinventory.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libitem.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libjson.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libmat4.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libpack.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libplayer.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libquat.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libtime.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libtoml.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libvecn.cpp (100%) rename src/logic/scripting/lua/{ => libs}/libworld.cpp (100%) diff --git a/src/logic/scripting/lua/api_lua.hpp b/src/logic/scripting/lua/libs/api_lua.hpp similarity index 98% rename from src/logic/scripting/lua/api_lua.hpp rename to src/logic/scripting/lua/libs/api_lua.hpp index 6a8f3dd2..f856f5f4 100644 --- a/src/logic/scripting/lua/api_lua.hpp +++ b/src/logic/scripting/lua/libs/api_lua.hpp @@ -3,7 +3,7 @@ #include #include -#include "lua_util.hpp" +#include "../lua_util.hpp" /// Definitions can be found in local .cpp files /// having same names as declarations diff --git a/src/logic/scripting/lua/lib__rigidbody.cpp b/src/logic/scripting/lua/libs/lib__rigidbody.cpp similarity index 100% rename from src/logic/scripting/lua/lib__rigidbody.cpp rename to src/logic/scripting/lua/libs/lib__rigidbody.cpp diff --git a/src/logic/scripting/lua/lib__skeleton.cpp b/src/logic/scripting/lua/libs/lib__skeleton.cpp similarity index 100% rename from src/logic/scripting/lua/lib__skeleton.cpp rename to src/logic/scripting/lua/libs/lib__skeleton.cpp diff --git a/src/logic/scripting/lua/lib__transform.cpp b/src/logic/scripting/lua/libs/lib__transform.cpp similarity index 100% rename from src/logic/scripting/lua/lib__transform.cpp rename to src/logic/scripting/lua/libs/lib__transform.cpp diff --git a/src/logic/scripting/lua/libaudio.cpp b/src/logic/scripting/lua/libs/libaudio.cpp similarity index 100% rename from src/logic/scripting/lua/libaudio.cpp rename to src/logic/scripting/lua/libs/libaudio.cpp diff --git a/src/logic/scripting/lua/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp similarity index 100% rename from src/logic/scripting/lua/libblock.cpp rename to src/logic/scripting/lua/libs/libblock.cpp diff --git a/src/logic/scripting/lua/libcamera.cpp b/src/logic/scripting/lua/libs/libcamera.cpp similarity index 100% rename from src/logic/scripting/lua/libcamera.cpp rename to src/logic/scripting/lua/libs/libcamera.cpp diff --git a/src/logic/scripting/lua/libconsole.cpp b/src/logic/scripting/lua/libs/libconsole.cpp similarity index 100% rename from src/logic/scripting/lua/libconsole.cpp rename to src/logic/scripting/lua/libs/libconsole.cpp diff --git a/src/logic/scripting/lua/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp similarity index 100% rename from src/logic/scripting/lua/libcore.cpp rename to src/logic/scripting/lua/libs/libcore.cpp diff --git a/src/logic/scripting/lua/libentity.cpp b/src/logic/scripting/lua/libs/libentity.cpp similarity index 100% rename from src/logic/scripting/lua/libentity.cpp rename to src/logic/scripting/lua/libs/libentity.cpp diff --git a/src/logic/scripting/lua/libentity.hpp b/src/logic/scripting/lua/libs/libentity.hpp similarity index 100% rename from src/logic/scripting/lua/libentity.hpp rename to src/logic/scripting/lua/libs/libentity.hpp diff --git a/src/logic/scripting/lua/libfile.cpp b/src/logic/scripting/lua/libs/libfile.cpp similarity index 100% rename from src/logic/scripting/lua/libfile.cpp rename to src/logic/scripting/lua/libs/libfile.cpp diff --git a/src/logic/scripting/lua/libgeneration.cpp b/src/logic/scripting/lua/libs/libgeneration.cpp similarity index 98% rename from src/logic/scripting/lua/libgeneration.cpp rename to src/logic/scripting/lua/libs/libgeneration.cpp index ec425345..d24fae0c 100644 --- a/src/logic/scripting/lua/libgeneration.cpp +++ b/src/logic/scripting/lua/libs/libgeneration.cpp @@ -7,7 +7,7 @@ #include "world/generator/VoxelFragment.hpp" #include "content/ContentLoader.hpp" #include "engine.hpp" -#include "lua_custom_types.hpp" +#include "../lua_custom_types.hpp" using namespace scripting; diff --git a/src/logic/scripting/lua/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp similarity index 100% rename from src/logic/scripting/lua/libgui.cpp rename to src/logic/scripting/lua/libs/libgui.cpp diff --git a/src/logic/scripting/lua/libhud.cpp b/src/logic/scripting/lua/libs/libhud.cpp similarity index 100% rename from src/logic/scripting/lua/libhud.cpp rename to src/logic/scripting/lua/libs/libhud.cpp diff --git a/src/logic/scripting/lua/libinput.cpp b/src/logic/scripting/lua/libs/libinput.cpp similarity index 100% rename from src/logic/scripting/lua/libinput.cpp rename to src/logic/scripting/lua/libs/libinput.cpp diff --git a/src/logic/scripting/lua/libinventory.cpp b/src/logic/scripting/lua/libs/libinventory.cpp similarity index 100% rename from src/logic/scripting/lua/libinventory.cpp rename to src/logic/scripting/lua/libs/libinventory.cpp diff --git a/src/logic/scripting/lua/libitem.cpp b/src/logic/scripting/lua/libs/libitem.cpp similarity index 100% rename from src/logic/scripting/lua/libitem.cpp rename to src/logic/scripting/lua/libs/libitem.cpp diff --git a/src/logic/scripting/lua/libjson.cpp b/src/logic/scripting/lua/libs/libjson.cpp similarity index 100% rename from src/logic/scripting/lua/libjson.cpp rename to src/logic/scripting/lua/libs/libjson.cpp diff --git a/src/logic/scripting/lua/libmat4.cpp b/src/logic/scripting/lua/libs/libmat4.cpp similarity index 100% rename from src/logic/scripting/lua/libmat4.cpp rename to src/logic/scripting/lua/libs/libmat4.cpp diff --git a/src/logic/scripting/lua/libpack.cpp b/src/logic/scripting/lua/libs/libpack.cpp similarity index 100% rename from src/logic/scripting/lua/libpack.cpp rename to src/logic/scripting/lua/libs/libpack.cpp diff --git a/src/logic/scripting/lua/libplayer.cpp b/src/logic/scripting/lua/libs/libplayer.cpp similarity index 100% rename from src/logic/scripting/lua/libplayer.cpp rename to src/logic/scripting/lua/libs/libplayer.cpp diff --git a/src/logic/scripting/lua/libquat.cpp b/src/logic/scripting/lua/libs/libquat.cpp similarity index 100% rename from src/logic/scripting/lua/libquat.cpp rename to src/logic/scripting/lua/libs/libquat.cpp diff --git a/src/logic/scripting/lua/libtime.cpp b/src/logic/scripting/lua/libs/libtime.cpp similarity index 100% rename from src/logic/scripting/lua/libtime.cpp rename to src/logic/scripting/lua/libs/libtime.cpp diff --git a/src/logic/scripting/lua/libtoml.cpp b/src/logic/scripting/lua/libs/libtoml.cpp similarity index 100% rename from src/logic/scripting/lua/libtoml.cpp rename to src/logic/scripting/lua/libs/libtoml.cpp diff --git a/src/logic/scripting/lua/libvecn.cpp b/src/logic/scripting/lua/libs/libvecn.cpp similarity index 100% rename from src/logic/scripting/lua/libvecn.cpp rename to src/logic/scripting/lua/libs/libvecn.cpp diff --git a/src/logic/scripting/lua/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp similarity index 100% rename from src/logic/scripting/lua/libworld.cpp rename to src/logic/scripting/lua/libs/libworld.cpp diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 6ae427e3..8055fc54 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -5,7 +5,7 @@ #include "debug/Logger.hpp" #include "util/stringutil.hpp" -#include "api_lua.hpp" +#include "libs/api_lua.hpp" #include "lua_custom_types.hpp" static debug::Logger logger("lua-state"); diff --git a/src/logic/scripting/lua/lua_extensions.cpp b/src/logic/scripting/lua/lua_extensions.cpp index 51185898..e1d7a230 100644 --- a/src/logic/scripting/lua/lua_extensions.cpp +++ b/src/logic/scripting/lua/lua_extensions.cpp @@ -1,6 +1,6 @@ #include -#include "api_lua.hpp" +#include "libs/api_lua.hpp" #include "debug/Logger.hpp" static debug::Logger logger("lua-debug"); diff --git a/src/logic/scripting/lua/lua_overrides.cpp b/src/logic/scripting/lua/lua_overrides.cpp index 8233d95a..12b0bbff 100644 --- a/src/logic/scripting/lua/lua_overrides.cpp +++ b/src/logic/scripting/lua/lua_overrides.cpp @@ -1,6 +1,6 @@ #include -#include "api_lua.hpp" +#include "libs/api_lua.hpp" /// @brief Modified version of luaB_print from lbaselib.c int l_print(lua::State* L) { diff --git a/src/logic/scripting/scripting_hud.cpp b/src/logic/scripting/scripting_hud.cpp index f48e42da..fa1cfe12 100644 --- a/src/logic/scripting/scripting_hud.cpp +++ b/src/logic/scripting/scripting_hud.cpp @@ -5,7 +5,7 @@ #include "files/files.hpp" #include "frontend/hud.hpp" #include "objects/Player.hpp" -#include "lua/api_lua.hpp" +#include "lua/libs/api_lua.hpp" #include "lua/lua_engine.hpp" #include "scripting.hpp" From 756de7b0b1873b375fd0275488b430ed143bdc4f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 5 Oct 2024 17:32:03 +0300 Subject: [PATCH 105/139] refactor lua_engine --- src/logic/scripting/lua/lua_engine.cpp | 55 +++++++++++++++----------- src/logic/scripting/lua/lua_engine.hpp | 7 ++++ 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 8055fc54..df8cffe6 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -28,49 +28,44 @@ static void remove_lib_funcs( } } -static void create_libs(lua::State* L) { - openlib(L, "audio", audiolib); +static void create_libs(lua::State* L, StateType stateType) { openlib(L, "block", blocklib); - openlib(L, "console", consolelib); openlib(L, "core", corelib); openlib(L, "file", filelib); - openlib(L, "gui", guilib); - openlib(L, "input", inputlib); - openlib(L, "inventory", inventorylib); openlib(L, "generation", generationlib); openlib(L, "item", itemlib); openlib(L, "json", jsonlib); openlib(L, "mat4", mat4lib); openlib(L, "pack", packlib); - openlib(L, "player", playerlib); openlib(L, "quat", quatlib); openlib(L, "time", timelib); openlib(L, "toml", tomllib); openlib(L, "vec2", vec2lib); openlib(L, "vec3", vec3lib); openlib(L, "vec4", vec4lib); - openlib(L, "world", worldlib); - openlib(L, "entities", entitylib); - openlib(L, "cameras", cameralib); + if (stateType == StateType::BASE) { + openlib(L, "gui", guilib); + openlib(L, "input", inputlib); + openlib(L, "inventory", inventorylib); + openlib(L, "world", worldlib); + openlib(L, "audio", audiolib); + openlib(L, "console", consolelib); + openlib(L, "player", playerlib); - // components - openlib(L, "__skeleton", skeletonlib); - openlib(L, "__rigidbody", rigidbodylib); - openlib(L, "__transform", transformlib); + openlib(L, "entities", entitylib); + openlib(L, "cameras", cameralib); + + // components + openlib(L, "__skeleton", skeletonlib); + openlib(L, "__rigidbody", rigidbodylib); + openlib(L, "__transform", transformlib); + } addfunc(L, "print", lua::wrap); } -void lua::initialize() { - logger.info() << LUA_VERSION; - logger.info() << LUAJIT_VERSION; - - auto L = luaL_newstate(); - if (L == nullptr) { - throw luaerror("could not to initialize Lua"); - } - main_thread = L; +void lua::init_state(lua::State* L, StateType stateType) { // Allowed standard libraries pop(L, luaopen_base(L)); pop(L, luaopen_math(L)); @@ -83,7 +78,7 @@ void lua::initialize() { const char* removed_os[] { "execute", "exit", "remove", "rename", "setlocale", "tmpname", nullptr}; remove_lib_funcs(L, "os", removed_os); - create_libs(L); + create_libs(L, stateType); pushglobals(L); setglobal(L, env_name(0)); @@ -101,6 +96,18 @@ void lua::initialize() { newusertype(L); } +void lua::initialize() { + logger.info() << LUA_VERSION; + logger.info() << LUAJIT_VERSION; + + auto L = luaL_newstate(); + if (L == nullptr) { + throw luaerror("could not to initialize Lua"); + } + main_thread = L; + init_state(L, StateType::BASE); +} + void lua::finalize() { lua_close(main_thread); } diff --git a/src/logic/scripting/lua/lua_engine.hpp b/src/logic/scripting/lua/lua_engine.hpp index 1690fbbd..b046fb6b 100644 --- a/src/logic/scripting/lua/lua_engine.hpp +++ b/src/logic/scripting/lua/lua_engine.hpp @@ -8,6 +8,11 @@ #include "lua_util.hpp" namespace lua { + enum class StateType { + BASE, + GENERATOR, + }; + void initialize(); void finalize(); @@ -17,4 +22,6 @@ namespace lua { std::function args = [](auto*) { return 0; } ); lua::State* get_main_thread(); + + void init_state(lua::State* L, StateType stateType); } From 7c73515d2d10a89acf38500a6be551ba1d87288e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 5 Oct 2024 17:35:31 +0300 Subject: [PATCH 106/139] update workflows --- .github/workflows/build.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d6c9c9b..f5155aab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main" ] pull_request: - branches: [ "main" ] + branches: [ "main", "heightmaps" ] # TODO: remove 'heightmaps' after merge jobs: build: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 1f9675ca..fefbbee7 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ "main", "heightmaps" ] # TODO: remove 'heightmaps' after merge jobs: build-dmg: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index f93b5ac9..349b1447 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main" ] pull_request: - branches: [ "main" ] + branches: [ "main", "heightmaps" ] # TODO: remove 'heightmaps' after merge jobs: build-windows: From 091805a16e9e827cc687d6f39c0de7744df2eb0c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 6 Oct 2024 17:28:44 +0300 Subject: [PATCH 107/139] refactor scripting_world_generation.cpp --- .../scripting/scripting_world_generation.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index dd4d8ad1..e935d818 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -15,20 +15,22 @@ #include "util/timeutil.hpp" class LuaGeneratorScript : public GeneratorScript { + lua::State* L; const GeneratorDef& def; scriptenv env; public: LuaGeneratorScript( + lua::State* L, const GeneratorDef& def, scriptenv env) - : def(def), + : L(L), + def(def), env(std::move(env)) {} std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, uint bpd ) override { - auto L = lua::get_main_thread(); lua::pushenv(L, *env); if (lua::getfield(L, "generate_heightmap")) { lua::pushivec_stack(L, offset); @@ -51,7 +53,6 @@ public: std::vector> maps; uint biomeParameters = def.biomeParameters; - auto L = lua::get_main_thread(); lua::pushenv(L, *env); if (lua::getfield(L, "generate_biome_parameters")) { lua::pushivec_stack(L, offset); @@ -80,7 +81,6 @@ public: ) override { std::vector placements; - auto L = lua::get_main_thread(); lua::stackguard _(L); lua::pushenv(L, *env); if (lua::getfield(L, "place_structures")) { @@ -97,7 +97,9 @@ public: lua::rawgeti(L, 1); int structIndex = 0; if (lua::isstring(L, -1)) { - const auto& found = def.structuresIndices.find(lua::require_string(L, -1)); + const auto& found = def.structuresIndices.find( + lua::require_string(L, -1) + ); if (found != def.structuresIndices.end()) { structIndex = found->second; } @@ -144,11 +146,10 @@ std::unique_ptr scripting::load_generator( lua::pop(L, load_script(*env, "generator", file)); } else { // Use default (empty) script - lua::pop(L, lua::execute(lua::get_main_thread(), *env, "", "")); + lua::pop(L, lua::execute(L, *env, "", "")); } return std::make_unique( - def, - std::move(env) + L, def, std::move(env) ); } From 73d96fd4f777d1a730953a52ba9a47c2e0c7a503 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 6 Oct 2024 18:23:33 +0300 Subject: [PATCH 108/139] move generator script execution to an isolated Lua state --- src/logic/scripting/lua/lua_engine.cpp | 36 +++++--- src/logic/scripting/lua/lua_engine.hpp | 10 ++- src/logic/scripting/lua/lua_util.cpp | 2 +- src/logic/scripting/lua/lua_util.hpp | 6 +- src/logic/scripting/scripting.cpp | 87 +++++++++---------- src/logic/scripting/scripting_functional.cpp | 6 +- src/logic/scripting/scripting_hud.cpp | 10 +-- .../scripting/scripting_world_generation.cpp | 19 +++- 8 files changed, 101 insertions(+), 75 deletions(-) diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index df8cffe6..b6cd8446 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -17,7 +17,7 @@ luaerror::luaerror(const std::string& message) : std::runtime_error(message) { } static void remove_lib_funcs( - lua::State* L, const char* libname, const char* funcs[] + State* L, const char* libname, const char* funcs[] ) { if (getglobal(L, libname)) { for (uint i = 0; funcs[i]; i++) { @@ -28,7 +28,15 @@ static void remove_lib_funcs( } } -static void create_libs(lua::State* L, StateType stateType) { +[[nodiscard]] scriptenv lua::create_environment(State* L) { + int id = lua::create_environment(L, 0); + return std::shared_ptr(new int(id), [=](int* id) { //-V508 + lua::remove_environment(L, *id); + delete id; + }); +} + +static void create_libs(State* L, StateType stateType) { openlib(L, "block", blocklib); openlib(L, "core", corelib); openlib(L, "file", filelib); @@ -65,7 +73,7 @@ static void create_libs(lua::State* L, StateType stateType) { addfunc(L, "print", lua::wrap); } -void lua::init_state(lua::State* L, StateType stateType) { +void lua::init_state(State* L, StateType stateType) { // Allowed standard libraries pop(L, luaopen_base(L)); pop(L, luaopen_math(L)); @@ -100,20 +108,15 @@ void lua::initialize() { logger.info() << LUA_VERSION; logger.info() << LUAJIT_VERSION; - auto L = luaL_newstate(); - if (L == nullptr) { - throw luaerror("could not to initialize Lua"); - } - main_thread = L; - init_state(L, StateType::BASE); + main_thread = create_state(StateType::BASE); } void lua::finalize() { - lua_close(main_thread); + lua::close(main_thread); } bool lua::emit_event( - lua::State* L, const std::string& name, std::function args + State* L, const std::string& name, std::function args ) { getglobal(L, "events"); getfield(L, "emit"); @@ -124,6 +127,15 @@ bool lua::emit_event( return result; } -lua::State* lua::get_main_thread() { +State* lua::get_main_state() { return main_thread; } + +State* lua::create_state(StateType stateType) { + auto L = luaL_newstate(); + if (L == nullptr) { + throw luaerror("could not initialize Lua state"); + } + init_state(L, stateType); + return L; +} diff --git a/src/logic/scripting/lua/lua_engine.hpp b/src/logic/scripting/lua/lua_engine.hpp index b046fb6b..494de626 100644 --- a/src/logic/scripting/lua/lua_engine.hpp +++ b/src/logic/scripting/lua/lua_engine.hpp @@ -17,11 +17,13 @@ namespace lua { void finalize(); bool emit_event( - lua::State*, + State*, const std::string& name, - std::function args = [](auto*) { return 0; } + std::function args = [](auto*) { return 0; } ); - lua::State* get_main_thread(); + State* get_main_state(); + State* create_state(StateType stateType); + [[nodiscard]] scriptenv create_environment(State* L); - void init_state(lua::State* L, StateType stateType); + void init_state(State* L, StateType stateType); } diff --git a/src/logic/scripting/lua/lua_util.cpp b/src/logic/scripting/lua/lua_util.cpp index 6152a254..44583e4c 100644 --- a/src/logic/scripting/lua/lua_util.cpp +++ b/src/logic/scripting/lua/lua_util.cpp @@ -277,7 +277,7 @@ int lua::create_environment(State* L, int parent) { return id; } -void lua::removeEnvironment(State* L, int id) { +void lua::remove_environment(State* L, int id) { if (id == 0) { return; } diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index f365cdba..05b37c1d 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -546,9 +546,13 @@ namespace lua { return 0; } int create_environment(lua::State*, int parent); - void removeEnvironment(lua::State*, int id); + void remove_environment(lua::State*, int id); void dump_stack(lua::State*); + inline void close(lua::State* L) { + lua_close(L); + } + inline void addfunc( lua::State* L, const std::string& name, lua_CFunction func ) { diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 412bd403..b06e7258 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -43,7 +43,7 @@ void scripting::load_script(const fs::path& name, bool throwable) { auto paths = scripting::engine->getPaths(); fs::path file = paths->getResourcesFolder() / fs::path("scripts") / name; std::string src = files::read_string(file); - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); lua::loadbuffer(L, 0, src, file.u8string()); if (throwable) { lua::call(L, 0, 0); @@ -57,7 +57,7 @@ int scripting::load_script( ) { std::string src = files::read_string(file); logger.info() << "script (" << type << ") " << file.u8string(); - return lua::execute(lua::get_main_thread(), env, src, file.u8string()); + return lua::execute(lua::get_main_state(), env, src, file.u8string()); } void scripting::initialize(Engine* engine) { @@ -74,18 +74,13 @@ void scripting::initialize(Engine* engine) { } [[nodiscard]] scriptenv scripting::create_environment() { - auto L = lua::get_main_thread(); - int id = lua::create_environment(L, 0); - return std::shared_ptr(new int(id), [=](int* id) { //-V508 - lua::removeEnvironment(L, *id); - delete id; - }); + return lua::create_environment(lua::get_main_state()); } [[nodiscard]] scriptenv scripting::create_pack_environment( const ContentPack& pack ) { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); int id = lua::create_environment(L, 0); lua::pushenv(L, id); lua::pushvalue(L, -1); @@ -94,7 +89,7 @@ void scripting::initialize(Engine* engine) { lua::setfield(L, "PACK_ID"); lua::pop(L); return std::shared_ptr(new int(id), [=](int* id) { //-V508 - lua::removeEnvironment(L, *id); + lua::remove_environment(L, *id); delete id; }); } @@ -102,7 +97,7 @@ void scripting::initialize(Engine* engine) { [[nodiscard]] scriptenv scripting::create_doc_environment( const scriptenv& parent, const std::string& name ) { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); int id = lua::create_environment(L, *parent); lua::pushenv(L, id); lua::pushvalue(L, -1); @@ -121,7 +116,7 @@ void scripting::initialize(Engine* engine) { } lua::pop(L); return std::shared_ptr(new int(id), [=](int* id) { //-V508 - lua::removeEnvironment(L, *id); + lua::remove_environment(L, *id); delete id; }); } @@ -129,7 +124,7 @@ void scripting::initialize(Engine* engine) { [[nodiscard]] static scriptenv create_component_environment( const scriptenv& parent, int entityIdx, const std::string& name ) { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); int id = lua::create_environment(L, *parent); lua::pushvalue(L, entityIdx); @@ -151,13 +146,13 @@ void scripting::initialize(Engine* engine) { lua::pop(L); return std::shared_ptr(new int(id), [=](int* id) { //-V508 - lua::removeEnvironment(L, *id); + lua::remove_environment(L, *id); delete id; }); } void scripting::process_post_runnables() { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); if (lua::getglobal(L, "__process_post_runnables")) { lua::call_nothrow(L, 0); } @@ -171,28 +166,28 @@ void scripting::on_world_load(LevelController* controller) { scripting::controller = controller; load_script("world.lua", false); - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); for (auto& pack : scripting::engine->getContentPacks()) { lua::emit_event(L, pack.id + ".worldopen"); } } void scripting::on_world_tick() { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); for (auto& pack : scripting::engine->getContentPacks()) { lua::emit_event(L, pack.id + ".worldtick"); } } void scripting::on_world_save() { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); for (auto& pack : scripting::engine->getContentPacks()) { lua::emit_event(L, pack.id + ".worldsave"); } } void scripting::on_world_quit() { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); for (auto& pack : scripting::engine->getContentPacks()) { lua::emit_event(L, pack.id + ".worldquit"); } @@ -217,21 +212,21 @@ void scripting::on_world_quit() { void scripting::on_blocks_tick(const Block& block, int tps) { std::string name = block.name + ".blockstick"; - lua::emit_event(lua::get_main_thread(), name, [tps](auto L) { + lua::emit_event(lua::get_main_state(), name, [tps](auto L) { return lua::pushinteger(L, tps); }); } void scripting::update_block(const Block& block, int x, int y, int z) { std::string name = block.name + ".update"; - lua::emit_event(lua::get_main_thread(), name, [x, y, z](auto L) { + lua::emit_event(lua::get_main_state(), name, [x, y, z](auto L) { return lua::pushivec_stack(L, glm::ivec3(x, y, z)); }); } void scripting::random_update_block(const Block& block, int x, int y, int z) { std::string name = block.name + ".randupdate"; - lua::emit_event(lua::get_main_thread(), name, [x, y, z](auto L) { + lua::emit_event(lua::get_main_state(), name, [x, y, z](auto L) { return lua::pushivec_stack(L, glm::ivec3(x, y, z)); }); } @@ -240,7 +235,7 @@ void scripting::on_block_placed( Player* player, const Block& block, int x, int y, int z ) { std::string name = block.name + ".placed"; - lua::emit_event(lua::get_main_thread(), name, [x, y, z, player](auto L) { + lua::emit_event(lua::get_main_state(), name, [x, y, z, player](auto L) { lua::pushivec_stack(L, glm::ivec3(x, y, z)); lua::pushinteger(L, player ? player->getId() : -1); return 4; @@ -254,7 +249,7 @@ void scripting::on_block_placed( for (auto& [packid, pack] : content->getPacks()) { if (pack->worldfuncsset.onblockplaced) { lua::emit_event( - lua::get_main_thread(), + lua::get_main_state(), packid + ".blockplaced", world_event_args ); @@ -268,7 +263,7 @@ void scripting::on_block_broken( if (block.rt.funcsset.onbroken) { std::string name = block.name + ".broken"; lua::emit_event( - lua::get_main_thread(), + lua::get_main_state(), name, [x, y, z, player](auto L) { lua::pushivec_stack(L, glm::ivec3(x, y, z)); @@ -286,7 +281,7 @@ void scripting::on_block_broken( for (auto& [packid, pack] : content->getPacks()) { if (pack->worldfuncsset.onblockbroken) { lua::emit_event( - lua::get_main_thread(), + lua::get_main_state(), packid + ".blockbroken", world_event_args ); @@ -298,7 +293,7 @@ bool scripting::on_block_interact( Player* player, const Block& block, glm::ivec3 pos ) { std::string name = block.name + ".interact"; - return lua::emit_event(lua::get_main_thread(), name, [pos, player](auto L) { + return lua::emit_event(lua::get_main_state(), name, [pos, player](auto L) { lua::pushivec_stack(L, pos); lua::pushinteger(L, player->getId()); return 4; @@ -308,7 +303,7 @@ bool scripting::on_block_interact( bool scripting::on_item_use(Player* player, const ItemDef& item) { std::string name = item.name + ".use"; return lua::emit_event( - lua::get_main_thread(), + lua::get_main_state(), name, [player](lua::State* L) { return lua::pushinteger(L, player->getId()); } ); @@ -319,7 +314,7 @@ bool scripting::on_item_use_on_block( ) { std::string name = item.name + ".useon"; return lua::emit_event( - lua::get_main_thread(), + lua::get_main_state(), name, [ipos, normal, player](auto L) { lua::pushivec_stack(L, ipos); @@ -335,7 +330,7 @@ bool scripting::on_item_break_block( ) { std::string name = item.name + ".blockbreakby"; return lua::emit_event( - lua::get_main_thread(), + lua::get_main_state(), name, [x, y, z, player](auto L) { lua::pushivec_stack(L, glm::ivec3(x, y, z)); @@ -348,7 +343,7 @@ bool scripting::on_item_break_block( dv::value scripting::get_component_value( const scriptenv& env, const std::string& name ) { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); lua::pushenv(L, *env); if (lua::getfield(L, name)) { return lua::tovalue(L, -1); @@ -363,7 +358,7 @@ void scripting::on_entity_spawn( const dv::value& args, const dv::value& saved ) { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); lua::requireglobal(L, STDCOMP); if (lua::getfield(L, "new_Entity")) { lua::pushinteger(L, eid); @@ -431,7 +426,7 @@ static void process_entity_callback( const std::string& name, std::function args ) { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); lua::pushenv(L, *env); if (lua::getfield(L, name)) { if (args) { @@ -461,7 +456,7 @@ void scripting::on_entity_despawn(const Entity& entity) { process_entity_callback( entity, "on_despawn", &entity_funcs_set::on_despawn, nullptr ); - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); lua::get_from(L, "stdcomp", "remove_Entity", true); lua::pushinteger(L, entity.getUID()); lua::call(L, 1, 0); @@ -561,7 +556,7 @@ void scripting::on_entity_used(const Entity& entity, Player* player) { } void scripting::on_entities_update(int tps, int parts, int part) { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); lua::get_from(L, STDCOMP, "update", true); lua::pushinteger(L, tps); lua::pushinteger(L, parts); @@ -571,7 +566,7 @@ void scripting::on_entities_update(int tps, int parts, int part) { } void scripting::on_entities_render(float delta) { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); lua::get_from(L, STDCOMP, "render", true); lua::pushnumber(L, delta); lua::call_nothrow(L, 1, 0); @@ -584,7 +579,7 @@ void scripting::on_ui_open( auto argsptr = std::make_shared>(std::move(args)); std::string name = layout->getId() + ".open"; - lua::emit_event(lua::get_main_thread(), name, [=](auto L) { + lua::emit_event(lua::get_main_state(), name, [=](auto L) { for (const auto& value : *argsptr) { lua::pushvalue(L, value); } @@ -596,7 +591,7 @@ void scripting::on_ui_progress( UiDocument* layout, int workDone, int workTotal ) { std::string name = layout->getId() + ".progress"; - lua::emit_event(lua::get_main_thread(), name, [=](auto L) { + lua::emit_event(lua::get_main_state(), name, [=](auto L) { lua::pushinteger(L, workDone); lua::pushinteger(L, workTotal); return 2; @@ -605,7 +600,7 @@ void scripting::on_ui_progress( void scripting::on_ui_close(UiDocument* layout, Inventory* inventory) { std::string name = layout->getId() + ".close"; - lua::emit_event(lua::get_main_thread(), name, [inventory](auto L) { + lua::emit_event(lua::get_main_state(), name, [inventory](auto L) { return lua::pushinteger(L, inventory ? inventory->getId() : 0); }); } @@ -613,7 +608,7 @@ void scripting::on_ui_close(UiDocument* layout, Inventory* inventory) { bool scripting::register_event( int env, const std::string& name, const std::string& id ) { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); if (lua::pushenv(L, env) == 0) { lua::pushglobals(L); } @@ -635,7 +630,7 @@ bool scripting::register_event( } int scripting::get_values_on_stack() { - return lua::gettop(lua::get_main_thread()); + return lua::gettop(lua::get_main_state()); } void scripting::load_block_script( @@ -645,7 +640,7 @@ void scripting::load_block_script( block_funcs_set& funcsset ) { int env = *senv; - lua::pop(lua::get_main_thread(), load_script(env, "block", file)); + lua::pop(lua::get_main_state(), load_script(env, "block", file)); funcsset.init = register_event(env, "init", prefix + ".init"); funcsset.update = register_event(env, "on_update", prefix + ".update"); funcsset.randupdate = @@ -665,7 +660,7 @@ void scripting::load_item_script( item_funcs_set& funcsset ) { int env = *senv; - lua::pop(lua::get_main_thread(), load_script(env, "item", file)); + lua::pop(lua::get_main_state(), load_script(env, "item", file)); funcsset.init = register_event(env, "init", prefix + ".init"); funcsset.on_use = register_event(env, "on_use", prefix + ".use"); funcsset.on_use_on_block = @@ -677,7 +672,7 @@ void scripting::load_item_script( void scripting::load_entity_component( const std::string& name, const fs::path& file ) { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); std::string src = files::read_string(file); logger.info() << "script (component) " << file.u8string(); lua::loadbuffer(L, 0, src, "C!" + name); @@ -691,7 +686,7 @@ void scripting::load_world_script( world_funcs_set& funcsset ) { int env = *senv; - lua::pop(lua::get_main_thread(), load_script(env, "world", file)); + lua::pop(lua::get_main_state(), load_script(env, "world", file)); register_event(env, "init", prefix + ".init"); register_event(env, "on_world_open", prefix + ".worldopen"); register_event(env, "on_world_tick", prefix + ".worldtick"); @@ -710,7 +705,7 @@ void scripting::load_layout_script( uidocscript& script ) { int env = *senv; - lua::pop(lua::get_main_thread(), load_script(env, "layout", file)); + lua::pop(lua::get_main_state(), load_script(env, "layout", file)); script.onopen = register_event(env, "on_open", prefix + ".open"); script.onprogress = register_event(env, "on_progress", prefix + ".progress"); diff --git a/src/logic/scripting/scripting_functional.cpp b/src/logic/scripting/scripting_functional.cpp index 02398b49..d09c9432 100644 --- a/src/logic/scripting/scripting_functional.cpp +++ b/src/logic/scripting/scripting_functional.cpp @@ -12,7 +12,7 @@ static debug::Logger logger("scripting_func"); runnable scripting::create_runnable( const scriptenv& env, const std::string& src, const std::string& file ) { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); try { lua::loadbuffer(L, *env, src, file); return lua::create_runnable(L); @@ -25,7 +25,7 @@ runnable scripting::create_runnable( static lua::State* process_callback( const scriptenv& env, const std::string& src, const std::string& file ) { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); try { if (lua::eval(L, *env, src, file) != 0) { return L; @@ -163,7 +163,7 @@ vec2supplier scripting::create_vec2_supplier( value_to_string_func scripting::create_tostring( const scriptenv& env, const std::string& src, const std::string& file ) { - auto L = lua::get_main_thread(); + auto L = lua::get_main_state(); try { lua::loadbuffer(L, *env, src, file); lua::call(L, 0, 1); diff --git a/src/logic/scripting/scripting_hud.cpp b/src/logic/scripting/scripting_hud.cpp index fa1cfe12..a711453c 100644 --- a/src/logic/scripting/scripting_hud.cpp +++ b/src/logic/scripting/scripting_hud.cpp @@ -17,11 +17,11 @@ Hud* scripting::hud = nullptr; void scripting::on_frontend_init(Hud* hud) { scripting::hud = hud; - lua::openlib(lua::get_main_thread(), "hud", hudlib); + lua::openlib(lua::get_main_state(), "hud", hudlib); for (auto& pack : engine->getContentPacks()) { lua::emit_event( - lua::get_main_thread(), + lua::get_main_state(), pack.id + ".hudopen", [&](lua::State* L) { return lua::pushinteger(L, hud->getPlayer()->getId()); @@ -33,7 +33,7 @@ void scripting::on_frontend_init(Hud* hud) { void scripting::on_frontend_render() { for (auto& pack : engine->getContentPacks()) { lua::emit_event( - lua::get_main_thread(), + lua::get_main_state(), pack.id + ".hudrender", [&](lua::State* L) { return 0; } ); @@ -43,7 +43,7 @@ void scripting::on_frontend_render() { void scripting::on_frontend_close() { for (auto& pack : engine->getContentPacks()) { lua::emit_event( - lua::get_main_thread(), + lua::get_main_state(), pack.id + ".hudclose", [&](lua::State* L) { return lua::pushinteger(L, hud->getPlayer()->getId()); @@ -60,7 +60,7 @@ void scripting::load_hud_script( std::string src = files::read_string(file); logger.info() << "loading script " << file.u8string(); - lua::execute(lua::get_main_thread(), env, src, file.u8string()); + lua::execute(lua::get_main_state(), env, src, file.u8string()); register_event(env, "init", packid + ".init"); register_event(env, "on_hud_open", packid + ".hudopen"); diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index e935d818..c8bb2799 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -13,6 +13,10 @@ #include "data/dv.hpp" #include "world/generator/GeneratorDef.hpp" #include "util/timeutil.hpp" +#include "files/files.hpp" +#include "debug/Logger.hpp" + +static debug::Logger logger("generator-scripting"); class LuaGeneratorScript : public GeneratorScript { lua::State* L; @@ -28,6 +32,13 @@ public: env(std::move(env)) {} + virtual ~LuaGeneratorScript() { + env.reset(); + if (L != lua::get_main_state()) { + lua::close(L); + } + } + std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, uint bpd ) override { @@ -130,8 +141,8 @@ public: std::unique_ptr scripting::load_generator( const GeneratorDef& def, const fs::path& file, const std::string& dirPath ) { - auto env = create_environment(); - auto L = lua::get_main_thread(); + auto L = lua::create_state(lua::StateType::GENERATOR); + auto env = lua::create_environment(L); lua::stackguard _(L); lua::pushenv(L, *env); @@ -143,7 +154,9 @@ std::unique_ptr scripting::load_generator( lua::pop(L); if (fs::exists(file)) { - lua::pop(L, load_script(*env, "generator", file)); + std::string src = files::read_string(file); + logger.info() << "script (generator) " << file.u8string(); + lua::pop(L, lua::execute(L, *env, src, file.u8string())); } else { // Use default (empty) script lua::pop(L, lua::execute(L, *env, "", "")); From d9a44f3b87a355e605321238091c8fd28efa4f3b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 6 Oct 2024 21:07:27 +0300 Subject: [PATCH 109/139] minor refactor --- src/logic/scripting/scripting.cpp | 4 - src/logic/scripting/scripting.hpp | 1 - .../scripting/scripting_world_generation.cpp | 133 +++++++++--------- 3 files changed, 64 insertions(+), 74 deletions(-) diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index b06e7258..eaab27d3 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -73,10 +73,6 @@ void scripting::initialize(Engine* engine) { return std::make_shared(0); } -[[nodiscard]] scriptenv scripting::create_environment() { - return lua::create_environment(lua::get_main_state()); -} - [[nodiscard]] scriptenv scripting::create_pack_environment( const ContentPack& pack ) { diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index 8a1a39cd..a34c21cf 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -52,7 +52,6 @@ namespace scripting { scriptenv get_root_environment(); scriptenv create_pack_environment(const ContentPack& pack); - scriptenv create_environment(); scriptenv create_doc_environment( const scriptenv& parent, const std::string& name ); diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index c8bb2799..c7655084 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -16,45 +16,42 @@ #include "files/files.hpp" #include "debug/Logger.hpp" +using namespace lua; + static debug::Logger logger("generator-scripting"); class LuaGeneratorScript : public GeneratorScript { - lua::State* L; + State* L; const GeneratorDef& def; scriptenv env; public: - LuaGeneratorScript( - lua::State* L, - const GeneratorDef& def, - scriptenv env) - : L(L), - def(def), - env(std::move(env)) - {} + LuaGeneratorScript(State* L, const GeneratorDef& def, scriptenv env) + : L(L), def(def), env(std::move(env)) { + } virtual ~LuaGeneratorScript() { env.reset(); - if (L != lua::get_main_state()) { - lua::close(L); + if (L != get_main_state()) { + close(L); } } std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, uint bpd ) override { - lua::pushenv(L, *env); - if (lua::getfield(L, "generate_heightmap")) { - lua::pushivec_stack(L, offset); - lua::pushivec_stack(L, size); - lua::pushinteger(L, seed); - lua::pushinteger(L, bpd); - if (lua::call_nothrow(L, 6)) { - auto map = lua::touserdata(L, -1)->getHeightmap(); - lua::pop(L, 2); + pushenv(L, *env); + if (getfield(L, "generate_heightmap")) { + pushivec_stack(L, offset); + pushivec_stack(L, size); + pushinteger(L, seed); + pushinteger(L, bpd); + if (call_nothrow(L, 6)) { + auto map = touserdata(L, -1)->getHeightmap(); + pop(L, 2); return map; } } - lua::pop(L); + pop(L); return std::make_shared(size.x, size.y); } @@ -64,22 +61,22 @@ public: std::vector> maps; uint biomeParameters = def.biomeParameters; - lua::pushenv(L, *env); - if (lua::getfield(L, "generate_biome_parameters")) { - lua::pushivec_stack(L, offset); - lua::pushivec_stack(L, size); - lua::pushinteger(L, seed); - lua::pushinteger(L, bpd); - if (lua::call_nothrow(L, 6, biomeParameters)) { + pushenv(L, *env); + if (getfield(L, "generate_biome_parameters")) { + pushivec_stack(L, offset); + pushivec_stack(L, size); + pushinteger(L, seed); + pushinteger(L, bpd); + if (call_nothrow(L, 6, biomeParameters)) { for (int i = biomeParameters-1; i >= 0; i--) { maps.push_back( - lua::touserdata(L, -1-i)->getHeightmap()); + touserdata(L, -1-i)->getHeightmap()); } - lua::pop(L, 1+biomeParameters); + pop(L, 1+biomeParameters); return maps; } } - lua::pop(L); + pop(L); for (uint i = 0; i < biomeParameters; i++) { maps.push_back(std::make_shared(size.x, size.y)); } @@ -92,46 +89,46 @@ public: ) override { std::vector placements; - lua::stackguard _(L); - lua::pushenv(L, *env); - if (lua::getfield(L, "place_structures")) { - lua::pushivec_stack(L, offset); - lua::pushivec_stack(L, size); - lua::pushinteger(L, seed); - lua::newuserdata(L, heightmap); - lua::pushinteger(L, chunkHeight); - if (lua::call_nothrow(L, 7, 1)) { - int len = lua::objlen(L, -1); + stackguard _(L); + pushenv(L, *env); + if (getfield(L, "place_structures")) { + pushivec_stack(L, offset); + pushivec_stack(L, size); + pushinteger(L, seed); + newuserdata(L, heightmap); + pushinteger(L, chunkHeight); + if (call_nothrow(L, 7, 1)) { + int len = objlen(L, -1); for (int i = 1; i <= len; i++) { - lua::rawgeti(L, i); + rawgeti(L, i); - lua::rawgeti(L, 1); + rawgeti(L, 1); int structIndex = 0; - if (lua::isstring(L, -1)) { + if (isstring(L, -1)) { const auto& found = def.structuresIndices.find( - lua::require_string(L, -1) + require_string(L, -1) ); if (found != def.structuresIndices.end()) { structIndex = found->second; } } else { - structIndex = lua::tointeger(L, -1); + structIndex = tointeger(L, -1); } - lua::pop(L); + pop(L); - lua::rawgeti(L, 2); - glm::ivec3 pos = lua::tovec3(L, -1); - lua::pop(L); + rawgeti(L, 2); + glm::ivec3 pos = tovec3(L, -1); + pop(L); - lua::rawgeti(L, 3); - int rotation = lua::tointeger(L, -1) & 0b11; - lua::pop(L); + rawgeti(L, 3); + int rotation = tointeger(L, -1) & 0b11; + pop(L); - lua::pop(L); + pop(L); placements.emplace_back(structIndex, pos, rotation); } - lua::pop(L); + pop(L); } } return placements; @@ -141,28 +138,26 @@ public: std::unique_ptr scripting::load_generator( const GeneratorDef& def, const fs::path& file, const std::string& dirPath ) { - auto L = lua::create_state(lua::StateType::GENERATOR); - auto env = lua::create_environment(L); - lua::stackguard _(L); + auto L = create_state(StateType::GENERATOR); + auto env = create_environment(L); + stackguard _(L); - lua::pushenv(L, *env); - lua::pushstring(L, dirPath); - lua::setfield(L, "__DIR__"); - lua::pushstring(L, dirPath + "/script.lua"); - lua::setfield(L, "__FILE__"); + pushenv(L, *env); + pushstring(L, dirPath); + setfield(L, "__DIR__"); + pushstring(L, dirPath + "/script.lua"); + setfield(L, "__FILE__"); - lua::pop(L); + pop(L); if (fs::exists(file)) { std::string src = files::read_string(file); logger.info() << "script (generator) " << file.u8string(); - lua::pop(L, lua::execute(L, *env, src, file.u8string())); + pop(L, execute(L, *env, src, file.u8string())); } else { // Use default (empty) script - lua::pop(L, lua::execute(L, *env, "", "")); + pop(L, execute(L, *env, "", "")); } - return std::make_unique( - L, def, std::move(env) - ); + return std::make_unique(L, def, std::move(env)); } From 8813d280dc376827663485462812ff656fd5804b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 6 Oct 2024 21:57:13 +0300 Subject: [PATCH 110/139] update workflows --- .github/workflows/build.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f5155aab..6d6c9c9b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main" ] pull_request: - branches: [ "main", "heightmaps" ] # TODO: remove 'heightmaps' after merge + branches: [ "main" ] jobs: build: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index fefbbee7..1f9675ca 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main", "heightmaps" ] # TODO: remove 'heightmaps' after merge + branches: [ "main" ] jobs: build-dmg: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 349b1447..f93b5ac9 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main" ] pull_request: - branches: [ "main", "heightmaps" ] # TODO: remove 'heightmaps' after merge + branches: [ "main" ] jobs: build-windows: From 116cbd61db85bae9d7a34a33517a4a101e85a0f6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 8 Oct 2024 02:32:45 +0300 Subject: [PATCH 111/139] feat: 'lines' (cave-like structures/tunnels) (WIP) --- res/generators/default.files/script.lua | 23 ++++++ .../scripting/scripting_world_generation.cpp | 79 +++++++++++++------ src/maths/util.hpp | 23 ++++++ src/world/generator/GeneratorDef.hpp | 9 ++- src/world/generator/StructurePlacement.hpp | 11 +++ src/world/generator/WorldGenerator.cpp | 58 ++++++++++++-- src/world/generator/WorldGenerator.hpp | 6 +- 7 files changed, 176 insertions(+), 33 deletions(-) create mode 100644 res/generators/default.files/script.lua diff --git a/res/generators/default.files/script.lua b/res/generators/default.files/script.lua new file mode 100644 index 00000000..3af69134 --- /dev/null +++ b/res/generators/default.files/script.lua @@ -0,0 +1,23 @@ +-- TODO: delete this file after caves complete implementation + +function generate_heightmap(x, y, w, h, seed, s) + local map = Heightmap(w, h) + map:add(0.25) + return map +end + + +function place_structures(x, z, w, d, seed, hmap, chunk_height) + local placements = {} + do + local sy = math.random() * (chunk_height / 4) + local ey = math.random() * (chunk_height / 4) + local sx = x + math.random() * 20 - 10 + local ex = x + math.random() * 20 - 10 + local sz = z + math.random() * 20 - 10 + local ez = z + math.random() * 20 - 10 + table.insert(placements, + {":line", 0, {sx - 10, sy, sz - 10}, {ex + 10, ey, ez + 10}, 2}) + end + return placements +end diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index c7655084..7a872e18 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -83,11 +83,62 @@ public: return maps; } - std::vector placeStructures( + void perform_line(lua::State* L, PrototypePlacements& placements) { + rawgeti(L, 2); + blockid_t block = touinteger(L, -1); + pop(L); + + rawgeti(L, 3); + glm::ivec3 a = tovec3(L, -1); + pop(L); + + rawgeti(L, 4); + glm::ivec3 b = tovec3(L, -1); + pop(L); + + rawgeti(L, 5); + int radius = touinteger(L, -1); + pop(L); + + placements.lines.emplace_back(block, a, b, radius); + } + + void perform_placement(lua::State* L, PrototypePlacements& placements) { + rawgeti(L, 1); + int structIndex = 0; + if (isstring(L, -1)) { + const char* name = require_string(L, -1); + if (!std::strcmp(name, ":line")) { + pop(L); + + perform_line(L, placements); + return; + } + const auto& found = def.structuresIndices.find(name); + if (found != def.structuresIndices.end()) { + structIndex = found->second; + } + } else { + structIndex = tointeger(L, -1); + } + pop(L); + + rawgeti(L, 2); + glm::ivec3 pos = tovec3(L, -1); + pop(L); + + rawgeti(L, 3); + int rotation = tointeger(L, -1) & 0b11; + pop(L); + + placements.structs.emplace_back(structIndex, pos, rotation); + } + + PrototypePlacements placeStructures( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap, uint chunkHeight ) override { - std::vector placements; + PrototypePlacements placements {}; stackguard _(L); pushenv(L, *env); @@ -102,31 +153,9 @@ public: for (int i = 1; i <= len; i++) { rawgeti(L, i); - rawgeti(L, 1); - int structIndex = 0; - if (isstring(L, -1)) { - const auto& found = def.structuresIndices.find( - require_string(L, -1) - ); - if (found != def.structuresIndices.end()) { - structIndex = found->second; - } - } else { - structIndex = tointeger(L, -1); - } - pop(L); - - rawgeti(L, 2); - glm::ivec3 pos = tovec3(L, -1); - pop(L); - - rawgeti(L, 3); - int rotation = tointeger(L, -1) & 0b11; - pop(L); + perform_placement(L, placements); pop(L); - - placements.emplace_back(structIndex, pos, rotation); } pop(L); } diff --git a/src/maths/util.hpp b/src/maths/util.hpp index 432fe0bb..ddedb855 100644 --- a/src/maths/util.hpp +++ b/src/maths/util.hpp @@ -4,7 +4,13 @@ #include +#define GLM_ENABLE_EXPERIMENTAL +#include +#include + namespace util { + constexpr inline float EPSILON = 1e-6f; + class PseudoRandom { unsigned short seed; public: @@ -63,4 +69,21 @@ namespace util { rand(); } }; + + /// @brief Find nearest point on segment to given + /// @param a segment point A + /// @param b segment point B + /// @param point given point (may be anywhere) + /// @return nearest point on the segment to given point + inline glm::vec3 closest_point_on_segment( + glm::vec3 a, glm::vec3 b, const glm::vec3& point + ) { + auto vec = b - a; + float da = glm::distance2(point, a); + float db = glm::distance2(point, b); + float len = glm::length2(vec); + float t = (((da - db) / len) * 0.5f + 0.5f); + t = std::min(1.0f, std::max(0.0f, t)); + return a + vec * t; + } } diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 630f7400..8dec4fbb 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -118,6 +118,11 @@ struct Biome { BlocksLayers seaLayers; }; +struct PrototypePlacements { + std::vector structs {}; + std::vector lines {}; +}; + /// @brief Generator behaviour and settings interface class GeneratorScript { public: @@ -156,8 +161,8 @@ public: /// @param seed world seed /// @param heightmap area heightmap /// @param chunkHeight chunk height to use as heights multiplier - /// @return structure placements - virtual std::vector placeStructures( + /// @return structure & line placements + virtual PrototypePlacements placeStructures( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap, uint chunkHeight) = 0; }; diff --git a/src/world/generator/StructurePlacement.hpp b/src/world/generator/StructurePlacement.hpp index ba688ddb..58f90c78 100644 --- a/src/world/generator/StructurePlacement.hpp +++ b/src/world/generator/StructurePlacement.hpp @@ -14,3 +14,14 @@ struct StructurePlacement { rotation(rotation) { } }; + +struct LinePlacement { + blockid_t block; + glm::ivec3 a; + glm::ivec3 b; + int radius; + + LinePlacement(blockid_t block, glm::ivec3 a, glm::ivec3 b, int radius) + : block(block), a(std::move(a)), b(std::move(b)), radius(radius) { + } +}; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index e31f3cb0..cb981c01 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -11,6 +11,7 @@ #include "util/timeutil.hpp" #include "util/listutil.hpp" #include "maths/voxmaths.hpp" +#include "maths/util.hpp" #include "debug/Logger.hpp" static debug::Logger logger("world-generator"); @@ -138,7 +139,7 @@ inline AABB gen_chunk_aabb(int chunkX, int chunkZ) { } void WorldGenerator::placeStructure( - const glm::ivec3 offset, size_t structureId, uint8_t rotation, + const glm::ivec3& offset, size_t structureId, uint8_t rotation, int chunkX, int chunkZ ) { auto& structure = *def.structures[structureId]->fragments[rotation]; @@ -157,8 +158,7 @@ void WorldGenerator::placeStructure( if (chunkAABB.intersect(aabb)) { otherPrototype.structures.emplace_back( structureId, - offset - - glm::ivec3(lcx * CHUNK_W, 0, lcz * CHUNK_D), + offset - glm::ivec3(lcx * CHUNK_W, 0, lcz * CHUNK_D), rotation ); } @@ -166,6 +166,33 @@ void WorldGenerator::placeStructure( } } +void WorldGenerator::placeLine(const LinePlacement& line) { + AABB aabb(line.a, line.b); + aabb.fix(); + aabb.a -= line.radius; + aabb.b += line.radius; + int cxa = floordiv(aabb.a.x, CHUNK_W); + int cza = floordiv(aabb.a.z, CHUNK_D); + int cxb = floordiv(aabb.b.x, CHUNK_W); + int czb = floordiv(aabb.b.z, CHUNK_D); + for (int cz = cza; cz <= czb; cz++) { + for (int cx = cxa; cx <= cxb; cx++) { + auto& otherPrototype = requirePrototype(cx, cz); + auto chunkAABB = gen_chunk_aabb(cx, cz); + chunkAABB.a -= line.radius; + chunkAABB.b += line.radius; + auto found = util::closest_point_on_segment(line.a, line.b, { + cx * CHUNK_W + CHUNK_W / 2, + 0, + cz * CHUNK_D + CHUNK_D / 2 + }); + if (chunkAABB.contains(found)) { + otherPrototype.lines.push_back(line); + } + } + } +} + void WorldGenerator::generateStructures( ChunkPrototype& prototype, int chunkX, int chunkZ ) { @@ -175,10 +202,12 @@ void WorldGenerator::generateStructures( const auto& biomes = prototype.biomes; const auto& heightmap = prototype.heightmap; - util::concat(prototype.structures, def.script->placeStructures( + auto placements = def.script->placeStructures( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, heightmap, CHUNK_H - )); + ); + util::concat(prototype.structures, placements.structs); + for (const auto& placement : prototype.structures) { const auto& offset = placement.position; if (placement.structure < 0 || placement.structure >= def.structures.size()) { @@ -188,6 +217,9 @@ void WorldGenerator::generateStructures( placeStructure( offset, placement.structure, placement.rotation, chunkX, chunkZ); } + for (const auto& line : placements.lines) { + placeLine(line); + } util::PseudoRandom structsRand; structsRand.setSeed(chunkX, chunkZ); @@ -358,6 +390,22 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { } } } + for (const auto& line : prototype.lines) { + int minY = std::max(0, std::min(line.a.y-line.radius, line.b.y-line.radius)); + int maxY = std::min(CHUNK_H, std::max(line.a.y+line.radius, line.b.y+line.radius)); + for (int y = minY; y < maxY; y++) { + for (int z = 0; z < CHUNK_D; z++) { + for (int x = 0; x < CHUNK_W; x++) { + int gx = x + chunkX * CHUNK_W; + int gz = z + chunkZ * CHUNK_D; + + if (glm::distance2(util::closest_point_on_segment(line.a, line.b, {gx, y, gz}), {gx, y, gz}) <= line.radius*line.radius) { + voxels[vox_index(x, y, z)] = {line.block, {}}; + } + } + } + } + } } WorldGenDebugInfo WorldGenerator::createDebugInfo() const { diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 7490ef4d..a503c312 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -32,6 +32,8 @@ struct ChunkPrototype { std::shared_ptr heightmap; std::vector structures; + + std::vector lines; }; struct WorldGenDebugInfo { @@ -69,9 +71,11 @@ class WorldGenerator { void generateHeightmap(ChunkPrototype& prototype, int x, int z); void placeStructure( - const glm::ivec3 offset, size_t structure, uint8_t rotation, + const glm::ivec3& offset, size_t structure, uint8_t rotation, int chunkX, int chunkZ ); + + void placeLine(const LinePlacement& line); public: WorldGenerator( const GeneratorDef& def, From 5a89d97b750123842d1948ca000d0ca8964fab53 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 8 Oct 2024 05:44:36 +0300 Subject: [PATCH 112/139] fix line placement --- res/generators/default.files/script.lua | 2 +- src/world/generator/WorldGenerator.cpp | 53 ++++++++++++++++--------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/res/generators/default.files/script.lua b/res/generators/default.files/script.lua index 3af69134..1bec4cab 100644 --- a/res/generators/default.files/script.lua +++ b/res/generators/default.files/script.lua @@ -17,7 +17,7 @@ function place_structures(x, z, w, d, seed, hmap, chunk_height) local sz = z + math.random() * 20 - 10 local ez = z + math.random() * 20 - 10 table.insert(placements, - {":line", 0, {sx - 10, sy, sz - 10}, {ex + 10, ey, ez + 10}, 2}) + {":line", 0, {sx - 10, sy, sz - 10}, {ex + 10, ey, ez + 10}, 3}) end return placements end diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index cb981c01..f1157343 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -178,17 +178,7 @@ void WorldGenerator::placeLine(const LinePlacement& line) { for (int cz = cza; cz <= czb; cz++) { for (int cx = cxa; cx <= cxb; cx++) { auto& otherPrototype = requirePrototype(cx, cz); - auto chunkAABB = gen_chunk_aabb(cx, cz); - chunkAABB.a -= line.radius; - chunkAABB.b += line.radius; - auto found = util::closest_point_on_segment(line.a, line.b, { - cx * CHUNK_W + CHUNK_W / 2, - 0, - cz * CHUNK_D + CHUNK_D / 2 - }); - if (chunkAABB.contains(found)) { - otherPrototype.lines.push_back(line); - } + otherPrototype.lines.push_back(line); } } } @@ -315,6 +305,14 @@ void WorldGenerator::update(int centerX, int centerY, int loadDistance) { surroundMap.setCenter(centerX, centerY); } +/// @return integer square of distance between two points +/// @note glm::distance2 does not support integer vectors +static inline int distance2(const glm::ivec3& a, const glm::ivec3& b) { + return (b.x - a.x) * (b.x - a.x) + + (b.y - a.y) * (b.y - a.y) + + (b.z - a.z) * (b.z - a.z); +} + void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { surroundMap.completeAt(chunkX, chunkZ); @@ -391,15 +389,32 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { } } for (const auto& line : prototype.lines) { - int minY = std::max(0, std::min(line.a.y-line.radius, line.b.y-line.radius)); - int maxY = std::min(CHUNK_H, std::max(line.a.y+line.radius, line.b.y+line.radius)); - for (int y = minY; y < maxY; y++) { - for (int z = 0; z < CHUNK_D; z++) { - for (int x = 0; x < CHUNK_W; x++) { - int gx = x + chunkX * CHUNK_W; - int gz = z + chunkZ * CHUNK_D; + int cgx = chunkX * CHUNK_W; + int cgz = chunkZ * CHUNK_D; - if (glm::distance2(util::closest_point_on_segment(line.a, line.b, {gx, y, gz}), {gx, y, gz}) <= line.radius*line.radius) { + int radius = line.radius; + + int minX = std::max(0, std::min(line.a.x-radius-cgx, line.b.x-radius-cgx)); + int maxX = std::min(CHUNK_W, std::max(line.a.x+radius-cgx, line.b.x+radius-cgx)); + + int minZ = std::max(0, std::min(line.a.z-radius-cgz, line.b.z-radius-cgz)); + int maxZ = std::min(CHUNK_D, std::max(line.a.z+radius-cgz, line.b.z+radius-cgz)); + + int minY = std::max(0, std::min(line.a.y-radius, line.b.y-radius)); + int maxY = std::min(CHUNK_H, std::max(line.a.y+radius, line.b.y+radius)); + + auto a = line.a; + auto b = line.b; + + for (int y = minY; y < maxY; y++) { + for (int z = minZ; z < maxZ; z++) { + for (int x = minX; x < maxX; x++) { + int gx = x + cgx; + int gz = z + cgz; + glm::ivec3 point {gx, y, gz}; + glm::ivec3 closest = util::closest_point_on_segment( + a, b, point); + if (distance2(closest, point) <= radius*radius) { voxels[vox_index(x, y, z)] = {line.block, {}}; } } From ba3ac11b4154669693c73b8de1c84ceb2b585bb6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 8 Oct 2024 16:26:26 +0300 Subject: [PATCH 113/139] optimize lines generation --- src/maths/util.hpp | 36 ++++++++++++++++++++++++-- src/world/generator/WorldGenerator.cpp | 10 +------ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/maths/util.hpp b/src/maths/util.hpp index ddedb855..a5302980 100644 --- a/src/maths/util.hpp +++ b/src/maths/util.hpp @@ -69,14 +69,28 @@ namespace util { rand(); } }; - + + /// @return integer square of distance between two points + /// @note glm::distance2 does not support integer vectors + inline int distance2(const glm::ivec3& a, const glm::ivec3& b) { + return (b.x - a.x) * (b.x - a.x) + + (b.y - a.y) * (b.y - a.y) + + (b.z - a.z) * (b.z - a.z); + } + + /// @return integer square of vector length + /// @note glm::length2 does not support integer vectors + inline int length2(const glm::ivec3& a) { + return a.x * a.x + a.y * a.y + a.z * a.z; + } + /// @brief Find nearest point on segment to given /// @param a segment point A /// @param b segment point B /// @param point given point (may be anywhere) /// @return nearest point on the segment to given point inline glm::vec3 closest_point_on_segment( - glm::vec3 a, glm::vec3 b, const glm::vec3& point + const glm::vec3& a, const glm::vec3& b, const glm::vec3& point ) { auto vec = b - a; float da = glm::distance2(point, a); @@ -86,4 +100,22 @@ namespace util { t = std::min(1.0f, std::max(0.0f, t)); return a + vec * t; } + + /// @brief Find nearest point on segment to given + /// @param a segment point A + /// @param b segment point B + /// @param point given point (may be anywhere) + /// @note this overload is actually faster (comment out method to compare) + /// @return nearest point on the segment to given point + inline glm::ivec3 closest_point_on_segment( + const glm::ivec3& a, const glm::ivec3& b, const glm::ivec3& point + ) { + auto vec = b - a; + float da = distance2(point, a); + float db = distance2(point, b); + float len = length2(vec); + float t = (((da - db) / len) * 0.5f + 0.5f); + t = std::min(1.0f, std::max(0.0f, t)); + return a + glm::ivec3(glm::vec3(vec) * t); + } } diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index f1157343..301049b7 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -305,14 +305,6 @@ void WorldGenerator::update(int centerX, int centerY, int loadDistance) { surroundMap.setCenter(centerX, centerY); } -/// @return integer square of distance between two points -/// @note glm::distance2 does not support integer vectors -static inline int distance2(const glm::ivec3& a, const glm::ivec3& b) { - return (b.x - a.x) * (b.x - a.x) + - (b.y - a.y) * (b.y - a.y) + - (b.z - a.z) * (b.z - a.z); -} - void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { surroundMap.completeAt(chunkX, chunkZ); @@ -414,7 +406,7 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { glm::ivec3 point {gx, y, gz}; glm::ivec3 closest = util::closest_point_on_segment( a, b, point); - if (distance2(closest, point) <= radius*radius) { + if (util::distance2(closest, point) <= radius*radius) { voxels[vox_index(x, y, z)] = {line.block, {}}; } } From 5966ca56176e95eadd958b00e1dd5f968b4454e2 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 8 Oct 2024 16:57:24 +0300 Subject: [PATCH 114/139] refactor WorldGenerator --- src/maths/util.hpp | 12 ++++++++++++ src/world/generator/WorldGenerator.cpp | 27 ++++++++++++++++---------- src/world/generator/WorldGenerator.hpp | 4 ++++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/maths/util.hpp b/src/maths/util.hpp index a5302980..5f08253f 100644 --- a/src/maths/util.hpp +++ b/src/maths/util.hpp @@ -78,12 +78,24 @@ namespace util { (b.z - a.z) * (b.z - a.z); } + /// @return integer square of distance between two points + inline int distance2(int ax, int ay, int az, int bx, int by, int bz) { + return (bx - ax) * (bx - ax) + + (by - ay) * (by - ay) + + (bz - az) * (bz - az); + } + /// @return integer square of vector length /// @note glm::length2 does not support integer vectors inline int length2(const glm::ivec3& a) { return a.x * a.x + a.y * a.y + a.z * a.z; } + /// @return integer square of vector length + inline int length2(int x, int y, int z) { + return x * x + y * y + z * z; + } + /// @brief Find nearest point on segment to given /// @param a segment point A /// @param b segment point B diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 301049b7..b29914c7 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -380,24 +380,30 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { } } } + generateLines(prototype, voxels, chunkX, chunkZ); +} + +void WorldGenerator::generateLines( + const ChunkPrototype& prototype, voxel* voxels, int chunkX, int chunkZ +) { for (const auto& line : prototype.lines) { int cgx = chunkX * CHUNK_W; int cgz = chunkZ * CHUNK_D; int radius = line.radius; - int minX = std::max(0, std::min(line.a.x-radius-cgx, line.b.x-radius-cgx)); - int maxX = std::min(CHUNK_W, std::max(line.a.x+radius-cgx, line.b.x+radius-cgx)); - - int minZ = std::max(0, std::min(line.a.z-radius-cgz, line.b.z-radius-cgz)); - int maxZ = std::min(CHUNK_D, std::max(line.a.z+radius-cgz, line.b.z+radius-cgz)); - - int minY = std::max(0, std::min(line.a.y-radius, line.b.y-radius)); - int maxY = std::min(CHUNK_H, std::max(line.a.y+radius, line.b.y+radius)); - auto a = line.a; auto b = line.b; + int minX = std::max(0, std::min(a.x-radius-cgx, b.x-radius-cgx)); + int maxX = std::min(CHUNK_W, std::max(a.x+radius-cgx, b.x+radius-cgx)); + + int minZ = std::max(0, std::min(a.z-radius-cgz, b.z-radius-cgz)); + int maxZ = std::min(CHUNK_D, std::max(a.z+radius-cgz, b.z+radius-cgz)); + + int minY = std::max(0, std::min(a.y-radius, b.y-radius)); + int maxY = std::min(CHUNK_H, std::max(a.y+radius, b.y+radius)); + for (int y = minY; y < maxY; y++) { for (int z = minZ; z < maxZ; z++) { for (int x = minX; x < maxX; x++) { @@ -405,7 +411,8 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { int gz = z + cgz; glm::ivec3 point {gx, y, gz}; glm::ivec3 closest = util::closest_point_on_segment( - a, b, point); + a, b, point + ); if (util::distance2(closest, point) <= radius*radius) { voxels[vox_index(x, y, z)] = {line.block, {}}; } diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index a503c312..784b4329 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -76,6 +76,10 @@ class WorldGenerator { ); void placeLine(const LinePlacement& line); + + void generateLines( + const ChunkPrototype& prototype, voxel* voxels, int x, int z + ); public: WorldGenerator( const GeneratorDef& def, From c022e11c3bf1e3895c790b8c8f75c97edfd0c2d2 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 8 Oct 2024 20:14:38 +0300 Subject: [PATCH 115/139] move test caves to base:demo --- .../base/generators/demo.files/script.lua | 21 +++++++++++++++++ res/generators/default.files/script.lua | 23 ------------------- src/world/generator/WorldGenerator.cpp | 7 +++++- src/world/generator/WorldGenerator.hpp | 3 +++ 4 files changed, 30 insertions(+), 24 deletions(-) delete mode 100644 res/generators/default.files/script.lua diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua index cd443163..7c103090 100644 --- a/res/content/base/generators/demo.files/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -1,3 +1,13 @@ +-- Get entry-point and filename from `entry-point:filename` path +-- // TODO: move to stdmin +function parse_path(path) + local index = string.find(path, ':') + if index == nil then + error("invalid path syntax (':' missing)") + end + return string.sub(path, 1, index-1), string.sub(path, index+1, -1) +end + local _, dir = parse_path(__DIR__) ores = file.read_combined_list(dir.."/ores.json") @@ -26,6 +36,17 @@ end function place_structures(x, z, w, d, seed, hmap, chunk_height) local placements = {} place_ores(placements, x, z, w, d, seed, hmap, chunk_height) + + if math.random() < 0.1 then -- generate caves + local sy = math.random() * (chunk_height / 2) + local ey = math.random() * (chunk_height / 2) + local sx = x + math.random() * 20 - 10 + local ex = x + math.random() * 20 - 10 + local sz = z + math.random() * 20 - 10 + local ez = z + math.random() * 20 - 10 + table.insert(placements, + {":line", 0, {sx - 10, sy, sz - 10}, {ex + 10, ey, ez + 10}, 3}) + end return placements end diff --git a/res/generators/default.files/script.lua b/res/generators/default.files/script.lua deleted file mode 100644 index 1bec4cab..00000000 --- a/res/generators/default.files/script.lua +++ /dev/null @@ -1,23 +0,0 @@ --- TODO: delete this file after caves complete implementation - -function generate_heightmap(x, y, w, h, seed, s) - local map = Heightmap(w, h) - map:add(0.25) - return map -end - - -function place_structures(x, z, w, d, seed, hmap, chunk_height) - local placements = {} - do - local sy = math.random() * (chunk_height / 4) - local ey = math.random() * (chunk_height / 4) - local sx = x + math.random() * 20 - 10 - local ex = x + math.random() * 20 - 10 - local sz = z + math.random() * 20 - 10 - local ez = z + math.random() * 20 - 10 - table.insert(placements, - {":line", 0, {sx - 10, sy, sz - 10}, {ex + 10, ey, ez + 10}, 3}) - end - return placements -end diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index b29914c7..daf22ece 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -341,7 +341,13 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { } } } + generateLines(prototype, voxels, chunkX, chunkZ); + generateStructures(prototype, voxels, chunkX, chunkZ); +} +void WorldGenerator::generateStructures( + const ChunkPrototype& prototype, voxel* voxels, int chunkX, int chunkZ +) { for (const auto& placement : prototype.structures) { if (placement.structure < 0 || placement.structure >= def.structures.size()) { logger.error() << "invalid structure index " << placement.structure; @@ -380,7 +386,6 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { } } } - generateLines(prototype, voxels, chunkX, chunkZ); } void WorldGenerator::generateLines( diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 784b4329..e40ce92f 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -80,6 +80,9 @@ class WorldGenerator { void generateLines( const ChunkPrototype& prototype, voxel* voxels, int x, int z ); + void generateStructures( + const ChunkPrototype& prototype, voxel* voxels, int x, int z + ); public: WorldGenerator( const GeneratorDef& def, From b4578cfb87ace34ea61e4d2a82ec3c15a9ced66a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 8 Oct 2024 22:37:41 +0300 Subject: [PATCH 116/139] fix caves issues & add block 'surface-replacement' property --- res/content/base/blocks/dirt.json | 5 +- src/content/ContentBuilder.cpp | 1 + src/content/ContentLoader.cpp | 1 + src/maths/util.hpp | 4 +- src/voxels/Block.hpp | 5 ++ src/world/generator/WorldGenerator.cpp | 101 +++++++++++++++++++++---- src/world/generator/WorldGenerator.hpp | 16 ++++ 7 files changed, 115 insertions(+), 18 deletions(-) diff --git a/res/content/base/blocks/dirt.json b/res/content/base/blocks/dirt.json index 1495377a..81453c4b 100644 --- a/res/content/base/blocks/dirt.json +++ b/res/content/base/blocks/dirt.json @@ -1,4 +1,5 @@ { "texture": "dirt", - "material": "base:ground" -} \ No newline at end of file + "material": "base:ground", + "surface-replacement": "base:grass_block" +} diff --git a/src/content/ContentBuilder.cpp b/src/content/ContentBuilder.cpp index 60dbf36e..30e65911 100644 --- a/src/content/ContentBuilder.cpp +++ b/src/content/ContentBuilder.cpp @@ -82,6 +82,7 @@ std::unique_ptr ContentBuilder::build() { // Now, it's time to resolve foreign keys for (Block* def : blockDefsIndices) { def->rt.pickingItem = content->items.require(def->pickingItem).rt.id; + def->rt.surfaceReplacement = content->blocks.require(def->surfaceReplacement).rt.id; } for (ItemDef* def : itemDefsIndices) { diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index ff0e371f..151ed19f 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -286,6 +286,7 @@ void ContentLoader::loadBlock( root.at("hidden").get(def.hidden); root.at("draw-group").get(def.drawGroup); root.at("picking-item").get(def.pickingItem); + root.at("surface-replacement").get(def.surfaceReplacement); root.at("script-name").get(def.scriptName); root.at("ui-layout").get(def.uiLayout); root.at("inventory-size").get(def.inventorySize); diff --git a/src/maths/util.hpp b/src/maths/util.hpp index 5f08253f..7500bf2a 100644 --- a/src/maths/util.hpp +++ b/src/maths/util.hpp @@ -74,8 +74,8 @@ namespace util { /// @note glm::distance2 does not support integer vectors inline int distance2(const glm::ivec3& a, const glm::ivec3& b) { return (b.x - a.x) * (b.x - a.x) + - (b.y - a.y) * (b.y - a.y) + - (b.z - a.z) * (b.z - a.z); + (b.y - a.y) * (b.y - a.y) + + (b.z - a.z) * (b.z - a.z); } /// @return integer square of distance between two points diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index 3ba6785a..e9b91664 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -181,6 +181,9 @@ public: /// @brief Block script name in blocks/ without extension std::string scriptName = name.substr(name.find(':') + 1); + /// @brief Block will be used instead of this if generated on surface + std::string surfaceReplacement = name; + /// @brief Default block layout will be used by hud.open_block(...) std::string uiLayout = name; @@ -214,6 +217,8 @@ public: /// @brief picking item integer id itemid_t pickingItem = 0; + + blockid_t surfaceReplacement = 0; } rt {}; Block(const std::string& name); diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index daf22ece..46009ec5 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -305,6 +305,65 @@ void WorldGenerator::update(int centerX, int centerY, int loadDistance) { surroundMap.setCenter(centerX, centerY); } +void WorldGenerator::generatePlants( + const ChunkPrototype& prototype, + float* heights, + voxel* voxels, + int chunkX, + int chunkZ, + const Biome** biomes +) { + const auto& indices = content->getIndices()->blocks; + util::PseudoRandom plantsRand; + plantsRand.setSeed(chunkX, chunkZ); + + for (uint z = 0; z < CHUNK_D; z++) { + for (uint x = 0; x < CHUNK_W; x++) { + const Biome* biome = biomes[z * CHUNK_W + x]; + + int height = heights[z * CHUNK_W + x] * CHUNK_H; + height = std::max(0, height); + + if (height+1 > def.seaLevel) { + float rand = plantsRand.randFloat(); + blockid_t plant = biome->plants.choose(rand); + if (plant) { + auto& voxel = voxels[vox_index(x, height+1, z)]; + auto& groundVoxel = voxels[vox_index(x, height, z)]; + if (indices.get(groundVoxel.id)->rt.solid) { + voxel = {plant, {}}; + } + } + } + } + } +} + +void WorldGenerator::generateLand( + const ChunkPrototype& prototype, + float* values, + voxel* voxels, + int chunkX, + int chunkZ, + const Biome** biomes +) { + uint seaLevel = def.seaLevel; + for (uint z = 0; z < CHUNK_D; z++) { + for (uint x = 0; x < CHUNK_W; x++) { + const Biome* biome = biomes[z * CHUNK_W + x]; + + int height = values[z * CHUNK_W + x] * CHUNK_H; + height = std::max(0, height); + + const auto& groundLayers = biome->groundLayers; + const auto& seaLayers = biome->seaLayers; + + generate_pole(seaLayers, seaLevel, height, seaLevel, voxels, x, z); + generate_pole(groundLayers, height, 0, seaLevel, voxels, x, z); + } + } +} + void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { surroundMap.completeAt(chunkX, chunkZ); @@ -315,9 +374,7 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { std::memset(voxels, 0, sizeof(voxel) * CHUNK_VOL); - util::PseudoRandom plantsRand; - plantsRand.setSeed(chunkX, chunkZ); - + const auto& indices = content->getIndices()->blocks; const auto& biomes = prototype.biomes.get(); for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { @@ -331,18 +388,20 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { generate_pole(seaLayers, seaLevel, height, seaLevel, voxels, x, z); generate_pole(groundLayers, height, 0, seaLevel, voxels, x, z); - - if (height+1 > seaLevel) { - float rand = plantsRand.randFloat(); - blockid_t plant = biome->plants.choose(rand); - if (plant) { - voxels[vox_index(x, height+1, z)].id = plant; - } - } } } generateLines(prototype, voxels, chunkX, chunkZ); + generatePlants(prototype, values, voxels, chunkX, chunkZ, biomes); generateStructures(prototype, voxels, chunkX, chunkZ); + +#ifndef NDEBUG + for (uint i = 0; i < CHUNK_VOL; i++) { + blockid_t id = voxels[i].id; + if (indices.get(id) == nullptr) { + abort(); + } + } +#endif } void WorldGenerator::generateStructures( @@ -391,11 +450,12 @@ void WorldGenerator::generateStructures( void WorldGenerator::generateLines( const ChunkPrototype& prototype, voxel* voxels, int chunkX, int chunkZ ) { + const auto& indices = content->getIndices()->blocks; for (const auto& line : prototype.lines) { int cgx = chunkX * CHUNK_W; int cgz = chunkZ * CHUNK_D; - int radius = line.radius; + int const radius = line.radius; auto a = line.a; auto b = line.b; @@ -418,8 +478,21 @@ void WorldGenerator::generateLines( glm::ivec3 closest = util::closest_point_on_segment( a, b, point ); - if (util::distance2(closest, point) <= radius*radius) { - voxels[vox_index(x, y, z)] = {line.block, {}}; + if (y > 0 && util::distance2(closest, point) <= radius*radius && line.block == BLOCK_AIR) { + auto& voxel = voxels[vox_index(x, y, z)]; + if (!indices.require(voxel.id).replaceable) { + voxel = {line.block, {}}; + } + auto& below = voxels[vox_index(x, y-1, z)]; + glm::ivec3 closest2 = util::closest_point_on_segment( + a, b, {gx, y-1, gz} + ); + if (util::distance2(closest2, {gx, y-1, gz}) > radius*radius) { + const auto& def = indices.require(below.id); + if (def.rt.surfaceReplacement != below.id) { + below = {def.rt.surfaceReplacement, {}}; + } + } } } } diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index e40ce92f..0e3f580b 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -83,6 +83,22 @@ class WorldGenerator { void generateStructures( const ChunkPrototype& prototype, voxel* voxels, int x, int z ); + void generatePlants( + const ChunkPrototype& prototype, + float* values, + voxel* voxels, + int x, + int z, + const Biome** biomes + ); + void generateLand( + const ChunkPrototype& prototype, + float* values, + voxel* voxels, + int x, + int z, + const Biome** biomes + ); public: WorldGenerator( const GeneratorDef& def, From 6e0304248bb5690c5247c30e5ca141d74aa8b784 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 9 Oct 2024 05:30:02 +0300 Subject: [PATCH 117/139] add stdmin.lua --- .../base/generators/demo.files/biomes.json | 2 +- .../base/generators/demo.files/script.lua | 12 +- res/scripts/stdlib.lua | 230 +----------------- res/scripts/stdmin.lua | 224 +++++++++++++++++ src/logic/scripting/lua/lua_engine.cpp | 12 +- src/logic/scripting/lua/lua_engine.hpp | 6 +- src/logic/scripting/scripting.cpp | 2 +- .../scripting/scripting_world_generation.cpp | 3 +- 8 files changed, 245 insertions(+), 246 deletions(-) create mode 100644 res/scripts/stdmin.lua diff --git a/res/content/base/generators/demo.files/biomes.json b/res/content/base/generators/demo.files/biomes.json index 52b39eae..52bb9598 100644 --- a/res/content/base/generators/demo.files/biomes.json +++ b/res/content/base/generators/demo.files/biomes.json @@ -28,7 +28,7 @@ }, "desert": { "parameters": [ - {"weight": 0.1, "value": 0}, + {"weight": 0.3, "value": 0}, {"weight": 0.1, "value": 0} ], "layers": [ diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua index 7c103090..4b87f041 100644 --- a/res/content/base/generators/demo.files/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -1,13 +1,3 @@ --- Get entry-point and filename from `entry-point:filename` path --- // TODO: move to stdmin -function parse_path(path) - local index = string.find(path, ':') - if index == nil then - error("invalid path syntax (':' missing)") - end - return string.sub(path, 1, index-1), string.sub(path, index+1, -1) -end - local _, dir = parse_path(__DIR__) ores = file.read_combined_list(dir.."/ores.json") @@ -45,7 +35,7 @@ function place_structures(x, z, w, d, seed, hmap, chunk_height) local sz = z + math.random() * 20 - 10 local ez = z + math.random() * 20 - 10 table.insert(placements, - {":line", 0, {sx - 10, sy, sz - 10}, {ex + 10, ey, ez + 10}, 3}) + {":line", 0, {sx - 10, sy, sz - 10}, {ex + 10, ey, ez + 10}, math.random()*2+2}) end return placements end diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 32dcc653..f6e2603a 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -1,24 +1,6 @@ --- kit of standard functions - --- Check if given table is an array -function is_array(x) - if #t > 0 then - return true - end - for k, v in pairs(x) do - return false - end - return true -end - --- Get entry-point and filename from `entry-point:filename` path -function parse_path(path) - local index = string.find(path, ':') - if index == nil then - error("invalid path syntax (':' missing)") - end - return string.sub(path, 1, index-1), string.sub(path, index+1, -1) -end +------------------------------------------------ +------ Extended kit of standard functions ------ +------------------------------------------------ package = { loaded={} @@ -92,20 +74,6 @@ function sleep(timesec) end end -function pack.is_installed(packid) - return file.isfile(packid..":package.json") -end - -function pack.data_file(packid, name) - file.mkdirs("world:data/"..packid) - return "world:data/"..packid.."/"..name -end - -function pack.shared_file(packid, name) - file.mkdirs("config:"..packid) - return "config:"..packid.."/"..name -end - -- events events = { handlers = {} @@ -233,59 +201,6 @@ function session.reset_entry(name) session.entries[name] = nil end -function timeit(iters, func, ...) - local tm = time.uptime() - for i=1,iters do - func(...) - end - print("[time mcs]", (time.uptime()-tm) * 1000000) -end - -function table.has(t, x) - for i,v in ipairs(t) do - if v == x then - return true - end - end - return false -end - -function table.index(t, x) - for i,v in ipairs(t) do - if v == x then - return i - end - end - return -1 -end - -function table.remove_value(t, x) - local index = table.index(t, x) - if index ~= -1 then - table.remove(t, index) - end -end - -function table.tostring(t) - local s = '[' - for i,v in ipairs(t) do - s = s..tostring(v) - if i < #t then - s = s..', ' - end - end - return s..']' -end - -function file.readlines(path) - local str = file.read(path) - local lines = {} - for s in str:gmatch("[^\r\n]+") do - table.insert(lines, s) - end - return lines -end - stdcomp = require "core:internal/stdcomp" entities.get = stdcomp.get_Entity entities.get_all = function(uids) @@ -302,145 +217,6 @@ end math.randomseed(time.uptime() * 1536227939) ----------------------------------------------- - -function math.clamp(_in, low, high) - return math.min(math.max(_in, low), high) -end - -function math.rand(low, high) - return low + (high - low) * math.random() -end - ----------------------------------------------- - -function table.copy(t) - local copied = {} - - for k, v in pairs(t) do - copied[k] = v - end - - return copied -end - -function table.count_pairs(t) - local count = 0 - - for k, v in pairs(t) do - count = count + 1 - end - - return count -end - -function table.random(t) - return t[math.random(1, #t)] -end - ----------------------------------------------- - -local pattern_escape_replacements = { - ["("] = "%(", - [")"] = "%)", - ["."] = "%.", - ["%"] = "%%", - ["+"] = "%+", - ["-"] = "%-", - ["*"] = "%*", - ["?"] = "%?", - ["["] = "%[", - ["]"] = "%]", - ["^"] = "%^", - ["$"] = "%$", - ["\0"] = "%z" -} - -function string.pattern_safe(str) - return string.gsub(str, ".", pattern_escape_replacements) -end - ---local totable = string.ToTable -local string_sub = string.sub -local string_find = string.find -local string_len = string.len -function string.explode(separator, str, withpattern) - --if (separator == "") then return totable(str) end - if (withpattern == nil) then withpattern = false end - - local ret = {} - local current_pos = 1 - - for i = 1, string_len(str) do - local start_pos, end_pos = string_find(str, separator, current_pos, not withpattern) - if (not start_pos) then break end - ret[i] = string_sub(str, current_pos, start_pos - 1) - current_pos = end_pos + 1 - end - - ret[#ret + 1] = string_sub(str, current_pos) - - return ret -end - -function string.split(str, delimiter) - return string.explode(delimiter, str) -end - -function string.formatted_time(seconds, format) - if (not seconds) then seconds = 0 end - local hours = math.floor(seconds / 3600) - local minutes = math.floor((seconds / 60) % 60) - local millisecs = (seconds - math.floor(seconds)) * 1000 - seconds = math.floor(seconds % 60) - - if (format) then - return string.format(format, minutes, seconds, millisecs) - else - return { h = hours, m = minutes, s = seconds, ms = millisecs } - end -end - -function string.replace(str, tofind, toreplace) - local tbl = string.Explode(tofind, str) - if (tbl[1]) then return table.concat(tbl, toreplace) end - return str -end - -function string.trim(s, char) - if char then char = string.pattern_safe(char) else char = "%s" end - return string.match(s, "^" .. char .. "*(.-)" .. char .. "*$") or s -end - -function string.trim_right(s, char) - if char then char = string.pattern_safe(char) else char = "%s" end - return string.match(s, "^(.-)" .. char .. "*$") or s -end - -function string.trim_left(s, char) - if char then char = string.pattern_safe(char) else char = "%s" end - return string.match(s, "^" .. char .. "*(.+)$") or s -end - -local meta = getmetatable("") - -function meta:__index(key) - local val = string[key] - if (val ~= nil) then - return val - elseif (tonumber(key)) then - return string.sub(self, key, key) - end -end - -function string.starts_with(str, start) - return string.sub(str, 1, string.len(start)) == start -end - -function string.ends_with(str, endStr) - return endStr == "" or string.sub(str, -string.len(endStr)) == endStr -end - -- --------- Deprecated functions ------ -- local function wrap_deprecated(func, name, alternatives) return function (...) diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua new file mode 100644 index 00000000..9fd982bc --- /dev/null +++ b/res/scripts/stdmin.lua @@ -0,0 +1,224 @@ +-- Check if given table is an array +function is_array(x) + if #t > 0 then + return true + end + for k, v in pairs(x) do + return false + end + return true +end + +-- Get entry-point and filename from `entry-point:filename` path +function parse_path(path) + local index = string.find(path, ':') + if index == nil then + error("invalid path syntax (':' missing)") + end + return string.sub(path, 1, index-1), string.sub(path, index+1, -1) +end + +function pack.is_installed(packid) + return file.isfile(packid..":package.json") +end + +function pack.data_file(packid, name) + file.mkdirs("world:data/"..packid) + return "world:data/"..packid.."/"..name +end + +function pack.shared_file(packid, name) + file.mkdirs("config:"..packid) + return "config:"..packid.."/"..name +end + + +function timeit(iters, func, ...) + local tm = time.uptime() + for i=1,iters do + func(...) + end + print("[time mcs]", (time.uptime()-tm) * 1000000) +end + +---------------------------------------------- + +function math.clamp(_in, low, high) + return math.min(math.max(_in, low), high) +end + +function math.rand(low, high) + return low + (high - low) * math.random() +end + +---------------------------------------------- + +function table.copy(t) + local copied = {} + + for k, v in pairs(t) do + copied[k] = v + end + + return copied +end + +function table.count_pairs(t) + local count = 0 + + for k, v in pairs(t) do + count = count + 1 + end + + return count +end + +function table.random(t) + return t[math.random(1, #t)] +end + +---------------------------------------------- + +local pattern_escape_replacements = { + ["("] = "%(", + [")"] = "%)", + ["."] = "%.", + ["%"] = "%%", + ["+"] = "%+", + ["-"] = "%-", + ["*"] = "%*", + ["?"] = "%?", + ["["] = "%[", + ["]"] = "%]", + ["^"] = "%^", + ["$"] = "%$", + ["\0"] = "%z" +} + +function string.pattern_safe(str) + return string.gsub(str, ".", pattern_escape_replacements) +end + +local string_sub = string.sub +local string_find = string.find +local string_len = string.len +function string.explode(separator, str, withpattern) + if (withpattern == nil) then withpattern = false end + + local ret = {} + local current_pos = 1 + + for i = 1, string_len(str) do + local start_pos, end_pos = string_find(str, separator, current_pos, not withpattern) + if (not start_pos) then break end + ret[i] = string_sub(str, current_pos, start_pos - 1) + current_pos = end_pos + 1 + end + + ret[#ret + 1] = string_sub(str, current_pos) + + return ret +end + +function string.split(str, delimiter) + return string.explode(delimiter, str) +end + +function string.formatted_time(seconds, format) + if (not seconds) then seconds = 0 end + local hours = math.floor(seconds / 3600) + local minutes = math.floor((seconds / 60) % 60) + local millisecs = (seconds - math.floor(seconds)) * 1000 + seconds = math.floor(seconds % 60) + + if (format) then + return string.format(format, minutes, seconds, millisecs) + else + return { h = hours, m = minutes, s = seconds, ms = millisecs } + end +end + +function string.replace(str, tofind, toreplace) + local tbl = string.Explode(tofind, str) + if (tbl[1]) then return table.concat(tbl, toreplace) end + return str +end + +function string.trim(s, char) + if char then char = string.pattern_safe(char) else char = "%s" end + return string.match(s, "^" .. char .. "*(.-)" .. char .. "*$") or s +end + +function string.trim_right(s, char) + if char then char = string.pattern_safe(char) else char = "%s" end + return string.match(s, "^(.-)" .. char .. "*$") or s +end + +function string.trim_left(s, char) + if char then char = string.pattern_safe(char) else char = "%s" end + return string.match(s, "^" .. char .. "*(.+)$") or s +end + +local meta = getmetatable("") + +function meta:__index(key) + local val = string[key] + if (val ~= nil) then + return val + elseif (tonumber(key)) then + return string.sub(self, key, key) + end +end + +function string.starts_with(str, start) + return string.sub(str, 1, string.len(start)) == start +end + +function string.ends_with(str, endStr) + return endStr == "" or string.sub(str, -string.len(endStr)) == endStr +end + +function table.has(t, x) + for i,v in ipairs(t) do + if v == x then + return true + end + end + return false +end + +function table.index(t, x) + for i,v in ipairs(t) do + if v == x then + return i + end + end + return -1 +end + +function table.remove_value(t, x) + local index = table.index(t, x) + if index ~= -1 then + table.remove(t, index) + end +end + +function table.tostring(t) + local s = '[' + for i,v in ipairs(t) do + s = s..tostring(v) + if i < #t then + s = s..', ' + end + end + return s..']' +end + +function file.readlines(path) + local str = file.read(path) + local lines = {} + for s in str:gmatch("[^\r\n]+") do + table.insert(lines, s) + end + return lines +end diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index b6cd8446..4c5e9856 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -3,6 +3,8 @@ #include #include +#include "files/files.hpp" +#include "files/engine_paths.hpp" #include "debug/Logger.hpp" #include "util/stringutil.hpp" #include "libs/api_lua.hpp" @@ -104,11 +106,11 @@ void lua::init_state(State* L, StateType stateType) { newusertype(L); } -void lua::initialize() { +void lua::initialize(const EnginePaths& paths) { logger.info() << LUA_VERSION; logger.info() << LUAJIT_VERSION; - main_thread = create_state(StateType::BASE); + main_thread = create_state(paths, StateType::BASE); } void lua::finalize() { @@ -131,11 +133,15 @@ State* lua::get_main_state() { return main_thread; } -State* lua::create_state(StateType stateType) { +State* lua::create_state(const EnginePaths& paths, StateType stateType) { auto L = luaL_newstate(); if (L == nullptr) { throw luaerror("could not initialize Lua state"); } init_state(L, stateType); + + auto resDir = paths.getResourcesFolder(); + auto src = files::read_string(resDir / fs::u8path("scripts/stdmin.lua")); + lua::pop(L, lua::execute(L, 0, src, "")); return L; } diff --git a/src/logic/scripting/lua/lua_engine.hpp b/src/logic/scripting/lua/lua_engine.hpp index 494de626..dd1e794d 100644 --- a/src/logic/scripting/lua/lua_engine.hpp +++ b/src/logic/scripting/lua/lua_engine.hpp @@ -7,13 +7,15 @@ #include "logic/scripting/scripting_functional.hpp" #include "lua_util.hpp" +class EnginePaths; + namespace lua { enum class StateType { BASE, GENERATOR, }; - void initialize(); + void initialize(const EnginePaths& paths); void finalize(); bool emit_event( @@ -22,7 +24,7 @@ namespace lua { std::function args = [](auto*) { return 0; } ); State* get_main_state(); - State* create_state(StateType stateType); + State* create_state(const EnginePaths& paths, StateType stateType); [[nodiscard]] scriptenv create_environment(State* L); void init_state(State* L, StateType stateType); diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index eaab27d3..42120534 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -62,7 +62,7 @@ int scripting::load_script( void scripting::initialize(Engine* engine) { scripting::engine = engine; - lua::initialize(); + lua::initialize(*engine->getPaths()); load_script(fs::path("stdlib.lua"), true); load_script(fs::path("stdcmd.lua"), true); diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 7a872e18..43e041c1 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -14,6 +14,7 @@ #include "world/generator/GeneratorDef.hpp" #include "util/timeutil.hpp" #include "files/files.hpp" +#include "engine.hpp" #include "debug/Logger.hpp" using namespace lua; @@ -167,7 +168,7 @@ public: std::unique_ptr scripting::load_generator( const GeneratorDef& def, const fs::path& file, const std::string& dirPath ) { - auto L = create_state(StateType::GENERATOR); + auto L = create_state(*engine->getPaths(), StateType::GENERATOR); auto env = create_environment(L); stackguard _(L); From a578cca3254ed89fdee52dce7e1cebf5bfba9f75 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 10 Oct 2024 02:00:13 +0300 Subject: [PATCH 118/139] add 'place_structures_wide' callback --- .../base/generators/demo.files/script.lua | 24 +++++--- .../scripting/scripting_world_generation.cpp | 30 +++++++++ src/world/generator/GeneratorDef.hpp | 7 +++ src/world/generator/WorldGenerator.cpp | 61 +++++++++++++------ src/world/generator/WorldGenerator.hpp | 10 ++- 5 files changed, 106 insertions(+), 26 deletions(-) diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua index 4b87f041..9f6f768b 100644 --- a/res/content/base/generators/demo.files/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -26,16 +26,26 @@ end function place_structures(x, z, w, d, seed, hmap, chunk_height) local placements = {} place_ores(placements, x, z, w, d, seed, hmap, chunk_height) + return placements +end +function place_structures_wide(x, z, w, d, seed, chunk_height) + local placements = {} if math.random() < 0.1 then -- generate caves - local sy = math.random() * (chunk_height / 2) - local ey = math.random() * (chunk_height / 2) - local sx = x + math.random() * 20 - 10 - local ex = x + math.random() * 20 - 10 - local sz = z + math.random() * 20 - 10 - local ez = z + math.random() * 20 - 10 + local sy = math.random() * (chunk_height / 6) + 80 + local ey = math.max(1, sy - chunk_height / 4) + local my = (sy + ey) / 2 + math.random() * 24 - 12 + local sx = x + math.random() * 60 - 30 + local ex = x + math.random() * 60 - 30 + local mx = (sx + ex) / 2 + math.random() * 32 - 16 + local sz = z + math.random() * 60 - 30 + local ez = z + math.random() * 60 - 30 + local mz = (sz + ez) / 2 + math.random() * 32 - 16 + local width = math.random()*3+2 table.insert(placements, - {":line", 0, {sx - 10, sy, sz - 10}, {ex + 10, ey, ez + 10}, math.random()*2+2}) + {":line", 0, {sx, sy, sz}, {mx, my, mz}, width}) + table.insert(placements, + {":line", 0, {mx, my, mz}, {ex, ey, ez}, width}) end return placements end diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 43e041c1..7a339adf 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -135,6 +135,36 @@ public: placements.structs.emplace_back(structIndex, pos, rotation); } + PrototypePlacements placeStructuresWide( + const glm::ivec2& offset, + const glm::ivec2& size, + uint64_t seed, + uint chunkHeight + ) override { + PrototypePlacements placements {}; + + stackguard _(L); + pushenv(L, *env); + if (getfield(L, "place_structures_wide")) { + pushivec_stack(L, offset); + pushivec_stack(L, size); + pushinteger(L, seed); + pushinteger(L, chunkHeight); + if (call_nothrow(L, 6, 1)) { + int len = objlen(L, -1); + for (int i = 1; i <= len; i++) { + rawgeti(L, i); + + perform_placement(L, placements); + + pop(L); + } + pop(L); + } + } + return placements; + } + PrototypePlacements placeStructures( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap, uint chunkHeight diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 8dec4fbb..e8d7adf0 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -154,6 +154,13 @@ public: uint bpd ) = 0; + virtual PrototypePlacements placeStructuresWide( + const glm::ivec2& offset, + const glm::ivec2& size, + uint64_t seed, + uint chunkHeight + ) = 0; + /// @brief Generate a list of structures placements. Structures may be /// placed to nearest chunks also (position of out area). /// @param offset position of the area diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 46009ec5..d4a6ee26 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -17,7 +17,7 @@ static debug::Logger logger("world-generator"); static inline constexpr uint MAX_PARAMETERS = 4; -static inline constexpr uint MAX_CHUNK_PROTOTYPE_LEVELS = 5; +static inline constexpr uint MAX_CHUNK_PROTOTYPE_LEVELS = 10; WorldGenerator::WorldGenerator( const GeneratorDef& def, const Content* content, uint64_t seed @@ -41,13 +41,16 @@ WorldGenerator::WorldGenerator( } prototypes[{x, z}] = generatePrototype(x, z); }); - surroundMap.setLevelCallback(2, [this](int const x, int const z) { + surroundMap.setLevelCallback(4, [this](int const x, int const z) { + generateStructuresWide(requirePrototype(x, z), x, z); + }); + surroundMap.setLevelCallback(7, [this](int const x, int const z) { generateBiomes(requirePrototype(x, z), x, z); }); - surroundMap.setLevelCallback(3, [this](int const x, int const z) { + surroundMap.setLevelCallback(8, [this](int const x, int const z) { generateHeightmap(requirePrototype(x, z), x, z); }); - surroundMap.setLevelCallback(4, [this](int const x, int const z) { + surroundMap.setLevelCallback(9, [this](int const x, int const z) { generateStructures(requirePrototype(x, z), x, z); }); for (int i = 0; i < def.structures.size(); i++) { @@ -183,6 +186,41 @@ void WorldGenerator::placeLine(const LinePlacement& line) { } } +void WorldGenerator::placeStructures( + const PrototypePlacements& placements, + ChunkPrototype& prototype, + int chunkX, + int chunkZ +) { + util::concat(prototype.structures, placements.structs); + for (const auto& placement : prototype.structures) { + const auto& offset = placement.position; + if (placement.structure < 0 || placement.structure >= def.structures.size()) { + logger.error() << "invalid structure index " << placement.structure; + continue; + } + placeStructure( + offset, placement.structure, placement.rotation, chunkX, chunkZ); + } + for (const auto& line : placements.lines) { + placeLine(line); + } +} + +void WorldGenerator::generateStructuresWide( + ChunkPrototype& prototype, int chunkX, int chunkZ +) { + if (prototype.level >= ChunkPrototypeLevel::WIDE_STRUCTS) { + return; + } + auto placements = def.script->placeStructuresWide( + {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, CHUNK_H + ); + placeStructures(placements, prototype, chunkX, chunkZ); + + prototype.level = ChunkPrototypeLevel::WIDE_STRUCTS; +} + void WorldGenerator::generateStructures( ChunkPrototype& prototype, int chunkX, int chunkZ ) { @@ -196,20 +234,7 @@ void WorldGenerator::generateStructures( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, heightmap, CHUNK_H ); - util::concat(prototype.structures, placements.structs); - - for (const auto& placement : prototype.structures) { - const auto& offset = placement.position; - if (placement.structure < 0 || placement.structure >= def.structures.size()) { - logger.error() << "invalid structure index " << placement.structure; - continue; - } - placeStructure( - offset, placement.structure, placement.rotation, chunkX, chunkZ); - } - for (const auto& line : placements.lines) { - placeLine(line); - } + placeStructures(placements, prototype, chunkX, chunkZ); util::PseudoRandom structsRand; structsRand.setSeed(chunkX, chunkZ); diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 0e3f580b..59a43e1c 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -17,9 +17,10 @@ struct GeneratorDef; class Heightmap; struct Biome; class VoxelFragment; +struct PrototypePlacements; enum class ChunkPrototypeLevel { - VOID=0, BIOMES, HEIGHTMAP, STRUCTURES + VOID=0, WIDE_STRUCTS, BIOMES, HEIGHTMAP, STRUCTURES }; struct ChunkPrototype { @@ -64,6 +65,8 @@ class WorldGenerator { ChunkPrototype& requirePrototype(int x, int z); + void generateStructuresWide(ChunkPrototype& prototype, int x, int z); + void generateStructures(ChunkPrototype& prototype, int x, int z); void generateBiomes(ChunkPrototype& prototype, int x, int z); @@ -99,6 +102,11 @@ class WorldGenerator { int z, const Biome** biomes ); + + void placeStructures( + const PrototypePlacements& placements, + ChunkPrototype& prototype, + int x, int z); public: WorldGenerator( const GeneratorDef& def, From 50eb83ad9b944153b3c9158b8768131169f1c49b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 10 Oct 2024 03:57:43 +0300 Subject: [PATCH 119/139] add generator param 'wide-structs-chunks-radius' & make caves more interesting --- .../base/generators/demo.files/script.lua | 42 ++++++++++++------- src/content/loading/GeneratorLoader.cpp | 1 + src/world/generator/GeneratorDef.hpp | 4 ++ src/world/generator/WorldGenerator.cpp | 25 +++++++---- 4 files changed, 49 insertions(+), 23 deletions(-) diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua index 9f6f768b..983ec628 100644 --- a/res/content/base/generators/demo.files/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -31,21 +31,35 @@ end function place_structures_wide(x, z, w, d, seed, chunk_height) local placements = {} - if math.random() < 0.1 then -- generate caves - local sy = math.random() * (chunk_height / 6) + 80 - local ey = math.max(1, sy - chunk_height / 4) - local my = (sy + ey) / 2 + math.random() * 24 - 12 - local sx = x + math.random() * 60 - 30 - local ex = x + math.random() * 60 - 30 - local mx = (sx + ex) / 2 + math.random() * 32 - 16 - local sz = z + math.random() * 60 - 30 - local ez = z + math.random() * 60 - 30 - local mz = (sz + ez) / 2 + math.random() * 32 - 16 + if math.random() < 0.3 then -- generate caves + local sx = x + math.random() * 10 - 5 + local sy = math.random() * (chunk_height / 6) + 40 + local sz = z + math.random() * 10 - 5 + + local dir = math.random() * math.pi * 2 + local dir_inertia = (math.random() - 0.5) * 2 + local elevation = -sy / (chunk_height / 6) local width = math.random()*3+2 - table.insert(placements, - {":line", 0, {sx, sy, sz}, {mx, my, mz}, width}) - table.insert(placements, - {":line", 0, {mx, my, mz}, {ex, ey, ez}, width}) + + for i=1,10 do + local dx = math.sin(dir) * 10 + local dz = -math.cos(dir) * 10 + + local ex = sx + dx + local ey = sy + elevation + local ez = sz + dz + + table.insert(placements, + {":line", 0, {sx, sy, sz}, {ex, ey, ez}, width}) + + sx = ex + sy = ey + sz = ez + + dir_inertia = dir_inertia * 0.8 + (math.random() - 0.5) * math.pow(math.random(), 2) * 8 + elevation = elevation * 0.9 + (math.random() - 0.4) * (1.0-math.pow(math.random(), 4)) * 8 + dir = dir + dir_inertia + end end return placements end diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index cdf0d893..eef4fb1c 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -196,6 +196,7 @@ void ContentLoader::loadGenerator( map.at("biome-bpd").get(def.biomesBPD); map.at("heights-bpd").get(def.heightsBPD); map.at("sea_level").get(def.seaLevel); + map.at("wide-structs-chunks-radius").get(def.wideStructsChunksRadius); auto folder = generatorsDir / fs::u8path(name + ".files"); auto scriptFile = folder / fs::u8path("script.lua"); diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index e8d7adf0..2247b3a8 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -204,6 +204,10 @@ struct GeneratorDef { /// @brief Heightmap blocks per dot uint heightsBPD = 4; + /// @brief Number of chunks must be generated before and after wide + /// structures placement triggered + uint wideStructsChunksRadius = 3; + std::unordered_map structuresIndices; std::vector> structures; std::vector biomes; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index d4a6ee26..9fa1ea76 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -17,16 +17,20 @@ static debug::Logger logger("world-generator"); static inline constexpr uint MAX_PARAMETERS = 4; -static inline constexpr uint MAX_CHUNK_PROTOTYPE_LEVELS = 10; +static inline constexpr uint BASIC_PROTOTYPE_LAYERS = 5; WorldGenerator::WorldGenerator( const GeneratorDef& def, const Content* content, uint64_t seed ) : def(def), content(content), - seed(seed), - surroundMap(0, MAX_CHUNK_PROTOTYPE_LEVELS) + seed(seed), + surroundMap(0, BASIC_PROTOTYPE_LAYERS + def.wideStructsChunksRadius * 2) { + uint levels = BASIC_PROTOTYPE_LAYERS + def.wideStructsChunksRadius * 2; + + surroundMap = SurroundMap(0, levels); + logger.info() << "total number of prototype levels is " << levels; surroundMap.setOutCallback([this](int const x, int const z, int8_t) { const auto& found = prototypes.find({x, z}); if (found == prototypes.end()) { @@ -41,16 +45,17 @@ WorldGenerator::WorldGenerator( } prototypes[{x, z}] = generatePrototype(x, z); }); - surroundMap.setLevelCallback(4, [this](int const x, int const z) { + surroundMap.setLevelCallback(def.wideStructsChunksRadius + 1, + [this](int const x, int const z) { generateStructuresWide(requirePrototype(x, z), x, z); }); - surroundMap.setLevelCallback(7, [this](int const x, int const z) { + surroundMap.setLevelCallback(levels-3, [this](int const x, int const z) { generateBiomes(requirePrototype(x, z), x, z); }); - surroundMap.setLevelCallback(8, [this](int const x, int const z) { + surroundMap.setLevelCallback(levels-2, [this](int const x, int const z) { generateHeightmap(requirePrototype(x, z), x, z); }); - surroundMap.setLevelCallback(9, [this](int const x, int const z) { + surroundMap.setLevelCallback(levels-1, [this](int const x, int const z) { generateStructures(requirePrototype(x, z), x, z); }); for (int i = 0; i < def.structures.size(); i++) { @@ -180,8 +185,10 @@ void WorldGenerator::placeLine(const LinePlacement& line) { int czb = floordiv(aabb.b.z, CHUNK_D); for (int cz = cza; cz <= czb; cz++) { for (int cx = cxa; cx <= cxb; cx++) { - auto& otherPrototype = requirePrototype(cx, cz); - otherPrototype.lines.push_back(line); + const auto& found = prototypes.find({cx, cz}); + if (found != prototypes.end()) { + found->second->lines.push_back(line); + } } } } From fb53b3941c564b123fd4fb4db0fab531c6f21ede Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 10 Oct 2024 04:44:44 +0300 Subject: [PATCH 120/139] update caves distribution --- res/content/base/generators/demo.files/script.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua index 983ec628..63445863 100644 --- a/res/content/base/generators/demo.files/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -31,17 +31,17 @@ end function place_structures_wide(x, z, w, d, seed, chunk_height) local placements = {} - if math.random() < 0.3 then -- generate caves + if math.random() < 0.05 then -- generate caves local sx = x + math.random() * 10 - 5 - local sy = math.random() * (chunk_height / 6) + 40 + local sy = math.random() * (chunk_height / 4) + 27 local sz = z + math.random() * 10 - 5 local dir = math.random() * math.pi * 2 local dir_inertia = (math.random() - 0.5) * 2 - local elevation = -sy / (chunk_height / 6) - local width = math.random()*3+2 + local elevation = -3 + local width = math.random() * 3 + 2 - for i=1,10 do + for i=1,18 do local dx = math.sin(dir) * 10 local dz = -math.cos(dir) * 10 @@ -84,7 +84,7 @@ function generate_heightmap(x, y, w, h, seed, s) rivermap:abs() rivermap:mul(2.0) rivermap:pow(0.15) - rivermap:max(0.3) + rivermap:max(0.5) map:mul(rivermap) return map end From b5c1050f431fb020b5a0175c830ada4c115b4de5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 11 Oct 2024 18:37:03 +0300 Subject: [PATCH 121/139] add placements priority --- .../base/generators/demo.files/ores.json | 2 +- .../base/generators/demo.files/script.lua | 4 +- .../scripting/scripting_world_generation.cpp | 34 ++- src/world/generator/GeneratorDef.hpp | 9 +- src/world/generator/StructurePlacement.hpp | 11 + src/world/generator/WorldGenerator.cpp | 227 ++++++++++-------- src/world/generator/WorldGenerator.hpp | 29 ++- 7 files changed, 190 insertions(+), 126 deletions(-) diff --git a/res/content/base/generators/demo.files/ores.json b/res/content/base/generators/demo.files/ores.json index 682bb23a..9ccf696c 100644 --- a/res/content/base/generators/demo.files/ores.json +++ b/res/content/base/generators/demo.files/ores.json @@ -1,3 +1,3 @@ [ - {"struct": "coal_ore0", "rarity": 22000} + {"struct": "coal_ore0", "rarity": 4400} ] diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua index 63445863..8e138fd2 100644 --- a/res/content/base/generators/demo.files/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -17,7 +17,7 @@ local function place_ores(placements, x, z, w, d, seed, hmap, chunk_height) local sz = math.random() * d local sy = math.random() * (chunk_height * 0.5) if sy < hmap:at(sx, sz) * chunk_height - 6 then - table.insert(placements, {ore.struct, {sx, sy, sz}, math.random()*4}) + table.insert(placements, {ore.struct, {sx, sy, sz}, math.random()*4, -1}) end end end @@ -33,7 +33,7 @@ function place_structures_wide(x, z, w, d, seed, chunk_height) local placements = {} if math.random() < 0.05 then -- generate caves local sx = x + math.random() * 10 - 5 - local sy = math.random() * (chunk_height / 4) + 27 + local sy = math.random() * (chunk_height / 4) + 10 local sz = z + math.random() * 10 - 5 local dir = math.random() * math.pi * 2 diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 7a339adf..a1865e48 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -84,7 +84,7 @@ public: return maps; } - void perform_line(lua::State* L, PrototypePlacements& placements) { + void perform_line(lua::State* L, std::vector& placements) { rawgeti(L, 2); blockid_t block = touinteger(L, -1); pop(L); @@ -101,10 +101,17 @@ public: int radius = touinteger(L, -1); pop(L); - placements.lines.emplace_back(block, a, b, radius); + int priority = 0; + if (objlen(L, -1) >= 6) { + rawgeti(L, 6); + priority = tointeger(L, -1); + pop(L); + } + + placements.emplace_back(priority, LinePlacement {block, a, b, radius}); } - void perform_placement(lua::State* L, PrototypePlacements& placements) { + void perform_placement(lua::State* L, std::vector& placements) { rawgeti(L, 1); int structIndex = 0; if (isstring(L, -1)) { @@ -129,19 +136,28 @@ public: pop(L); rawgeti(L, 3); - int rotation = tointeger(L, -1) & 0b11; + uint8_t rotation = tointeger(L, -1) & 0b11; pop(L); - placements.structs.emplace_back(structIndex, pos, rotation); + int priority = 1; + if (objlen(L, -1) >= 4) { + rawgeti(L, 4); + priority = tointeger(L, -1); + pop(L); + } + + placements.emplace_back( + priority, StructurePlacement {structIndex, pos, rotation} + ); } - PrototypePlacements placeStructuresWide( + std::vector placeStructuresWide( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, uint chunkHeight ) override { - PrototypePlacements placements {}; + std::vector placements {}; stackguard _(L); pushenv(L, *env); @@ -165,11 +181,11 @@ public: return placements; } - PrototypePlacements placeStructures( + std::vector placeStructures( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap, uint chunkHeight ) override { - PrototypePlacements placements {}; + std::vector placements {}; stackguard _(L); pushenv(L, *env); diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 2247b3a8..f23bea57 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -118,11 +118,6 @@ struct Biome { BlocksLayers seaLayers; }; -struct PrototypePlacements { - std::vector structs {}; - std::vector lines {}; -}; - /// @brief Generator behaviour and settings interface class GeneratorScript { public: @@ -154,7 +149,7 @@ public: uint bpd ) = 0; - virtual PrototypePlacements placeStructuresWide( + virtual std::vector placeStructuresWide( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, @@ -169,7 +164,7 @@ public: /// @param heightmap area heightmap /// @param chunkHeight chunk height to use as heights multiplier /// @return structure & line placements - virtual PrototypePlacements placeStructures( + virtual std::vector placeStructures( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap, uint chunkHeight) = 0; }; diff --git a/src/world/generator/StructurePlacement.hpp b/src/world/generator/StructurePlacement.hpp index 58f90c78..2ec7185e 100644 --- a/src/world/generator/StructurePlacement.hpp +++ b/src/world/generator/StructurePlacement.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -25,3 +26,13 @@ struct LinePlacement { : block(block), a(std::move(a)), b(std::move(b)), radius(radius) { } }; + +struct Placement { + int priority; + std::variant placement; + + Placement( + int priority, + std::variant placement + ) : priority(priority), placement(std::move(placement)) {} +}; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 9fa1ea76..6c1fdca7 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -1,6 +1,7 @@ #include "WorldGenerator.hpp" #include +#include #include "maths/util.hpp" #include "content/Content.hpp" @@ -147,34 +148,36 @@ inline AABB gen_chunk_aabb(int chunkX, int chunkZ) { } void WorldGenerator::placeStructure( - const glm::ivec3& offset, size_t structureId, uint8_t rotation, + const StructurePlacement& placement, int priority, int chunkX, int chunkZ ) { - auto& structure = *def.structures[structureId]->fragments[rotation]; - auto position = glm::ivec3(chunkX * CHUNK_W, 0, chunkZ * CHUNK_D)+offset; + auto& structure = + *def.structures[placement.structure]->fragments[placement.rotation]; + auto position = + glm::ivec3(chunkX * CHUNK_W, 0, chunkZ * CHUNK_D) + placement.position; auto size = structure.getSize() + glm::ivec3(0, CHUNK_H, 0); AABB aabb(position, position + size); for (int lcz = -1; lcz <= 1; lcz++) { for (int lcx = -1; lcx <= 1; lcx++) { - if (lcx == 0 && lcz == 0) { - continue; - } auto& otherPrototype = requirePrototype( chunkX + lcx, chunkZ + lcz ); auto chunkAABB = gen_chunk_aabb(chunkX + lcx, chunkZ + lcz); if (chunkAABB.intersect(aabb)) { - otherPrototype.structures.emplace_back( - structureId, - offset - glm::ivec3(lcx * CHUNK_W, 0, lcz * CHUNK_D), - rotation + otherPrototype.placements.emplace_back( + priority, + StructurePlacement { + placement.structure, + placement.position - + glm::ivec3(lcx * CHUNK_W, 0, lcz * CHUNK_D), + placement.rotation} ); } } } } -void WorldGenerator::placeLine(const LinePlacement& line) { +void WorldGenerator::placeLine(const LinePlacement& line, int priority) { AABB aabb(line.a, line.b); aabb.fix(); aabb.a -= line.radius; @@ -187,30 +190,29 @@ void WorldGenerator::placeLine(const LinePlacement& line) { for (int cx = cxa; cx <= cxb; cx++) { const auto& found = prototypes.find({cx, cz}); if (found != prototypes.end()) { - found->second->lines.push_back(line); + found->second->placements.emplace_back(priority, line); } } } } void WorldGenerator::placeStructures( - const PrototypePlacements& placements, + const std::vector& placements, ChunkPrototype& prototype, int chunkX, int chunkZ ) { - util::concat(prototype.structures, placements.structs); - for (const auto& placement : prototype.structures) { - const auto& offset = placement.position; - if (placement.structure < 0 || placement.structure >= def.structures.size()) { - logger.error() << "invalid structure index " << placement.structure; - continue; + for (const auto& placement : placements) { + if (auto sp = std::get_if(&placement.placement)) { + if (sp->structure < 0 || sp->structure >= def.structures.size()) { + logger.error() << "invalid structure index " << sp->structure; + continue; + } + placeStructure(*sp, placement.priority, chunkX, chunkZ); + } else { + const auto& line = std::get(placement.placement); + placeLine(line, placement.priority); } - placeStructure( - offset, placement.structure, placement.rotation, chunkX, chunkZ); - } - for (const auto& line : placements.lines) { - placeLine(line); } } @@ -251,7 +253,7 @@ void WorldGenerator::generateStructures( for (uint x = 0; x < CHUNK_W; x++) { float rand = structsRand.randFloat(); const Biome* biome = biomes[z * CHUNK_W + x]; - size_t structureId = biome->structures.choose(rand, -1); + int structureId = biome->structures.choose(rand, -1); if (structureId == -1) { continue; } @@ -264,12 +266,16 @@ void WorldGenerator::generateStructures( glm::ivec3 position {x, height, z}; position.x -= structure.getSize().x / 2; position.z -= structure.getSize().z / 2; - prototype.structures.push_back({ - static_cast(structureId), position, rotation}); + prototype.placements.emplace_back( + 1, StructurePlacement {structureId, position, rotation} + ); placeStructure( - position, - structureId, - rotation, + StructurePlacement { + structureId, + position, + rotation + }, + 1, chunkX, chunkZ ); @@ -361,6 +367,9 @@ void WorldGenerator::generatePlants( blockid_t plant = biome->plants.choose(rand); if (plant) { auto& voxel = voxels[vox_index(x, height+1, z)]; + if (voxel.id) { + continue; + } auto& groundVoxel = voxels[vox_index(x, height, z)]; if (indices.get(groundVoxel.id)->rt.solid) { voxel = {plant, {}}; @@ -422,9 +431,8 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { generate_pole(groundLayers, height, 0, seaLevel, voxels, x, z); } } - generateLines(prototype, voxels, chunkX, chunkZ); + generatePlacements(prototype, voxels, chunkX, chunkZ); generatePlants(prototype, values, voxels, chunkX, chunkZ, biomes); - generateStructures(prototype, voxels, chunkX, chunkZ); #ifndef NDEBUG for (uint i = 0; i < CHUNK_VOL; i++) { @@ -436,42 +444,64 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { #endif } -void WorldGenerator::generateStructures( +void WorldGenerator::generatePlacements( const ChunkPrototype& prototype, voxel* voxels, int chunkX, int chunkZ ) { - for (const auto& placement : prototype.structures) { - if (placement.structure < 0 || placement.structure >= def.structures.size()) { - logger.error() << "invalid structure index " << placement.structure; + auto placements = prototype.placements; + std::stable_sort( + placements.begin(), + placements.end(), + [](const auto& a, const auto& b) { + return a.priority < b.priority; + } + ); + for (const auto& placement : placements) { + if (auto structure = std::get_if(&placement.placement)) { + generateStructure(prototype, *structure, voxels, chunkX, chunkZ); + } else { + const auto& line = std::get(placement.placement); + generateLine(prototype, line, voxels, chunkX, chunkZ); + } + } +} + +void WorldGenerator::generateStructure( + const ChunkPrototype& prototype, + const StructurePlacement& placement, + voxel* voxels, + int chunkX, int chunkZ +) { + if (placement.structure < 0 || placement.structure >= def.structures.size()) { + logger.error() << "invalid structure index " << placement.structure; + return; + } + auto& generatingStructure = def.structures[placement.structure]; + auto& structure = *generatingStructure->fragments[placement.rotation]; + auto& structVoxels = structure.getRuntimeVoxels(); + const auto& offset = placement.position; + const auto& size = structure.getSize(); + for (int y = 0; y < size.y; y++) { + int sy = y + offset.y; + if (sy < 0 || sy >= CHUNK_H) { continue; } - auto& generatingStructure = def.structures[placement.structure]; - auto& structure = *generatingStructure->fragments[placement.rotation]; - auto& structVoxels = structure.getRuntimeVoxels(); - const auto& offset = placement.position; - const auto& size = structure.getSize(); - for (int y = 0; y < size.y; y++) { - int sy = y + offset.y; - if (sy < 0 || sy >= CHUNK_H) { + for (int z = 0; z < size.z; z++) { + int sz = z + offset.z; + if (sz < 0 || sz >= CHUNK_D) { continue; } - for (int z = 0; z < size.z; z++) { - int sz = z + offset.z; - if (sz < 0 || sz >= CHUNK_D) { + for (int x = 0; x < size.x; x++) { + int sx = x + offset.x; + if (sx < 0 || sx >= CHUNK_W) { continue; } - for (int x = 0; x < size.x; x++) { - int sx = x + offset.x; - if (sx < 0 || sx >= CHUNK_W) { - continue; - } - const auto& structVoxel = - structVoxels[vox_index(x, y, z, size.x, size.z)]; - if (structVoxel.id) { - if (structVoxel.id == BLOCK_STRUCT_AIR) { - voxels[vox_index(sx, sy, sz)] = {0, {}}; - } else { - voxels[vox_index(sx, sy, sz)] = structVoxel; - } + const auto& structVoxel = + structVoxels[vox_index(x, y, z, size.x, size.z)]; + if (structVoxel.id) { + if (structVoxel.id == BLOCK_STRUCT_AIR) { + voxels[vox_index(sx, sy, sz)] = {0, {}}; + } else { + voxels[vox_index(sx, sy, sz)] = structVoxel; } } } @@ -479,51 +509,56 @@ void WorldGenerator::generateStructures( } } -void WorldGenerator::generateLines( - const ChunkPrototype& prototype, voxel* voxels, int chunkX, int chunkZ +void WorldGenerator::generateLine( + const ChunkPrototype& prototype, + const LinePlacement& line, + voxel* voxels, + int chunkX, int chunkZ ) { const auto& indices = content->getIndices()->blocks; - for (const auto& line : prototype.lines) { - int cgx = chunkX * CHUNK_W; - int cgz = chunkZ * CHUNK_D; - int const radius = line.radius; + int cgx = chunkX * CHUNK_W; + int cgz = chunkZ * CHUNK_D; - auto a = line.a; - auto b = line.b; + int const radius = line.radius; - int minX = std::max(0, std::min(a.x-radius-cgx, b.x-radius-cgx)); - int maxX = std::min(CHUNK_W, std::max(a.x+radius-cgx, b.x+radius-cgx)); + auto a = line.a; + auto b = line.b; - int minZ = std::max(0, std::min(a.z-radius-cgz, b.z-radius-cgz)); - int maxZ = std::min(CHUNK_D, std::max(a.z+radius-cgz, b.z+radius-cgz)); + int minX = std::max(0, std::min(a.x-radius-cgx, b.x-radius-cgx)); + int maxX = std::min(CHUNK_W, std::max(a.x+radius-cgx, b.x+radius-cgx)); - int minY = std::max(0, std::min(a.y-radius, b.y-radius)); - int maxY = std::min(CHUNK_H, std::max(a.y+radius, b.y+radius)); + int minZ = std::max(0, std::min(a.z-radius-cgz, b.z-radius-cgz)); + int maxZ = std::min(CHUNK_D, std::max(a.z+radius-cgz, b.z+radius-cgz)); - for (int y = minY; y < maxY; y++) { - for (int z = minZ; z < maxZ; z++) { - for (int x = minX; x < maxX; x++) { - int gx = x + cgx; - int gz = z + cgz; - glm::ivec3 point {gx, y, gz}; - glm::ivec3 closest = util::closest_point_on_segment( - a, b, point + int minY = std::max(0, std::min(a.y-radius, b.y-radius)); + int maxY = std::min(CHUNK_H, std::max(a.y+radius, b.y+radius)); + + for (int y = minY; y < maxY; y++) { + for (int z = minZ; z < maxZ; z++) { + for (int x = minX; x < maxX; x++) { + int gx = x + cgx; + int gz = z + cgz; + glm::ivec3 point {gx, y, gz}; + glm::ivec3 closest = util::closest_point_on_segment( + a, b, point + ); + if (y > 0 && + util::distance2(closest, point) <= radius * radius && + line.block == BLOCK_AIR + ) { + auto& voxel = voxels[vox_index(x, y, z)]; + if (!indices.require(voxel.id).replaceable) { + voxel = {line.block, {}}; + } + auto& below = voxels[vox_index(x, y-1, z)]; + glm::ivec3 closest2 = util::closest_point_on_segment( + a, b, {gx, y-1, gz} ); - if (y > 0 && util::distance2(closest, point) <= radius*radius && line.block == BLOCK_AIR) { - auto& voxel = voxels[vox_index(x, y, z)]; - if (!indices.require(voxel.id).replaceable) { - voxel = {line.block, {}}; - } - auto& below = voxels[vox_index(x, y-1, z)]; - glm::ivec3 closest2 = util::closest_point_on_segment( - a, b, {gx, y-1, gz} - ); - if (util::distance2(closest2, {gx, y-1, gz}) > radius*radius) { - const auto& def = indices.require(below.id); - if (def.rt.surfaceReplacement != below.id) { - below = {def.rt.surfaceReplacement, {}}; - } + if (util::distance2(closest2, {gx, y-1, gz}) > radius*radius) { + const auto& def = indices.require(below.id); + if (def.rt.surfaceReplacement != below.id) { + below = {def.rt.surfaceReplacement, {}}; } } } diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 59a43e1c..c3943e47 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -17,7 +17,6 @@ struct GeneratorDef; class Heightmap; struct Biome; class VoxelFragment; -struct PrototypePlacements; enum class ChunkPrototypeLevel { VOID=0, WIDE_STRUCTS, BIOMES, HEIGHTMAP, STRUCTURES @@ -32,9 +31,7 @@ struct ChunkPrototype { /// @brief chunk heightmap std::shared_ptr heightmap; - std::vector structures; - - std::vector lines; + std::vector placements; }; struct WorldGenDebugInfo { @@ -69,22 +66,32 @@ class WorldGenerator { void generateStructures(ChunkPrototype& prototype, int x, int z); + void generatePlacements( + const ChunkPrototype& prototype, voxel* voxels, int x, int z + ); + void generateBiomes(ChunkPrototype& prototype, int x, int z); void generateHeightmap(ChunkPrototype& prototype, int x, int z); void placeStructure( - const glm::ivec3& offset, size_t structure, uint8_t rotation, + const StructurePlacement& placement, int priority, int chunkX, int chunkZ ); - void placeLine(const LinePlacement& line); + void placeLine(const LinePlacement& line, int priority); - void generateLines( - const ChunkPrototype& prototype, voxel* voxels, int x, int z + void generateLine( + const ChunkPrototype& prototype, + const LinePlacement& placement, + voxel* voxels, + int x, int z ); - void generateStructures( - const ChunkPrototype& prototype, voxel* voxels, int x, int z + void generateStructure( + const ChunkPrototype& prototype, + const StructurePlacement& placement, + voxel* voxels, + int x, int z ); void generatePlants( const ChunkPrototype& prototype, @@ -104,7 +111,7 @@ class WorldGenerator { ); void placeStructures( - const PrototypePlacements& placements, + const std::vector& placements, ChunkPrototype& prototype, int x, int z); public: From 40612dd7da8e4f3ad49dcb072dd0b99c3290d8be Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 11 Oct 2024 19:06:38 +0300 Subject: [PATCH 122/139] update doc-comments --- src/world/generator/GeneratorDef.hpp | 7 +++++++ src/world/generator/WorldGenerator.cpp | 3 +++ 2 files changed, 10 insertions(+) diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index f23bea57..10ab6ff4 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -149,6 +149,13 @@ public: uint bpd ) = 0; + /// @brief Generate a list of structures placements. Structures may be + /// placed to nearest N chunks also (position of out area), where N is + /// wide-structs-chunks-radius + /// @param offset position of the area + /// @param size size of the area (blocks) + /// @param seed world seed + /// @param chunkHeight chunk height to use as heights multiplier virtual std::vector placeStructuresWide( const glm::ivec2& offset, const glm::ivec2& size, diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 6c1fdca7..db3d157d 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -17,7 +17,10 @@ static debug::Logger logger("world-generator"); +/// @brief Max number of biome parameters static inline constexpr uint MAX_PARAMETERS = 4; + +/// @brief Initial + wide_structs + biomes + heightmaps + complete static inline constexpr uint BASIC_PROTOTYPE_LAYERS = 5; WorldGenerator::WorldGenerator( From 7c56d8fd7ff0346bdb89ee23f6f694ee96f32e1f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 13 Oct 2024 21:50:39 +0300 Subject: [PATCH 123/139] replace biomes.json with biomes.toml & make it combined object --- .../base/generators/demo.files/biomes.json | 68 ------------------- .../base/generators/demo.files/biomes.toml | 67 ++++++++++++++++++ res/generators/default.files/biomes.json | 11 --- res/generators/default.files/biomes.toml | 8 +++ src/coders/json.cpp | 2 +- src/content/ContentLoader.cpp | 6 +- src/content/ContentLoader.hpp | 8 ++- src/content/loading/GeneratorLoader.cpp | 19 +++--- src/engine.cpp | 4 +- src/files/engine_paths.cpp | 27 +++++++- src/files/engine_paths.hpp | 4 +- src/world/generator/WorldGenerator.cpp | 1 + 12 files changed, 130 insertions(+), 95 deletions(-) delete mode 100644 res/content/base/generators/demo.files/biomes.json create mode 100644 res/content/base/generators/demo.files/biomes.toml delete mode 100644 res/generators/default.files/biomes.json create mode 100644 res/generators/default.files/biomes.toml diff --git a/res/content/base/generators/demo.files/biomes.json b/res/content/base/generators/demo.files/biomes.json deleted file mode 100644 index 52bb9598..00000000 --- a/res/content/base/generators/demo.files/biomes.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "forest": { - "parameters": [ - {"weight": 1, "value": 1}, - {"weight": 0.5, "value": 0.2} - ], - "layers": [ - {"below_sea_level": false, "height": 1, "block": "base:grass_block"}, - {"below_sea_level": false, "height": 7, "block": "base:dirt"}, - {"height": -1, "block": "base:stone"}, - {"height": 1, "block": "base:bazalt"} - ], - "sea_layers": [ - {"height": -1, "block": "base:water"} - ], - "plant_chance": 0.4, - "plants": [ - {"weight": 1, "block": "base:grass"}, - {"weight": 0.03, "block": "base:flower"} - ], - "structure_chance": 0.032, - "structures": [ - {"name": "tree0", "weight": 1}, - {"name": "tree1", "weight": 1}, - {"name": "tree2", "weight": 1}, - {"name": "tower", "weight": 0.002} - ] - }, - "desert": { - "parameters": [ - {"weight": 0.3, "value": 0}, - {"weight": 0.1, "value": 0} - ], - "layers": [ - {"height": 6, "block": "base:sand"}, - {"height": -1, "block": "base:stone"}, - {"height": 1, "block": "base:bazalt"} - ], - "sea_layers": [ - {"height": -1, "block": "base:water"} - ] - }, - "plains": { - "parameters": [ - {"weight": 0.6, "value": 0.5}, - {"weight": 0.6, "value": 0.5} - ], - "layers": [ - {"below_sea_level": false, "height": 1, "block": "base:grass_block"}, - {"below_sea_level": false, "height": 5, "block": "base:dirt"}, - {"height": -1, "block": "base:stone"}, - {"height": 1, "block": "base:bazalt"} - ], - "sea_layers": [ - {"height": -1, "block": "base:water"} - ], - "plant_chance": 0.3, - "plants": [ - {"weight": 1, "block": "base:grass"}, - {"weight": 0.03, "block": "base:flower"} - ], - "structure_chance": 0.0001, - "structures": [ - {"name": "tree0", "weight": 1}, - {"name": "tree1", "weight": 1} - ] - } -} diff --git a/res/content/base/generators/demo.files/biomes.toml b/res/content/base/generators/demo.files/biomes.toml new file mode 100644 index 00000000..d782d991 --- /dev/null +++ b/res/content/base/generators/demo.files/biomes.toml @@ -0,0 +1,67 @@ +[forest] +parameters = [ + {weight=1, value=1}, + {weight=0.5, value=0.2} +] +layers = [ + {below_sea_level=false, height=1, block="base:grass_block"}, + {below_sea_level=false, height=7, block="base:dirt"}, + {height=-1, block="base:stone"}, + {height=1, block="base:bazalt"} +] +sea_layers = [ + {height=-1, block="base:water"} +] +plant_chance = 0.4 +plants = [ + {weight=1, block="base:grass"}, + {weight=0.03, block="base:flower"} +] +structure_chance = 0.032 +structures = [ + {name="tree0", weight=1}, + {name="tree1", weight=1}, + {name="tree2", weight=1}, + {name="tower", weight=0.002} +] + + +[desert] +parameters = [ + {weight=0.3, value=0}, + {weight=0.1, value=0} +] +layers = [ + {height=6, block="base:sand"}, + {height=-1, block="base:stone"}, + {height=1, block="base:bazalt"} +] +sea_layers = [ + {height=-1, block="base:water"} +] + + +[plains] +parameters = [ + {weight=0.6, value=0.5}, + {weight=0.6, value=0.5} +] +layers = [ + {below_sea_level=false, height=1, block="base:grass_block"}, + {below_sea_level=false, height=5, block="base:dirt"}, + {height=-1, block="base:stone"}, + {height=1, block="base:bazalt"} +] +sea_layers = [ + {height=-1, block="base:water"} +] +plant_chance = 0.3 +plants = [ + {weight=1, block="base:grass"}, + {weight=0.03, block="base:flower"} +] +structure_chance=0.0001 +structures = [ + {name="tree0", weight=1}, + {name="tree1", weight=1} +] diff --git a/res/generators/default.files/biomes.json b/res/generators/default.files/biomes.json deleted file mode 100644 index baf0bf9b..00000000 --- a/res/generators/default.files/biomes.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "flat": { - "parameters": [], - "layers": [ - {"height": -1, "block": "core:obstacle"} - ], - "sea_layers": [ - {"height": -1, "block": "core:obstacle"} - ] - } -} diff --git a/res/generators/default.files/biomes.toml b/res/generators/default.files/biomes.toml new file mode 100644 index 00000000..94d6e7cd --- /dev/null +++ b/res/generators/default.files/biomes.toml @@ -0,0 +1,8 @@ +[flat] +parameters = [] +layers = [ + {height=-1, block="core:obstacle"} +] +sea_layers = [ + {height=-1, block="core:obstacle"} +] diff --git a/src/coders/json.cpp b/src/coders/json.cpp index 9c189998..05db4e13 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -242,7 +242,7 @@ dv::value Parser::parseValue() { } else if (literal == "nan") { return NAN; } - throw error("invalid literal "); + throw error("invalid keyword " + literal); } if (next == '{') { return parseObject(); diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 151ed19f..bc03b810 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -28,8 +28,10 @@ using namespace data; static debug::Logger logger("content-loader"); -ContentLoader::ContentLoader(ContentPack* pack, ContentBuilder& builder) - : pack(pack), builder(builder) { +ContentLoader::ContentLoader( + ContentPack* pack, ContentBuilder& builder, const ResPaths& paths +) + : pack(pack), builder(builder), paths(paths) { auto runtime = std::make_unique( *pack, scripting::create_pack_environment(*pack) ); diff --git a/src/content/ContentLoader.hpp b/src/content/ContentLoader.hpp index 762b3fbe..efd54efd 100644 --- a/src/content/ContentLoader.hpp +++ b/src/content/ContentLoader.hpp @@ -16,6 +16,7 @@ struct EntityDef; struct ContentPack; struct GeneratorDef; +class ResPaths; class ContentBuilder; class ContentPackRuntime; struct ContentPackStats; @@ -26,6 +27,7 @@ class ContentLoader { scriptenv env; ContentBuilder& builder; ContentPackStats* stats; + const ResPaths& paths; void loadBlock( Block& def, const std::string& full, const std::string& name @@ -55,7 +57,11 @@ class ContentLoader { void loadContent(const dv::value& map); public: - ContentLoader(ContentPack* pack, ContentBuilder& builder); + ContentLoader( + ContentPack* pack, + ContentBuilder& builder, + const ResPaths& paths + ); // Refresh pack content.json static bool fixPackIndices( diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index eef4fb1c..72cd330c 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -3,10 +3,12 @@ #include "../ContentPack.hpp" #include "files/files.hpp" +#include "files/engine_paths.hpp" #include "logic/scripting/scripting.hpp" #include "world/generator/GeneratorDef.hpp" #include "world/generator/VoxelFragment.hpp" #include "debug/Logger.hpp" +#include "util/stringutil.hpp" static BlocksLayer load_layer( const dv::value& map, uint& lastLayersHeight, bool& hasResizeableLayer @@ -167,11 +169,10 @@ static void load_structures(GeneratorDef& def, const fs::path& structuresFile) { } static inline const auto STRUCTURES_FILE = fs::u8path("structures.json"); -static inline const auto BIOMES_FILE = fs::u8path("biomes.json"); +static inline const auto BIOMES_FILE = fs::u8path("biomes.toml"); static inline const auto GENERATORS_DIR = fs::u8path("generators"); -static void load_biomes(GeneratorDef& def, const fs::path& file) { - auto root = files::read_json(file); +static void load_biomes(GeneratorDef& def, const dv::value& root) { for (const auto& [biomeName, biomeMap] : root.asObject()) { try { def.biomes.push_back( @@ -205,14 +206,16 @@ void ContentLoader::loadGenerator( if (fs::exists(structuresFile)) { load_structures(def, structuresFile); } - auto biomesFiles = folder / BIOMES_FILE; - if (!fs::exists(biomesFiles)) { + + auto biomesFile = GENERATORS_DIR / fs::u8path(name + ".files") / BIOMES_FILE; + auto biomesMap = paths.readCombinedObject(biomesFile.u8string()); + if (biomesMap.empty()) { throw std::runtime_error( - BIOMES_FILE.u8string() + - ": file not found (at least one biome required)" + "generator " + util::quote(def.name) + + ": at least one biome required" ); } - load_biomes(def, biomesFiles); + load_biomes(def, biomesMap); def.script = scripting::load_generator( def, scriptFile, pack->id+":generators/"+name+".files"); } diff --git a/src/engine.cpp b/src/engine.cpp index a7b94bbb..b32128f7 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -322,11 +322,11 @@ void Engine::loadContent() { // Load content { - ContentLoader(&corePack, contentBuilder).load(); + ContentLoader(&corePack, contentBuilder, *resPaths).load(); load_configs(corePack.folder); } for (auto& pack : contentPacks) { - ContentLoader(&pack, contentBuilder).load(); + ContentLoader(&pack, contentBuilder, *resPaths).load(); load_configs(pack.folder); } diff --git a/src/files/engine_paths.cpp b/src/files/engine_paths.cpp index c826457f..6bc42cc1 100644 --- a/src/files/engine_paths.cpp +++ b/src/files/engine_paths.cpp @@ -255,7 +255,7 @@ std::vector ResPaths::listdir( return entries; } -dv::value ResPaths::readCombinedList(const std::string& filename) { +dv::value ResPaths::readCombinedList(const std::string& filename) const { dv::value list = dv::list(); for (const auto& root : roots) { auto path = root.path / fs::u8path(filename); @@ -280,6 +280,31 @@ dv::value ResPaths::readCombinedList(const std::string& filename) { return list; } +dv::value ResPaths::readCombinedObject(const std::string& filename) const { + dv::value object = dv::object(); + for (const auto& root : roots) { + auto path = root.path / fs::u8path(filename); + if (!fs::exists(path)) { + continue; + } + try { + auto value = files::read_object(path); + if (!value.isObject()) { + logger.warning() + << "reading combined object " << root.name << ": " + << filename << " is not an object (skipped)"; + } + for (const auto& [key, element] : value.asObject()) { + object[key] = element; + } + } catch (const std::runtime_error& err) { + logger.warning() << "reading combined object " << root.name << ":" + << filename << ": " << err.what(); + } + } + return object; +} + const std::filesystem::path& ResPaths::getMainRoot() const { return mainRoot; } diff --git a/src/files/engine_paths.hpp b/src/files/engine_paths.hpp index ec0c38d5..3abcb4ea 100644 --- a/src/files/engine_paths.hpp +++ b/src/files/engine_paths.hpp @@ -68,7 +68,9 @@ public: /// @brief Read all found list versions from all packs and combine into a /// single list. Invalid versions will be skipped with logging a warning /// @param file *.json file path relative to entry point - dv::value readCombinedList(const std::string& file); + dv::value readCombinedList(const std::string& file) const; + + dv::value readCombinedObject(const std::string& file) const; const std::filesystem::path& getMainRoot() const; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index db3d157d..14c0c5aa 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -251,6 +251,7 @@ void WorldGenerator::generateStructures( util::PseudoRandom structsRand; structsRand.setSeed(chunkX, chunkZ); + // Place structures defined in biome auto heights = heightmap->getValues(); for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { From de0041445693ea772683a2edf81620645ba3ebed Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 13 Oct 2024 22:53:12 +0300 Subject: [PATCH 124/139] add generator 'caption' property --- res/layouts/pages/generators.xml.lua | 4 +- res/layouts/pages/new_world.xml.lua | 9 +---- res/texts/ru_RU.txt | 2 +- src/content/ContentLoader.cpp | 37 +++++++++++++++++-- src/content/ContentLoader.hpp | 2 +- src/content/loading/GeneratorLoader.cpp | 1 + .../scripting/lua/libs/libgeneration.cpp | 8 ++-- src/util/stringutil.cpp | 7 ++++ src/util/stringutil.hpp | 2 + src/world/generator/GeneratorDef.cpp | 5 ++- src/world/generator/GeneratorDef.hpp | 3 ++ 11 files changed, 59 insertions(+), 21 deletions(-) diff --git a/res/layouts/pages/generators.xml.lua b/res/layouts/pages/generators.xml.lua index 04c1d8bb..19e3ce60 100644 --- a/res/layouts/pages/generators.xml.lua +++ b/res/layouts/pages/generators.xml.lua @@ -5,11 +5,11 @@ function on_open() table.sort(names) local panel = document.root - for _,k in ipairs(names) do + for k, caption in pairs(names) do panel:add(gui.template("generator", { callback=string.format("settings.generator=%q menu:back()", k), id=k, - name=settings.generator_name(k) + name=settings.generator_name(caption) })) end panel:add("") diff --git a/res/layouts/pages/new_world.xml.lua b/res/layouts/pages/new_world.xml.lua index e92d5ae0..5c0c1480 100644 --- a/res/layouts/pages/new_world.xml.lua +++ b/res/layouts/pages/new_world.xml.lua @@ -10,12 +10,7 @@ function save_state() end function settings.generator_name(id) - local prefix, name = parse_path(id) - if prefix == "core" then - return gui.str(name, "world.generators") - else - return id - end + return gui.str(id, "world.generators"):gsub("^%l", string.upper) end function create_world() @@ -39,7 +34,7 @@ function on_open() document.generator_btn.text = string.format( "%s: %s", gui.str("World generator", "world"), - settings.generator_name(settings.generator) + settings.generator_name(generation.get_generators()[settings.generator]) ) document.name_box.text = settings.name or '' document.seed_box.text = settings.seed or '' diff --git a/res/texts/ru_RU.txt b/res/texts/ru_RU.txt index fbae5756..cd2168b2 100644 --- a/res/texts/ru_RU.txt +++ b/res/texts/ru_RU.txt @@ -42,7 +42,7 @@ menu.Contents Menu=Меню контентпаков world.Seed=Зерно world.Name=Название world.World generator=Генератор мира -world.generators.default=Обычный +world.generators.default=По-умолчанию world.generators.flat=Плоский world.Create World=Создать Мир world.convert-request=Есть изменения в индексах! Конвертировать мир? diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index bc03b810..89fabec0 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -54,7 +54,9 @@ static void detect_defs( continue; } if (fs::is_regular_file(file) && files::is_data_file(file)) { - detected.push_back(prefix.empty() ? name : prefix + ":" + name); + auto map = files::read_object(file); + std::string id = prefix.empty() ? name : prefix + ":" + name; + detected.emplace_back(id); } else if (fs::is_directory(file) && file.extension() != fs::u8path(".files")) { detect_defs(file, name, detected); @@ -63,11 +65,38 @@ static void detect_defs( } } -std::vector ContentLoader::scanContent( +static void detect_defs_pairs( + const fs::path& folder, + const std::string& prefix, + std::vector>& detected +) { + if (fs::is_directory(folder)) { + for (const auto& entry : fs::directory_iterator(folder)) { + const fs::path& file = entry.path(); + std::string name = file.stem().string(); + if (name[0] == '_') { + continue; + } + if (fs::is_regular_file(file) && files::is_data_file(file)) { + auto map = files::read_object(file); + std::string id = prefix.empty() ? name : prefix + ":" + name; + std::string caption = util::id_to_caption(id); + map.at("caption").get(caption); + detected.emplace_back(id, name); + } else if (fs::is_directory(file) && + file.extension() != fs::u8path(".files")) { + detect_defs_pairs(file, name, detected); + } + } + } +} + +std::vector> ContentLoader::scanContent( const ContentPack& pack, ContentType type ) { - std::vector detected; - detect_defs(pack.folder / ContentPack::getFolderFor(type), pack.id, detected); + std::vector> detected; + detect_defs_pairs( + pack.folder / ContentPack::getFolderFor(type), pack.id, detected); return detected; } diff --git a/src/content/ContentLoader.hpp b/src/content/ContentLoader.hpp index efd54efd..0f904172 100644 --- a/src/content/ContentLoader.hpp +++ b/src/content/ContentLoader.hpp @@ -70,7 +70,7 @@ public: const std::string& contentSection ); - static std::vector scanContent( + static std::vector> scanContent( const ContentPack& pack, ContentType type ); diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index 72cd330c..2c38de52 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -193,6 +193,7 @@ void ContentLoader::loadGenerator( return; } auto map = files::read_toml(generatorsDir / fs::u8path(name + ".toml")); + map.at("caption").get(def.caption); map.at("biome_parameters").get(def.biomeParameters); map.at("biome-bpd").get(def.biomesBPD); map.at("heights-bpd").get(def.heightsBPD); diff --git a/src/logic/scripting/lua/libs/libgeneration.cpp b/src/logic/scripting/lua/libs/libgeneration.cpp index d24fae0c..793dc868 100644 --- a/src/logic/scripting/lua/libs/libgeneration.cpp +++ b/src/logic/scripting/lua/libs/libgeneration.cpp @@ -52,10 +52,10 @@ static int l_get_generators(lua::State* L) { int i = 1; for (const auto& pack : packs) { - auto names = ContentLoader::scanContent(pack, ContentType::GENERATOR); - for (const auto& name : names) { - lua::pushstring(L, name); - lua::rawseti(L, i); + auto pairs = ContentLoader::scanContent(pack, ContentType::GENERATOR); + for (const auto& [name, caption] : pairs) { + lua::pushstring(L, caption); + lua::setfield(L, name); i++; } } diff --git a/src/util/stringutil.cpp b/src/util/stringutil.cpp index d1e5ef9f..c104bd5e 100644 --- a/src/util/stringutil.cpp +++ b/src/util/stringutil.cpp @@ -405,6 +405,13 @@ std::wstring util::capitalized(const std::wstring& str) { str.substr(1); } +std::string util::capitalized(const std::string& str) { + if (str.empty()) return str; + static const std::locale loc(""); + return std::string({static_cast(std::toupper(str[0], loc))}) + + str.substr(1); +} + std::wstring util::pascal_case(const std::wstring& str) { if (str.empty()) return str; static const std::locale loc(""); diff --git a/src/util/stringutil.hpp b/src/util/stringutil.hpp index cb251e83..c7bc2fb5 100644 --- a/src/util/stringutil.hpp +++ b/src/util/stringutil.hpp @@ -71,6 +71,8 @@ namespace util { double parse_double(const std::string& str); double parse_double(const std::string& str, size_t offset, size_t len); + std::string capitalized(const std::string& str); + std::wstring lower_case(const std::wstring& str); std::wstring upper_case(const std::wstring& str); std::wstring capitalized(const std::wstring& str); diff --git a/src/world/generator/GeneratorDef.cpp b/src/world/generator/GeneratorDef.cpp index 515d27e7..1dda81d9 100644 --- a/src/world/generator/GeneratorDef.cpp +++ b/src/world/generator/GeneratorDef.cpp @@ -10,8 +10,9 @@ VoxelStructure::VoxelStructure( std::unique_ptr structure ) : fragments({std::move(structure)}), meta(std::move(meta)) {} - -GeneratorDef::GeneratorDef(std::string name) : name(std::move(name)) {} +GeneratorDef::GeneratorDef(std::string name) + : name(std::move(name)), caption(util::id_to_caption(name)) { +} void GeneratorDef::prepare(const Content* content) { for (auto& biome : biomes) { diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 10ab6ff4..d978a41c 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -192,6 +192,9 @@ struct VoxelStructure { struct GeneratorDef { /// @brief Generator full name - packid:name std::string name; + /// @brief Generator display name + std::string caption; + std::unique_ptr script; /// @brief Sea level (top of seaLayers) From 04e490c8961170aefab5c96ed219bb0beee7a9ef Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 14 Oct 2024 02:56:33 +0300 Subject: [PATCH 125/139] add config/defaults.toml --- res/config/defaults.toml | 1 + res/content/base/config/defaults.toml | 1 + res/layouts/pages/new_world.xml.lua | 2 +- src/engine.cpp | 7 +++++-- src/files/engine_paths.hpp | 3 +++ src/logic/scripting/lua/libs/libcore.cpp | 7 ------- src/logic/scripting/lua/libs/libgeneration.cpp | 10 ++++++++++ src/world/World.cpp | 3 --- src/world/generator/WorldGenerator.hpp | 3 --- 9 files changed, 21 insertions(+), 16 deletions(-) create mode 100644 res/config/defaults.toml create mode 100644 res/content/base/config/defaults.toml diff --git a/res/config/defaults.toml b/res/config/defaults.toml new file mode 100644 index 00000000..53de523f --- /dev/null +++ b/res/config/defaults.toml @@ -0,0 +1 @@ +generator = "core:default" diff --git a/res/content/base/config/defaults.toml b/res/content/base/config/defaults.toml new file mode 100644 index 00000000..d292d2ce --- /dev/null +++ b/res/content/base/config/defaults.toml @@ -0,0 +1 @@ +generator = "base:demo" diff --git a/res/layouts/pages/new_world.xml.lua b/res/layouts/pages/new_world.xml.lua index 5c0c1480..25e6f424 100644 --- a/res/layouts/pages/new_world.xml.lua +++ b/res/layouts/pages/new_world.xml.lua @@ -29,7 +29,7 @@ function on_open() "%s [%s]", gui.str("Content", "menu"), #pack.get_installed() ) if settings.generator == nil then - settings.generator = core.get_default_generator() + settings.generator = generation.get_default_generator() end document.generator_btn.text = string.format( "%s: %s", diff --git a/src/engine.cpp b/src/engine.cpp index b32128f7..6ba4fcee 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -345,6 +345,11 @@ void Engine::resetContent() { resRoots.push_back({"core", pack.folder}); load_configs(pack.folder); } + auto manager = createPacksManager(fs::path()); + manager.scan(); + for (const auto& pack : manager.getAll(basePacks)) { + resRoots.push_back({pack.id, pack.folder}); + } resPaths = std::make_unique(resdir, resRoots); contentPacks.clear(); content.reset(); @@ -353,8 +358,6 @@ void Engine::resetContent() { loadAssets(); onAssetsLoaded(); - auto manager = createPacksManager(fs::path()); - manager.scan(); contentPacks = manager.getAll(basePacks); } diff --git a/src/files/engine_paths.hpp b/src/files/engine_paths.hpp index 3abcb4ea..c5289243 100644 --- a/src/files/engine_paths.hpp +++ b/src/files/engine_paths.hpp @@ -44,6 +44,9 @@ public: std::filesystem::path resolve(const std::string& path, bool throwErr = true); static std::tuple parsePath(std::string_view view); + + static inline auto CONFIG_DEFAULTS = + std::filesystem::u8path("config/defaults.toml"); private: std::filesystem::path userFilesFolder {"."}; std::filesystem::path resourcesFolder {"res"}; diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index 84033656..6f0dd8b8 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -173,12 +173,6 @@ static int l_quit(lua::State*) { return 0; } -/// @brief Get the default world generator -/// @return The ID of the default world generator -static int l_get_default_generator(lua::State* L) { - return lua::pushstring(L, WorldGenerator::DEFAULT); -} - const luaL_Reg corelib[] = { {"new_world", lua::wrap}, {"open_world", lua::wrap}, @@ -191,5 +185,4 @@ const luaL_Reg corelib[] = { {"str_setting", lua::wrap}, {"get_setting_info", lua::wrap}, {"quit", lua::wrap}, - {"get_default_generator", lua::wrap}, {NULL, NULL}}; diff --git a/src/logic/scripting/lua/libs/libgeneration.cpp b/src/logic/scripting/lua/libs/libgeneration.cpp index 793dc868..8ff0c32a 100644 --- a/src/logic/scripting/lua/libs/libgeneration.cpp +++ b/src/logic/scripting/lua/libs/libgeneration.cpp @@ -62,8 +62,18 @@ static int l_get_generators(lua::State* L) { return 1; } +/// @brief Get the default world generator +/// @return The ID of the default world generator +static int l_get_default_generator(lua::State* L) { + auto combined = engine->getResPaths()->readCombinedObject( + EnginePaths::CONFIG_DEFAULTS.u8string() + ); + return lua::pushstring(L, combined["generator"].asString()); +} + const luaL_Reg generationlib[] = { {"save_structure", lua::wrap}, {"load_structure", lua::wrap}, {"get_generators", lua::wrap}, + {"get_default_generator", lua::wrap}, {NULL, NULL}}; diff --git a/src/world/World.cpp b/src/world/World.cpp index 52fac83f..5267561d 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -206,9 +206,6 @@ void WorldInfo::deserialize(const dv::value& root) { generator = root["generator"].asString(generator); seed = root["seed"].asInteger(seed); - if (generator.empty()) { - generator = WorldGenerator::DEFAULT; - } if (root.has("version")) { auto& verobj = root["version"]; major = verobj["major"].asInteger(); diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index c3943e47..c6f37fa1 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -131,7 +131,4 @@ public: void generate(voxel* voxels, int x, int z); WorldGenDebugInfo createDebugInfo() const; - - /// @brief Default generator name // TODO: move to config - inline static std::string DEFAULT = "core:default"; }; From e3c6e55a419646200acc7d43bdaf71cbd57e2106 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 14 Oct 2024 04:45:11 +0300 Subject: [PATCH 126/139] convert generator parameters naming to the project style --- .../base/generators/demo.files/biomes.toml | 22 +++++++++---------- res/content/base/generators/demo.toml | 4 ++-- res/generators/default.files/biomes.toml | 2 +- res/generators/default.toml | 2 +- src/content/loading/GeneratorLoader.cpp | 12 +++++----- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/res/content/base/generators/demo.files/biomes.toml b/res/content/base/generators/demo.files/biomes.toml index d782d991..bd2e11e8 100644 --- a/res/content/base/generators/demo.files/biomes.toml +++ b/res/content/base/generators/demo.files/biomes.toml @@ -4,20 +4,20 @@ parameters = [ {weight=0.5, value=0.2} ] layers = [ - {below_sea_level=false, height=1, block="base:grass_block"}, - {below_sea_level=false, height=7, block="base:dirt"}, + {below-sea-level=false, height=1, block="base:grass_block"}, + {below-sea-level=false, height=7, block="base:dirt"}, {height=-1, block="base:stone"}, {height=1, block="base:bazalt"} ] -sea_layers = [ +sea-layers = [ {height=-1, block="base:water"} ] -plant_chance = 0.4 +plant-chance = 0.4 plants = [ {weight=1, block="base:grass"}, {weight=0.03, block="base:flower"} ] -structure_chance = 0.032 +structure-chance = 0.032 structures = [ {name="tree0", weight=1}, {name="tree1", weight=1}, @@ -36,7 +36,7 @@ layers = [ {height=-1, block="base:stone"}, {height=1, block="base:bazalt"} ] -sea_layers = [ +sea-layers = [ {height=-1, block="base:water"} ] @@ -47,20 +47,20 @@ parameters = [ {weight=0.6, value=0.5} ] layers = [ - {below_sea_level=false, height=1, block="base:grass_block"}, - {below_sea_level=false, height=5, block="base:dirt"}, + {below-sea-level=false, height=1, block="base:grass_block"}, + {below-sea-level=false, height=5, block="base:dirt"}, {height=-1, block="base:stone"}, {height=1, block="base:bazalt"} ] -sea_layers = [ +sea-layers = [ {height=-1, block="base:water"} ] -plant_chance = 0.3 +plant-chance = 0.3 plants = [ {weight=1, block="base:grass"}, {weight=0.03, block="base:flower"} ] -structure_chance=0.0001 +structure-chance=0.0001 structures = [ {name="tree0", weight=1}, {name="tree1", weight=1} diff --git a/res/content/base/generators/demo.toml b/res/content/base/generators/demo.toml index a5cad573..f9952f61 100644 --- a/res/content/base/generators/demo.toml +++ b/res/content/base/generators/demo.toml @@ -1,4 +1,4 @@ # 1 - temperature # 2 - humidity -biome_parameters = 2 -sea_level = 64 +biome-parameters = 2 +sea-level = 64 diff --git a/res/generators/default.files/biomes.toml b/res/generators/default.files/biomes.toml index 94d6e7cd..90a98744 100644 --- a/res/generators/default.files/biomes.toml +++ b/res/generators/default.files/biomes.toml @@ -3,6 +3,6 @@ parameters = [] layers = [ {height=-1, block="core:obstacle"} ] -sea_layers = [ +sea-layers = [ {height=-1, block="core:obstacle"} ] diff --git a/res/generators/default.toml b/res/generators/default.toml index b5e19313..2360052d 100644 --- a/res/generators/default.toml +++ b/res/generators/default.toml @@ -1 +1 @@ -biome_parameters = 0 +biome-parameters = 0 diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index 2c38de52..54b9b288 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -16,7 +16,7 @@ static BlocksLayer load_layer( const auto& name = map["block"].asString(); int height = map["height"].asInteger(); bool belowSeaLevel = true; - map.at("below_sea_level").get(belowSeaLevel); + map.at("below-sea-level").get(belowSeaLevel); if (hasResizeableLayer) { lastLayersHeight += height; @@ -75,11 +75,11 @@ static inline BiomeElementList load_biome_element_list( } static inline BiomeElementList load_plants(const dv::value& biomeMap) { - return load_biome_element_list(biomeMap, "plant_chance", "plants", "block"); + return load_biome_element_list(biomeMap, "plant-chance", "plants", "block"); } static inline BiomeElementList load_structures(const dv::value map) { - return load_biome_element_list(map, "structure_chance", "structures", "name"); + return load_biome_element_list(map, "structure-chance", "structures", "name"); } static debug::Logger logger("generator-loader"); @@ -105,7 +105,7 @@ static inline Biome load_biome( auto plants = load_plants(biomeMap); auto groundLayers = load_layers(biomeMap["layers"], "layers"); - auto seaLayers = load_layers(biomeMap["sea_layers"], "sea_layers"); + auto seaLayers = load_layers(biomeMap["sea-layers"], "sea-layers"); BiomeElementList structures; if (biomeMap.has("structures")) { @@ -194,10 +194,10 @@ void ContentLoader::loadGenerator( } auto map = files::read_toml(generatorsDir / fs::u8path(name + ".toml")); map.at("caption").get(def.caption); - map.at("biome_parameters").get(def.biomeParameters); + map.at("biome-parameters").get(def.biomeParameters); map.at("biome-bpd").get(def.biomesBPD); map.at("heights-bpd").get(def.heightsBPD); - map.at("sea_level").get(def.seaLevel); + map.at("sea-level").get(def.seaLevel); map.at("wide-structs-chunks-radius").get(def.wideStructsChunksRadius); auto folder = generatorsDir / fs::u8path(name + ".files"); From 4c3ce8c1747b6c18e956ff231d8b86b3566ca1bd Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 14 Oct 2024 07:18:24 +0300 Subject: [PATCH 127/139] add resource-aliases.json --- res/content/base/resource-aliases.json | 8 ++++++++ src/content/Content.hpp | 11 ++++++++++- src/content/ContentLoader.cpp | 22 ++++++++++++++++++++++ src/content/ContentLoader.hpp | 1 + 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 res/content/base/resource-aliases.json diff --git a/res/content/base/resource-aliases.json b/res/content/base/resource-aliases.json new file mode 100644 index 00000000..1d51f5c3 --- /dev/null +++ b/res/content/base/resource-aliases.json @@ -0,0 +1,8 @@ +{ + "camera": { + "base:first-person": "core:first-person", + "base:third-person-front": "core:third-person-front", + "base:third-person-back": "core:third-person-back", + "base:cinematic": "core:cinematic" + } +} diff --git a/src/content/Content.hpp b/src/content/Content.hpp index 9c9c9743..3a836c4e 100644 --- a/src/content/Content.hpp +++ b/src/content/Content.hpp @@ -137,12 +137,21 @@ public: static constexpr size_t MISSING = SIZE_MAX; - void add(std::string name, dv::value map) { + void add(const std::string& name, dv::value map) { indices[name] = names.size(); names.push_back(name); savedData->push_back(std::move(map)); } + void addAlias(const std::string& name, const std::string& alias) { + size_t index = indexOf(name); + if (index == MISSING) { + throw std::runtime_error( + "resource does not exists: "+name); + } + indices[alias] = index; + } + const std::string& getName(size_t index) const { return names.at(index); } diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 89fabec0..7a772d84 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -790,6 +790,20 @@ void ContentLoader::load() { } } + // Load pack resources aliases + fs::path aliasesFile = folder / fs::u8path("resource-aliases.json"); + if (fs::exists(aliasesFile)) { + auto resRoot = files::read_json(aliasesFile); + for (const auto& [key, arr] : resRoot.asObject()) { + if (auto resType = ResourceType_from(key)) { + loadResourceAliases(*resType, arr); + } else { + // Ignore unknown resources + logger.warning() << "unknown resource type: " << key; + } + } + } + // Load block materials fs::path materialsDir = folder / fs::u8path("block_materials"); if (fs::is_directory(materialsDir)) { @@ -835,3 +849,11 @@ void ContentLoader::loadResources(ResourceType type, const dv::value& list) { ); } } + +void ContentLoader::loadResourceAliases(ResourceType type, const dv::value& aliases) { + for (const auto& [alias, name] : aliases.asObject()) { + builder.resourceIndices[static_cast(type)].addAlias( + name.asString(), alias + ); + } +} diff --git a/src/content/ContentLoader.hpp b/src/content/ContentLoader.hpp index 0f904172..1b011cbb 100644 --- a/src/content/ContentLoader.hpp +++ b/src/content/ContentLoader.hpp @@ -54,6 +54,7 @@ class ContentLoader { EntityDef& def, const std::string& name, const fs::path& file ); void loadResources(ResourceType type, const dv::value& list); + void loadResourceAliases(ResourceType type, const dv::value& aliases); void loadContent(const dv::value& map); public: From a1407a1d1fce5f96e6baed694f748f7ae2ada037 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 14 Oct 2024 07:38:54 +0300 Subject: [PATCH 128/139] extend stdmin.lua & add module base:generation/ores --- .../base/generators/demo.files/script.lua | 27 +------- res/content/base/modules/generation/ores.lua | 29 +++++++++ res/scripts/stdlib.lua | 65 ------------------- res/scripts/stdmin.lua | 65 +++++++++++++++++++ 4 files changed, 97 insertions(+), 89 deletions(-) create mode 100644 res/content/base/modules/generation/ores.lua diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua index 8e138fd2..7596d226 100644 --- a/res/content/base/generators/demo.files/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -1,31 +1,10 @@ local _, dir = parse_path(__DIR__) -ores = file.read_combined_list(dir.."/ores.json") - -local function place_ores(placements, x, z, w, d, seed, hmap, chunk_height) - local BLOCKS_PER_CHUNK = w * d * chunk_height - for _, ore in ipairs(ores) do - local count = BLOCKS_PER_CHUNK / ore.rarity - - -- average count is less than 1 - local addchance = math.fmod(count, 1.0) - if math.random() < addchance then - count = count + 1 - end - - for i=1,count do - local sx = math.random() * w - local sz = math.random() * d - local sy = math.random() * (chunk_height * 0.5) - if sy < hmap:at(sx, sz) * chunk_height - 6 then - table.insert(placements, {ore.struct, {sx, sy, sz}, math.random()*4, -1}) - end - end - end -end +local ores = require "base:generation/ores" +ores.load(dir) function place_structures(x, z, w, d, seed, hmap, chunk_height) local placements = {} - place_ores(placements, x, z, w, d, seed, hmap, chunk_height) + ores.place(placements, x, z, w, d, seed, hmap, chunk_height) return placements end diff --git a/res/content/base/modules/generation/ores.lua b/res/content/base/modules/generation/ores.lua new file mode 100644 index 00000000..3b2c2f91 --- /dev/null +++ b/res/content/base/modules/generation/ores.lua @@ -0,0 +1,29 @@ +local ores = {} + +function ores.load(directory) + ores.ores = file.read_combined_list(directory.."/ores.json") +end + +function ores.place(placements, x, z, w, d, seed, hmap, chunk_height) + local BLOCKS_PER_CHUNK = w * d * chunk_height + for _, ore in ipairs(ores.ores) do + local count = BLOCKS_PER_CHUNK / ore.rarity + + -- average count is less than 1 + local addchance = math.fmod(count, 1.0) + if math.random() < addchance then + count = count + 1 + end + + for i=1,count do + local sx = math.random() * w + local sz = math.random() * d + local sy = math.random() * (chunk_height * 0.5) + if sy < hmap:at(sx, sz) * chunk_height - 6 then + table.insert(placements, {ore.struct, {sx, sy, sz}, math.random()*4, -1}) + end + end + end +end + +return ores diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index f6e2603a..8fbc779f 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -2,71 +2,6 @@ ------ Extended kit of standard functions ------ ------------------------------------------------ -package = { - loaded={} -} -local __cached_scripts = {} -local __warnings_hidden = {} - -function on_deprecated_call(name, alternatives) - if __warnings_hidden[name] then - return - end - __warnings_hidden[name] = true - if alternatives then - debug.warning("deprecated function called ("..name.."), use ".. - alternatives.." instead\n"..debug.traceback()) - else - debug.warning("deprecated function called ("..name..")\n"..debug.traceback()) - end -end - --- Load script with caching --- --- path - script path `contentpack:filename`. --- Example `base:scripts/tests.lua` --- --- nocache - ignore cached script, load anyway -local function __load_script(path, nocache) - local packname, filename = parse_path(path) - - -- __cached_scripts used in condition because cached result may be nil - if not nocache and __cached_scripts[path] ~= nil then - return package.loaded[path] - end - if not file.isfile(path) then - error("script '"..filename.."' not found in '"..packname.."'") - end - - local script, err = load(file.read(path), path) - if script == nil then - error(err) - end - local result = script() - if not nocache then - __cached_scripts[path] = script - package.loaded[path] = result - end - return result -end - -function __scripts_cleanup() - print("cleaning scripts cache") - for k, v in pairs(__cached_scripts) do - local packname, _ = parse_path(k) - if packname ~= "core" then - print("unloaded "..k) - __cached_scripts[k] = nil - package.loaded[k] = nil - end - end -end - -function require(path) - local prefix, file = parse_path(path) - return __load_script(prefix..":modules/"..file..".lua") -end - function sleep(timesec) local start = time.uptime() while time.uptime() - start < timesec do diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 9fd982bc..a063ee8d 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -222,3 +222,68 @@ function file.readlines(path) end return lines end + +package = { + loaded={} +} +local __cached_scripts = {} +local __warnings_hidden = {} + +function on_deprecated_call(name, alternatives) + if __warnings_hidden[name] then + return + end + __warnings_hidden[name] = true + if alternatives then + debug.warning("deprecated function called ("..name.."), use ".. + alternatives.." instead\n"..debug.traceback()) + else + debug.warning("deprecated function called ("..name..")\n"..debug.traceback()) + end +end + +-- Load script with caching +-- +-- path - script path `contentpack:filename`. +-- Example `base:scripts/tests.lua` +-- +-- nocache - ignore cached script, load anyway +local function __load_script(path, nocache) + local packname, filename = parse_path(path) + + -- __cached_scripts used in condition because cached result may be nil + if not nocache and __cached_scripts[path] ~= nil then + return package.loaded[path] + end + if not file.isfile(path) then + error("script '"..filename.."' not found in '"..packname.."'") + end + + local script, err = load(file.read(path), path) + if script == nil then + error(err) + end + local result = script() + if not nocache then + __cached_scripts[path] = script + package.loaded[path] = result + end + return result +end + +function require(path) + local prefix, file = parse_path(path) + return __load_script(prefix..":modules/"..file..".lua") +end + +function __scripts_cleanup() + print("cleaning scripts cache") + for k, v in pairs(__cached_scripts) do + local packname, _ = parse_path(k) + if packname ~= "core" then + print("unloaded "..k) + __cached_scripts[k] = nil + package.loaded[k] = nil + end + end +end From b9074ebe4788d0016a9fd7563b59816b6300c06d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 14 Oct 2024 09:43:51 +0300 Subject: [PATCH 129/139] fix block.get_hitbox with non rotatable blocks --- src/logic/scripting/lua/libs/libblock.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 193c8520..df388fea 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -281,7 +281,13 @@ static int l_get_model(lua::State* L) { static int l_get_hitbox(lua::State* L) { if (auto def = require_block(L)) { - auto& hitbox = def->rt.hitboxes[lua::tointeger(L, 2)].at(0); + size_t rotation = lua::tointeger(L, 2); + if (def->rotatable) { + rotation %= def->rotations.MAX_COUNT; + } else { + rotation = 0; + } + auto& hitbox = def->rt.hitboxes[rotation].at(0); lua::createtable(L, 2, 0); lua::pushvec3(L, hitbox.min()); From cc6891dde80a54aca508892e4a5c0015b57d4a81 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 14 Oct 2024 19:38:30 +0300 Subject: [PATCH 130/139] feat: boolean type support finally --- src/data/dv.hpp | 3 +++ src/logic/CommandsInterpreter.cpp | 6 ++++++ src/logic/CommandsInterpreter.hpp | 4 +++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/data/dv.hpp b/src/data/dv.hpp index 94d96453..8070849f 100644 --- a/src/data/dv.hpp +++ b/src/data/dv.hpp @@ -505,6 +505,9 @@ namespace dv { inline bool isNumber() const noexcept { return type == value_type::number; } + inline bool isBoolean() const noexcept { + return type == value_type::boolean; + } }; inline bool is_numeric(const value& val) { diff --git a/src/logic/CommandsInterpreter.cpp b/src/logic/CommandsInterpreter.cpp index d2e5bb03..cc8ff900 100644 --- a/src/logic/CommandsInterpreter.cpp +++ b/src/logic/CommandsInterpreter.cpp @@ -39,6 +39,7 @@ class CommandParser : BasicParser { {"int", ArgType::integer}, {"str", ArgType::string}, {"sel", ArgType::selector}, + {"bool", ArgType::boolean}, {"enum", ArgType::enumvalue}, }; public: @@ -250,6 +251,11 @@ public: return selectorCheck(arg, value); case ArgType::integer: return typeCheck(arg, dv::value_type::integer, value, "integer"); + case ArgType::boolean: + if (!arg->optional) { + throw typeError(arg->name, "boolean", value); + } + return value.isBoolean(); case ArgType::string: if (!value.isString()) { return !arg->optional; diff --git a/src/logic/CommandsInterpreter.hpp b/src/logic/CommandsInterpreter.hpp index af3e54ac..ce9ccaba 100644 --- a/src/logic/CommandsInterpreter.hpp +++ b/src/logic/CommandsInterpreter.hpp @@ -8,7 +8,7 @@ #include "data/dv.hpp" namespace cmd { - enum class ArgType { number, integer, enumvalue, selector, string }; + enum class ArgType { number, integer, enumvalue, selector, boolean, string }; inline std::string argtype_name(ArgType type) { switch (type) { @@ -20,6 +20,8 @@ namespace cmd { return "enumeration"; case ArgType::selector: return "selector"; + case ArgType::boolean: + return "boolean"; case ArgType::string: return "string"; default: From fd2bd1565892bd90a62a3f9d3edb6445f2e94766 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 14 Oct 2024 19:39:58 +0300 Subject: [PATCH 131/139] update voxel fragments lua api & replace structure.save command with fragment.save & add 'export' entry point --- .gitignore | 2 + res/scripts/stdcmd.lua | 14 ++++-- src/files/engine_paths.cpp | 8 ++++ .../scripting/lua/libs/libgeneration.cpp | 47 +++++++++++-------- src/logic/scripting/lua/lua_custom_types.hpp | 16 +++---- src/logic/scripting/lua/lua_engine.cpp | 2 +- .../lua/usertypes/lua_type_voxelstructure.cpp | 29 +++++++++--- src/world/generator/VoxelFragment.cpp | 32 ++++++++++++- src/world/generator/VoxelFragment.hpp | 7 ++- 9 files changed, 117 insertions(+), 40 deletions(-) diff --git a/.gitignore b/.gitignore index 8039c093..1847e03c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ Debug/voxel_engine /build /cmake-build-** /screenshots +/export +/config /out /misc diff --git a/res/scripts/stdcmd.lua b/res/scripts/stdcmd.lua index 9d05a3b5..ef742d5e 100644 --- a/res/scripts/stdcmd.lua +++ b/res/scripts/stdcmd.lua @@ -151,8 +151,8 @@ console.add_command( ) console.add_command( - "structure.save x:int y:int z:int w:int h:int d:int name:str='untitled'", - "Save structure", + "fragment.save x:int y:int z:int w:int h:int d:int name:str='untitled' crop:bool=false", + "Save fragment", function(args, kwargs) local x = args[1] local y = args[2] @@ -163,6 +163,14 @@ console.add_command( local d = args[6] local name = args[7] - generation.save_structure({x, y, z}, {x+w, y+h, z+d}, name..'.vox', false) + local crop = args[8] + + local fragment = generation.create_fragment( + {x, y, z}, {x + w, y + h, z + d}, crop, false + ) + local filename = 'export:'..name..'.vox' + generation.save_fragment(fragment, filename, crop) + console.log("fragment with size "..vec3.tostring(fragment.size).. + " has been saved as "..file.resolve(filename)) end ) diff --git a/src/files/engine_paths.cpp b/src/files/engine_paths.cpp index 6bc42cc1..11d88bca 100644 --- a/src/files/engine_paths.cpp +++ b/src/files/engine_paths.cpp @@ -18,6 +18,7 @@ static inline auto SCREENSHOTS_FOLDER = std::filesystem::u8path("screenshots"); static inline auto CONTENT_FOLDER = std::filesystem::u8path("content"); static inline auto WORLDS_FOLDER = std::filesystem::u8path("worlds"); static inline auto CONFIG_FOLDER = std::filesystem::u8path("config"); +static inline auto EXPORT_FOLDER = std::filesystem::u8path("export"); static inline auto CONTROLS_FILE = std::filesystem::u8path("controls.toml"); static inline auto SETTINGS_FILE = std::filesystem::u8path("settings.toml"); @@ -51,6 +52,10 @@ void EnginePaths::prepare() { if (!fs::is_directory(contentFolder)) { fs::create_directories(contentFolder); } + auto exportFolder = userFilesFolder / EXPORT_FOLDER; + if (!fs::is_directory(exportFolder)) { + fs::create_directories(exportFolder); + } } std::filesystem::path EnginePaths::getUserFilesFolder() const { @@ -187,6 +192,9 @@ std::filesystem::path EnginePaths::resolve( if (prefix == "world") { return currentWorldFolder / fs::u8path(filename); } + if (prefix == "export") { + return userFilesFolder / EXPORT_FOLDER / fs::u8path(filename); + } if (contentPacks) { for (auto& pack : *contentPacks) { diff --git a/src/logic/scripting/lua/libs/libgeneration.cpp b/src/logic/scripting/lua/libs/libgeneration.cpp index 8ff0c32a..2874dbad 100644 --- a/src/logic/scripting/lua/libs/libgeneration.cpp +++ b/src/logic/scripting/lua/libs/libgeneration.cpp @@ -11,24 +11,30 @@ using namespace scripting; -static int l_save_structure(lua::State* L) { - auto pointA = lua::tovec<3>(L, 1); - auto pointB = lua::tovec<3>(L, 2); - auto filename = lua::require_string(L, 3); - if (!files::is_valid_name(filename)) { - throw std::runtime_error("invalid file name"); - } - bool saveEntities = lua::toboolean(L, 4); - - auto structure = VoxelFragment::create(level, pointA, pointB, saveEntities); - auto map = structure->serialize(); - - auto bytes = json::to_binary(map); - files::write_bytes(fs::u8path(filename), bytes.data(), bytes.size()); +static int l_save_fragment(lua::State* L) { + auto paths = engine->getPaths(); + auto fragment = lua::touserdata(L, 1); + auto file = paths->resolve(lua::require_string(L, 2), true); + auto map = fragment->getFragment()->serialize(); + auto bytes = json::to_binary(map, true); + files::write_bytes(file, bytes.data(), bytes.size()); return 0; } -static int l_load_structure(lua::State* L) { +static int l_create_fragment(lua::State* L) { + auto pointA = lua::tovec<3>(L, 1); + auto pointB = lua::tovec<3>(L, 2); + bool crop = lua::toboolean(L, 3); + bool saveEntities = lua::toboolean(L, 4); + + auto fragment = + VoxelFragment::create(level, pointA, pointB, crop, saveEntities); + return lua::newuserdata( + L, std::shared_ptr(std::move(fragment)) + ); +} + +static int l_load_fragment(lua::State* L) { auto paths = engine->getPaths(); auto [prefix, filename] = EnginePaths::parsePath(lua::require_string(L, 1)); @@ -38,9 +44,9 @@ static int l_load_structure(lua::State* L) { } auto map = files::read_binary_json(path); - auto structure = std::make_shared(); - structure->deserialize(map); - return lua::newuserdata(L, std::move(structure)); + auto fragment = std::make_shared(); + fragment->deserialize(map); + return lua::newuserdata(L, std::move(fragment)); } /// @brief Get a list of all world generators @@ -72,8 +78,9 @@ static int l_get_default_generator(lua::State* L) { } const luaL_Reg generationlib[] = { - {"save_structure", lua::wrap}, - {"load_structure", lua::wrap}, + {"create_fragment", lua::wrap}, + {"save_fragment", lua::wrap}, + {"load_fragment", lua::wrap}, {"get_generators", lua::wrap}, {"get_default_generator", lua::wrap}, {NULL, NULL}}; diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index dcd78800..bb8037ee 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -71,15 +71,15 @@ namespace lua { }; static_assert(!std::is_abstract()); - class LuaVoxelStructure : public Userdata { - std::shared_ptr structure; + class LuaVoxelFragment : public Userdata { + std::shared_ptr fragment; public: - LuaVoxelStructure(std::shared_ptr structure); + LuaVoxelFragment(std::shared_ptr fragment); - virtual ~LuaVoxelStructure(); + virtual ~LuaVoxelFragment(); - std::shared_ptr getStructure() const { - return structure; + std::shared_ptr getFragment() const { + return fragment; } const std::string& getTypeName() const override { @@ -87,7 +87,7 @@ namespace lua { } static int createMetatable(lua::State*); - inline static std::string TYPENAME = "VoxelStructure"; + inline static std::string TYPENAME = "VoxelFragment"; }; - static_assert(!std::is_abstract()); + static_assert(!std::is_abstract()); } diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 4c5e9856..42ea18ec 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -103,7 +103,7 @@ void lua::init_state(State* L, StateType stateType) { newusertype(L); newusertype(L); - newusertype(L); + newusertype(L); } void lua::initialize(const EnginePaths& paths) { diff --git a/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp b/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp index 08a3ef84..fd40ee83 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp @@ -7,20 +7,37 @@ using namespace lua; -LuaVoxelStructure::LuaVoxelStructure(std::shared_ptr structure) - : structure(std::move(structure)) {} +LuaVoxelFragment::LuaVoxelFragment(std::shared_ptr fragment) + : fragment(std::move(fragment)) {} -LuaVoxelStructure::~LuaVoxelStructure() { +LuaVoxelFragment::~LuaVoxelFragment() { } static int l_meta_tostring(lua::State* L) { - return pushstring(L, "VoxelStructure(0x" + util::tohex( + return pushstring(L, "VoxelFragment(0x" + util::tohex( reinterpret_cast(topointer(L, 1)))+")"); } -int LuaVoxelStructure::createMetatable(lua::State* L) { - createtable(L, 0, 1); +static int l_meta_index(lua::State* L) { + auto fragment = touserdata(L, 1); + if (fragment == nullptr) { + return 0; + } + if (isstring(L, 2)) { + auto fieldname = tostring(L, 2); + if (!std::strcmp(fieldname, "size")) { + return pushivec(L, fragment->getFragment()->getSize()); + } + } + return 0; +} + + +int LuaVoxelFragment::createMetatable(lua::State* L) { + createtable(L, 0, 2); pushcfunction(L, lua::wrap); setfield(L, "__tostring"); + pushcfunction(L, lua::wrap); + setfield(L, "__index"); return 1; } diff --git a/src/world/generator/VoxelFragment.cpp b/src/world/generator/VoxelFragment.cpp index 2dd49fb7..5289051b 100644 --- a/src/world/generator/VoxelFragment.cpp +++ b/src/world/generator/VoxelFragment.cpp @@ -11,11 +11,41 @@ #include "world/Level.hpp" std::unique_ptr VoxelFragment::create( - Level* level, const glm::ivec3& a, const glm::ivec3& b, bool entities + Level* level, + const glm::ivec3& a, + const glm::ivec3& b, + bool crop, + bool entities ) { auto start = glm::min(a, b); auto size = glm::abs(a - b); + if (crop) { + VoxelsVolume volume(size.x, size.y, size.z); + volume.setPosition(start.x, start.y, start.z); + level->chunksStorage->getVoxels(&volume); + + auto end = start + size; + + auto min = end; + auto max = start; + + for (int y = start.y; y < end.y; y++) { + for (int z = start.z; z < end.z; z++) { + for (int x = start.x; x < end.x; x++) { + if (volume.pickBlockId(x, y, z)) { + min = glm::min(min, {x, y, z}); + max = glm::max(max, {x+1, y+1, z+1}); + } + } + } + } + if (glm::min(min, max) == min) { + start = min; + size = max - min; + } + } + VoxelsVolume volume(size.x, size.y, size.z); volume.setPosition(start.x, start.y, start.z); level->chunksStorage->getVoxels(&volume); diff --git a/src/world/generator/VoxelFragment.hpp b/src/world/generator/VoxelFragment.hpp index 0a75359e..f9e94ab8 100644 --- a/src/world/generator/VoxelFragment.hpp +++ b/src/world/generator/VoxelFragment.hpp @@ -44,7 +44,12 @@ public: std::unique_ptr rotated(const Content& content) const; static std::unique_ptr create( - Level* level, const glm::ivec3& a, const glm::ivec3& b, bool entities); + Level* level, + const glm::ivec3& a, + const glm::ivec3& b, + bool crop, + bool entities + ); const glm::ivec3& getSize() const { return size; From 44eedadc061bd9e04b05c1a22690aac3343f8115 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 14 Oct 2024 20:47:37 +0300 Subject: [PATCH 132/139] make world generator less pedantic in structures placement --- src/world/generator/WorldGenerator.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 14c0c5aa..0f79c4e8 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -162,9 +162,11 @@ void WorldGenerator::placeStructure( AABB aabb(position, position + size); for (int lcz = -1; lcz <= 1; lcz++) { for (int lcx = -1; lcx <= 1; lcx++) { - auto& otherPrototype = requirePrototype( - chunkX + lcx, chunkZ + lcz - ); + const auto& found = prototypes.find({chunkX + lcx, chunkZ + lcz}); + if (found == prototypes.end()) { + continue; + } + auto& otherPrototype = *found->second; auto chunkAABB = gen_chunk_aabb(chunkX + lcx, chunkZ + lcz); if (chunkAABB.intersect(aabb)) { otherPrototype.placements.emplace_back( From 48143c5a2b166634a545ab967b0ebe5e889b4f5a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 15 Oct 2024 00:03:06 +0300 Subject: [PATCH 133/139] add fragment.crop, fragment:crop() & fix fragments rotation when width is not equal to depth & fix extra structures placements --- .../generators/demo.files/fragments/tower.vox | Bin 343 -> 241 bytes .../generators/demo.files/fragments/tree0.vox | Bin 262 -> 204 bytes .../generators/demo.files/fragments/tree1.vox | Bin 299 -> 243 bytes .../generators/demo.files/fragments/tree2.vox | Bin 291 -> 229 bytes res/scripts/stdcmd.lua | 13 +++++ src/content/loading/GeneratorLoader.cpp | 3 + .../scripting/lua/libs/libgeneration.cpp | 5 +- .../lua/usertypes/lua_type_voxelstructure.cpp | 17 +++++- src/voxels/ChunksStorage.cpp | 6 +- src/world/generator/VoxelFragment.cpp | 54 ++++++++++++++++-- src/world/generator/VoxelFragment.hpp | 1 + src/world/generator/WorldGenerator.cpp | 4 +- src/world/generator/WorldGenerator.hpp | 10 ++-- 13 files changed, 93 insertions(+), 20 deletions(-) diff --git a/res/content/base/generators/demo.files/fragments/tower.vox b/res/content/base/generators/demo.files/fragments/tower.vox index c992fe45175e0c29a4ced15147f0b5aff93f0957..a6b58857f876677a626a1989513825a0fea0b5ae 100644 GIT binary patch literal 241 zcmV3ZK0$~z zke68uuY`GznSxjYFsmDv1~5ScK7d}rg>U%~q^QBB<$ZlE&so3EMzHkyK)1VQUK_c=4~f5-7g92 r3~%*hMtXQ=Qk>2sn|?@&A1Ozx7YfRsEaV z_sg;Q&wT!${dXdf<+s(_Q*YO_XoSXgpGcE9yzAw$UmvS4nHjD*UVG=Nzy0QY)wQ>`{@yqB fMlIXQKd0<}Pn9_&u{%-w-bwMoD~?Y@Rz-Oz6on=FP6{U zMGfb>nmpo<`5Qk&jMlN>7&(*H`38Tgw$xXxm$Yl&rPOb(uje~1^}Gbjz4?cn&?`M0 zc9T4kpJqY;z@QCvax|j#K`$+JC$0BlbB`2B=Zo!lAwU0C*rEVO#*TXOUly)W{AK1J G2LJ$$?_*g2 literal 262 zcmV+h0r~zPiwFp>2=8V919Wm_WiT#wZ+HOh*UhTJFc5~}N#ddhH{OUh;L1CZs#z4P zfd;C0^`{jS1UF`a@#p2CErrfBgnlV8tEEUEPQHy|o#3k(Hz_^_0A-MW;CGRKpd9TE zOIuI=fpnVujkRVtr}oHGKe=2rJv8;AtOYc*-)*Vv*at^S{g{8_+|xbmT@ShSNE?HG z9)J|@kbmvFbFCk#`Oj7V>Ny0oO77ott{eyI08imfUU*@Vt7>ZYsyhMRRA{o)wHwwhk=T3`3W!?8Jgv2He!-gmJH2XRLJ M0gAB5;$J`j0G3IA9smFU diff --git a/res/content/base/generators/demo.files/fragments/tree1.vox b/res/content/base/generators/demo.files/fragments/tree1.vox index 7dda751dfcc3095bd7f6523019ef26af1fc59f9e..6c2aec445a987c1e61a2b8b09666ae6066f47239 100644 GIT binary patch literal 243 zcmVhiwFP!000001MQdL3WFdJ$4?#1Sg$hny|=M;A7U*bp^ZKLY#^-B=`2SK z;8&b{WtcFePd5+R7c@&K4t%|3&qDhF&7`$*X_jP ze0+R9ZcfZI`=oSn?7nZOWTR`q$@;OE`54A)KQ*CrnR8xzHTs4XJh15Z|xZL%PJ~vVtg2TNb}q1WgI_7uHAakqJp+6@@6rKew8 z5SFrqHWQqRe|{(kPE_!lMBUS)$e7+^pT!3Oe&E85`8@!zLH>d5BLBd4k$+&lG=F#e z#a4>`0oc&w*DLGiF+TIpA7*S-jrmtoTQbIFotyTW{xRoY`y9a1^mCtA&A;x8Hh+J= zhwr^Z(?2ZNAEK_ciQG)El z+JEUEOJBcceJS#fwX=Nxt?RYV4*&oF0000000000uo80U)A8lu)V{_nzG+0{n1;A- xyP>(A4i0UO`?r*yo4fg_kL@^}oV$-GKJ3DM*vS}&+@+HQCua}2>!n{%0092&n@9iv diff --git a/res/content/base/generators/demo.files/fragments/tree2.vox b/res/content/base/generators/demo.files/fragments/tree2.vox index 98fc3bef1ffdf3fc90a02b808229d7e6cb64fe3e..0aac534c37676823ec8fe2ea1c9e1b1c7da122ca 100644 GIT binary patch literal 229 zcmVgf0R>C3Clp1$1L_~_~_%|{ literal 291 zcmV+;0o?u{iwFP!000001MS(tYJ)%!#_`#m9xT1&*q7)FRfD4*-0Sf1q9DA7~f(2iiscfp%H* z&(~$ZSJb*3E;UR3YwH2nS+W!V3vi{cdthJW@Z+Y)*^$cpp z4%~yS`hIO~_*1w3zn*tk#{#s-t9M@Ze~)_yVB?*C0KTmAhjsn$aeV;wwpcq@=l}dW z1pIh?L)_x}0DO^O00000000000001hyC;V+A3t7B{bwo?yG}%oc}n|!n7YUF&Y>@9 p|CRH5_q6Vfsh`gWm*FQ#)SW)-PUbX~A)iEp`~r;2=8Rup003mslwbe= diff --git a/res/scripts/stdcmd.lua b/res/scripts/stdcmd.lua index ef742d5e..a2edd9b8 100644 --- a/res/scripts/stdcmd.lua +++ b/res/scripts/stdcmd.lua @@ -174,3 +174,16 @@ console.add_command( " has been saved as "..file.resolve(filename)) end ) + +console.add_command( + "fragment.crop filename:str", + "Crop fragment", + function(args, kwargs) + local filename = args[1] + local fragment = generation.load_fragment(filename) + fragment:crop() + generation.save_fragment(fragment, filename, crop) + console.log("fragment with size "..vec3.tostring(fragment.size).. + " has been saved as "..file.resolve(filename)) + end +) diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index 54b9b288..ec8315f5 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -145,6 +145,9 @@ static std::vector> load_structures( } auto fragment = std::make_unique(); fragment->deserialize(files::read_binary_json(structFile)); + logger.info() << "fragment " << name << " has size [" << + fragment->getSize().x << ", " << fragment->getSize().y << ", " << + fragment->getSize().z << "]"; structures.push_back(std::make_unique( load_structure_meta(name, config), diff --git a/src/logic/scripting/lua/libs/libgeneration.cpp b/src/logic/scripting/lua/libs/libgeneration.cpp index 2874dbad..ca97815c 100644 --- a/src/logic/scripting/lua/libs/libgeneration.cpp +++ b/src/logic/scripting/lua/libs/libgeneration.cpp @@ -36,9 +36,8 @@ static int l_create_fragment(lua::State* L) { static int l_load_fragment(lua::State* L) { auto paths = engine->getPaths(); - auto [prefix, filename] = EnginePaths::parsePath(lua::require_string(L, 1)); - - auto path = paths->resolve(prefix+":generators/"+filename+".vox"); + auto filename = lua::require_string(L, 1); + auto path = paths->resolve(filename); if (!std::filesystem::exists(path)) { throw std::runtime_error("file "+path.u8string()+" does not exist"); } diff --git a/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp b/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp index fd40ee83..eb6a24ab 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_voxelstructure.cpp @@ -13,6 +13,17 @@ LuaVoxelFragment::LuaVoxelFragment(std::shared_ptr fragment) LuaVoxelFragment::~LuaVoxelFragment() { } +static int l_crop(lua::State* L) { + if (auto fragment = touserdata(L, 1)) { + fragment->getFragment()->crop(); + } + return 0; +} + +static std::unordered_map methods { + {"crop", lua::wrap}, +}; + static int l_meta_tostring(lua::State* L) { return pushstring(L, "VoxelFragment(0x" + util::tohex( reinterpret_cast(topointer(L, 1)))+")"); @@ -27,12 +38,16 @@ static int l_meta_index(lua::State* L) { auto fieldname = tostring(L, 2); if (!std::strcmp(fieldname, "size")) { return pushivec(L, fragment->getFragment()->getSize()); + } else { + auto found = methods.find(tostring(L, 2)); + if (found != methods.end()) { + return pushcfunction(L, found->second); + } } } return 0; } - int LuaVoxelFragment::createMetatable(lua::State* L) { createtable(L, 0, 2); pushcfunction(L, lua::wrap); diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index 75925a38..75b37b9e 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -108,10 +108,10 @@ void ChunksStorage::getVoxels(VoxelsVolume* volume, bool backlight) const { int ecz = floordiv(z + d, CHUNK_D); int cw = ecx - scx + 1; - int ch = ecz - scz + 1; + int cd = ecz - scz + 1; - // cw*ch chunks will be scanned - for (int cz = scz; cz < scz + ch; cz++) { + // cw*cd chunks will be scanned + for (int cz = scz; cz < scz + cd; cz++) { for (int cx = scx; cx < scx + cw; cx++) { const auto& found = chunksMap.find(glm::ivec2(cx, cz)); if (found == chunksMap.end()) { diff --git a/src/world/generator/VoxelFragment.cpp b/src/world/generator/VoxelFragment.cpp index 5289051b..96191725 100644 --- a/src/world/generator/VoxelFragment.cpp +++ b/src/world/generator/VoxelFragment.cpp @@ -9,6 +9,7 @@ #include "voxels/ChunksStorage.hpp" #include "voxels/VoxelsVolume.hpp" #include "world/Level.hpp" +#include "core_defs.hpp" std::unique_ptr VoxelFragment::create( Level* level, @@ -51,10 +52,10 @@ std::unique_ptr VoxelFragment::create( level->chunksStorage->getVoxels(&volume); auto volVoxels = volume.getVoxels(); - std::vector voxels(size.x*size.y*size.z); + std::vector voxels(size.x * size.y * size.z); - std::vector blockNames; - std::unordered_map blocksRegistered; + std::vector blockNames {CORE_AIR}; + std::unordered_map blocksRegistered {{0, 0}}; auto contentIndices = level->content->getIndices(); for (size_t i = 0 ; i < voxels.size(); i++) { blockid_t id = volVoxels[i].id; @@ -113,6 +114,49 @@ void VoxelFragment::deserialize(const dv::value& src) { } } +void VoxelFragment::crop() { + glm::ivec3 min = size; + glm::ivec3 max = {}; + + blockid_t air; + const auto& found = std::find(blockNames.begin(), blockNames.end(), CORE_AIR); + if (found == blockNames.end()) { + throw std::runtime_error(CORE_AIR+" is not found in fragment"); + } + air = found - blockNames.begin(); + + for (int y = 0; y < size.y; y++) { + for (int z = 0; z < size.z; z++) { + for (int x = 0; x < size.x; x++) { + if (voxels[vox_index(x, y, z, size.x, size.z)].id != air) { + min = glm::min(min, {x, y, z}); + max = glm::max(max, {x+1, y+1, z+1}); + } + } + } + } + if (glm::min(min, max) == min) { + auto newSize = max - min; + std::vector newVoxels(newSize.x * newSize.y * newSize.z); + for (int y = 0; y < newSize.y; y++) { + for (int z = 0; z < newSize.z; z++) { + for (int x = 0; x < newSize.x; x++) { + newVoxels[vox_index(x, y, z, newSize.x, newSize.z)] = + voxels[vox_index( + x + min.x, + y + min.y, + z + min.z, + size.x, + size.z + )]; + } + } + } + voxels = std::move(newVoxels); + size = newSize; + } +} + void VoxelFragment::prepare(const Content& content) { auto volume = size.x*size.y*size.z; voxelsRuntime.resize(volume); @@ -129,8 +173,8 @@ std::unique_ptr VoxelFragment::rotated(const Content& content) co for (int y = 0; y < size.y; y++) { for (int z = 0; z < size.z; z++) { for (int x = 0; x < size.x; x++) { - auto& voxel = newVoxels[vox_index(x, y, z, size.x, size.z)]; - voxel = voxels[vox_index(size.z-z-1, y, x, size.z, size.x)]; + auto& voxel = newVoxels[vox_index(size.z-z-1, y, x, size.z, size.x)]; + voxel = voxels[vox_index(x, y, z, size.x, size.z)]; // swap X and Z segment bits voxel.state.segment = ((voxel.state.segment & 0b001) << 2) | (voxel.state.segment & 0b010) diff --git a/src/world/generator/VoxelFragment.hpp b/src/world/generator/VoxelFragment.hpp index f9e94ab8..95a5375b 100644 --- a/src/world/generator/VoxelFragment.hpp +++ b/src/world/generator/VoxelFragment.hpp @@ -35,6 +35,7 @@ public: dv::value serialize() const override; void deserialize(const dv::value& src) override; + void crop(); /// @brief Build runtime voxel indices /// @param content world content diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 0f79c4e8..b0236ec2 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -272,9 +272,6 @@ void WorldGenerator::generateStructures( glm::ivec3 position {x, height, z}; position.x -= structure.getSize().x / 2; position.z -= structure.getSize().z / 2; - prototype.placements.emplace_back( - 1, StructurePlacement {structureId, position, rotation} - ); placeStructure( StructurePlacement { structureId, @@ -486,6 +483,7 @@ void WorldGenerator::generateStructure( auto& structVoxels = structure.getRuntimeVoxels(); const auto& offset = placement.position; const auto& size = structure.getSize(); + for (int y = 0; y < size.y; y++) { int sy = y + offset.y; if (sy < 0 || sy >= CHUNK_H) { diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index c6f37fa1..fa8a51e0 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -66,10 +66,6 @@ class WorldGenerator { void generateStructures(ChunkPrototype& prototype, int x, int z); - void generatePlacements( - const ChunkPrototype& prototype, voxel* voxels, int x, int z - ); - void generateBiomes(ChunkPrototype& prototype, int x, int z); void generateHeightmap(ChunkPrototype& prototype, int x, int z); @@ -81,6 +77,9 @@ class WorldGenerator { void placeLine(const LinePlacement& line, int priority); + void generatePlacements( + const ChunkPrototype& prototype, voxel* voxels, int x, int z + ); void generateLine( const ChunkPrototype& prototype, const LinePlacement& placement, @@ -113,7 +112,8 @@ class WorldGenerator { void placeStructures( const std::vector& placements, ChunkPrototype& prototype, - int x, int z); + int x, int z + ); public: WorldGenerator( const GeneratorDef& def, From c34bc184a4c2ef3b248a8bc6b4c680727a4d2773 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 15 Oct 2024 02:04:17 +0300 Subject: [PATCH 134/139] update heightmap:dump(...) filename argument --- .../scripting/lua/usertypes/lua_type_heightmap.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp b/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp index 8a58e099..06a031da 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp @@ -8,10 +8,11 @@ #include "util/functional_util.hpp" #define FNL_IMPL #include "maths/FastNoiseLite.h" -#include "coders/png.hpp" +#include "coders/imageio.hpp" #include "files/util.hpp" #include "graphics/core/ImageData.hpp" #include "maths/Heightmap.hpp" +#include "engine.hpp" #include "../lua_util.hpp" using namespace lua; @@ -49,11 +50,9 @@ const float* LuaHeightmap::getValues() const { } static int l_dump(lua::State* L) { + auto paths = scripting::engine->getPaths(); if (auto heightmap = touserdata(L, 1)) { - auto filename = require_string(L, 2); - if (!files::is_valid_name(filename)) { - throw std::runtime_error("invalid file name"); - } + auto file = paths->resolve(require_string(L, 2)); uint w = heightmap->getWidth(); uint h = heightmap->getHeight(); ImageData image(ImageFormat::rgb888, w, h); @@ -69,7 +68,7 @@ static int l_dump(lua::State* L) { raster[i*3 + 2] = val; } } - png::write_image(filename, &image); + imageio::write(file.u8string(), &image); } return 0; } From 5ff42929f9d2a82b7420172dd42d7b7518a7fa4a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 15 Oct 2024 02:55:49 +0300 Subject: [PATCH 135/139] remove extra inventory convert implementation --- src/items/Inventory.cpp | 14 ++++---------- src/items/Inventory.hpp | 2 ++ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/items/Inventory.cpp b/src/items/Inventory.cpp index dd869a93..92f925ef 100644 --- a/src/items/Inventory.cpp +++ b/src/items/Inventory.cpp @@ -91,17 +91,11 @@ void Inventory::convert(const ContentReport* report) { } } -// TODO: remove void Inventory::convert(dv::value& data, const ContentReport* report) { - auto& slotsarr = data["slots"]; - for (auto& item : data["slots"]) { - itemid_t id = item["id"].asInteger(ITEM_EMPTY); - itemid_t replacement = report->items.getId(id); - item["id"] = replacement; - if (replacement == 0 && item.has("count")) { - item.erase("count"); - } - } + Inventory inventory; + inventory.deserialize(data); + inventory.convert(report); + data = inventory.serialize(); } const size_t Inventory::npos = -1; diff --git a/src/items/Inventory.hpp b/src/items/Inventory.hpp index 95d0936a..e12029f8 100644 --- a/src/items/Inventory.hpp +++ b/src/items/Inventory.hpp @@ -14,6 +14,8 @@ class Inventory : public Serializable { int64_t id; std::vector slots; public: + Inventory() = default; + Inventory(int64_t id, size_t size); Inventory(const Inventory& orig); From f87bb960a9c5e69ffa28c1fb126c8ddd94a482a2 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 15 Oct 2024 03:22:19 +0300 Subject: [PATCH 136/139] replace structures.json with structures.toml --- res/content/base/generators/demo.files/structures.json | 7 ------- res/content/base/generators/demo.files/structures.toml | 5 +++++ src/content/loading/GeneratorLoader.cpp | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) delete mode 100644 res/content/base/generators/demo.files/structures.json create mode 100644 res/content/base/generators/demo.files/structures.toml diff --git a/res/content/base/generators/demo.files/structures.json b/res/content/base/generators/demo.files/structures.json deleted file mode 100644 index 18f22b26..00000000 --- a/res/content/base/generators/demo.files/structures.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "tree0": {}, - "tree1": {}, - "tree2": {}, - "tower": {}, - "coal_ore0": {} -} diff --git a/res/content/base/generators/demo.files/structures.toml b/res/content/base/generators/demo.files/structures.toml new file mode 100644 index 00000000..3dcf3cda --- /dev/null +++ b/res/content/base/generators/demo.files/structures.toml @@ -0,0 +1,5 @@ +tree0 = {} +tree1 = {} +tree2 = {} +tower = {} +coal_ore0 = {} diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index ec8315f5..fcbcebc5 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -133,7 +133,7 @@ static std::vector> load_structures( const fs::path& structuresFile ) { auto structuresDir = structuresFile.parent_path() / fs::path("fragments"); - auto map = files::read_json(structuresFile); + auto map = files::read_object(structuresFile); std::vector> structures; for (auto& [name, config] : map.asObject()) { @@ -171,7 +171,7 @@ static void load_structures(GeneratorDef& def, const fs::path& structuresFile) { } } -static inline const auto STRUCTURES_FILE = fs::u8path("structures.json"); +static inline const auto STRUCTURES_FILE = fs::u8path("structures.toml"); static inline const auto BIOMES_FILE = fs::u8path("biomes.toml"); static inline const auto GENERATORS_DIR = fs::u8path("generators"); From 3146499433b0b9555e2968a6924dfd6ec784444e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 15 Oct 2024 03:32:16 +0300 Subject: [PATCH 137/139] fix fragment rotation bits & increase player spawn attempts number --- src/objects/Player.cpp | 19 +++++++++++-------- src/world/generator/VoxelFragment.cpp | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index 76fd6c9d..223c1828 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -16,13 +16,14 @@ #include "world/Level.hpp" #include "data/dv_util.hpp" -const float CROUCH_SPEED_MUL = 0.35f; -const float RUN_SPEED_MUL = 1.5f; -const float PLAYER_GROUND_DAMPING = 10.0f; -const float PLAYER_AIR_DAMPING = 7.0f; -const float FLIGHT_SPEED_MUL = 4.0f; -const float CHEAT_SPEED_MUL = 5.0f; -const float JUMP_FORCE = 8.0f; +constexpr float CROUCH_SPEED_MUL = 0.35f; +constexpr float RUN_SPEED_MUL = 1.5f; +constexpr float PLAYER_GROUND_DAMPING = 10.0f; +constexpr float PLAYER_AIR_DAMPING = 7.0f; +constexpr float FLIGHT_SPEED_MUL = 4.0f; +constexpr float CHEAT_SPEED_MUL = 5.0f; +constexpr float JUMP_FORCE = 8.0f; +constexpr int SPAWN_ATTEMPTS_PER_UPDATE = 64; Player::Player( Level* level, @@ -156,7 +157,9 @@ void Player::postUpdate() { flight = false; } if (spawnpoint.y <= 0.1) { - attemptToFindSpawnpoint(); + for (int i = 0; i < SPAWN_ATTEMPTS_PER_UPDATE; i++) { + attemptToFindSpawnpoint(); + } } auto& skeleton = entity->getSkeleton(); diff --git a/src/world/generator/VoxelFragment.cpp b/src/world/generator/VoxelFragment.cpp index 96191725..70dda238 100644 --- a/src/world/generator/VoxelFragment.cpp +++ b/src/world/generator/VoxelFragment.cpp @@ -183,7 +183,7 @@ std::unique_ptr VoxelFragment::rotated(const Content& content) co if (def.rotations.name == BlockRotProfile::PANE_NAME || def.rotations.name == BlockRotProfile::PIPE_NAME){ if (voxel.state.rotation < 4) { - voxel.state.rotation = (voxel.state.rotation + 1) & 0b11; + voxel.state.rotation = (voxel.state.rotation + 3) & 0b11; } } } From 07169761cda3383e0dcc699c5dd65cd216dd35a7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 15 Oct 2024 03:43:49 +0300 Subject: [PATCH 138/139] use debug.log for stdmin messages --- res/scripts/stdmin.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index a063ee8d..6cd175ba 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -277,11 +277,11 @@ function require(path) end function __scripts_cleanup() - print("cleaning scripts cache") + debug.log("cleaning scripts cache") for k, v in pairs(__cached_scripts) do local packname, _ = parse_path(k) if packname ~= "core" then - print("unloaded "..k) + debug.log("unloaded "..k) __cached_scripts[k] = nil package.loaded[k] = nil end From c0c0104b5ef3e3e7f919a07537438d9fc2f9caa6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 15 Oct 2024 04:12:30 +0300 Subject: [PATCH 139/139] sort generators in ui --- res/layouts/pages/generators.xml.lua | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/res/layouts/pages/generators.xml.lua b/res/layouts/pages/generators.xml.lua index 19e3ce60..64649cd5 100644 --- a/res/layouts/pages/generators.xml.lua +++ b/res/layouts/pages/generators.xml.lua @@ -2,13 +2,18 @@ settings = session.get_entry('new_world') function on_open() local names = generation.get_generators() - table.sort(names) + local keys = {} + for key in pairs(names) do + table.insert(keys, key) + end + table.sort(keys) local panel = document.root - for k, caption in pairs(names) do + for _, key in ipairs(keys) do + local caption = names[key] panel:add(gui.template("generator", { - callback=string.format("settings.generator=%q menu:back()", k), - id=k, + callback=string.format("settings.generator=%q menu:back()", key), + id=key, name=settings.generator_name(caption) })) end