*
This commit is contained in:
@@ -32,9 +32,9 @@ add_library(luavox_common INTERFACE)
|
|||||||
target_compile_features(luavox_common INTERFACE cxx_std_23)
|
target_compile_features(luavox_common INTERFACE cxx_std_23)
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
target_compile_options(luavox_common INTERFACE -fsanitize=address,undefined -fno-omit-frame-pointer -fno-sanitize-recover=all)
|
# target_compile_options(luavox_common INTERFACE -fsanitize=address,undefined -fno-omit-frame-pointer -fno-sanitize-recover=all)
|
||||||
target_link_options(luavox_common INTERFACE -fsanitize=address,undefined)
|
# target_link_options(luavox_common INTERFACE -fsanitize=address,undefined)
|
||||||
set(ENV{ASAN_OPTIONS} detect_leaks=0)
|
# set(ENV{ASAN_OPTIONS} detect_leaks=0)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
|
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
|
||||||
|
|||||||
@@ -36,6 +36,26 @@ namespace std {
|
|||||||
|
|
||||||
namespace LV::Client::VK {
|
namespace LV::Client::VK {
|
||||||
|
|
||||||
|
void ChunkMeshGenerator::changeThreadsCount(uint8_t threads) {
|
||||||
|
Sync.NeedShutdown = true;
|
||||||
|
std::unique_lock lock(Sync.Mutex);
|
||||||
|
Sync.CV_CountInRun.wait(lock, [&]() { return Sync.CountInRun == 0; });
|
||||||
|
|
||||||
|
for(std::thread& thr : Threads)
|
||||||
|
thr.join();
|
||||||
|
|
||||||
|
Sync.NeedShutdown = false;
|
||||||
|
|
||||||
|
Threads.resize(threads);
|
||||||
|
for(int iter = 0; iter < threads; iter++)
|
||||||
|
Threads[iter] = std::thread(&ChunkMeshGenerator::run, this, iter);
|
||||||
|
|
||||||
|
Sync.CV_CountInRun.wait(lock, [&]() { return Sync.CountInRun == Threads.size() || Sync.NeedShutdown; });
|
||||||
|
|
||||||
|
if(Sync.NeedShutdown)
|
||||||
|
MAKE_ERROR("Ошибка обработчика вершин чанков");
|
||||||
|
}
|
||||||
|
|
||||||
void ChunkMeshGenerator::run(uint8_t id) {
|
void ChunkMeshGenerator::run(uint8_t id) {
|
||||||
Logger LOG = "ChunkMeshGenerator<"+std::to_string(id)+'>';
|
Logger LOG = "ChunkMeshGenerator<"+std::to_string(id)+'>';
|
||||||
|
|
||||||
@@ -562,6 +582,136 @@ void ChunkMeshGenerator::run(uint8_t id) {
|
|||||||
LOG.debug() << "Завершение потока верширования чанков";
|
LOG.debug() << "Завершение потока верширования чанков";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ChunkPreparator::tickSync(const TickSyncData& data) {
|
||||||
|
// Обработать изменения в чанках
|
||||||
|
// Пересчёт соседних чанков
|
||||||
|
// Проверить необходимость пересчёта чанков при изменении профилей
|
||||||
|
|
||||||
|
// Добавляем к изменёным чанкам пересчёт соседей
|
||||||
|
{
|
||||||
|
std::vector<std::tuple<WorldId_t, Pos::GlobalChunk, uint32_t>> toBuild;
|
||||||
|
for(auto& [wId, chunks] : data.ChangedChunks) {
|
||||||
|
std::vector<Pos::GlobalChunk> list;
|
||||||
|
for(const Pos::GlobalChunk& pos : chunks) {
|
||||||
|
list.push_back(pos);
|
||||||
|
list.push_back(pos+Pos::GlobalChunk(1, 0, 0));
|
||||||
|
list.push_back(pos+Pos::GlobalChunk(-1, 0, 0));
|
||||||
|
list.push_back(pos+Pos::GlobalChunk(0, 1, 0));
|
||||||
|
list.push_back(pos+Pos::GlobalChunk(0, -1, 0));
|
||||||
|
list.push_back(pos+Pos::GlobalChunk(0, 0, 1));
|
||||||
|
list.push_back(pos+Pos::GlobalChunk(0, 0, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(list.begin(), list.end());
|
||||||
|
auto eraseIter = std::unique(list.begin(), list.end());
|
||||||
|
list.erase(eraseIter, list.end());
|
||||||
|
|
||||||
|
|
||||||
|
for(Pos::GlobalChunk& pos : list) {
|
||||||
|
Pos::GlobalRegion rPos = pos >> 2;
|
||||||
|
auto iterRegion = Requests[wId].find(rPos);
|
||||||
|
if(iterRegion != Requests[wId].end())
|
||||||
|
toBuild.emplace_back(wId, pos, iterRegion->second);
|
||||||
|
else
|
||||||
|
toBuild.emplace_back(wId, pos, Requests[wId][rPos] = NextRequest++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CMG.Input.lock()->push_range(toBuild);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Чистим запросы и чанки
|
||||||
|
{
|
||||||
|
uint8_t frameRetirement = (FrameRoulette+FRAME_COUNT_RESOURCE_LATENCY) % FRAME_COUNT_RESOURCE_LATENCY;
|
||||||
|
for(auto& [wId, regions] : data.LostRegions) {
|
||||||
|
if(auto iterWorld = Requests.find(wId); iterWorld != Requests.end()) {
|
||||||
|
for(const Pos::GlobalRegion& rPos : regions)
|
||||||
|
if(auto iterRegion = iterWorld->second.find(rPos); iterRegion != iterWorld->second.end())
|
||||||
|
iterWorld->second.erase(iterRegion);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(auto iterWorld = ChunksMesh.find(wId); iterWorld != ChunksMesh.end()) {
|
||||||
|
for(const Pos::GlobalRegion& rPos : regions)
|
||||||
|
if(auto iterRegion = iterWorld->second.find(rPos); iterRegion != iterWorld->second.end()) {
|
||||||
|
for(int iter = 0; iter < 4*4*4; iter++) {
|
||||||
|
auto& chunk = iterRegion->second[iter];
|
||||||
|
if(chunk.VoxelPointer)
|
||||||
|
VPV_ToFree[frameRetirement].emplace_back(std::move(chunk.VoxelPointer));
|
||||||
|
if(chunk.NodePointer) {
|
||||||
|
VPN_ToFree[frameRetirement].emplace_back(std::move(chunk.NodePointer), std::move(chunk.NodeIndexes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterWorld->second.erase(iterRegion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем готовые чанки
|
||||||
|
{
|
||||||
|
std::vector<ChunkMeshGenerator::ChunkObj_t> chunks = std::move(*CMG.Output.lock());
|
||||||
|
for(auto& chunk : chunks) {
|
||||||
|
auto iterWorld = Requests.find(chunk.WId);
|
||||||
|
if(iterWorld == Requests.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto iterRegion = iterWorld->second.find(chunk.Pos >> 2);
|
||||||
|
if(iterRegion == iterWorld->second.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(iterRegion->second != chunk.RequestId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Чанк ожидаем
|
||||||
|
auto& rChunk = ChunksMesh[chunk.WId][chunk.Pos >> 2][Pos::bvec4u(chunk.Pos & 0x3).pack()];
|
||||||
|
rChunk.Voxels = std::move(chunk.VoxelDefines);
|
||||||
|
if(!chunk.VoxelVertexs.empty())
|
||||||
|
rChunk.VoxelPointer = VertexPool_Voxels.pushVertexs(std::move(chunk.VoxelVertexs));
|
||||||
|
rChunk.Nodes = std::move(chunk.NodeDefines);
|
||||||
|
if(!chunk.NodeVertexs.empty())
|
||||||
|
rChunk.NodePointer = VertexPool_Nodes.pushVertexs(std::move(chunk.NodeVertexs));
|
||||||
|
|
||||||
|
if(std::vector<uint16_t>* ptr = std::get_if<std::vector<uint16_t>>(&chunk.NodeIndexes)) {
|
||||||
|
if(!ptr->empty())
|
||||||
|
rChunk.NodeIndexes = IndexPool_Nodes_16.pushVertexs(std::move(*ptr));
|
||||||
|
} else if(std::vector<uint32_t>* ptr = std::get_if<std::vector<uint32_t>>(&chunk.NodeIndexes)) {
|
||||||
|
if(!ptr->empty())
|
||||||
|
rChunk.NodeIndexes = IndexPool_Nodes_32.pushVertexs(std::move(*ptr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VertexPool_Voxels.update(CMDPool);
|
||||||
|
VertexPool_Nodes.update(CMDPool);
|
||||||
|
IndexPool_Nodes_16.update(CMDPool);
|
||||||
|
IndexPool_Nodes_32.update(CMDPool);
|
||||||
|
|
||||||
|
CMG.endTickSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChunkPreparator::pushFrame() {
|
||||||
|
FrameRoulette = (FrameRoulette+1) % FRAME_COUNT_RESOURCE_LATENCY;
|
||||||
|
|
||||||
|
for(auto pointer : VPV_ToFree[FrameRoulette]) {
|
||||||
|
VertexPool_Voxels.dropVertexs(pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
VPV_ToFree[FrameRoulette].clear();
|
||||||
|
|
||||||
|
for(auto& pointer : VPN_ToFree[FrameRoulette]) {
|
||||||
|
VertexPool_Nodes.dropVertexs(std::get<0>(pointer));
|
||||||
|
if(IndexPool<uint16_t>::Pointer* ind = std::get_if<IndexPool<uint16_t>::Pointer>(&std::get<1>(pointer))) {
|
||||||
|
IndexPool_Nodes_16.dropVertexs(*ind);
|
||||||
|
} else if(IndexPool<uint32_t>::Pointer* ind = std::get_if<IndexPool<uint32_t>::Pointer>(&std::get<1>(pointer))) {
|
||||||
|
IndexPool_Nodes_32.dropVertexs(*ind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VPN_ToFree[FrameRoulette].clear();
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<
|
std::pair<
|
||||||
std::vector<std::tuple<Pos::GlobalChunk, std::pair<VkBuffer, int>, uint32_t>>,
|
std::vector<std::tuple<Pos::GlobalChunk, std::pair<VkBuffer, int>, uint32_t>>,
|
||||||
std::vector<std::tuple<Pos::GlobalChunk, std::pair<VkBuffer, int>, std::pair<VkBuffer, int>, bool, uint32_t>>
|
std::vector<std::tuple<Pos::GlobalChunk, std::pair<VkBuffer, int>, std::pair<VkBuffer, int>, bool, uint32_t>>
|
||||||
|
|||||||
@@ -5,10 +5,12 @@
|
|||||||
#include <Client/Vulkan/Vulkan.hpp>
|
#include <Client/Vulkan/Vulkan.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
#include <string_view>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
@@ -20,6 +22,7 @@
|
|||||||
#include "glm/fwd.hpp"
|
#include "glm/fwd.hpp"
|
||||||
#include "../FrustumCull.h"
|
#include "../FrustumCull.h"
|
||||||
#include "glm/geometric.hpp"
|
#include "glm/geometric.hpp"
|
||||||
|
#include <execution>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
У движка есть один текстурный атлас VK_IMAGE_VIEW_TYPE_2D_ARRAY(RGBA_UINT) и к нему Storage с инфой о положении текстур
|
У движка есть один текстурный атлас VK_IMAGE_VIEW_TYPE_2D_ARRAY(RGBA_UINT) и к нему Storage с инфой о положении текстур
|
||||||
@@ -49,6 +52,302 @@ struct WorldPCO {
|
|||||||
|
|
||||||
static_assert(sizeof(WorldPCO) == 128);
|
static_assert(sizeof(WorldPCO) == 128);
|
||||||
|
|
||||||
|
class ModelProvider {
|
||||||
|
struct Transformations {
|
||||||
|
std::vector<Transformation> OPs;
|
||||||
|
|
||||||
|
void apply(std::vector<Vertex>& vertices) const {
|
||||||
|
if (vertices.empty() || OPs.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
glm::mat4 transform(1.0f);
|
||||||
|
|
||||||
|
for (const auto& op : OPs) {
|
||||||
|
switch (op.Op) {
|
||||||
|
case Transformation::MoveX: transform = glm::translate(transform, glm::vec3(op.Value, 0.0f, 0.0f)); break;
|
||||||
|
case Transformation::MoveY: transform = glm::translate(transform, glm::vec3(0.0f, op.Value, 0.0f)); break;
|
||||||
|
case Transformation::MoveZ: transform = glm::translate(transform, glm::vec3(0.0f, 0.0f, op.Value)); break;
|
||||||
|
case Transformation::ScaleX: transform = glm::scale(transform, glm::vec3(op.Value, 1.0f, 1.0f)); break;
|
||||||
|
case Transformation::ScaleY: transform = glm::scale(transform, glm::vec3(1.0f, op.Value, 1.0f)); break;
|
||||||
|
case Transformation::ScaleZ: transform = glm::scale(transform, glm::vec3(1.0f, 1.0f, op.Value)); break;
|
||||||
|
case Transformation::RotateX: transform = glm::rotate(transform, op.Value, glm::vec3(1.0f, 0.0f, 0.0f)); break;
|
||||||
|
case Transformation::RotateY: transform = glm::rotate(transform, op.Value, glm::vec3(0.0f, 1.0f, 0.0f)); break;
|
||||||
|
case Transformation::RotateZ: transform = glm::rotate(transform, op.Value, glm::vec3(0.0f, 0.0f, 1.0f)); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::transform(
|
||||||
|
std::execution::unseq,
|
||||||
|
vertices.begin(),
|
||||||
|
vertices.end(),
|
||||||
|
vertices.begin(),
|
||||||
|
[transform](Vertex v) -> Vertex {
|
||||||
|
glm::vec4 pos_h(v.Pos, 1.0f);
|
||||||
|
pos_h = transform * pos_h;
|
||||||
|
v.Pos = glm::vec3(pos_h) / pos_h.w;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Vertex> apply(const std::vector<Vertex>& vertices) const {
|
||||||
|
std::vector<Vertex> result = vertices;
|
||||||
|
apply(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Model {
|
||||||
|
// В вершинах текущей модели TexId ссылается на локальный текстурный ключ
|
||||||
|
// 0 -> default_texture -> luavox:grass.png
|
||||||
|
std::vector<std::string> TextureKeys;
|
||||||
|
// Привязка локальных ключей к глобальным
|
||||||
|
std::unordered_map<std::string, TexturePipeline> TextureMap;
|
||||||
|
// Вершины со всеми применёнными трансформациями, с CullFace
|
||||||
|
std::unordered_map<EnumFace, std::vector<Vertex>> Vertecies;
|
||||||
|
// Текстуры этой модели не будут переписаны вышестоящими
|
||||||
|
bool UniqueTextures = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ModelObject : public Model {
|
||||||
|
// Зависимости, их трансформации (здесь может повторятся одна и таже модель)
|
||||||
|
// и перезаписи идентификаторов текстур
|
||||||
|
std::vector<std::tuple<ResourceId, Transformations>> Depends;
|
||||||
|
|
||||||
|
// Те кто использовали модель как зависимость в ней отметятся
|
||||||
|
std::vector<ResourceId> UpUse;
|
||||||
|
// При изменении/удалении модели убрать метки с зависимостей
|
||||||
|
std::vector<ResourceId> DownUse;
|
||||||
|
// Для постройки зависимостей
|
||||||
|
bool Ready = false;
|
||||||
|
|
||||||
|
// Если модель использовалась для рендера нод, то для неё надо переформировать вершины
|
||||||
|
// std::optional<std::vector<NodeVertexStatic>> NodeVertecies;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Предкомпилирует модель
|
||||||
|
Model getModel(ResourceId id) {
|
||||||
|
auto lock = Models.lock();
|
||||||
|
std::vector<ResourceId> used;
|
||||||
|
return getModelSynced(*lock, id, used);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Применяет изменения, возвращая все затронутые модели
|
||||||
|
std::vector<ResourceId> onModelChanges(std::vector<std::tuple<ResourceId, Resource>> newOrChanged, std::vector<ResourceId> lost) {
|
||||||
|
auto lock = Models.lock();
|
||||||
|
std::vector<ResourceId> result;
|
||||||
|
|
||||||
|
std::move_only_function<void(ResourceId)> makeUnready;
|
||||||
|
makeUnready = [&](ResourceId id) {
|
||||||
|
auto iterModel = lock->find(id);
|
||||||
|
if(iterModel == lock->end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(!iterModel->second.Ready)
|
||||||
|
return;
|
||||||
|
|
||||||
|
result.push_back(id);
|
||||||
|
|
||||||
|
for(ResourceId downId : iterModel->second.DownUse) {
|
||||||
|
auto iterModel = lock->find(downId);
|
||||||
|
if(iterModel == lock->end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto iter = std::find(iterModel->second.UpUse.begin(), iterModel->second.UpUse.end(), id);
|
||||||
|
assert(iter != iterModel->second.UpUse.end());
|
||||||
|
iterModel->second.UpUse.erase(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(ResourceId upId : iterModel->second.UpUse) {
|
||||||
|
makeUnready(upId);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(iterModel->second.UpUse.empty());
|
||||||
|
|
||||||
|
iterModel->second.Ready = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
for(ResourceId lostId : lost) {
|
||||||
|
makeUnready(lostId);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(ResourceId lostId : lost) {
|
||||||
|
auto iterModel = lock->find(lostId);
|
||||||
|
if(iterModel == lock->end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
lock->erase(iterModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto& [key, resource] : newOrChanged) {
|
||||||
|
makeUnready(key);
|
||||||
|
ModelObject model;
|
||||||
|
std::string type = "unknown";
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::u8string_view data((const char8_t*) resource.data(), resource.size());
|
||||||
|
if(data.starts_with((const char8_t*) "bm")) {
|
||||||
|
type = "InternalBinary";
|
||||||
|
// Компилированная модель внутреннего формата
|
||||||
|
LV::PreparedModel pm((std::u8string) data.substr(2));
|
||||||
|
model.TextureKeys = {};
|
||||||
|
|
||||||
|
for(const PreparedModel::Cuboid& cb : pm.Cuboids) {
|
||||||
|
for(const auto& [face, params] : cb.Faces) {
|
||||||
|
// params.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// glm::vec3 From, To;
|
||||||
|
|
||||||
|
// struct Face {
|
||||||
|
// glm::vec4 UV;
|
||||||
|
// std::string Texture;
|
||||||
|
// std::optional<EnumFace> Cullface;
|
||||||
|
// int TintIndex = -1;
|
||||||
|
// int16_t Rotation = 0;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// std::unordered_map<EnumFace, Face> Faces;
|
||||||
|
|
||||||
|
// std::vector<Transformation> Transformations;
|
||||||
|
|
||||||
|
} else if(data.starts_with((const char8_t*) "glTF")) {
|
||||||
|
type = "glb";
|
||||||
|
|
||||||
|
} else if(data.starts_with((const char8_t*) "bgl")) {
|
||||||
|
type = "InternalGLTF";
|
||||||
|
|
||||||
|
} else if(data.starts_with((const char8_t*) "{")) {
|
||||||
|
type = "InternalJson или glTF";
|
||||||
|
// Модель внутреннего формата или glTF
|
||||||
|
}
|
||||||
|
} catch(const std::exception& exc) {
|
||||||
|
LOG.warn() << "Не удалось распарсить модель " << type << ":\n\t" << exc.what();
|
||||||
|
}
|
||||||
|
|
||||||
|
lock->insert({key, std::move(model)});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::sort(result.begin(), result.end());
|
||||||
|
auto eraseIter = std::unique(result.begin(), result.end());
|
||||||
|
result.erase(eraseIter, result.end());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Logger LOG = "Client>ModelProvider";
|
||||||
|
// Таблица моделей
|
||||||
|
TOS::SpinlockObject<std::unordered_map<ResourceId, ModelObject>> Models;
|
||||||
|
uint64_t UniqId = 0;
|
||||||
|
|
||||||
|
Model getModelSynced(std::unordered_map<ResourceId, ModelObject>& models, ResourceId id, std::vector<ResourceId>& used) {
|
||||||
|
auto iterModel = models.find(id);
|
||||||
|
if(iterModel == models.end()) {
|
||||||
|
// Нет такой модели, ну и хрен с ним
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelObject& model = iterModel->second;
|
||||||
|
if(!model.Ready) {
|
||||||
|
std::vector<ResourceId> deps;
|
||||||
|
for(const auto&[id, _] : model.Depends) {
|
||||||
|
deps.push_back(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(deps.begin(), deps.end());
|
||||||
|
auto eraseIter = std::unique(deps.begin(), deps.end());
|
||||||
|
deps.erase(eraseIter, deps.end());
|
||||||
|
|
||||||
|
// Отмечаемся в зависимостях
|
||||||
|
for(ResourceId subId : deps) {
|
||||||
|
auto iterModel = models.find(subId);
|
||||||
|
if(iterModel == models.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
iterModel->second.UpUse.push_back(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
model.Ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Собрать зависимости
|
||||||
|
std::vector<Model> subModels;
|
||||||
|
used.push_back(id);
|
||||||
|
|
||||||
|
for(const auto&[id, trans] : model.Depends) {
|
||||||
|
if(std::find(used.begin(), used.end(), id) != used.end()) {
|
||||||
|
// Цикл зависимостей
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Model model = getModelSynced(models, id, used);
|
||||||
|
|
||||||
|
for(auto& [face, vertecies] : model.Vertecies)
|
||||||
|
trans.apply(vertecies);
|
||||||
|
|
||||||
|
subModels.emplace_back(std::move(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
subModels.push_back(model);
|
||||||
|
used.pop_back();
|
||||||
|
|
||||||
|
// Собрать всё воедино
|
||||||
|
Model result;
|
||||||
|
|
||||||
|
for(Model& subModel : subModels) {
|
||||||
|
std::vector<ResourceId> localRelocate;
|
||||||
|
|
||||||
|
if(subModel.UniqueTextures) {
|
||||||
|
std::string extraKey = "#" + std::to_string(UniqId++);
|
||||||
|
for(std::string& key : subModel.TextureKeys) {
|
||||||
|
key += extraKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::string, TexturePipeline> newTable;
|
||||||
|
for(auto& [key, _] : subModel.TextureMap) {
|
||||||
|
newTable[key + extraKey] = _;
|
||||||
|
}
|
||||||
|
|
||||||
|
subModel.TextureMap = std::move(newTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const std::string& key : subModel.TextureKeys) {
|
||||||
|
auto iterKey = std::find(result.TextureKeys.begin(), result.TextureKeys.end(), key);
|
||||||
|
if(iterKey == result.TextureKeys.end()) {
|
||||||
|
localRelocate.push_back(result.TextureKeys.size());
|
||||||
|
result.TextureKeys.push_back(key);
|
||||||
|
} else {
|
||||||
|
localRelocate.push_back(iterKey-result.TextureKeys.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto& [face, vertecies] : subModel.Vertecies) {
|
||||||
|
auto& resVerts = result.Vertecies[face];
|
||||||
|
|
||||||
|
for(Vertex v : vertecies) {
|
||||||
|
v.TexId = localRelocate[v.TexId];
|
||||||
|
resVerts.push_back(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto& [key, dk] : subModel.TextureMap) {
|
||||||
|
result.TextureMap[key] = dk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModelProviderForChunkMeshGeneretaor {
|
||||||
|
public:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Объект, занимающийся генерацией меша на основе нод и вокселей
|
Объект, занимающийся генерацией меша на основе нод и вокселей
|
||||||
Требует доступ к профилям в ServerSession (ServerSession должен быть заблокирован только на чтение)
|
Требует доступ к профилям в ServerSession (ServerSession должен быть заблокирован только на чтение)
|
||||||
@@ -81,7 +380,6 @@ struct ChunkMeshGenerator {
|
|||||||
// Выход
|
// Выход
|
||||||
TOS::SpinlockObject<std::vector<ChunkObj_t>> Output;
|
TOS::SpinlockObject<std::vector<ChunkObj_t>> Output;
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ChunkMeshGenerator(IServerSession* serverSession)
|
ChunkMeshGenerator(IServerSession* serverSession)
|
||||||
: SS(serverSession)
|
: SS(serverSession)
|
||||||
@@ -94,25 +392,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Меняет количество обрабатывающих потоков
|
// Меняет количество обрабатывающих потоков
|
||||||
void changeThreadsCount(uint8_t threads) {
|
void changeThreadsCount(uint8_t threads);
|
||||||
Sync.NeedShutdown = true;
|
|
||||||
std::unique_lock lock(Sync.Mutex);
|
|
||||||
Sync.CV_CountInRun.wait(lock, [&]() { return Sync.CountInRun == 0; });
|
|
||||||
|
|
||||||
for(std::thread& thr : Threads)
|
|
||||||
thr.join();
|
|
||||||
|
|
||||||
Sync.NeedShutdown = false;
|
|
||||||
|
|
||||||
Threads.resize(threads);
|
|
||||||
for(int iter = 0; iter < threads; iter++)
|
|
||||||
Threads[iter] = std::thread(&ChunkMeshGenerator::run, this, iter);
|
|
||||||
|
|
||||||
Sync.CV_CountInRun.wait(lock, [&]() { return Sync.CountInRun == Threads.size() || Sync.NeedShutdown; });
|
|
||||||
|
|
||||||
if(Sync.NeedShutdown)
|
|
||||||
MAKE_ERROR("Ошибка обработчика вершин чанков");
|
|
||||||
}
|
|
||||||
|
|
||||||
void prepareTickSync() {
|
void prepareTickSync() {
|
||||||
Sync.Stop = true;
|
Sync.Stop = true;
|
||||||
@@ -128,7 +408,6 @@ public:
|
|||||||
Sync.CV_CountInRun.notify_all();
|
Sync.CV_CountInRun.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct {
|
struct {
|
||||||
std::mutex Mutex;
|
std::mutex Mutex;
|
||||||
@@ -139,8 +418,6 @@ private:
|
|||||||
} Sync;
|
} Sync;
|
||||||
|
|
||||||
IServerSession *SS;
|
IServerSession *SS;
|
||||||
|
|
||||||
// Потоки
|
|
||||||
std::vector<std::thread> Threads;
|
std::vector<std::thread> Threads;
|
||||||
|
|
||||||
void run(uint8_t id);
|
void run(uint8_t id);
|
||||||
@@ -201,135 +478,10 @@ public:
|
|||||||
CMG.pushStageTickSync();
|
CMG.pushStageTickSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void tickSync(const TickSyncData& data) {
|
void tickSync(const TickSyncData& data);
|
||||||
// Обработать изменения в чанках
|
|
||||||
// Пересчёт соседних чанков
|
|
||||||
// Проверить необходимость пересчёта чанков при изменении профилей
|
|
||||||
|
|
||||||
// Добавляем к изменёным чанкам пересчёт соседей
|
|
||||||
{
|
|
||||||
std::vector<std::tuple<WorldId_t, Pos::GlobalChunk, uint32_t>> toBuild;
|
|
||||||
for(auto& [wId, chunks] : data.ChangedChunks) {
|
|
||||||
std::vector<Pos::GlobalChunk> list;
|
|
||||||
for(const Pos::GlobalChunk& pos : chunks) {
|
|
||||||
list.push_back(pos);
|
|
||||||
list.push_back(pos+Pos::GlobalChunk(1, 0, 0));
|
|
||||||
list.push_back(pos+Pos::GlobalChunk(-1, 0, 0));
|
|
||||||
list.push_back(pos+Pos::GlobalChunk(0, 1, 0));
|
|
||||||
list.push_back(pos+Pos::GlobalChunk(0, -1, 0));
|
|
||||||
list.push_back(pos+Pos::GlobalChunk(0, 0, 1));
|
|
||||||
list.push_back(pos+Pos::GlobalChunk(0, 0, -1));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(list.begin(), list.end());
|
|
||||||
auto eraseIter = std::unique(list.begin(), list.end());
|
|
||||||
list.erase(eraseIter, list.end());
|
|
||||||
|
|
||||||
|
|
||||||
for(Pos::GlobalChunk& pos : list) {
|
|
||||||
Pos::GlobalRegion rPos = pos >> 2;
|
|
||||||
auto iterRegion = Requests[wId].find(rPos);
|
|
||||||
if(iterRegion != Requests[wId].end())
|
|
||||||
toBuild.emplace_back(wId, pos, iterRegion->second);
|
|
||||||
else
|
|
||||||
toBuild.emplace_back(wId, pos, Requests[wId][rPos] = NextRequest++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CMG.Input.lock()->push_range(toBuild);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Чистим запросы и чанки
|
|
||||||
{
|
|
||||||
uint8_t frameRetirement = (FrameRoulette+FRAME_COUNT_RESOURCE_LATENCY) % FRAME_COUNT_RESOURCE_LATENCY;
|
|
||||||
for(auto& [wId, regions] : data.LostRegions) {
|
|
||||||
if(auto iterWorld = Requests.find(wId); iterWorld != Requests.end()) {
|
|
||||||
for(const Pos::GlobalRegion& rPos : regions)
|
|
||||||
if(auto iterRegion = iterWorld->second.find(rPos); iterRegion != iterWorld->second.end())
|
|
||||||
iterWorld->second.erase(iterRegion);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(auto iterWorld = ChunksMesh.find(wId); iterWorld != ChunksMesh.end()) {
|
|
||||||
for(const Pos::GlobalRegion& rPos : regions)
|
|
||||||
if(auto iterRegion = iterWorld->second.find(rPos); iterRegion != iterWorld->second.end()) {
|
|
||||||
for(int iter = 0; iter < 4*4*4; iter++) {
|
|
||||||
auto& chunk = iterRegion->second[iter];
|
|
||||||
if(chunk.VoxelPointer)
|
|
||||||
VPV_ToFree[frameRetirement].emplace_back(std::move(chunk.VoxelPointer));
|
|
||||||
if(chunk.NodePointer) {
|
|
||||||
VPN_ToFree[frameRetirement].emplace_back(std::move(chunk.NodePointer), std::move(chunk.NodeIndexes));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iterWorld->second.erase(iterRegion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаем готовые чанки
|
|
||||||
{
|
|
||||||
std::vector<ChunkMeshGenerator::ChunkObj_t> chunks = std::move(*CMG.Output.lock());
|
|
||||||
for(auto& chunk : chunks) {
|
|
||||||
auto iterWorld = Requests.find(chunk.WId);
|
|
||||||
if(iterWorld == Requests.end())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto iterRegion = iterWorld->second.find(chunk.Pos >> 2);
|
|
||||||
if(iterRegion == iterWorld->second.end())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(iterRegion->second != chunk.RequestId)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Чанк ожидаем
|
|
||||||
auto& rChunk = ChunksMesh[chunk.WId][chunk.Pos >> 2][Pos::bvec4u(chunk.Pos & 0x3).pack()];
|
|
||||||
rChunk.Voxels = std::move(chunk.VoxelDefines);
|
|
||||||
if(!chunk.VoxelVertexs.empty())
|
|
||||||
rChunk.VoxelPointer = VertexPool_Voxels.pushVertexs(std::move(chunk.VoxelVertexs));
|
|
||||||
rChunk.Nodes = std::move(chunk.NodeDefines);
|
|
||||||
if(!chunk.NodeVertexs.empty())
|
|
||||||
rChunk.NodePointer = VertexPool_Nodes.pushVertexs(std::move(chunk.NodeVertexs));
|
|
||||||
|
|
||||||
if(std::vector<uint16_t>* ptr = std::get_if<std::vector<uint16_t>>(&chunk.NodeIndexes)) {
|
|
||||||
if(!ptr->empty())
|
|
||||||
rChunk.NodeIndexes = IndexPool_Nodes_16.pushVertexs(std::move(*ptr));
|
|
||||||
} else if(std::vector<uint32_t>* ptr = std::get_if<std::vector<uint32_t>>(&chunk.NodeIndexes)) {
|
|
||||||
if(!ptr->empty())
|
|
||||||
rChunk.NodeIndexes = IndexPool_Nodes_32.pushVertexs(std::move(*ptr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VertexPool_Voxels.update(CMDPool);
|
|
||||||
VertexPool_Nodes.update(CMDPool);
|
|
||||||
IndexPool_Nodes_16.update(CMDPool);
|
|
||||||
IndexPool_Nodes_32.update(CMDPool);
|
|
||||||
|
|
||||||
CMG.endTickSync();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Готовность кадров определяет когда можно удалять ненужные ресурсы, которые ещё используются в рендере
|
// Готовность кадров определяет когда можно удалять ненужные ресурсы, которые ещё используются в рендере
|
||||||
void pushFrame() {
|
void pushFrame();
|
||||||
FrameRoulette = (FrameRoulette+1) % FRAME_COUNT_RESOURCE_LATENCY;
|
|
||||||
|
|
||||||
for(auto pointer : VPV_ToFree[FrameRoulette]) {
|
|
||||||
VertexPool_Voxels.dropVertexs(pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
VPV_ToFree[FrameRoulette].clear();
|
|
||||||
|
|
||||||
for(auto& pointer : VPN_ToFree[FrameRoulette]) {
|
|
||||||
VertexPool_Nodes.dropVertexs(std::get<0>(pointer));
|
|
||||||
if(IndexPool<uint16_t>::Pointer* ind = std::get_if<IndexPool<uint16_t>::Pointer>(&std::get<1>(pointer))) {
|
|
||||||
IndexPool_Nodes_16.dropVertexs(*ind);
|
|
||||||
} else if(IndexPool<uint32_t>::Pointer* ind = std::get_if<IndexPool<uint32_t>::Pointer>(&std::get<1>(pointer))) {
|
|
||||||
IndexPool_Nodes_32.dropVertexs(*ind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VPN_ToFree[FrameRoulette].clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Выдаёт буферы для рендера в порядке от ближнего к дальнему. distance - радиус в регионах
|
// Выдаёт буферы для рендера в порядке от ближнего к дальнему. distance - радиус в регионах
|
||||||
std::pair<
|
std::pair<
|
||||||
|
|||||||
@@ -1594,7 +1594,7 @@ PreparedModel::PreparedModel(const std::string_view modid, const js::object& pro
|
|||||||
const js::object& textures = textures_val->as_object();
|
const js::object& textures = textures_val->as_object();
|
||||||
|
|
||||||
for(const auto& [key, value] : textures) {
|
for(const auto& [key, value] : textures) {
|
||||||
Textures[key] = value.as_string();
|
Textures[key] = compileTexturePipeline((std::string) value.as_string(), modid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1634,20 +1634,20 @@ PreparedModel::PreparedModel(const std::string_view modid, const js::object& pro
|
|||||||
const js::object& faces = cuboid.at("faces").as_object();
|
const js::object& faces = cuboid.at("faces").as_object();
|
||||||
|
|
||||||
for(const auto& [key, value] : faces) {
|
for(const auto& [key, value] : faces) {
|
||||||
Cuboid::EnumFace type;
|
EnumFace type;
|
||||||
|
|
||||||
if(key == "down")
|
if(key == "down")
|
||||||
type = Cuboid::EnumFace::Down;
|
type = EnumFace::Down;
|
||||||
else if(key == "up")
|
else if(key == "up")
|
||||||
type = Cuboid::EnumFace::Up;
|
type = EnumFace::Up;
|
||||||
else if(key == "north")
|
else if(key == "north")
|
||||||
type = Cuboid::EnumFace::North;
|
type = EnumFace::North;
|
||||||
else if(key == "south")
|
else if(key == "south")
|
||||||
type = Cuboid::EnumFace::South;
|
type = EnumFace::South;
|
||||||
else if(key == "west")
|
else if(key == "west")
|
||||||
type = Cuboid::EnumFace::West;
|
type = EnumFace::West;
|
||||||
else if(key == "east")
|
else if(key == "east")
|
||||||
type = Cuboid::EnumFace::East;
|
type = EnumFace::East;
|
||||||
else
|
else
|
||||||
MAKE_ERROR("Unknown face");
|
MAKE_ERROR("Unknown face");
|
||||||
|
|
||||||
@@ -1671,17 +1671,17 @@ PreparedModel::PreparedModel(const std::string_view modid, const js::object& pro
|
|||||||
const std::string_view cullface = cullface_val->as_string();
|
const std::string_view cullface = cullface_val->as_string();
|
||||||
|
|
||||||
if(cullface == "down")
|
if(cullface == "down")
|
||||||
face.Cullface = Cuboid::EnumFace::Down;
|
face.Cullface = EnumFace::Down;
|
||||||
else if(cullface == "up")
|
else if(cullface == "up")
|
||||||
face.Cullface = Cuboid::EnumFace::Up;
|
face.Cullface = EnumFace::Up;
|
||||||
else if(cullface == "north")
|
else if(cullface == "north")
|
||||||
face.Cullface = Cuboid::EnumFace::North;
|
face.Cullface = EnumFace::North;
|
||||||
else if(cullface == "south")
|
else if(cullface == "south")
|
||||||
face.Cullface = Cuboid::EnumFace::South;
|
face.Cullface = EnumFace::South;
|
||||||
else if(cullface == "west")
|
else if(cullface == "west")
|
||||||
face.Cullface = Cuboid::EnumFace::West;
|
face.Cullface = EnumFace::West;
|
||||||
else if(cullface == "east")
|
else if(cullface == "east")
|
||||||
face.Cullface = Cuboid::EnumFace::East;
|
face.Cullface = EnumFace::East;
|
||||||
else
|
else
|
||||||
MAKE_ERROR("Unknown face");
|
MAKE_ERROR("Unknown face");
|
||||||
}
|
}
|
||||||
@@ -1721,17 +1721,17 @@ PreparedModel::PreparedModel(const std::string_view modid, const js::object& pro
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(key == "x")
|
if(key == "x")
|
||||||
result.Transformations.emplace_back(Cuboid::Transformation::MoveX, f_value);
|
result.Transformations.emplace_back(Transformation::MoveX, f_value);
|
||||||
else if(key == "y")
|
else if(key == "y")
|
||||||
result.Transformations.emplace_back(Cuboid::Transformation::MoveY, f_value);
|
result.Transformations.emplace_back(Transformation::MoveY, f_value);
|
||||||
else if(key == "z")
|
else if(key == "z")
|
||||||
result.Transformations.emplace_back(Cuboid::Transformation::MoveZ, f_value);
|
result.Transformations.emplace_back(Transformation::MoveZ, f_value);
|
||||||
else if(key == "rx")
|
else if(key == "rx")
|
||||||
result.Transformations.emplace_back(Cuboid::Transformation::RotateX, f_value);
|
result.Transformations.emplace_back(Transformation::RotateX, f_value);
|
||||||
else if(key == "ry")
|
else if(key == "ry")
|
||||||
result.Transformations.emplace_back(Cuboid::Transformation::RotateY, f_value);
|
result.Transformations.emplace_back(Transformation::RotateY, f_value);
|
||||||
else if(key == "rz")
|
else if(key == "rz")
|
||||||
result.Transformations.emplace_back(Cuboid::Transformation::RotateZ, f_value);
|
result.Transformations.emplace_back(Transformation::RotateZ, f_value);
|
||||||
else
|
else
|
||||||
MAKE_ERROR("Неизвестный ключ трансформации");
|
MAKE_ERROR("Неизвестный ключ трансформации");
|
||||||
}
|
}
|
||||||
@@ -1813,7 +1813,17 @@ PreparedModel::PreparedModel(const std::u8string& data) {
|
|||||||
for(int counter = 0; counter < size; counter++) {
|
for(int counter = 0; counter < size; counter++) {
|
||||||
std::string tkey, pipeline;
|
std::string tkey, pipeline;
|
||||||
lr >> tkey >> pipeline;
|
lr >> tkey >> pipeline;
|
||||||
Textures.insert({tkey, pipeline});
|
TexturePipeline pipe;
|
||||||
|
|
||||||
|
uint16_t size;
|
||||||
|
lr >> size;
|
||||||
|
pipe.BinTextures.reserve(size);
|
||||||
|
for(int iter = 0; iter < size; iter++)
|
||||||
|
pipe.BinTextures.push_back(lr.read<ResourceId>());
|
||||||
|
|
||||||
|
lr >> (std::string&) pipe.Pipeline;
|
||||||
|
|
||||||
|
CompiledTextures.insert({tkey, std::move(pipe)});
|
||||||
}
|
}
|
||||||
|
|
||||||
lr >> size;
|
lr >> size;
|
||||||
@@ -1843,12 +1853,12 @@ PreparedModel::PreparedModel(const std::u8string& data) {
|
|||||||
lr >> face.Texture;
|
lr >> face.Texture;
|
||||||
uint8_t val = lr.read<uint8_t>();
|
uint8_t val = lr.read<uint8_t>();
|
||||||
if(val != uint8_t(-1)) {
|
if(val != uint8_t(-1)) {
|
||||||
face.Cullface = Cuboid::EnumFace(val);
|
face.Cullface = EnumFace(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
lr >> face.TintIndex >> face.Rotation;
|
lr >> face.TintIndex >> face.Rotation;
|
||||||
|
|
||||||
cuboid.Faces.insert({(Cuboid::EnumFace) type, face});
|
cuboid.Faces.insert({(EnumFace) type, face});
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t transformationsSize;
|
uint16_t transformationsSize;
|
||||||
@@ -1856,8 +1866,8 @@ PreparedModel::PreparedModel(const std::u8string& data) {
|
|||||||
cuboid.Transformations.reserve(transformationsSize);
|
cuboid.Transformations.reserve(transformationsSize);
|
||||||
|
|
||||||
for(int counter2 = 0; counter2 < transformationsSize; counter2++) {
|
for(int counter2 = 0; counter2 < transformationsSize; counter2++) {
|
||||||
Cuboid::Transformation tsf;
|
Transformation tsf;
|
||||||
tsf.Op = (Cuboid::Transformation::EnumTransform) lr.read<uint8_t>();
|
tsf.Op = (Transformation::EnumTransform) lr.read<uint8_t>();
|
||||||
lr >> tsf.Value;
|
lr >> tsf.Value;
|
||||||
cuboid.Transformations.emplace_back(tsf);
|
cuboid.Transformations.emplace_back(tsf);
|
||||||
}
|
}
|
||||||
@@ -1882,6 +1892,8 @@ PreparedModel::PreparedModel(const std::u8string& data) {
|
|||||||
std::u8string PreparedModel::dump() const {
|
std::u8string PreparedModel::dump() const {
|
||||||
Net::Packet result;
|
Net::Packet result;
|
||||||
|
|
||||||
|
result << 'b' << 'm';
|
||||||
|
|
||||||
if(GuiLight.has_value()) {
|
if(GuiLight.has_value()) {
|
||||||
result << uint8_t(1);
|
result << uint8_t(1);
|
||||||
result << uint8_t(GuiLight.value());
|
result << uint8_t(GuiLight.value());
|
||||||
@@ -1916,12 +1928,19 @@ std::u8string PreparedModel::dump() const {
|
|||||||
assert(Textures.size() < (1 << 16));
|
assert(Textures.size() < (1 << 16));
|
||||||
result << uint16_t(Textures.size());
|
result << uint16_t(Textures.size());
|
||||||
|
|
||||||
for(const auto& [tkey, dk] : Textures) {
|
assert(CompiledTextures.size() == Textures.size());
|
||||||
|
|
||||||
|
for(const auto& [tkey, dk] : CompiledTextures) {
|
||||||
assert(tkey.size() < 32);
|
assert(tkey.size() < 32);
|
||||||
result << tkey;
|
result << tkey;
|
||||||
|
|
||||||
assert(dk.size() < 512);
|
assert(dk.BinTextures.size() < 512);
|
||||||
result << dk;
|
result << uint16_t(dk.BinTextures.size());
|
||||||
|
for(size_t iter = 0; iter < dk.BinTextures.size(); iter++) {
|
||||||
|
result << dk.BinTextures[iter];
|
||||||
|
}
|
||||||
|
|
||||||
|
result << (const std::string&) dk.Pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(Cuboids.size() < (1 << 16));
|
assert(Cuboids.size() < (1 << 16));
|
||||||
@@ -1976,6 +1995,36 @@ std::u8string PreparedModel::dump() const {
|
|||||||
return result.complite();
|
return result.complite();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PreparedGLTF::PreparedGLTF(const std::string_view modid, const js::object& gltf) {
|
||||||
|
// gltf
|
||||||
|
|
||||||
|
// Сцена по умолчанию
|
||||||
|
// Сцены -> Ноды
|
||||||
|
// Ноды -> Ноды, меши, матрицы, translation, rotation
|
||||||
|
// Меши -> Примитивы
|
||||||
|
// Примитивы -> Материал, вершинные данные
|
||||||
|
// Материалы -> текстуры
|
||||||
|
// Текстуры
|
||||||
|
// Буферы
|
||||||
|
}
|
||||||
|
|
||||||
|
PreparedGLTF::PreparedGLTF(const std::string_view modid, Resource glb) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PreparedGLTF::PreparedGLTF(std::u8string_view data) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::u8string PreparedGLTF::dump() const {
|
||||||
|
std::unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PreparedGLTF::load(std::u8string_view data) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
struct Resource::InlineMMap {
|
struct Resource::InlineMMap {
|
||||||
boost::interprocess::file_mapping MMap;
|
boost::interprocess::file_mapping MMap;
|
||||||
boost::interprocess::mapped_region Region;
|
boost::interprocess::mapped_region Region;
|
||||||
|
|||||||
@@ -391,6 +391,7 @@ struct Object_t {
|
|||||||
|
|
||||||
|
|
||||||
using ResourceId = uint32_t;
|
using ResourceId = uint32_t;
|
||||||
|
struct Resource;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Объекты, собранные из папки assets или зарегистрированные модами.
|
Объекты, собранные из папки assets или зарегистрированные модами.
|
||||||
@@ -495,6 +496,43 @@ void unCompressNodes(const std::u8string& compressed, Node* ptr);
|
|||||||
std::u8string compressLinear(const std::u8string& data);
|
std::u8string compressLinear(const std::u8string& data);
|
||||||
std::u8string unCompressLinear(const std::u8string& data);
|
std::u8string unCompressLinear(const std::u8string& data);
|
||||||
|
|
||||||
|
inline std::pair<std::string, std::string> parseDomainKey(const std::string& value, const std::string_view defaultDomain = "core") {
|
||||||
|
auto regResult = TOS::Str::match(value, "(?:([\\w\\d_]+):)?([\\w\\d/_.]+)");
|
||||||
|
if(!regResult)
|
||||||
|
MAKE_ERROR("Недействительный домен:ключ");
|
||||||
|
|
||||||
|
if(regResult->at(1)) {
|
||||||
|
return std::pair<std::string, std::string>{*regResult->at(1), *regResult->at(2)};
|
||||||
|
} else {
|
||||||
|
return std::pair<std::string, std::string>{defaultDomain, *regResult->at(2)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PrecompiledTexturePipeline {
|
||||||
|
// Локальные идентификаторы пайплайна в домен+ключ
|
||||||
|
std::vector<std::pair<std::string, std::string>> Assets;
|
||||||
|
// Чистый код текстурных преобразований, локальные идентификаторы связаны с Assets
|
||||||
|
std::u8string Pipeline;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TexturePipeline {
|
||||||
|
// Разыменованые идентификаторы
|
||||||
|
std::vector<AssetsTexture> BinTextures;
|
||||||
|
// Чистый код текстурных преобразований, локальные идентификаторы связаны с BinTextures
|
||||||
|
std::u8string Pipeline;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Компилятор текстурных потоков
|
||||||
|
inline PrecompiledTexturePipeline compileTexturePipeline(const std::string &cmd, const std::string_view defaultDomain = "core") {
|
||||||
|
PrecompiledTexturePipeline result;
|
||||||
|
|
||||||
|
auto [domain, key] = parseDomainKey(cmd, defaultDomain);
|
||||||
|
|
||||||
|
result.Assets.emplace_back(domain, key);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
struct NodestateEntry {
|
struct NodestateEntry {
|
||||||
std::string Name;
|
std::string Name;
|
||||||
int Variability = 0; // Количество возможный значений состояния
|
int Variability = 0; // Количество возможный значений состояния
|
||||||
@@ -585,6 +623,22 @@ private:
|
|||||||
std::vector<Transformation> parseTransormations(const js::array& arr);
|
std::vector<Transformation> parseTransormations(const js::array& arr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum class EnumFace {
|
||||||
|
Down, Up, North, South, West, East, None
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Transformation {
|
||||||
|
enum EnumTransform {
|
||||||
|
MoveX, MoveY, MoveZ,
|
||||||
|
RotateX, RotateY, RotateZ,
|
||||||
|
ScaleX, ScaleY, ScaleZ,
|
||||||
|
MAX_ENUM
|
||||||
|
} Op;
|
||||||
|
|
||||||
|
float Value;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Парсит json модель
|
Парсит json модель
|
||||||
*/
|
*/
|
||||||
@@ -604,16 +658,13 @@ struct PreparedModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::unordered_map<std::string, FullTransformation> Display;
|
std::unordered_map<std::string, FullTransformation> Display;
|
||||||
std::unordered_map<std::string, std::string> Textures;
|
std::unordered_map<std::string, PrecompiledTexturePipeline> Textures;
|
||||||
|
std::unordered_map<std::string, TexturePipeline> CompiledTextures;
|
||||||
|
|
||||||
struct Cuboid {
|
struct Cuboid {
|
||||||
bool Shade;
|
bool Shade;
|
||||||
glm::vec3 From, To;
|
glm::vec3 From, To;
|
||||||
|
|
||||||
enum class EnumFace {
|
|
||||||
Down, Up, North, South, West, East
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Face {
|
struct Face {
|
||||||
glm::vec4 UV;
|
glm::vec4 UV;
|
||||||
std::string Texture;
|
std::string Texture;
|
||||||
@@ -623,16 +674,6 @@ struct PreparedModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::unordered_map<EnumFace, Face> Faces;
|
std::unordered_map<EnumFace, Face> Faces;
|
||||||
|
|
||||||
struct Transformation {
|
|
||||||
enum EnumTransform {
|
|
||||||
MoveX, MoveY, MoveZ,
|
|
||||||
RotateX, RotateY, RotateZ,
|
|
||||||
MAX_ENUM
|
|
||||||
} Op;
|
|
||||||
|
|
||||||
float Value;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Transformation> Transformations;
|
std::vector<Transformation> Transformations;
|
||||||
};
|
};
|
||||||
@@ -662,7 +703,38 @@ struct PreparedModel {
|
|||||||
std::u8string dump() const;
|
std::u8string dump() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool load(const std::u8string& data) noexcept;
|
void load(std::u8string_view data);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
glm::vec3 Pos;
|
||||||
|
glm::vec2 UV;
|
||||||
|
uint32_t TexId;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PreparedGLTF {
|
||||||
|
std::vector<std::string> TextureKey;
|
||||||
|
std::unordered_map<std::string, PrecompiledTexturePipeline> Textures;
|
||||||
|
std::unordered_map<std::string, TexturePipeline> CompiledTextures;
|
||||||
|
std::vector<Vertex> Vertices;
|
||||||
|
|
||||||
|
|
||||||
|
PreparedGLTF(const std::string_view modid, const js::object& gltf);
|
||||||
|
PreparedGLTF(const std::string_view modid, Resource glb);
|
||||||
|
PreparedGLTF(std::u8string_view data);
|
||||||
|
|
||||||
|
PreparedGLTF() = default;
|
||||||
|
PreparedGLTF(const PreparedGLTF&) = default;
|
||||||
|
PreparedGLTF(PreparedGLTF&&) = default;
|
||||||
|
|
||||||
|
PreparedGLTF& operator=(const PreparedGLTF&) = default;
|
||||||
|
PreparedGLTF& operator=(PreparedGLTF&&) = default;
|
||||||
|
|
||||||
|
// Пишет в сжатый двоичный формат
|
||||||
|
std::u8string dump() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void load(std::u8string_view data);
|
||||||
};
|
};
|
||||||
|
|
||||||
enum struct TexturePipelineCMD : uint8_t {
|
enum struct TexturePipelineCMD : uint8_t {
|
||||||
@@ -672,43 +744,6 @@ enum struct TexturePipelineCMD : uint8_t {
|
|||||||
|
|
||||||
using Hash_t = std::array<uint8_t, 32>;
|
using Hash_t = std::array<uint8_t, 32>;
|
||||||
|
|
||||||
inline std::pair<std::string, std::string> parseDomainKey(const std::string& value, const std::string_view defaultDomain = "core") {
|
|
||||||
auto regResult = TOS::Str::match(value, "(?:([\\w\\d_]+):)?([\\w\\d/_.]+)");
|
|
||||||
if(!regResult)
|
|
||||||
MAKE_ERROR("Недействительный домен:ключ");
|
|
||||||
|
|
||||||
if(regResult->at(1)) {
|
|
||||||
return std::pair<std::string, std::string>{*regResult->at(1), *regResult->at(2)};
|
|
||||||
} else {
|
|
||||||
return std::pair<std::string, std::string>{defaultDomain, *regResult->at(2)};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PrecompiledTexturePipeline {
|
|
||||||
// Локальные идентификаторы пайплайна в домен+ключ
|
|
||||||
std::vector<std::pair<std::string, std::string>> Assets;
|
|
||||||
// Чистый код текстурных преобразований, локальные идентификаторы связаны с Assets
|
|
||||||
std::u8string Pipeline;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TexturePipeline {
|
|
||||||
// Разыменованые идентификаторы
|
|
||||||
std::vector<AssetsTexture> BinTextures;
|
|
||||||
// Чистый код текстурных преобразований, локальные идентификаторы связаны с BinTextures
|
|
||||||
std::u8string Pipeline;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Компилятор текстурных потоков
|
|
||||||
inline PrecompiledTexturePipeline compileTexturePipeline(const std::string &cmd, const std::string_view defaultDomain = "core") {
|
|
||||||
PrecompiledTexturePipeline result;
|
|
||||||
|
|
||||||
auto [domain, key] = parseDomainKey(cmd, defaultDomain);
|
|
||||||
|
|
||||||
result.Assets.emplace_back(domain, key);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Resource {
|
struct Resource {
|
||||||
private:
|
private:
|
||||||
struct InlineMMap;
|
struct InlineMMap;
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ PreparedModel::PreparedModel(const std::string& domain, const LV::PreparedModel&
|
|||||||
Cuboids.reserve(model.Cuboids.size());
|
Cuboids.reserve(model.Cuboids.size());
|
||||||
|
|
||||||
for(auto& [key, cmd] : model.Textures) {
|
for(auto& [key, cmd] : model.Textures) {
|
||||||
PrecompiledTexturePipeline ptp = compileTexturePipeline(cmd, domain);
|
for(auto& [domain, key] : cmd.Assets) {
|
||||||
for(auto& [domain, key] : ptp.Assets) {
|
|
||||||
TextureDependencies[domain].push_back(key);
|
TextureDependencies[domain].push_back(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,20 +41,7 @@ PreparedModel::PreparedModel(const std::string& domain, const LV::PreparedModel&
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
PreparedModel::PreparedModel(const std::string& domain, const js::object& glTF) {
|
PreparedModel::PreparedModel(const std::string& domain, const PreparedGLTF& glTF) {
|
||||||
// gltf
|
|
||||||
|
|
||||||
// Сцена по умолчанию
|
|
||||||
// Сцены -> Ноды
|
|
||||||
// Ноды -> Ноды, меши, матрицы, translation, rotation
|
|
||||||
// Меши -> Примитивы
|
|
||||||
// Примитивы -> Материал, вершинные данные
|
|
||||||
// Материалы -> текстуры
|
|
||||||
// Текстуры
|
|
||||||
// Буферы
|
|
||||||
}
|
|
||||||
|
|
||||||
PreparedModel::PreparedModel(const std::string& domain, Resource glb) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,32 +96,24 @@ void AssetsManager::loadResourceFromFile_Model(ResourceChangeObj& out, const std
|
|||||||
json, glTF, glB
|
json, glTF, glB
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Либо это внутренний формат, либо glTF
|
|
||||||
|
|
||||||
Resource res(path);
|
Resource res(path);
|
||||||
std::filesystem::file_time_type ftt = fs::last_write_time(path);
|
std::filesystem::file_time_type ftt = fs::last_write_time(path);
|
||||||
PreparedModel pmc;
|
|
||||||
|
|
||||||
auto extension = path.extension();
|
auto extension = path.extension();
|
||||||
|
|
||||||
if(extension == ".json") {
|
if(extension == ".json") {
|
||||||
js::object obj = js::parse(std::string_view((const char*) res.data(), res.size())).as_object();
|
js::object obj = js::parse(std::string_view((const char*) res.data(), res.size())).as_object();
|
||||||
LV::PreparedModel pm(domain, obj);
|
LV::PreparedModel pm(domain, obj);
|
||||||
std::u8string data = pm.dump();
|
out.NewOrChange_Models[domain].emplace_back(key, std::move(pm), ftt);
|
||||||
pmc = PreparedModel(domain, pm);
|
|
||||||
out.NewOrChange[(int) EnumAssets::Model][domain].emplace_back(key, Resource((const uint8_t*) data.data(), data.size()), ftt);
|
|
||||||
} else if(extension == ".gltf") {
|
} else if(extension == ".gltf") {
|
||||||
js::object obj = js::parse(std::string_view((const char*) res.data(), res.size())).as_object();
|
js::object obj = js::parse(std::string_view((const char*) res.data(), res.size())).as_object();
|
||||||
pmc = PreparedModel(domain, obj);
|
PreparedGLTF gltf(domain, obj);
|
||||||
out.NewOrChange[(int) EnumAssets::Model][domain].emplace_back(key, res, ftt);
|
out.NewOrChange_Models[domain].emplace_back(key, std::move(gltf), ftt);
|
||||||
} else if(extension == ".glb") {
|
} else if(extension == ".glb") {
|
||||||
pmc = PreparedModel(domain, res);
|
PreparedGLTF gltf(domain, res);
|
||||||
out.NewOrChange[(int) EnumAssets::Model][domain].emplace_back(key, res, ftt);
|
out.NewOrChange_Models[domain].emplace_back(key, std::move(gltf), ftt);
|
||||||
} else {
|
} else {
|
||||||
MAKE_ERROR("Не поддерживаемый формат файла");
|
MAKE_ERROR("Не поддерживаемый формат файла");
|
||||||
}
|
}
|
||||||
|
|
||||||
out.Models[domain].emplace_back(key, pmc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsManager::loadResourceFromFile_Texture(ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const {
|
void AssetsManager::loadResourceFromFile_Texture(ResourceChangeObj& out, const std::string& domain, const std::string& key, fs::path path) const {
|
||||||
@@ -513,6 +491,110 @@ AssetsManager::Out_applyResourceChange AssetsManager::applyResourceChange(const
|
|||||||
std::set_difference(l.begin(), l.end(), noc.begin(), noc.end(), std::back_inserter(result.Lost[type]));
|
std::set_difference(l.begin(), l.end(), noc.begin(), noc.end(), std::back_inserter(result.Lost[type]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!orr.Nodestates.empty())
|
||||||
|
{
|
||||||
|
auto lock = LocalObj.lock();
|
||||||
|
for(auto& [domain, table] : orr.Nodestates) {
|
||||||
|
for(auto& [key, value] : table) {
|
||||||
|
ResourceId resId = lock->getId(EnumAssets::Nodestate, domain, key);
|
||||||
|
|
||||||
|
std::vector<AssetsModel> models;
|
||||||
|
|
||||||
|
for(auto& [domain2, key2] : value.ModelToLocalId) {
|
||||||
|
models.push_back(lock->getId(EnumAssets::Model, domain2, key2));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::sort(models.begin(), models.end());
|
||||||
|
auto iterErase = std::unique(models.begin(), models.end());
|
||||||
|
models.erase(iterErase, models.end());
|
||||||
|
models.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
lock->Table_NodeState[resId / TableEntry<DataEntry>::ChunkSize]
|
||||||
|
->Entries[resId % TableEntry<DataEntry>::ChunkSize] = std::move(models);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Приёмка новых/изменённых моделей
|
||||||
|
if(!orr.NewOrChange_Models.empty())
|
||||||
|
{
|
||||||
|
auto lock = LocalObj.lock();
|
||||||
|
for(auto& [domain, table] : orr.NewOrChange_Models) {
|
||||||
|
auto& keyToIdDomain = lock->KeyToId[(int) EnumAssets::Model][domain];
|
||||||
|
|
||||||
|
for(auto& [key, _model, ftt] : table) {
|
||||||
|
ResourceId resId = -1;
|
||||||
|
std::optional<DataEntry>* data = nullptr;
|
||||||
|
|
||||||
|
if(auto iterId = keyToIdDomain.find(key); iterId != keyToIdDomain.end()) {
|
||||||
|
resId = iterId->second;
|
||||||
|
data = &lock->Table[(int) EnumAssets::Model][resId / TableEntry<DataEntry>::ChunkSize]->Entries[resId % TableEntry<DataEntry>::ChunkSize];
|
||||||
|
} else {
|
||||||
|
auto [_id, _data] = lock->nextId((EnumAssets) EnumAssets::Model);
|
||||||
|
resId = _id;
|
||||||
|
data = &_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
keyToIdDomain[key] = resId;
|
||||||
|
|
||||||
|
// Ресолвим текстуры
|
||||||
|
std::variant<LV::PreparedModel, PreparedGLTF> model = _model;
|
||||||
|
std::visit([&lock](auto& val) {
|
||||||
|
for(const auto& [key, pipeline] : val.Textures) {
|
||||||
|
TexturePipeline pipe;
|
||||||
|
pipe.Pipeline = pipeline.Pipeline;
|
||||||
|
|
||||||
|
for(const auto& [domain, key] : pipeline.Assets) {
|
||||||
|
ResourceId texId = lock->getId(EnumAssets::Texture, domain, key);
|
||||||
|
pipe.BinTextures.push_back(texId);
|
||||||
|
}
|
||||||
|
|
||||||
|
val.CompiledTextures[key] = std::move(pipe);
|
||||||
|
}
|
||||||
|
}, model);
|
||||||
|
|
||||||
|
// Сдампим для отправки клиенту (Кеш в пролёте?)
|
||||||
|
std::u8string dump = std::visit<std::u8string>([&lock](auto& val) {
|
||||||
|
return val.dump();
|
||||||
|
}, model);
|
||||||
|
Resource res(std::move(dump));
|
||||||
|
|
||||||
|
// На оповещение
|
||||||
|
result.NewOrChange[(int) EnumAssets::Model].push_back({resId, res});
|
||||||
|
|
||||||
|
// Запись в таблице ресурсов
|
||||||
|
data->emplace(ftt, res, domain, key);
|
||||||
|
|
||||||
|
lock->HashToId[res.hash()] = {EnumAssets::Model, resId};
|
||||||
|
|
||||||
|
// Для нужд сервера, ресолвим зависимости
|
||||||
|
PreparedModel pm = std::visit<PreparedModel>([&domain](auto& val) {
|
||||||
|
return PreparedModel(domain, val);
|
||||||
|
}, model);
|
||||||
|
|
||||||
|
ModelDependency deps;
|
||||||
|
for(auto& [domain2, list] : pm.ModelDependencies) {
|
||||||
|
for(const std::string& key2 : list) {
|
||||||
|
ResourceId subResId = lock->getId(EnumAssets::Model, domain2, key2);
|
||||||
|
deps.ModelDeps.push_back(subResId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto& [domain2, list] : pm.TextureDependencies) {
|
||||||
|
for(const std::string& key2 : list) {
|
||||||
|
ResourceId subResId = lock->getId(EnumAssets::Texture, domain2, key2);
|
||||||
|
deps.TextureDeps.push_back(subResId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lock->Table_Model[resId / TableEntry<DataEntry>::ChunkSize]
|
||||||
|
->Entries[resId % TableEntry<DataEntry>::ChunkSize] = std::move(deps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Дамп ключей assets
|
// Дамп ключей assets
|
||||||
{
|
{
|
||||||
std::stringstream result;
|
std::stringstream result;
|
||||||
@@ -546,60 +628,6 @@ AssetsManager::Out_applyResourceChange AssetsManager::applyResourceChange(const
|
|||||||
LOG.debug() << "Дамп ассетов:\n" << result.str();
|
LOG.debug() << "Дамп ассетов:\n" << result.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!orr.Nodestates.empty())
|
|
||||||
{
|
|
||||||
auto lock = LocalObj.lock();
|
|
||||||
for(auto& [domain, table] : orr.Nodestates) {
|
|
||||||
for(auto& [key, value] : table) {
|
|
||||||
ResourceId resId = lock->getId(EnumAssets::Nodestate, domain, key);
|
|
||||||
|
|
||||||
std::vector<AssetsModel> models;
|
|
||||||
|
|
||||||
for(auto& [domain2, key2] : value.ModelToLocalId) {
|
|
||||||
models.push_back(lock->getId(EnumAssets::Model, domain2, key2));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
std::sort(models.begin(), models.end());
|
|
||||||
auto iterErase = std::unique(models.begin(), models.end());
|
|
||||||
models.erase(iterErase, models.end());
|
|
||||||
models.shrink_to_fit();
|
|
||||||
}
|
|
||||||
|
|
||||||
lock->Table_NodeState[resId / TableEntry<DataEntry>::ChunkSize]
|
|
||||||
->Entries[resId % TableEntry<DataEntry>::ChunkSize] = std::move(models);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!orr.Models.empty())
|
|
||||||
{
|
|
||||||
auto lock = LocalObj.lock();
|
|
||||||
for(auto& [domain, table] : orr.Models) {
|
|
||||||
for(auto& [key, value] : table) {
|
|
||||||
ResourceId resId = lock->getId(EnumAssets::Model, domain, key);
|
|
||||||
|
|
||||||
ModelDependency deps;
|
|
||||||
for(auto& [domain2, list] : value.ModelDependencies) {
|
|
||||||
for(const std::string& key2 : list) {
|
|
||||||
ResourceId subResId = lock->getId(EnumAssets::Model, domain2, key2);
|
|
||||||
deps.ModelDeps.push_back(subResId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto& [domain2, list] : value.TextureDependencies) {
|
|
||||||
for(const std::string& key2 : list) {
|
|
||||||
ResourceId subResId = lock->getId(EnumAssets::Texture, domain2, key2);
|
|
||||||
deps.TextureDeps.push_back(subResId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lock->Table_Model[resId / TableEntry<DataEntry>::ChunkSize]
|
|
||||||
->Entries[resId % TableEntry<DataEntry>::ChunkSize] = std::move(deps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Вычислить зависимости моделей
|
// Вычислить зависимости моделей
|
||||||
{
|
{
|
||||||
// Затираем старые данные
|
// Затираем старые данные
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ struct PreparedModel {
|
|||||||
std::unordered_map<std::string, std::vector<std::string>> ModelDependencies;
|
std::unordered_map<std::string, std::vector<std::string>> ModelDependencies;
|
||||||
|
|
||||||
PreparedModel(const std::string& domain, const LV::PreparedModel& model);
|
PreparedModel(const std::string& domain, const LV::PreparedModel& model);
|
||||||
PreparedModel(const std::string& domain, const js::object& glTF);
|
PreparedModel(const std::string& domain, const PreparedGLTF& glTF);
|
||||||
PreparedModel(const std::string& domain, Resource glb);
|
|
||||||
|
|
||||||
PreparedModel() = default;
|
PreparedModel() = default;
|
||||||
PreparedModel(const PreparedModel&) = default;
|
PreparedModel(const PreparedModel&) = default;
|
||||||
@@ -66,10 +65,13 @@ public:
|
|||||||
std::unordered_map<std::string, std::vector<std::string>> Lost[(int) EnumAssets::MAX_ENUM];
|
std::unordered_map<std::string, std::vector<std::string>> Lost[(int) EnumAssets::MAX_ENUM];
|
||||||
// Домен и ключ ресурса
|
// Домен и ключ ресурса
|
||||||
std::unordered_map<std::string, std::vector<std::tuple<std::string, Resource, fs::file_time_type>>> NewOrChange[(int) EnumAssets::MAX_ENUM];
|
std::unordered_map<std::string, std::vector<std::tuple<std::string, Resource, fs::file_time_type>>> NewOrChange[(int) EnumAssets::MAX_ENUM];
|
||||||
|
std::unordered_map<std::string, std::vector<std::tuple<std::string, std::variant<
|
||||||
|
LV::PreparedModel,
|
||||||
|
PreparedGLTF
|
||||||
|
>, fs::file_time_type>>> NewOrChange_Models;
|
||||||
|
|
||||||
std::unordered_map<std::string, std::vector<std::pair<std::string, PreparedNodeState>>> Nodestates;
|
std::unordered_map<std::string, std::vector<std::pair<std::string, PreparedNodeState>>> Nodestates;
|
||||||
std::unordered_map<std::string, std::vector<std::pair<std::string, PreparedModel>>> Models;
|
// std::unordered_map<std::string, std::vector<std::pair<std::string, PreparedModel>>> Models;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
Reference in New Issue
Block a user