From d02f747ca018763ad3cde67c159d0b43fb97a6ba Mon Sep 17 00:00:00 2001 From: DrSocalkwe3n Date: Thu, 21 Aug 2025 17:45:45 +0600 Subject: [PATCH] * --- Src/Common/Abstract.cpp | 351 ++++++++++++++++++++++++++++++++++- Src/Common/Abstract.hpp | 77 +++++++- Src/Server/AssetsManager.cpp | 45 ++++- Src/Server/AssetsManager.hpp | 100 ++++++++-- 4 files changed, 553 insertions(+), 20 deletions(-) diff --git a/Src/Common/Abstract.cpp b/Src/Common/Abstract.cpp index 3123395..ea94372 100644 --- a/Src/Common/Abstract.cpp +++ b/Src/Common/Abstract.cpp @@ -1,11 +1,15 @@ #include "Abstract.hpp" +#include "TOSLib.hpp" #include "boost/json.hpp" +#include "boost/json/array.hpp" +#include "boost/json/object.hpp" #include "boost/json/string_view.hpp" #include #include #include #include #include +#include #include #include #include @@ -1010,6 +1014,8 @@ bool PreparedNodeState::read_uint16(std::basic_istream& stream, uint16_ } bool PreparedNodeState::load(const std::u8string& data) noexcept { + // TODO: Это нейронка писала + std::basic_istringstream stream(data); char8_t byte; uint16_t size, v16; @@ -1558,7 +1564,7 @@ std::pair transforms; if(const auto weight_val = obj.try_at("weight")) { - weight = weight_val->as_double(); + weight = weight_val->to_number(); } if(const auto uvlock_val = obj.try_at("uvlock")) { @@ -1673,4 +1679,347 @@ std::vector PreparedNodeState::parseTransorma return result; } + +PreparedModel::PreparedModel(const std::string_view modid, const js::object& profile) { + if(profile.contains("parent")) { + auto [domain, key] = parseDomainKey((const std::string) profile.at("parent").as_string(), modid); + Parent.emplace(std::move(domain), std::move(key)); + } + + if(profile.contains("gui_light")) { + std::string_view gui_light = profile.at("gui_light").as_string(); + + GuiLight = EnumGuiLight::Default; + } + + if(profile.contains("AmbientOcclusion")) { + AmbientOcclusion = profile.at("ambientocclusion").as_bool(); + } + + if(profile.contains("display")) { + const js::object& list = profile.at("display").as_object(); + for(auto& [key, value] : list) { + FullTransformation result; + + const js::object& display_value = value.as_object(); + + if(boost::system::result arr_val = display_value.try_at("rotation")) { + const js::array& arr = arr_val->as_array(); + if(arr.size() != 3) + MAKE_ERROR("3"); + + for(int iter = 0; iter < 3; iter++) + result.Rotation[iter] = arr[iter].to_number(); + } + + if(boost::system::result arr_val = display_value.try_at("translation")) { + const js::array& arr = arr_val->as_array(); + if(arr.size() != 3) + MAKE_ERROR("3"); + + for(int iter = 0; iter < 3; iter++) + result.Translation[iter] = arr[iter].to_number(); + } + + if(boost::system::result arr_val = display_value.try_at("scale")) { + const js::array& arr = arr_val->as_array(); + if(arr.size() != 3) + MAKE_ERROR("3"); + + for(int iter = 0; iter < 3; iter++) + result.Scale[iter] = arr[iter].to_number(); + } + + Display[key] = result; + } + + } + + if(boost::system::result textures_val = profile.try_at("textures")) { + const js::object& textures = textures_val->as_object(); + + for(const auto& [key, value] : textures) { + auto [domain, key2] = parseDomainKey((const std::string) value.as_string(), modid); + Textures[key] = {domain, key2}; + } + } + + if(boost::system::result cuboids_val = profile.try_at("cuboids")) { + const js::array& cuboids = cuboids_val->as_array(); + + for(const auto& value : cuboids) { + const js::object& cuboid = value.as_object(); + + Cuboid result; + + if(boost::system::result shade_val = cuboid.try_at("shade")) { + result.Shade = shade_val->as_bool(); + } else { + result.Shade = true; + } + + { + const js::array& from = cuboid.at("from").as_array(); + if(from.size() != 3) + MAKE_ERROR("3"); + + for(int iter = 0; iter < 3; iter++) + result.From[iter] = from[iter].to_number(); + } + + { + const js::array& to = cuboid.at("to").as_array(); + if(to.size() != 3) + MAKE_ERROR("3"); + + for(int iter = 0; iter < 3; iter++) + result.To[iter] = to[iter].to_number(); + } + + { + const js::object& faces = cuboid.at("faces").as_object(); + + for(const auto& [key, value] : faces) { + Cuboid::EnumFace type; + + if(key == "down") + type = Cuboid::EnumFace::Down; + else if(key == "up") + type = Cuboid::EnumFace::Up; + else if(key == "north") + type = Cuboid::EnumFace::North; + else if(key == "south") + type = Cuboid::EnumFace::South; + else if(key == "west") + type = Cuboid::EnumFace::West; + else if(key == "east") + type = Cuboid::EnumFace::East; + else + MAKE_ERROR("Unknown face"); + + const js::object& js_face = value.as_object(); + Cuboid::Face face; + + if(boost::system::result uv_val = js_face.try_at("uv")) { + const js::array& arr = uv_val->as_array(); + if(arr.size() != 4) + MAKE_ERROR(4); + + for(int iter = 0; iter < 4; iter++) + face.UV[iter] = arr[iter].to_number(); + } + + if(boost::system::result texture_val = js_face.try_at("texture")) { + face.Texture = texture_val->as_string(); + } + + if(boost::system::result cullface_val = js_face.try_at("cullface")) { + const std::string_view cullface = cullface_val->as_string(); + + if(cullface == "down") + face.Cullface = Cuboid::EnumFace::Down; + else if(cullface == "up") + face.Cullface = Cuboid::EnumFace::Up; + else if(cullface == "north") + face.Cullface = Cuboid::EnumFace::North; + else if(cullface == "south") + face.Cullface = Cuboid::EnumFace::South; + else if(cullface == "west") + face.Cullface = Cuboid::EnumFace::West; + else if(cullface == "east") + face.Cullface = Cuboid::EnumFace::East; + else + MAKE_ERROR("Unknown face"); + } + + if(boost::system::result tintindex_val = js_face.try_at("tintindex")) { + face.TintIndex = tintindex_val->to_number(); + } else + face.TintIndex = -1; + + if(boost::system::result rotation_val = js_face.try_at("rotation")) { + face.Rotation = rotation_val->to_number(); + } else + face.Rotation = 0; + + + result.Faces[type] = face; + } + } + + if(boost::system::result transformations_val = cuboid.try_at("transformations")) { + const js::array& transformations = transformations_val->as_array(); + + for(const js::value& tobj : transformations) { + const js::string_view transform = tobj.as_string(); + + auto pos = transform.find('='); + std::string_view key = transform.substr(0, pos); + std::string_view value = transform.substr(pos+1); + + float f_value; + auto [partial_ptr, partial_ec] = std::from_chars(value.data(), value.data() + value.size(), f_value); + + if(partial_ec == std::errc{} && partial_ptr != value.data() + value.size()) { + MAKE_ERROR("Converted part of the string: " << value << " (remaining: " << std::string_view(partial_ptr, value.data() + value.size() - partial_ptr) << ")"); + } else if(partial_ec != std::errc{}) { + MAKE_ERROR("Error converting partial string: " << value); + } + + if(key == "x") + result.Transformations.emplace_back(Cuboid::Transformation::MoveX, f_value); + else if(key == "y") + result.Transformations.emplace_back(Cuboid::Transformation::MoveY, f_value); + else if(key == "z") + result.Transformations.emplace_back(Cuboid::Transformation::MoveZ, f_value); + else if(key == "rx") + result.Transformations.emplace_back(Cuboid::Transformation::RotateX, f_value); + else if(key == "ry") + result.Transformations.emplace_back(Cuboid::Transformation::RotateY, f_value); + else if(key == "rz") + result.Transformations.emplace_back(Cuboid::Transformation::RotateZ, f_value); + else + MAKE_ERROR("Неизвестный ключ трансформации"); + } + } + } + } + + if(boost::system::result gltf_val = profile.try_at("gltf")) { + const js::array& gltf = gltf_val->as_array(); + + for(const js::value& sub_val : gltf) { + SubGLTF result; + const js::object& sub = sub_val.as_object(); + auto [domain, key] = parseDomainKey((std::string) sub.at("path").as_string(), modid); + result.Domain = std::move(domain); + result.Key = std::move(key); + + if(boost::system::result scene_val = profile.try_at("scene")) + result.Scene = scene_val->to_number(); + } + } +} + +PreparedModel::PreparedModel(const std::string_view modid, const sol::table& profile) { + std::unreachable(); +} + +PreparedModel::PreparedModel(const std::string_view modid, const std::u8string& data) { + std::unreachable(); +} + +std::u8string PreparedModel::dump() const { + std::basic_ostringstream result; + + uint16_t val; + + auto push16 = [&]() { + result.put(uint8_t(val & 0xff)); + result.put(uint8_t((val >> 8) & 0xff)); + }; + + if(Parent.has_value()) { + result.put(1); + + assert(Parent->first.size() < 32); + val = Parent->first.size(); + push16(); + result.write((const char8_t*) Parent->first.data(), val); + + assert(Parent->second.size() < 32); + val = Parent->second.size(); + push16(); + result.write((const char8_t*) Parent->second.data(), val); + } else { + result.put(0); + } + + if(GuiLight.has_value()) { + result.put(1); + result.put(int(GuiLight.value())); + } else + result.put(0); + + if(AmbientOcclusion.has_value()) { + if(*AmbientOcclusion) + result.put(2); + else + result.put(1); + } else + result.put(0); + + assert(Display.size() < (1 << 16)); + val = Display.size(); + push16(); + + union { + float f_value; + uint32_t u_value; + }; + + for(const auto& [key, tsf] : Display) { + assert(key.size() < (1 << 16)); + val = key.size(); + push16(); + result.write((const char8_t*) key.data(), val); + + for(int iter = 0; iter < 3; iter++) { + f_value = tsf.Rotation[iter]; + + for(int iter2 = 0; iter2 < 4; iter2++) { + result.put(uint8_t((u_value >> (iter2*8)) & 0xff)); + } + } + + for(int iter = 0; iter < 3; iter++) { + f_value = tsf.Translation[iter]; + + for(int iter2 = 0; iter2 < 4; iter2++) { + result.put(uint8_t((u_value >> (iter2*8)) & 0xff)); + } + } + + for(int iter = 0; iter < 3; iter++) { + f_value = tsf.Scale[iter]; + + for(int iter2 = 0; iter2 < 4; iter2++) { + result.put(uint8_t((u_value >> (iter2*8)) & 0xff)); + } + } + } + + assert(Textures.size() < (1 << 16)); + val = Textures.size(); + push16(); + + for(const auto& [tkey, dk] : Textures) { + assert(tkey.size() < 32); + val = tkey.size(); + push16(); + result.write((const char8_t*) tkey.data(), val); + + assert(dk.first.size() < 32); + val = dk.first.size(); + push16(); + result.write((const char8_t*) dk.first.data(), val); + + assert(dk.second.size() < 32); + val = dk.second.size(); + push16(); + result.write((const char8_t*) dk.second.data(), val); + } + + assert(Cuboids.size() < (1 << 16)); + val = Cuboids.size(); + push16(); + + for(const Cub) + + // Cuboids + // GLTF + + return result.str(); +} + } \ No newline at end of file diff --git a/Src/Common/Abstract.hpp b/Src/Common/Abstract.hpp index 7896cea..cf28f57 100644 --- a/Src/Common/Abstract.hpp +++ b/Src/Common/Abstract.hpp @@ -598,11 +598,84 @@ private: }; /* - Хранит распаршенную и по необходимости упрощённую модель - + Парсит json модель */ struct PreparedModel { + enum class EnumGuiLight { + Default + }; + + std::optional> Parent; + std::optional GuiLight = EnumGuiLight::Default; + std::optional AmbientOcclusion = false; + + struct FullTransformation { + glm::vec3 + Rotation = glm::vec3(0), + Translation = glm::vec3(0), + Scale = glm::vec3(1); + }; + + std::unordered_map Display; + std::unordered_map> Textures; + struct Cuboid { + bool Shade; + glm::vec3 From, To; + + enum class EnumFace { + Down, Up, North, South, West, East + }; + + struct Face { + glm::vec4 UV; + std::string Texture; + std::optional Cullface; + int TintIndex = -1; + int16_t Rotation = 0; + }; + + std::unordered_map Faces; + + struct Transformation { + enum EnumTransform { + MoveX, MoveY, MoveZ, + RotateX, RotateY, RotateZ, + MAX_ENUM + } Op; + + float Value; + }; + + std::vector Transformations; + }; + + std::vector Cuboids; + + struct SubGLTF { + std::string Domain, Key; + std::optional Scene; + }; + + std::vector GLTF; + + // Json + PreparedModel(const std::string_view modid, const js::object& profile); + PreparedModel(const std::string_view modid, const sol::table& profile); + PreparedModel(const std::string_view modid, const std::u8string& data); + + PreparedModel() = default; + PreparedModel(const PreparedModel&) = default; + PreparedModel(PreparedModel&&) = default; + + PreparedModel& operator=(const PreparedModel&) = default; + PreparedModel& operator=(PreparedModel&&) = default; + + // Пишет в сжатый двоичный формат + std::u8string dump() const; + +private: + bool load(const std::u8string& data) noexcept; }; struct TexturePipeline { diff --git a/Src/Server/AssetsManager.cpp b/Src/Server/AssetsManager.cpp index 16296f8..c8fbee3 100644 --- a/Src/Server/AssetsManager.cpp +++ b/Src/Server/AssetsManager.cpp @@ -1,6 +1,10 @@ #include "AssetsManager.hpp" #include "Common/Abstract.hpp" +#include "boost/json/impl/parse.ipp" +#include "boost/json/object.hpp" +#include "boost/json/parser.hpp" #include "png++/rgb_pixel.hpp" +#include #include #include #include @@ -11,6 +15,27 @@ namespace LV::Server { +PreparedModelCollision::PreparedModelCollision(const PreparedModel& model) { + +} + +PreparedModelCollision::PreparedModelCollision(const std::string& domain, const js::object& glTF) { + // gltf + + // Сцена по умолчанию + // Сцены -> Ноды + // Ноды -> Ноды, меши, матрицы, translation, rotation + // Меши -> Примитивы + // Примитивы -> Материал, вершинные данные + // Материалы -> текстуры + // Текстуры + // Буферы +} + +PreparedModelCollision::PreparedModelCollision(const std::string& domain, Resource res) { + +} + void AssetsManager::loadResourceFromFile(EnumAssets type, ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const { switch(type) { case EnumAssets::Nodestate: loadResourceFromFile_Nodestate (out, domain, key, path); return; @@ -40,7 +65,13 @@ void AssetsManager::loadResourceFromLua(EnumAssets type, ResourceChangeObj& out, } void AssetsManager::loadResourceFromFile_Nodestate(ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const { - std::unreachable(); + Resource res(path); + js::object obj = js::parse(std::string_view((const char*) res.data(), res.size())).as_object(); + PreparedNodeState pns(domain, obj); + std::u8string data = pns.dump(); + Resource result((const uint8_t*) data.data(), data.size()); + out.Nodestates[domain].emplace_back(key, std::move(pns)); + out.NewOrChange[(int) EnumAssets::Nodestate][domain].emplace_back(key, result, fs::last_write_time(path)); } void AssetsManager::loadResourceFromFile_Particle(ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const { @@ -53,9 +84,17 @@ void AssetsManager::loadResourceFromFile_Animation(ResourceChangeObj& out, const void AssetsManager::loadResourceFromFile_Model(ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const { /* - json, obj, glTF + json, glTF glB */ - std::unreachable(); + + // Либо это внутренний формат, либо glTF + + Resource res(path); + + if(path.extension() == "json" || path.extension() == "gltf") { + js::object obj = js::parse(std::string_view((const char*) res.data(), res.size())).as_object(); + PreparedModelCollision pmc(domain, obj, path.extension() == "gltf"); + } } void AssetsManager::loadResourceFromFile_Texture(ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const { diff --git a/Src/Server/AssetsManager.hpp b/Src/Server/AssetsManager.hpp index 9b5aba5..8b004b0 100644 --- a/Src/Server/AssetsManager.hpp +++ b/Src/Server/AssetsManager.hpp @@ -3,6 +3,7 @@ #include "Abstract.hpp" #include "Common/Abstract.hpp" #include "TOSLib.hpp" +#include "assets.hpp" #include "boost/asio/io_context.hpp" #include "sha2.hpp" #include @@ -10,14 +11,60 @@ #include #include #include +#include namespace LV::Server { namespace fs = std::filesystem; -struct DefModel { +/* + Используется для расчёта коллизии, + если это необходимо. + + glTF конвертируется в кубы +*/ +struct PreparedModelCollision { + struct Cuboid { + glm::vec3 From, To; + + enum EnumFace { + Down, Up, North, South, West, East + }; + + struct Face { + std::optional Cullface; + uint8_t Rotation = 0; + }; + + std::unordered_map Faces; + + struct Transformation { + enum EnumTransform { + MoveX, MoveY, MoveZ, + RotateX, RotateY, RotateZ, + MAX_ENUM + } Op; + + float Value; + }; + + std::vector Transformations; + }; + + std::vector Cuboids; + + PreparedModelCollision(const PreparedModel& model); + PreparedModelCollision(const std::string& domain, const js::object& glTF); + PreparedModelCollision(const std::string& domain, Resource res); + + PreparedModelCollision() = default; + PreparedModelCollision(const PreparedModelCollision&) = default; + PreparedModelCollision(PreparedModelCollision&&) = default; + + PreparedModelCollision& operator=(const PreparedModelCollision&) = default; + PreparedModelCollision& operator=(PreparedModelCollision&&) = default; }; /* @@ -29,25 +76,46 @@ class AssetsManager { public: struct Resource { private: - struct Inline { + struct InlineMMap { boost::interprocess::file_mapping MMap; boost::interprocess::mapped_region Region; Hash_t Hash; - Inline(fs::path path) + InlineMMap(fs::path path) : MMap(path.c_str(), boost::interprocess::read_only), Region(MMap, boost::interprocess::read_only) - {} + { + Hash = sha2::sha256((const uint8_t*) Region.get_address(), Region.get_size()); + } + + const std::byte* data() const { return (const std::byte*) Region.get_address(); } + size_t size() const { return Region.get_size(); } }; - std::shared_ptr In; + struct InlinePtr { + std::vector Data; + Hash_t Hash; + + InlinePtr(const uint8_t* data, size_t size) { + Data.resize(size); + std::copy(data, data+size, Data.data()); + Hash = sha2::sha256(data, size); + } + + const std::byte* data() const { return (const std::byte*) Data.data(); } + size_t size() const { return Data.size(); } + }; + + std::shared_ptr> In; public: Resource(fs::path path) - : In(std::make_shared(path)) - { - In->Hash = sha2::sha256((const uint8_t*) In->Region.get_address(), In->Region.get_size()); - } + : In(std::make_shared>(InlineMMap(path))) + {} + + Resource(const uint8_t* data, size_t size) + : In(std::make_shared>(InlinePtr(data, size))) + {} Resource(const Resource&) = default; Resource(Resource&&) = default; @@ -55,9 +123,9 @@ public: Resource& operator=(Resource&&) = default; bool operator<=>(const Resource&) const = default; - const std::byte* data() const { return (const std::byte*) In->Region.get_address(); } - size_t size() const { return In->Region.get_size(); } - Hash_t hash() const { return In->Hash; } + const std::byte* data() const { return std::visit([](auto& obj){ return obj.data(); }, *In); } + size_t size() const { return std::visit([](auto& obj){ return obj.size(); }, *In); } + Hash_t hash() const { return std::visit([](auto& obj){ return obj.Hash; }, *In); } }; struct ResourceChangeObj { @@ -65,6 +133,10 @@ public: std::unordered_map> Lost[(int) EnumAssets::MAX_ENUM]; // Домен и ключ ресурса std::unordered_map>> NewOrChange[(int) EnumAssets::MAX_ENUM]; + + + std::unordered_map>> Nodestates; + std::unordered_map>> Models; }; private: @@ -93,8 +165,8 @@ private: std::vector>> Table[(int) EnumAssets::MAX_ENUM]; // Распаршенные ресурсы, для использования сервером - // std::vector>> Table_NodeState; - std::vector>> Table_Model; + std::vector>> Table_NodeState; + std::vector>> Table_Model; // Связь домены -> {ключ -> идентификатор} std::unordered_map> KeyToId[(int) EnumAssets::MAX_ENUM];