#pragma once #include "TOSLib.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace LV::Server { namespace js = boost::json; // В одном регионе может быть максимум 2^16 сущностей. Клиенту адресуются сущности в формате <мир>+<позиция региона>+ // И если сущность перешла из одного региона в другой, идентификатор сущности на стороне клиента сохраняется using RegionEntityId_t = uint16_t; using ClientEntityId_t = RegionEntityId_t; using ServerEntityId_t = std::tuple; using RegionFuncEntityId_t = uint16_t; using ClientFuncEntityId_t = RegionFuncEntityId_t; using ServerFuncEntityId_t = std::tuple; using MediaStreamId_t = uint16_t; using ContentBridgeId_t = ResourceId_t; using PlayerId_t = ResourceId_t; using DefGeneratorId_t = ResourceId_t; /* Сервер загружает информацию о локальных текстурах Пересмотр списка текстур? Динамичные текстуры? */ struct ResourceFile { using Hash_t = sha2::sha256_hash; // boost::uuids::detail::sha1::digest_type; Hash_t Hash; std::vector Data; void calcHash() { Hash = sha2::sha256((const uint8_t*) Data.data(), Data.size()); } }; struct ServerTime { uint32_t Seconds : 24, Sub : 8; }; struct VoxelCube_Region { union { struct { DefVoxelId_t VoxelId : 24, Meta : 8; }; DefVoxelId_t Data = 0; }; Pos::bvec1024u Left, Right; // TODO: заменить на позицию и размер auto operator<=>(const VoxelCube_Region& other) const { if (auto cmp = Left <=> other.Left; cmp != 0) return cmp; if (auto cmp = Right <=> other.Right; cmp != 0) return cmp; return Data <=> other.Data; } bool operator==(const VoxelCube_Region& other) const { return Left == other.Left && Right == other.Right && Data == other.Data; } }; struct AABB { Pos::Object VecMin, VecMax; void sortMinMax() { Pos::Object::Type left, right; for(int iter = 0; iter < 3; iter++) { left = std::min(VecMin[iter], VecMax[iter]); right = std::max(VecMin[iter], VecMax[iter]); VecMin.set(iter, left); VecMax.set(iter, right); } } bool isCollideWith(const AABB &other, bool axis[3] = nullptr) { return calcBoxToBoxCollide(VecMin, VecMax, other.VecMin, other.VecMax, axis); } bool collideWithDelta(const AABB &other, const Pos::Object &my_speed, int32_t &delta, bool axis[3] = nullptr) { return calcBoxToBoxCollideWithDelta(VecMin, VecMax, other.VecMin, other.VecMax, my_speed, &delta, Pos::Object_t::BS, axis); } }; struct LocalAABB { uint64_t x : 20, y : 20, z : 20; AABB atPos(const Pos::Object &pos) const { return {pos-Pos::Object(x/2, y/2, z/2), pos+Pos::Object(x/2, y/2, z/2)}; } }; struct CollisionAABB : public AABB { enum struct EnumType { Voxel, Node, Entity, FuncEntity, Barrier, Portal, Another } Type; union { struct { RegionEntityId_t Index; } Entity; struct { RegionFuncEntityId_t Index; } FuncEntity; struct { Pos::bvec4u Chunk; Pos::bvec16u Pos; } Node; struct { Pos::bvec4u Chunk; uint32_t Index; } Voxel; struct { } Barrier; struct { } Portal; struct { } Another; }; bool Skip = false; }; /* Модель, считанная с файла и предкомпилированна Для компиляции нужно собрать зависимости модели, и в случае их изменения нужно перекомпилировать модель (может просто перекомпилировать всё разом?) */ struct PreCompiledModel_t { }; struct NodestateEntry { std::string Name; int Variability = 0; std::vector ValueNames; }; struct StateExpression { std::bitset<256> CT; StateExpression(std::vector, const std::string& expression) { // Скомпилировать выражение и просчитать таблицу CT struct Node { std::variant Val1, Val2; char Func; }; std::vector nodes; std::function(const std::string_view&)> lambda = [&](const std::string_view& exp) -> std::variant { std::vector> tokens; // Парсим токены и выражения в круглых скобках for(size_t pos = 0; pos < exp.size(); pos++) { if( (exp[pos] >= 'a' && exp[pos] <= 'z') || (exp[pos] >= 'A' && exp[pos] <= 'Z') || (exp[pos] >= '0' && exp[pos] <= '9') ) { if(tokens.empty() || tokens.back().index() != 0) { tokens.push_back(exp[pos]); } std::string& token = std::get<0>(tokens.back()); token += exp[pos]; } else if(exp[pos] == '(') { int depth = 0; for(size_t pos2 = pos; pos2 < exp.size(); pos2++) { if(exp[pos2] == '(') depth++; else if(exp[pos2] == ')') depth--; if(depth == 0) { auto res = lambda(exp.substr(pos+1, pos2-pos-1)); if(res.index() == 0) tokens.push_back(std::get<0>(res)); else if(res.index() == 1) tokens.push_back(std::get<1>(res)); break; } } if(depth != 0) { MAKE_ERROR("Неожиданное завершение выражения"); } } else { tokens.push_back(exp[pos]); } } for(ssize_t pos = tokens.size()-1; pos >= 0; pos--) { // Проверка на логическое НЕ и унарные операции auto& token = tokens[pos]; // Если справа знак =, то этот случай не обрабатываем if(token.index() != 1 || (std::get<1>(token) != '!' && std::get<1>(token) != '+' && std::get<1>(token) != '-')) continue; // Справа должен быть операнд if(pos == tokens.size()-1) { MAKE_ERROR("Отсутствует операнд"); } auto& rightToken = tokens[pos+1]; char oper = std::get<1>(token); if(oper == '!' && rightToken.index() == 1 && std::get<1>(rightToken) == '=') continue; if(pos != 0 && (oper == '+' || oper == '-') && tokens[pos-1].index() != 1) continue; // Если справа нода if(rightToken.index() == 2) { nodes.push_back(std::get<2>(rightToken)); uint16_t index = nodes.size()-1; Node newNode; newNode.Func = '!'; newNode.Val2 = index; token = newNode; tokens.erase(tokens.begin()+pos+1); } else if(rightToken.index() == 1) { MAKE_ERROR("Недопустимый операнд"); } else if(rightToken.index() == 0) { // Если справа переменная или число const std::string& valOrVar = std::get<0>(rightToken); assert(!valOrVar.empty()); if(std::isdigit(valOrVar[0])) { // Это число int val = std::stoi(valOrVar); if(oper == '+') token = valOrVar; else if(oper == '-') token = std::to_string(-val); else if(oper == '!') token = val ? "0" : "1"; tokens.erase(tokens.begin()+pos+1); } else { // Это переменная Node newNode; newNode.Func = oper; newNode.Val2 = valOrVar; token = newNode; tokens.erase(tokens.begin()+pos+1); } } } for(ssize_t pos = 0; pos < tokens.size(); pos++) { // Проверка на *.% auto& token = tokens[pos]; if(token.index() != 1 || (std::get<1>(token) != '*' && std::get<1>(token) != '/' && std::get<1>(token) != '%')) continue; char oper = std::get<1>(token); // Слева и справа должны быть операнды if(pos == 0 || pos == tokens.size()-1) { MAKE_ERROR("Отсутствует операнд"); } auto& leftToken = tokens[pos-1]; auto& rightToken = tokens[pos+1]; if(leftToken.index() == 0 && rightToken.index() == 0 && std::isdigit(std::get<0>(leftToken)[0]) && std::isdigit(std::get<0>(rightToken)[0]) ) { // Можно высчитать на месте int leftVal = std::stoi(std::get<0>(leftToken)); int rightVal = std::stoi(std::get<0>(rightToken)); if(oper == '*') token = std::to_string(leftVal*rightVal); else if(oper == '/') token = std::to_string(leftVal/rightVal); else if(oper == '%') token = std::to_string(leftVal%rightVal); tokens.erase(tokens.begin()+pos+1); tokens.erase(tokens.begin()+pos-1); pos--; } else { Node newNode; newNode.Func = oper; if(leftToken.index() == 0) newNode.Val1 = std::get<0>(leftToken); else if(leftToken.index() == 1) MAKE_ERROR("Недопустимый операнд"); else { nodes.push_back(std::get<2>(leftToken)); uint16_t index = nodes.size()-1; newNode.Val1 = index; tokens.erase(tokens.begin()+pos-1); pos--; } if(rightToken.index() == 0) newNode.Val2 = std::get<0>(rightToken); else if(rightToken.index() == 1) MAKE_ERROR("Недопустимый операнд"); else { nodes.push_back(std::get<2>(rightToken)); uint16_t index = nodes.size()-1; newNode.Val2 = index; tokens.erase(tokens.begin()+pos+1); } tokens[pos] = newNode; } } for(ssize_t pos = 0; pos < tokens.size(); pos++) { // Проверка на +- auto& token = tokens[pos]; if(token.index() != 1 || (std::get<1>(token) != '+' && std::get<1>(token) != '-')) continue; char oper = std::get<1>(token); // Слева и справа должны быть операнды if(pos == 0 || pos == tokens.size()-1) { MAKE_ERROR("Отсутствует операнд"); } auto& leftToken = tokens[pos-1]; auto& rightToken = tokens[pos+1]; if(leftToken.index() == 0 && rightToken.index() == 0 && std::isdigit(std::get<0>(leftToken)[0]) && std::isdigit(std::get<0>(rightToken)[0]) ) { // Можно высчитать на месте int leftVal = std::stoi(std::get<0>(leftToken)); int rightVal = std::stoi(std::get<0>(rightToken)); if(oper == '+') token = std::to_string(leftVal+rightVal); else if(oper == '-') token = std::to_string(leftVal-rightVal); tokens.erase(tokens.begin()+pos+1); tokens.erase(tokens.begin()+pos-1); pos--; } else { Node newNode; newNode.Func = oper; if(leftToken.index() == 0) newNode.Val1 = std::get<0>(leftToken); else if(leftToken.index() == 1) MAKE_ERROR("Недопустимый операнд"); else { nodes.push_back(std::get<2>(leftToken)); uint16_t index = nodes.size()-1; newNode.Val1 = index; tokens.erase(tokens.begin()+pos-1); pos--; } if(rightToken.index() == 0) newNode.Val2 = std::get<0>(rightToken); else if(rightToken.index() == 1) MAKE_ERROR("Недопустимый операнд"); else { nodes.push_back(std::get<2>(rightToken)); uint16_t index = nodes.size()-1; newNode.Val2 = index; tokens.erase(tokens.begin()+pos+1); } tokens[pos] = newNode; } } for(ssize_t pos = 0; pos < tokens.size(); pos++) { // Проверка на < <= => > auto& token = tokens[pos]; if(token.index() != 1 || (std::get<1>(token) != '<' && std::get<1>(token) != '>')) continue; // Слева и справа должны быть операнды if(pos == 0 || pos == tokens.size()-1) { MAKE_ERROR("Отсутствует операнд"); } uint8_t oper = std::get<1>(token) == '<' ? 0 : 2; auto* leftToken = &tokens[pos-1]; auto* rightToken = &tokens[pos+1]; if(rightToken->index() == 1) { if(std::get<1>(*rightToken) == '=') { oper++; if(pos+2 >= tokens.size()) MAKE_ERROR("Отсутствует операнд"); rightToken = &tokens[pos+2]; if(rightToken->index() == 1) MAKE_ERROR("Недопустимый операнд"); } else MAKE_ERROR("Недопустимый операнд"); } if((leftToken->index() == 0 && rightToken->index() == 0) && std::isdigit(std::get<0>(*leftToken)[0]) && std::isdigit(std::get<0>(*rightToken)[0]) ) { // Можно высчитать на месте int leftVal = std::stoi(std::get<0>(*leftToken)); int rightVal = std::stoi(std::get<0>(*rightToken)); if(oper == 0) token = leftVal < rightVal ? "1" : "0"; else if(oper == 1) token = leftVal <= rightVal ? "1" : "0"; else if(oper == 2) token = leftVal > rightVal ? "1" : "0"; else if(oper == 3) token = leftVal >= rightVal ? "1" : "0"; tokens.erase(tokens.begin()+pos+1); if(oper == 1 || oper == 3) tokens.erase(tokens.begin()+pos+1); tokens.erase(tokens.begin()+pos-1); pos--; } else { Node newNode; newNode.Func = oper; if(leftToken->index() == 0) newNode.Val1 = std::get<0>(*leftToken); else if(leftToken->index() == 1) MAKE_ERROR("Недопустимый операнд"); else { nodes.push_back(std::get<2>(*leftToken)); uint16_t index = nodes.size()-1; newNode.Val1 = index; tokens.erase(tokens.begin()+pos-1); pos--; } if(rightToken->index() == 0) newNode.Val2 = std::get<0>(*rightToken); else if(rightToken->index() == 1) MAKE_ERROR("Недопустимый операнд"); else { nodes.push_back(std::get<2>(*rightToken)); uint16_t index = nodes.size()-1; newNode.Val2 = index; tokens.erase(tokens.begin()+pos+1); if(oper == 1 || oper == 3) tokens.erase(tokens.begin()+pos+1); } tokens[pos] = newNode; } } for(ssize_t pos = 0; pos < tokens.size(); pos++) { // Проверка на != == auto& token = tokens[pos]; if(token.index() != 1 || (std::get<1>(token) != '!' && std::get<1>(token) != '=')) continue; // Слева и справа должны быть операнды if(pos == 0 || pos == tokens.size()-1) { MAKE_ERROR("Отсутствует операнд"); } uint8_t oper = std::get<1>(token); auto* leftToken = &tokens[pos-1]; auto* rightToken = &tokens[pos+1]; if(rightToken->index() == 1) { if(std::get<1>(*rightToken) == '=') { if(pos+2 >= tokens.size()) MAKE_ERROR("Отсутствует операнд"); rightToken = &tokens[pos+2]; if(rightToken->index() == 1) MAKE_ERROR("Недопустимый операнд"); } else MAKE_ERROR("Недопустимый оператор"); } else continue; if((leftToken->index() == 0 && rightToken->index() == 0) && std::isdigit(std::get<0>(*leftToken)[0]) && std::isdigit(std::get<0>(*rightToken)[0]) ) { // Можно высчитать на месте int leftVal = std::stoi(std::get<0>(*leftToken)); int rightVal = std::stoi(std::get<0>(*rightToken)); if(oper == '!') token = leftVal != rightVal ? "1" : "0"; else if(oper == '=') token = leftVal == rightVal ? "1" : "0"; tokens.erase(tokens.begin()+pos+1); tokens.erase(tokens.begin()+pos+1); tokens.erase(tokens.begin()+pos-1); pos--; } else { Node newNode; newNode.Func = oper == '!' ? '@' : '='; if(leftToken->index() == 0) newNode.Val1 = std::get<0>(*leftToken); else if(leftToken->index() == 1) MAKE_ERROR("Недопустимый операнд"); else { nodes.push_back(std::get<2>(*leftToken)); uint16_t index = nodes.size()-1; newNode.Val1 = index; tokens.erase(tokens.begin()+pos-1); pos--; } if(rightToken->index() == 0) newNode.Val2 = std::get<0>(*rightToken); else if(rightToken->index() == 1) MAKE_ERROR("Недопустимый операнд"); else { nodes.push_back(std::get<2>(*rightToken)); uint16_t index = nodes.size()-1; newNode.Val2 = index; tokens.erase(tokens.begin()+pos+1); tokens.erase(tokens.begin()+pos+1); } tokens[pos] = newNode; } } for(ssize_t pos = 0; pos < tokens.size(); pos++) { // Проверка на & auto& token = tokens[pos]; if(token.index() != 1 || std::get<1>(token) != '&') continue; // Слева и справа должны быть операнды if(pos == 0 || pos == tokens.size()-1) { MAKE_ERROR("Отсутствует операнд"); } uint8_t oper = std::get<1>(token); auto* leftToken = &tokens[pos-1]; auto* rightToken = &tokens[pos+1]; if((leftToken->index() == 0 && rightToken->index() == 0) && std::isdigit(std::get<0>(*leftToken)[0]) && std::isdigit(std::get<0>(*rightToken)[0]) ) { // Можно высчитать на месте int leftVal = std::stoi(std::get<0>(*leftToken)); int rightVal = std::stoi(std::get<0>(*rightToken)); token = leftVal && rightVal ? "1" : "0"; tokens.erase(tokens.begin()+pos+1); tokens.erase(tokens.begin()+pos-1); pos--; } else { Node newNode; newNode.Func = oper; if(leftToken->index() == 0) newNode.Val1 = std::get<0>(*leftToken); else if(leftToken->index() == 1) MAKE_ERROR("Недопустимый операнд"); else { nodes.push_back(std::get<2>(*leftToken)); uint16_t index = nodes.size()-1; newNode.Val1 = index; tokens.erase(tokens.begin()+pos-1); pos--; } if(rightToken->index() == 0) newNode.Val2 = std::get<0>(*rightToken); else if(rightToken->index() == 1) MAKE_ERROR("Недопустимый операнд"); else { nodes.push_back(std::get<2>(*rightToken)); uint16_t index = nodes.size()-1; newNode.Val2 = index; tokens.erase(tokens.begin()+pos+1); } tokens[pos] = newNode; } } for(ssize_t pos = 0; pos < tokens.size(); pos++) { // Проверка на | auto& token = tokens[pos]; if(token.index() != 1 || std::get<1>(token) != '|') continue; // Слева и справа должны быть операнды if(pos == 0 || pos == tokens.size()-1) { MAKE_ERROR("Отсутствует операнд"); } uint8_t oper = std::get<1>(token); auto* leftToken = &tokens[pos-1]; auto* rightToken = &tokens[pos+1]; if((leftToken->index() == 0 && rightToken->index() == 0) && std::isdigit(std::get<0>(*leftToken)[0]) && std::isdigit(std::get<0>(*rightToken)[0]) ) { // Можно высчитать на месте int leftVal = std::stoi(std::get<0>(*leftToken)); int rightVal = std::stoi(std::get<0>(*rightToken)); token = leftVal || rightVal ? "1" : "0"; tokens.erase(tokens.begin()+pos+1); tokens.erase(tokens.begin()+pos-1); pos--; } else { Node newNode; newNode.Func = oper; if(leftToken->index() == 0) newNode.Val1 = std::get<0>(*leftToken); else if(leftToken->index() == 1) MAKE_ERROR("Недопустимый операнд"); else { nodes.push_back(std::get<2>(*leftToken)); uint16_t index = nodes.size()-1; newNode.Val1 = index; tokens.erase(tokens.begin()+pos-1); pos--; } if(rightToken->index() == 0) newNode.Val2 = std::get<0>(*rightToken); else if(rightToken->index() == 1) MAKE_ERROR("Недопустимый операнд"); else { nodes.push_back(std::get<2>(*rightToken)); uint16_t index = nodes.size()-1; newNode.Val2 = index; tokens.erase(tokens.begin()+pos+1); } tokens[pos] = newNode; } } if(tokens.empty()) MAKE_ERROR("Пустое выражение"); else if(tokens.size() == 1) { assert(tokens[0].index() != 1); if(tokens[0].index() == 0) return std::get<0>(tokens[0]); else return std::get<2>(tokens[0]); } else { MAKE_ERROR("Выражение не корректно"); } }; const std::string exp = TOS::Str::replace(expression, " ", ""); // exp = TOS::Str::replace(expression, "\0", ""); std::variant result = lambda(exp); for(int meta = 0; meta < 256; meta++) { CT[meta] = true; } } bool operator()(uint8_t meta) { return CT[meta]; } }; struct DefNodeStates_t { /* Указать модель, текстуры и поворот по конкретным осям. Может быть вариативность моделей относительно одного условия (случайность в зависимости от координат?) Допускается активация нескольких условий одновременно условия snowy=false "snowy=false": [{"model": "node/grass_node"}, {"model": "node/grass_node", transformations: ["y=90", "x=67"]}] <- модель будет выбрана случайно или : [{models: [], weight: 1}, {}] <- в models можно перечислить сразу несколько моделей, и они будут использоваться одновременно или "": {"model": "node/grass", weight <вес влияющий на шанс отображения именно этой модели>} или просто "model": "node/grass_node" В условия добавить простые проверки !><=&|() in ['1', 2] в задании параметров модели использовать формулы с применением состояний uvlock ? https://minecraft.wiki/w/Blockstates_definition/format */ }; // Скомпилированный профиль ноды struct DefNode_t { // Зарегистрированные состояния (мета) struct { // Подгружается с файла assets//blockstate/node/nodeId.json DefNodeStates_t StateRouter; } States; // Параметры рендера struct { bool hasHalfTransparency = false; } Render; // Параметры коллизии struct { enum class EnumCollisionType { None, ByRender, }; std::variant CollisionType = EnumCollisionType::None; } Collision; // События struct { } Events; // Если нода умная, то для неё будет создаваться дополнительный более активный объект sol::protected_function NodeAdvancementFactory; }; class Entity { DefEntityId_t DefId; public: LocalAABB ABBOX; // PosQuat DefWorldId_t WorldId; Pos::Object Pos, Speed, Acceleration; glm::quat Quat; static constexpr uint16_t HP_BS = 4096, HP_BS_Bit = 12; uint32_t HP = 0; Pos::GlobalRegion InRegionPos; // State std::unordered_map Tags; // m_attached_particle_spawners // states bool // Сущность будет удалена в слудующем такте NeedRemove = false, // Сущность была удалена или не действительна IsRemoved = false; public: Entity(DefEntityId_t defId); AABB aabbAtPos() { return {Pos-Pos::Object(ABBOX.x/2, ABBOX.y/2, ABBOX.z/2), Pos+Pos::Object(ABBOX.x/2, ABBOX.y/2, ABBOX.z/2)}; } DefEntityId_t getDefId() const { return DefId; } }; template struct VoxelCuboidsFuncs { // Кубы должны быть отсортированы static bool canMerge(const Vec& a, const Vec& b) { if (a.VoxelId != b.VoxelId) return false; // Проверяем, что кубы смежны по одной из осей bool xAdjacent = (a.Right.x == b.Left.x) && (a.Left.y == b.Left.y) && (a.Right.z == b.Right.z) && (a.Left.z == b.Left.z) && (a.Right.z == b.Right.z); bool yAdjacent = (a.Right.y == b.Left.y) && (a.Left.x == b.Left.x) && (a.Right.x == b.Right.x) && (a.Left.z == b.Left.z) && (a.Right.z == b.Right.z); bool zAdjacent = (a.Right.z == b.Left.z) && (a.Left.x == b.Left.x) && (a.Right.x == b.Right.x) && (a.Left.y == b.Left.y) && (a.Right.y == b.Right.y); return xAdjacent || yAdjacent || zAdjacent; } static Vec mergeCubes(const Vec& a, const Vec& b) { Vec merged; merged.VoxelId = a.VoxelId; // Объединяем кубы по минимальным и максимальным координатам merged.Left.x = std::min(a.Left.x, b.Left.x); merged.Left.y = std::min(a.Left.y, b.Left.y); merged.Left.z = std::min(a.Left.z, b.Left.z); merged.Right.x = std::max(a.Right.x, b.Right.x); merged.Right.y = std::max(a.Right.y, b.Right.y); merged.Right.z = std::max(a.Right.z, b.Right.z); return merged; } static std::vector optimizeVoxelRegions(std::vector regions) { bool changed; do { changed = false; for (size_t i = 0; i < regions.size(); ++i) { for (size_t j = i + 1; j < regions.size(); ++j) { if (canMerge(regions[i], regions[j])) { regions[i] = mergeCubes(regions[i], regions[j]); regions.erase(regions.begin() + j); changed = true; --j; } } } } while (changed); return regions; } static bool isCubesIntersect(const Vec& a, const Vec& b) { return !(a.Right.X < b.Left.X || a.Left.X > b.Right.X || a.Right.Y < b.Left.Y || a.Left.Y > b.Right.Y || a.Right.Z < b.Left.Z || a.Left.Z > b.Right.Z); } static std::vector subtractCube(const Vec& a, const Vec& b) { std::vector result; if (!isCubesIntersect(a, b)) { result.push_back(a); return result; } decltype(a.Left) intersectLeft = { std::max(a.Left.X, b.Left.X), std::max(a.Left.Y, b.Left.Y), std::max(a.Left.Z, b.Left.Z) }; decltype(a.Left) intersectRight = { std::min(a.Right.X, b.Right.X), std::min(a.Right.Y, b.Right.Y), std::min(a.Right.Z, b.Right.Z) }; // Разделяем куб a на меньшие кубы, исключая пересечение if (a.Left.X < intersectLeft.X) { result.push_back({a.Left, decltype(a.Left)(intersectLeft.X - 1, a.Right.Y, a.Right.Z), a.Material}); } if (a.Right.X > intersectRight.X) { result.push_back({decltype(a.Left)(intersectRight.X + 1, a.Left.Y, a.Left.Z), a.Right, a.Material}); } if (a.Left.Y < intersectLeft.Y) { result.push_back({ {intersectLeft.X, a.Left.Y, a.Left.Z}, decltype(a.Left)(intersectRight.X, intersectLeft.Y - 1, a.Right.Z), a.Material }); } if (a.Right.Y > intersectRight.Y) { result.push_back({ decltype(a.Left)(intersectLeft.X, intersectRight.Y + 1, a.Left.Z), {intersectRight.X, a.Right.Y, a.Right.Z}, a.Material }); } if (a.Left.Z < intersectLeft.Z) { result.push_back({ {intersectLeft.X, intersectLeft.Y, a.Left.Z}, decltype(a.Left)(intersectRight.X, intersectRight.Y, intersectLeft.Z - 1), a.Material }); } if (a.Right.Z > intersectRight.Z) { result.push_back({ decltype(a.Left)(intersectLeft.X, intersectLeft.Y, intersectRight.Z + 1), {intersectRight.X, intersectRight.Y, a.Right.Z}, a.Material }); } return result; } }; inline void convertRegionVoxelsToChunks(const std::vector& regions, std::unordered_map> &chunks) { for (const auto& region : regions) { int minX = region.Left.x >> 8; int minY = region.Left.y >> 8; int minZ = region.Left.z >> 8; int maxX = region.Right.x >> 8; int maxY = region.Right.y >> 8; int maxZ = region.Right.z >> 8; for (int x = minX; x <= maxX; ++x) { for (int y = minY; y <= maxY; ++y) { for (int z = minZ; z <= maxZ; ++z) { Pos::bvec256u left { static_cast(std::max((x << 8), region.Left.x) - (x << 8)), static_cast(std::max((y << 8), region.Left.y) - (y << 8)), static_cast(std::max((z << 8), region.Left.z) - (z << 8)) }; Pos::bvec256u right { static_cast(std::min(((x+1) << 8)-1, region.Right.x) - (x << 8)), static_cast(std::min(((y+1) << 8)-1, region.Right.y) - (y << 8)), static_cast(std::min(((z+1) << 8)-1, region.Right.z) - (z << 8)) }; chunks[Pos::bvec4u(x, y, z)].push_back({ region.VoxelId, region.Meta, left, right }); } } } } } inline void convertChunkVoxelsToRegion(const std::unordered_map> &chunks, std::vector ®ions) { for(const auto& [pos, voxels] : chunks) { Pos::bvec1024u left = pos << 8; for (const auto& cube : voxels) { regions.push_back({ cube.VoxelId, cube.Meta, Pos::bvec1024u(left.x+cube.Pos.x, left.y+cube.Pos.y, left.z+cube.Pos.z), Pos::bvec1024u(left.x+cube.Pos.x+cube.Size.x, left.y+cube.Pos.y+cube.Size.y, left.z+cube.Pos.z+cube.Size.z) }); } } std::sort(regions.begin(), regions.end()); regions = VoxelCuboidsFuncs::optimizeVoxelRegions(regions); } }