From fd2bd1565892bd90a62a3f9d3edb6445f2e94766 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 14 Oct 2024 19:39:58 +0300 Subject: [PATCH] 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;