Состояния нод на стороне сервера
This commit is contained in:
@@ -873,11 +873,11 @@ PreparedNodeState::PreparedNodeState(const std::u8string& data) {
|
|||||||
uint16_t size;
|
uint16_t size;
|
||||||
lr >> size;
|
lr >> size;
|
||||||
|
|
||||||
ResourceToLocalId.reserve(size);
|
ModelToLocalId.reserve(size);
|
||||||
for(int counter = 0; counter < size; counter++) {
|
for(int counter = 0; counter < size; counter++) {
|
||||||
std::string domain, key;
|
std::string domain, key;
|
||||||
lr >> domain >> key;
|
lr >> domain >> key;
|
||||||
ResourceToLocalId.emplace_back(std::move(domain), std::move(key));
|
ModelToLocalId.emplace_back(std::move(domain), std::move(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
lr >> size;
|
lr >> size;
|
||||||
@@ -987,10 +987,10 @@ std::u8string PreparedNodeState::dump() const {
|
|||||||
Net::Packet result;
|
Net::Packet result;
|
||||||
|
|
||||||
// ResourceToLocalId
|
// ResourceToLocalId
|
||||||
assert(ResourceToLocalId.size() < (1 << 16));
|
assert(ModelToLocalId.size() < (1 << 16));
|
||||||
result << uint16_t(ResourceToLocalId.size());
|
result << uint16_t(ModelToLocalId.size());
|
||||||
|
|
||||||
for(const auto& [domain, key] : ResourceToLocalId) {
|
for(const auto& [domain, key] : ModelToLocalId) {
|
||||||
assert(domain.size() < 32);
|
assert(domain.size() < 32);
|
||||||
result << domain;
|
result << domain;
|
||||||
assert(key.size() < 32);
|
assert(key.size() < 32);
|
||||||
@@ -1421,7 +1421,7 @@ uint16_t PreparedNodeState::parseCondition(const std::string_view expression) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::pair<float, std::variant<PreparedNodeState::Model, PreparedNodeState::VectorModel>> PreparedNodeState::parseModel(const std::string_view modid, const js::object& obj) {
|
std::pair<float, std::variant<PreparedNodeState::Model, PreparedNodeState::VectorModel>> PreparedNodeState::parseModel(const std::string_view modid, const js::object& obj) {
|
||||||
// ResourceToLocalId
|
// ModelToLocalId
|
||||||
|
|
||||||
bool uvlock;
|
bool uvlock;
|
||||||
float weight = 1;
|
float weight = 1;
|
||||||
@@ -1449,15 +1449,15 @@ std::pair<float, std::variant<PreparedNodeState::Model, PreparedNodeState::Vecto
|
|||||||
auto [domain, key] = parseDomainKey((std::string) *model_key, modid);
|
auto [domain, key] = parseDomainKey((std::string) *model_key, modid);
|
||||||
|
|
||||||
uint16_t resId = 0;
|
uint16_t resId = 0;
|
||||||
for(auto& [lDomain, lKey] : ResourceToLocalId) {
|
for(auto& [lDomain, lKey] : ModelToLocalId) {
|
||||||
if(lDomain == domain && lKey == key)
|
if(lDomain == domain && lKey == key)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
resId++;
|
resId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(resId == ResourceToLocalId.size()) {
|
if(resId == ModelToLocalId.size()) {
|
||||||
ResourceToLocalId.emplace_back(domain, key);
|
ModelToLocalId.emplace_back(domain, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Id = resId;
|
result.Id = resId;
|
||||||
@@ -1484,15 +1484,15 @@ std::pair<float, std::variant<PreparedNodeState::Model, PreparedNodeState::Vecto
|
|||||||
auto [domain, key] = parseDomainKey((std::string) js_obj.at("model").as_string(), modid);
|
auto [domain, key] = parseDomainKey((std::string) js_obj.at("model").as_string(), modid);
|
||||||
|
|
||||||
uint16_t resId = 0;
|
uint16_t resId = 0;
|
||||||
for(auto& [lDomain, lKey] : ResourceToLocalId) {
|
for(auto& [lDomain, lKey] : ModelToLocalId) {
|
||||||
if(lDomain == domain && lKey == key)
|
if(lDomain == domain && lKey == key)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
resId++;
|
resId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(resId == ResourceToLocalId.size()) {
|
if(resId == ModelToLocalId.size()) {
|
||||||
ResourceToLocalId.emplace_back(domain, key);
|
ModelToLocalId.emplace_back(domain, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
subModel.Id = resId;
|
subModel.Id = resId;
|
||||||
@@ -1598,8 +1598,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) {
|
||||||
auto [domain, key2] = parseDomainKey((const std::string) value.as_string(), modid);
|
Textures[key] = value.as_string();
|
||||||
Textures[key] = {domain, key2};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1807,9 +1806,9 @@ PreparedModel::PreparedModel(const std::u8string& data) {
|
|||||||
lr >> size;
|
lr >> size;
|
||||||
Textures.reserve(size);
|
Textures.reserve(size);
|
||||||
for(int counter = 0; counter < size; counter++) {
|
for(int counter = 0; counter < size; counter++) {
|
||||||
std::string tkey, domain, key;
|
std::string tkey, pipeline;
|
||||||
lr >> tkey >> domain >> key;
|
lr >> tkey >> pipeline;
|
||||||
Textures.insert({tkey, {std::move(domain), std::move(key)}});
|
Textures.insert({tkey, pipeline});
|
||||||
}
|
}
|
||||||
|
|
||||||
lr >> size;
|
lr >> size;
|
||||||
@@ -1916,11 +1915,8 @@ std::u8string PreparedModel::dump() const {
|
|||||||
assert(tkey.size() < 32);
|
assert(tkey.size() < 32);
|
||||||
result << tkey;
|
result << tkey;
|
||||||
|
|
||||||
assert(dk.first.size() < 32);
|
assert(dk.size() < 512);
|
||||||
result << dk.first;
|
result << dk;
|
||||||
|
|
||||||
assert(dk.second.size() < 32);
|
|
||||||
result << dk.second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(Cuboids.size() < (1 << 16));
|
assert(Cuboids.size() < (1 << 16));
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <glm/ext.hpp>
|
#include <glm/ext.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sol/forward.hpp>
|
#include <sol/forward.hpp>
|
||||||
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -494,13 +495,6 @@ 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);
|
||||||
|
|
||||||
enum struct TexturePipelineCMD : uint8_t {
|
|
||||||
Texture, // Указание текстуры
|
|
||||||
Combine, // Комбинирование
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct NodestateEntry {
|
struct NodestateEntry {
|
||||||
std::string Name;
|
std::string Name;
|
||||||
int Variability = 0; // Количество возможный значений состояния
|
int Variability = 0; // Количество возможный значений состояния
|
||||||
@@ -551,7 +545,7 @@ struct PreparedNodeState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Локальный идентификатор в именной ресурс
|
// Локальный идентификатор в именной ресурс
|
||||||
std::vector<std::pair<std::string, std::string>> ResourceToLocalId;
|
std::vector<std::pair<std::string, std::string>> ModelToLocalId;
|
||||||
// Ноды выражений
|
// Ноды выражений
|
||||||
std::vector<Node> Nodes;
|
std::vector<Node> Nodes;
|
||||||
// Условия -> вариации модели + веса
|
// Условия -> вариации модели + веса
|
||||||
@@ -578,6 +572,7 @@ struct PreparedNodeState {
|
|||||||
// Пишет в сжатый двоичный формат
|
// Пишет в сжатый двоичный формат
|
||||||
std::u8string dump() const;
|
std::u8string dump() const;
|
||||||
|
|
||||||
|
// Если зависит от случайного распределения по миру
|
||||||
bool hasVariability() const {
|
bool hasVariability() const {
|
||||||
return HasVariability;
|
return HasVariability;
|
||||||
}
|
}
|
||||||
@@ -609,7 +604,7 @@ struct PreparedModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::unordered_map<std::string, FullTransformation> Display;
|
std::unordered_map<std::string, FullTransformation> Display;
|
||||||
std::unordered_map<std::string, std::pair<std::string, std::string>> Textures;
|
std::unordered_map<std::string, std::string> Textures;
|
||||||
|
|
||||||
struct Cuboid {
|
struct Cuboid {
|
||||||
bool Shade;
|
bool Shade;
|
||||||
@@ -670,9 +665,9 @@ private:
|
|||||||
bool load(const std::u8string& data) noexcept;
|
bool load(const std::u8string& data) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TexturePipeline {
|
enum struct TexturePipelineCMD : uint8_t {
|
||||||
std::vector<AssetsTexture> BinTextures;
|
Texture, // Указание текстуры
|
||||||
std::u8string Pipeline;
|
Combine, // Комбинирование
|
||||||
};
|
};
|
||||||
|
|
||||||
using Hash_t = std::array<uint8_t, 32>;
|
using Hash_t = std::array<uint8_t, 32>;
|
||||||
@@ -689,6 +684,30 @@ inline std::pair<std::string, std::string> parseDomainKey(const std::string& val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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:
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "Common/Abstract.hpp"
|
#include "Common/Abstract.hpp"
|
||||||
#include "boost/json.hpp"
|
#include "boost/json.hpp"
|
||||||
#include "png++/rgb_pixel.hpp"
|
#include "png++/rgb_pixel.hpp"
|
||||||
|
#include <algorithm>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <png.h>
|
#include <png.h>
|
||||||
@@ -14,25 +15,34 @@
|
|||||||
|
|
||||||
namespace LV::Server {
|
namespace LV::Server {
|
||||||
|
|
||||||
PreparedModelCollision::PreparedModelCollision(const PreparedModel& model) {
|
PreparedModel::PreparedModel(const std::string& domain, const LV::PreparedModel& model) {
|
||||||
Cuboids.reserve(model.Cuboids.size());
|
Cuboids.reserve(model.Cuboids.size());
|
||||||
|
|
||||||
for(const PreparedModel::Cuboid& cuboid : model.Cuboids) {
|
for(auto& [key, cmd] : model.Textures) {
|
||||||
Cuboid result;
|
PrecompiledTexturePipeline ptp = compileTexturePipeline(cmd, domain);
|
||||||
result.From = cuboid.From;
|
for(auto& [domain, key] : ptp.Assets) {
|
||||||
result.To = cuboid.To;
|
TextureDependencies[domain].push_back(key);
|
||||||
result.Faces = 0;
|
}
|
||||||
|
|
||||||
for(const auto& [key, _] : cuboid.Faces)
|
|
||||||
result.Faces |= (1 << int(key));
|
|
||||||
|
|
||||||
result.Transformations = cuboid.Transformations;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SubModels = model.SubModels;
|
for(auto& sub : model.SubModels) {
|
||||||
|
ModelDependencies[sub.Domain].push_back(sub.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// for(const PreparedModel::Cuboid& cuboid : model.Cuboids) {
|
||||||
|
// Cuboid result;
|
||||||
|
// result.From = cuboid.From;
|
||||||
|
// result.To = cuboid.To;
|
||||||
|
// result.Faces = 0;
|
||||||
|
|
||||||
|
// for(const auto& [key, _] : cuboid.Faces)
|
||||||
|
// result.Faces |= (1 << int(key));
|
||||||
|
|
||||||
|
// result.Transformations = cuboid.Transformations;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
PreparedModelCollision::PreparedModelCollision(const std::string& domain, const js::object& glTF) {
|
PreparedModel::PreparedModel(const std::string& domain, const js::object& glTF) {
|
||||||
// gltf
|
// gltf
|
||||||
|
|
||||||
// Сцена по умолчанию
|
// Сцена по умолчанию
|
||||||
@@ -45,7 +55,7 @@ PreparedModelCollision::PreparedModelCollision(const std::string& domain, const
|
|||||||
// Буферы
|
// Буферы
|
||||||
}
|
}
|
||||||
|
|
||||||
PreparedModelCollision::PreparedModelCollision(const std::string& domain, Resource glb) {
|
PreparedModel::PreparedModel(const std::string& domain, Resource glb) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,26 +114,26 @@ void AssetsManager::loadResourceFromFile_Model(ResourceChangeObj& out, const std
|
|||||||
|
|
||||||
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;
|
||||||
|
|
||||||
if(path.extension() == "json") {
|
if(path.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();
|
||||||
PreparedModel pm(domain, obj);
|
LV::PreparedModel pm(domain, obj);
|
||||||
PreparedModelCollision pmc(pm);
|
|
||||||
std::u8string data = pm.dump();
|
std::u8string data = pm.dump();
|
||||||
out.Models[domain].emplace_back(key, pmc);
|
pmc = PreparedModel(domain, pm);
|
||||||
out.NewOrChange[(int) EnumAssets::Model][domain].emplace_back(key, Resource((const uint8_t*) data.data(), data.size()), ftt);
|
out.NewOrChange[(int) EnumAssets::Model][domain].emplace_back(key, Resource((const uint8_t*) data.data(), data.size()), ftt);
|
||||||
} else if(path.extension() == "gltf") {
|
} else if(path.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();
|
||||||
PreparedModelCollision pmc(domain, obj);
|
pmc = PreparedModel(domain, obj);
|
||||||
out.Models[domain].emplace_back(key, pmc);
|
|
||||||
out.NewOrChange[(int) EnumAssets::Model][domain].emplace_back(key, res, ftt);
|
out.NewOrChange[(int) EnumAssets::Model][domain].emplace_back(key, res, ftt);
|
||||||
} else if(path.extension() == "glb") {
|
} else if(path.extension() == "glb") {
|
||||||
PreparedModelCollision pmc(domain, res);
|
pmc = PreparedModel(domain, res);
|
||||||
out.Models[domain].emplace_back(key, pmc);
|
|
||||||
out.NewOrChange[(int) EnumAssets::Model][domain].emplace_back(key, res, ftt);
|
out.NewOrChange[(int) EnumAssets::Model][domain].emplace_back(key, res, 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 {
|
||||||
@@ -436,14 +446,21 @@ AssetsManager::Out_applyResourceChange AssetsManager::applyResourceChange(const
|
|||||||
assert(iter != keyToIdDomain.end());
|
assert(iter != keyToIdDomain.end());
|
||||||
|
|
||||||
ResourceId resId = iter->second;
|
ResourceId resId = iter->second;
|
||||||
// keyToIdDomain.erase(iter);
|
|
||||||
// lost[type].push_back(resId);
|
|
||||||
|
|
||||||
uint32_t localId = resId % TableEntry<DataEntry>::ChunkSize;
|
if(type == (int) EnumAssets::Nodestate) {
|
||||||
|
if(resId / TableEntry<PreparedNodeState>::ChunkSize < lock->Table_NodeState.size()) {
|
||||||
|
lock->Table_NodeState[resId / TableEntry<PreparedNodeState>::ChunkSize]
|
||||||
|
->Entries[resId % TableEntry<PreparedNodeState>::ChunkSize].reset();
|
||||||
|
}
|
||||||
|
} else if(type == (int) EnumAssets::Model) {
|
||||||
|
if(resId / TableEntry<ModelDependency>::ChunkSize < lock->Table_Model.size()) {
|
||||||
|
lock->Table_Model[resId / TableEntry<ModelDependency>::ChunkSize]
|
||||||
|
->Entries[resId % TableEntry<ModelDependency>::ChunkSize].reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto& chunk = lock->Table[type][resId / TableEntry<DataEntry>::ChunkSize];
|
auto& chunk = lock->Table[type][resId / TableEntry<DataEntry>::ChunkSize];
|
||||||
// chunk->IsFull = false;
|
chunk->Entries[resId % TableEntry<DataEntry>::ChunkSize].reset();
|
||||||
// chunk->Empty.set(localId);
|
|
||||||
chunk->Entries[localId].reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -484,6 +501,130 @@ 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& [domain, key] : value.ModelToLocalId) {
|
||||||
|
models.push_back(lock->getId(EnumAssets::Model, domain, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
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& [domain, list] : value.ModelDependencies) {
|
||||||
|
ResourceId subResId = lock->getId(EnumAssets::Model, domain, key);
|
||||||
|
deps.ModelDeps.push_back(subResId);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto& [domain, list] : value.TextureDependencies) {
|
||||||
|
ResourceId subResId = lock->getId(EnumAssets::Texture, domain, key);
|
||||||
|
deps.TextureDeps.push_back(subResId);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock->Table_Model[resId / TableEntry<DataEntry>::ChunkSize]
|
||||||
|
->Entries[resId % TableEntry<DataEntry>::ChunkSize] = std::move(deps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вычислить зависимости моделей
|
||||||
|
{
|
||||||
|
// Затираем старые данные
|
||||||
|
auto lock = LocalObj.lock();
|
||||||
|
for(auto& entriesChunk : lock->Table_Model) {
|
||||||
|
for(auto& entry : entriesChunk->Entries) {
|
||||||
|
if(!entry)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
entry->Ready = false;
|
||||||
|
entry->FullSubTextureDeps.clear();
|
||||||
|
entry->FullSubModelDeps.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вычисляем зависимости
|
||||||
|
std::function<void(AssetsModel resId, ModelDependency&)> calcDeps = [&](AssetsModel resId, ModelDependency& entry) {
|
||||||
|
for(AssetsModel subResId : entry.ModelDeps) {
|
||||||
|
auto& model = lock->Table_Model[subResId / TableEntry<ModelDependency>::ChunkSize]
|
||||||
|
->Entries[subResId % TableEntry<ModelDependency>::ChunkSize];
|
||||||
|
|
||||||
|
if(!model)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(!model->Ready)
|
||||||
|
calcDeps(subResId, *model);
|
||||||
|
|
||||||
|
if(std::binary_search(model->FullSubModelDeps.begin(), model->FullSubModelDeps.end(), resId)) {
|
||||||
|
// Циклическая зависимость
|
||||||
|
const auto object1 = lock->getResource(EnumAssets::Model, resId);
|
||||||
|
const auto object2 = lock->getResource(EnumAssets::Model, subResId);
|
||||||
|
assert(object1);
|
||||||
|
|
||||||
|
LOG.warn() << "В моделе " << std::get<1>(*object1) << ':' << std::get<2>(*object1)
|
||||||
|
<< " обнаружена циклическая зависимость с " << std::get<1>(*object2) << ':'
|
||||||
|
<< std::get<2>(*object2);
|
||||||
|
} else {
|
||||||
|
entry.FullSubTextureDeps.append_range(model->FullSubTextureDeps);
|
||||||
|
entry.FullSubModelDeps.push_back(subResId);
|
||||||
|
entry.FullSubModelDeps.append_range(model->FullSubModelDeps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::sort(entry.FullSubTextureDeps.begin(), entry.FullSubTextureDeps.end());
|
||||||
|
auto eraseIter = std::unique(entry.FullSubTextureDeps.begin(), entry.FullSubTextureDeps.end());
|
||||||
|
entry.FullSubTextureDeps.erase(eraseIter, entry.FullSubTextureDeps.end());
|
||||||
|
entry.FullSubTextureDeps.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::sort(entry.FullSubModelDeps.begin(), entry.FullSubModelDeps.end());
|
||||||
|
auto eraseIter = std::unique(entry.FullSubModelDeps.begin(), entry.FullSubModelDeps.end());
|
||||||
|
entry.FullSubModelDeps.erase(eraseIter, entry.FullSubModelDeps.end());
|
||||||
|
entry.FullSubModelDeps.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Ready = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
ssize_t iter = -1;
|
||||||
|
for(auto& entriesChunk : lock->Table_Model) {
|
||||||
|
for(auto& entry : entriesChunk->Entries) {
|
||||||
|
iter++;
|
||||||
|
|
||||||
|
if(!entry || entry->Ready)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Собираем зависимости
|
||||||
|
calcDeps(iter, *entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
|
|
||||||
@@ -17,31 +18,40 @@ namespace fs = std::filesystem;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Используется для расчёта коллизии,
|
Используется для расчёта коллизии,
|
||||||
если это необходимо.
|
если это необходимо, а также зависимостей к ассетам.
|
||||||
|
|
||||||
glTF конвертируется в кубы
|
|
||||||
*/
|
*/
|
||||||
struct PreparedModelCollision {
|
struct PreparedModel {
|
||||||
struct Cuboid {
|
// Упрощённая коллизия
|
||||||
glm::vec3 From, To;
|
std::vector<std::pair<glm::vec3, glm::vec3>> Cuboids;
|
||||||
uint8_t Faces;
|
// Зависимости от текстур, которые нужно сообщить клиенту
|
||||||
|
std::unordered_map<std::string, std::vector<std::string>> TextureDependencies;
|
||||||
|
// Зависимости от моделей
|
||||||
|
std::unordered_map<std::string, std::vector<std::string>> ModelDependencies;
|
||||||
|
|
||||||
std::vector<PreparedModel::Cuboid::Transformation> Transformations;
|
PreparedModel(const std::string& domain, const LV::PreparedModel& model);
|
||||||
};
|
PreparedModel(const std::string& domain, const js::object& glTF);
|
||||||
|
PreparedModel(const std::string& domain, Resource glb);
|
||||||
|
|
||||||
std::vector<Cuboid> Cuboids;
|
PreparedModel() = default;
|
||||||
std::vector<PreparedModel::SubModel> SubModels;
|
PreparedModel(const PreparedModel&) = default;
|
||||||
|
PreparedModel(PreparedModel&&) = default;
|
||||||
|
|
||||||
PreparedModelCollision(const PreparedModel& model);
|
PreparedModel& operator=(const PreparedModel&) = default;
|
||||||
PreparedModelCollision(const std::string& domain, const js::object& glTF);
|
PreparedModel& operator=(PreparedModel&&) = default;
|
||||||
PreparedModelCollision(const std::string& domain, Resource glb);
|
};
|
||||||
|
|
||||||
PreparedModelCollision() = default;
|
struct ModelDependency {
|
||||||
PreparedModelCollision(const PreparedModelCollision&) = default;
|
// Прямые зависимости к тестурам и моделям
|
||||||
PreparedModelCollision(PreparedModelCollision&&) = default;
|
std::vector<AssetsTexture> TextureDeps;
|
||||||
|
std::vector<AssetsModel> ModelDeps;
|
||||||
|
// Коллизия
|
||||||
|
std::vector<std::pair<glm::vec3, glm::vec3>> Cuboids;
|
||||||
|
|
||||||
PreparedModelCollision& operator=(const PreparedModelCollision&) = default;
|
//
|
||||||
PreparedModelCollision& operator=(PreparedModelCollision&&) = default;
|
bool Ready = false;
|
||||||
|
// Полный список зависимостей рекурсивно
|
||||||
|
std::vector<AssetsTexture> FullSubTextureDeps;
|
||||||
|
std::vector<AssetsModel> FullSubModelDeps;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -59,7 +69,7 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
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, PreparedModelCollision>>> Models;
|
std::unordered_map<std::string, std::vector<std::pair<std::string, PreparedModel>>> Models;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -76,7 +86,7 @@ private:
|
|||||||
static constexpr size_t ChunkSize = 4096;
|
static constexpr size_t ChunkSize = 4096;
|
||||||
bool IsFull = false;
|
bool IsFull = false;
|
||||||
std::bitset<ChunkSize> Empty;
|
std::bitset<ChunkSize> Empty;
|
||||||
std::array<std::optional<DataEntry>, ChunkSize> Entries;
|
std::array<std::optional<T>, ChunkSize> Entries;
|
||||||
|
|
||||||
TableEntry() {
|
TableEntry() {
|
||||||
Empty.set();
|
Empty.set();
|
||||||
@@ -87,14 +97,64 @@ private:
|
|||||||
// Связь ресурсов по идентификаторам
|
// Связь ресурсов по идентификаторам
|
||||||
std::vector<std::unique_ptr<TableEntry<DataEntry>>> Table[(int) EnumAssets::MAX_ENUM];
|
std::vector<std::unique_ptr<TableEntry<DataEntry>>> Table[(int) EnumAssets::MAX_ENUM];
|
||||||
|
|
||||||
// Распаршенные ресурсы, для использования сервером
|
// Распаршенные ресурсы, для использования сервером (сбор зависимостей профиля нод и расчёт коллизии если нужно)
|
||||||
std::vector<std::unique_ptr<TableEntry<PreparedNodeState>>> Table_NodeState;
|
// Первичные зависимости Nodestate к моделям
|
||||||
std::vector<std::unique_ptr<TableEntry<PreparedModelCollision>>> Table_Model;
|
std::vector<std::unique_ptr<TableEntry<std::vector<AssetsModel>>>> Table_NodeState;
|
||||||
|
// Упрощённые модели для коллизии
|
||||||
|
std::vector<std::unique_ptr<TableEntry<ModelDependency>>> Table_Model;
|
||||||
|
|
||||||
// Связь домены -> {ключ -> идентификатор}
|
// Связь домены -> {ключ -> идентификатор}
|
||||||
std::unordered_map<std::string, std::unordered_map<std::string, ResourceId>> KeyToId[(int) EnumAssets::MAX_ENUM];
|
std::unordered_map<std::string, std::unordered_map<std::string, ResourceId>> KeyToId[(int) EnumAssets::MAX_ENUM];
|
||||||
|
|
||||||
std::tuple<ResourceId, std::optional<DataEntry>&> nextId(EnumAssets type);
|
std::tuple<ResourceId, std::optional<DataEntry>&> nextId(EnumAssets type);
|
||||||
|
|
||||||
|
|
||||||
|
ResourceId getId(EnumAssets type, const std::string& domain, const std::string& key) {
|
||||||
|
auto& keyToId = KeyToId[(int) type];
|
||||||
|
if(auto iterKTI = keyToId.find(domain); iterKTI != keyToId.end()) {
|
||||||
|
if(auto iterKey = iterKTI->second.find(key); iterKey != iterKTI->second.end()) {
|
||||||
|
return iterKey->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto [id, entry] = nextId(type);
|
||||||
|
keyToId[domain][key] = id;
|
||||||
|
|
||||||
|
// Расширяем таблицу с ресурсами, если необходимо
|
||||||
|
ssize_t tableChunks = (ssize_t) (id/TableEntry<DataEntry>::ChunkSize)-(ssize_t) Table[(int) type].size()+1;
|
||||||
|
for(; tableChunks > 0; tableChunks--) {
|
||||||
|
Table[(int) type].emplace_back(std::make_unique<TableEntry<DataEntry>>());
|
||||||
|
|
||||||
|
if(type == EnumAssets::Nodestate)
|
||||||
|
Table_NodeState.emplace_back(std::make_unique<TableEntry<std::vector<AssetsModel>>>());
|
||||||
|
else if(type == EnumAssets::Model)
|
||||||
|
Table_Model.emplace_back(std::make_unique<TableEntry<ModelDependency>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::tuple<Resource, const std::string&, const std::string&>> getResource(EnumAssets type, ResourceId id) {
|
||||||
|
assert(id < Table[(int) type].size()*TableEntry<DataEntry>::ChunkSize);
|
||||||
|
auto& value = Table[(int) type][id / TableEntry<DataEntry>::ChunkSize]->Entries[id % TableEntry<DataEntry>::ChunkSize];
|
||||||
|
if(value)
|
||||||
|
return {{value->Res, value->Domain, value->Key}};
|
||||||
|
else
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::optional<std::vector<AssetsModel>>& getResourceNodestate(ResourceId id) {
|
||||||
|
assert(id < Table_NodeState.size()*TableEntry<DataEntry>::ChunkSize);
|
||||||
|
return Table_NodeState[id / TableEntry<DataEntry>::ChunkSize]
|
||||||
|
->Entries[id % TableEntry<DataEntry>::ChunkSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const std::optional<ModelDependency>& getResourceModel(ResourceId id) {
|
||||||
|
assert(id < Table_Model.size()*TableEntry<DataEntry>::ChunkSize);
|
||||||
|
return Table_Model[id / TableEntry<DataEntry>::ChunkSize]
|
||||||
|
->Entries[id % TableEntry<DataEntry>::ChunkSize];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TOS::SpinlockObject<Local> LocalObj;
|
TOS::SpinlockObject<Local> LocalObj;
|
||||||
@@ -167,28 +227,56 @@ public:
|
|||||||
resource должен содержать домен и путь
|
resource должен содержать домен и путь
|
||||||
*/
|
*/
|
||||||
ResourceId getId(EnumAssets type, const std::string& domain, const std::string& key) {
|
ResourceId getId(EnumAssets type, const std::string& domain, const std::string& key) {
|
||||||
|
return LocalObj.lock()->getId(type, domain, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выдаёт ресурс по идентификатору
|
||||||
|
std::optional<std::tuple<Resource, const std::string&, const std::string&>> getResource(EnumAssets type, ResourceId id) {
|
||||||
|
return LocalObj.lock()->getResource(type, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выдаёт зависимости к ресурсам профиля ноды
|
||||||
|
std::tuple<AssetsNodestate, std::vector<AssetsModel>, std::vector<AssetsTexture>>
|
||||||
|
getNodeDependency(const std::string& domain, const std::string& key)
|
||||||
|
{
|
||||||
auto lock = LocalObj.lock();
|
auto lock = LocalObj.lock();
|
||||||
auto& keyToId = lock->KeyToId[(int) type];
|
AssetsNodestate nodestateId = lock->getId(EnumAssets::Nodestate, domain, key);
|
||||||
if(auto iterKTI = keyToId.find(domain); iterKTI != keyToId.end()) {
|
|
||||||
if(auto iterKey = iterKTI->second.find(key); iterKey != iterKTI->second.end()) {
|
std::vector<AssetsModel> models;
|
||||||
return iterKey->second;
|
std::vector<AssetsTexture> textures;
|
||||||
|
|
||||||
|
if(auto subModelsPtr = lock->getResourceNodestate(nodestateId)) {
|
||||||
|
for(AssetsModel resId : *subModelsPtr) {
|
||||||
|
const auto& subModel = lock->getResourceModel(resId);
|
||||||
|
|
||||||
|
if(!subModel)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
models.push_back(resId);
|
||||||
|
models.append_range(subModel->FullSubModelDeps);
|
||||||
|
textures.append_range(subModel->FullSubTextureDeps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto [id, entry] = lock->nextId(type);
|
{
|
||||||
keyToId[domain][key] = id;
|
std::sort(models.begin(), models.end());
|
||||||
return id;
|
auto eraseIter = std::unique(models.begin(), models.end());
|
||||||
|
models.erase(eraseIter, models.end());
|
||||||
|
models.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::sort(textures.begin(), textures.end());
|
||||||
|
auto eraseIter = std::unique(textures.begin(), textures.end());
|
||||||
|
textures.erase(eraseIter, textures.end());
|
||||||
|
textures.shrink_to_fit();
|
||||||
|
}
|
||||||
|
return {nodestateId, std::move(models), std::move(textures)};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::tuple<Resource, const std::string&, const std::string&>> getResource(EnumAssets type, ResourceId id) {
|
private:
|
||||||
auto lock = LocalObj.lock();
|
TOS::Logger LOG = "Server>AssetsManager";
|
||||||
assert(id < lock->Table[(int) type].size()*TableEntry<DataEntry>::ChunkSize);
|
|
||||||
auto& value = lock->Table[(int) type][id / TableEntry<DataEntry>::ChunkSize]->Entries[id % TableEntry<DataEntry>::ChunkSize];
|
|
||||||
if(value)
|
|
||||||
return {{value->Res, value->Domain, value->Key}};
|
|
||||||
else
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1512,6 +1512,15 @@ void GameServer::prerun() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GameServer::run() {
|
void GameServer::run() {
|
||||||
|
{
|
||||||
|
IWorldSaveBackend::TickSyncInfo_In in;
|
||||||
|
for(int x = -1; x <= 1; x++)
|
||||||
|
for(int y = -1; y <= 1; y++)
|
||||||
|
for(int z = -1; z <= 1; z++)
|
||||||
|
in.Load[0].push_back(Pos::GlobalChunk(x, y, z));
|
||||||
|
|
||||||
|
stepGeneratorAndLuaAsync(SaveBackend.World->tickSync(std::move(in)));
|
||||||
|
}
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
((uint32_t&) Game.AfterStartTime) += (uint32_t) (CurrentTickDuration*256);
|
((uint32_t&) Game.AfterStartTime) += (uint32_t) (CurrentTickDuration*256);
|
||||||
|
|||||||
Reference in New Issue
Block a user