codex-5.2: кое как доведено до почти рабочего состояния
This commit is contained in:
@@ -73,6 +73,68 @@ static std::u8string readOptionalMeta(const fs::path& path) {
|
|||||||
return readFileBytes(metaPath);
|
return readFileBytes(metaPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<uint32_t> collectTexturePipelineIds(const std::vector<uint8_t>& code);
|
||||||
|
|
||||||
|
struct ParsedModelHeader {
|
||||||
|
std::vector<AssetsManager::AssetId> ModelDeps;
|
||||||
|
std::vector<std::vector<uint8_t>> TexturePipelines;
|
||||||
|
std::vector<AssetsManager::AssetId> TextureDeps;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<std::vector<AssetsManager::AssetId>> parseNodestateHeaderBytes(const std::vector<uint8_t>& header) {
|
||||||
|
if(header.empty() || header.size() % sizeof(AssetsManager::AssetId) != 0)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const size_t count = header.size() / sizeof(AssetsManager::AssetId);
|
||||||
|
std::vector<AssetsManager::AssetId> deps;
|
||||||
|
deps.resize(count);
|
||||||
|
for(size_t i = 0; i < count; ++i) {
|
||||||
|
AssetsManager::AssetId raw = 0;
|
||||||
|
std::memcpy(&raw, header.data() + i * sizeof(AssetsManager::AssetId), sizeof(AssetsManager::AssetId));
|
||||||
|
deps[i] = raw;
|
||||||
|
}
|
||||||
|
return deps;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ParsedModelHeader> parseModelHeaderBytes(const std::vector<uint8_t>& header) {
|
||||||
|
if(header.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
ParsedModelHeader result;
|
||||||
|
try {
|
||||||
|
TOS::ByteBuffer buffer(header.size(), header.data());
|
||||||
|
auto reader = buffer.reader();
|
||||||
|
|
||||||
|
uint16_t modelCount = reader.readUInt16();
|
||||||
|
result.ModelDeps.reserve(modelCount);
|
||||||
|
for(uint16_t i = 0; i < modelCount; ++i)
|
||||||
|
result.ModelDeps.push_back(reader.readUInt32());
|
||||||
|
|
||||||
|
uint16_t texCount = reader.readUInt16();
|
||||||
|
result.TexturePipelines.reserve(texCount);
|
||||||
|
for(uint16_t i = 0; i < texCount; ++i) {
|
||||||
|
uint32_t size32 = reader.readUInt32();
|
||||||
|
TOS::ByteBuffer pipe;
|
||||||
|
reader.readBuffer(pipe);
|
||||||
|
if(pipe.size() != size32)
|
||||||
|
return std::nullopt;
|
||||||
|
result.TexturePipelines.emplace_back(pipe.begin(), pipe.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<AssetsManager::AssetId> seen;
|
||||||
|
for(const auto& pipe : result.TexturePipelines) {
|
||||||
|
for(uint32_t id : collectTexturePipelineIds(pipe)) {
|
||||||
|
if(seen.insert(id).second)
|
||||||
|
result.TextureDeps.push_back(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(const std::exception&) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
struct PipelineRemapResult {
|
struct PipelineRemapResult {
|
||||||
bool Ok = true;
|
bool Ok = true;
|
||||||
std::string Error;
|
std::string Error;
|
||||||
@@ -508,9 +570,23 @@ AssetsManager::PackReloadResult AssetsManager::reloadPacks(const PackRegister& r
|
|||||||
auto [mDomain, mKey] = parseDomainKey(model, entry.Domain);
|
auto [mDomain, mKey] = parseDomainKey(model, entry.Domain);
|
||||||
return getOrCreateLocalId(AssetType::Model, mDomain, mKey);
|
return getOrCreateLocalId(AssetType::Model, mDomain, mKey);
|
||||||
};
|
};
|
||||||
|
auto normalizeTexturePipelineSrc = [](std::string_view src) -> std::string {
|
||||||
|
std::string out(src);
|
||||||
|
auto isSpace = [](unsigned char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; };
|
||||||
|
size_t start = 0;
|
||||||
|
while(start < out.size() && isSpace(static_cast<unsigned char>(out[start])))
|
||||||
|
++start;
|
||||||
|
if(out.compare(start, 3, "tex") != 0) {
|
||||||
|
std::string pref = "tex ";
|
||||||
|
pref += out.substr(start);
|
||||||
|
return pref;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
auto textureResolver = [&](std::string_view textureSrc) -> std::vector<uint8_t> {
|
auto textureResolver = [&](std::string_view textureSrc) -> std::vector<uint8_t> {
|
||||||
TexturePipelineProgram tpp;
|
TexturePipelineProgram tpp;
|
||||||
if(!tpp.compile(std::string(textureSrc)))
|
if(!tpp.compile(normalizeTexturePipelineSrc(textureSrc)))
|
||||||
return {};
|
return {};
|
||||||
auto textureIdResolver = [&](std::string_view name) -> std::optional<uint32_t> {
|
auto textureIdResolver = [&](std::string_view name) -> std::optional<uint32_t> {
|
||||||
auto [tDomain, tKey] = parseDomainKey(name, entry.Domain);
|
auto [tDomain, tKey] = parseDomainKey(name, entry.Domain);
|
||||||
@@ -759,52 +835,21 @@ std::optional<AssetsManager::ParsedHeader> AssetsManager::parseHeader(AssetType
|
|||||||
result.Type = type;
|
result.Type = type;
|
||||||
|
|
||||||
if(type == AssetType::Nodestate) {
|
if(type == AssetType::Nodestate) {
|
||||||
if(header.size() % sizeof(AssetId) != 0)
|
auto deps = parseNodestateHeaderBytes(header);
|
||||||
|
if(!deps)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
const size_t count = header.size() / sizeof(AssetId);
|
result.ModelDeps = std::move(*deps);
|
||||||
result.ModelDeps.resize(count);
|
|
||||||
for(size_t i = 0; i < count; ++i) {
|
|
||||||
AssetId raw = 0;
|
|
||||||
std::memcpy(&raw, header.data() + i * sizeof(AssetId), sizeof(AssetId));
|
|
||||||
result.ModelDeps[i] = raw;
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(type == AssetType::Model) {
|
if(type == AssetType::Model) {
|
||||||
try {
|
auto parsed = parseModelHeaderBytes(header);
|
||||||
TOS::ByteBuffer buffer(header.size(), header.data());
|
if(!parsed)
|
||||||
auto reader = buffer.reader();
|
|
||||||
|
|
||||||
uint16_t modelCount = reader.readUInt16();
|
|
||||||
result.ModelDeps.reserve(modelCount);
|
|
||||||
for(uint16_t i = 0; i < modelCount; ++i)
|
|
||||||
result.ModelDeps.push_back(reader.readUInt32());
|
|
||||||
|
|
||||||
uint16_t texCount = reader.readUInt16();
|
|
||||||
result.TexturePipelines.reserve(texCount);
|
|
||||||
for(uint16_t i = 0; i < texCount; ++i) {
|
|
||||||
uint32_t size32 = reader.readUInt32();
|
|
||||||
TOS::ByteBuffer pipe;
|
|
||||||
reader.readBuffer(pipe);
|
|
||||||
if(pipe.size() != size32) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
result.TexturePipelines.emplace_back(pipe.begin(), pipe.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_set<AssetId> seen;
|
|
||||||
for(const auto& pipe : result.TexturePipelines) {
|
|
||||||
for(uint32_t id : collectTexturePipelineIds(pipe)) {
|
|
||||||
if(seen.insert(id).second)
|
|
||||||
result.TextureDeps.push_back(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} catch(const std::exception&) {
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
result.ModelDeps = std::move(parsed->ModelDeps);
|
||||||
|
result.TexturePipelines = std::move(parsed->TexturePipelines);
|
||||||
|
result.TextureDeps = std::move(parsed->TextureDeps);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|||||||
@@ -1424,68 +1424,73 @@ coro<> ServerSession::rP_AssetsInitSend(Net::AsyncSocket &sock) {
|
|||||||
Hash_t hash;
|
Hash_t hash;
|
||||||
co_await sock.read((std::byte*) hash.data(), hash.size());
|
co_await sock.read((std::byte*) hash.data(), hash.size());
|
||||||
|
|
||||||
bool found = false;
|
std::vector<AssetLoadingEntry> matches;
|
||||||
EnumAssets type = EnumAssets::Texture;
|
|
||||||
std::string domain;
|
|
||||||
std::string key;
|
|
||||||
|
|
||||||
for(int typeIndex = 0; typeIndex < (int) EnumAssets::MAX_ENUM && !found; ++typeIndex) {
|
for(int typeIndex = 0; typeIndex < (int) EnumAssets::MAX_ENUM; ++typeIndex) {
|
||||||
auto& waitingByDomain = AsyncContext.ResourceWait[typeIndex];
|
auto& waitingByDomain = AsyncContext.ResourceWait[typeIndex];
|
||||||
for(auto iterDomain = waitingByDomain.begin(); iterDomain != waitingByDomain.end() && !found; ) {
|
for(auto iterDomain = waitingByDomain.begin(); iterDomain != waitingByDomain.end(); ) {
|
||||||
auto& entries = iterDomain->second;
|
auto& entries = iterDomain->second;
|
||||||
for(size_t i = 0; i < entries.size(); ++i) {
|
for(size_t i = 0; i < entries.size(); ) {
|
||||||
if(entries[i].second == hash) {
|
if(entries[i].second == hash) {
|
||||||
type = static_cast<EnumAssets>(typeIndex);
|
EnumAssets type = static_cast<EnumAssets>(typeIndex);
|
||||||
domain = iterDomain->first;
|
const std::string& domain = iterDomain->first;
|
||||||
key = entries[i].first;
|
const std::string& key = entries[i].first;
|
||||||
|
ResourceId localId = AM->getOrCreateLocalId(type, domain, key);
|
||||||
|
matches.push_back(AssetLoadingEntry{
|
||||||
|
.Type = type,
|
||||||
|
.Id = localId,
|
||||||
|
.Domain = domain,
|
||||||
|
.Key = key
|
||||||
|
});
|
||||||
entries.erase(entries.begin() + i);
|
entries.erase(entries.begin() + i);
|
||||||
if(entries.empty())
|
} else {
|
||||||
iterDomain = waitingByDomain.erase(iterDomain);
|
++i;
|
||||||
else
|
|
||||||
++iterDomain;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!found)
|
if(entries.empty())
|
||||||
|
iterDomain = waitingByDomain.erase(iterDomain);
|
||||||
|
else
|
||||||
++iterDomain;
|
++iterDomain;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!found) {
|
if(matches.empty()) {
|
||||||
LOG.warn() << "AssetsInitSend for unknown hash " << int(hash[0]) << '.'
|
LOG.warn() << "AssetsInitSend for unknown hash " << int(hash[0]) << '.'
|
||||||
<< int(hash[1]) << '.' << int(hash[2]) << '.' << int(hash[3]);
|
<< int(hash[1]) << '.' << int(hash[2]) << '.' << int(hash[3]);
|
||||||
AsyncContext.AssetsLoading[hash] = AssetLoading{
|
AsyncContext.AssetsLoading[hash] = AssetLoading{
|
||||||
EnumAssets::Texture, 0, {}, {},
|
.Entries = {},
|
||||||
std::u8string(size, '\0'), 0
|
.Data = std::u8string(size, '\0'),
|
||||||
|
.Offset = 0
|
||||||
};
|
};
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceId localId = AM->getOrCreateLocalId(type, domain, key);
|
AssetLoadingEntry first = matches.front();
|
||||||
|
|
||||||
if(domain == "test"
|
if(first.Domain == "test"
|
||||||
&& (type == EnumAssets::Nodestate
|
&& (first.Type == EnumAssets::Nodestate
|
||||||
|| type == EnumAssets::Model
|
|| first.Type == EnumAssets::Model
|
||||||
|| type == EnumAssets::Texture))
|
|| first.Type == EnumAssets::Texture))
|
||||||
{
|
{
|
||||||
uint32_t idx = debugResourceLogCount.fetch_add(1);
|
uint32_t idx = debugResourceLogCount.fetch_add(1);
|
||||||
if(idx < 128) {
|
if(idx < 128) {
|
||||||
LOG.debug() << "AssetsInitSend type=" << assetTypeName(type)
|
LOG.debug() << "AssetsInitSend type=" << assetTypeName(first.Type)
|
||||||
<< " id=" << localId
|
<< " id=" << first.Id
|
||||||
<< " key=" << domain << ':' << key
|
<< " key=" << first.Domain << ':' << first.Key
|
||||||
<< " size=" << size;
|
<< " size=" << size
|
||||||
|
<< " matches=" << matches.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncContext.AssetsLoading[hash] = AssetLoading{
|
AsyncContext.AssetsLoading[hash] = AssetLoading{
|
||||||
type, localId, std::move(domain), std::move(key),
|
.Entries = std::move(matches),
|
||||||
std::u8string(size, '\0'), 0
|
.Data = std::u8string(size, '\0'),
|
||||||
|
.Offset = 0
|
||||||
};
|
};
|
||||||
LOG.debug() << "Server started sending type=" << assetTypeName(type)
|
LOG.debug() << "Server started sending type=" << assetTypeName(first.Type)
|
||||||
<< " id=" << localId
|
<< " id=" << first.Id
|
||||||
<< " key=" << AsyncContext.AssetsLoading[hash].Domain << ':'
|
<< " key=" << first.Domain << ':'
|
||||||
<< AsyncContext.AssetsLoading[hash].Key
|
<< first.Key
|
||||||
<< " hash=" << int(hash[0]) << '.'
|
<< " hash=" << int(hash[0]) << '.'
|
||||||
<< int(hash[1]) << '.'
|
<< int(hash[1]) << '.'
|
||||||
<< int(hash[2]) << '.'
|
<< int(hash[2]) << '.'
|
||||||
@@ -1517,43 +1522,47 @@ coro<> ServerSession::rP_AssetsNextSend(Net::AsyncSocket &sock) {
|
|||||||
if(al.Offset != al.Data.size())
|
if(al.Offset != al.Data.size())
|
||||||
co_return;
|
co_return;
|
||||||
|
|
||||||
if(!al.Domain.empty() || !al.Key.empty()) {
|
if(!al.Entries.empty()) {
|
||||||
if(al.Domain == "test"
|
|
||||||
&& (al.Type == EnumAssets::Nodestate
|
|
||||||
|| al.Type == EnumAssets::Model
|
|
||||||
|| al.Type == EnumAssets::Texture))
|
|
||||||
{
|
|
||||||
uint32_t idx = debugResourceLogCount.fetch_add(1);
|
|
||||||
if(idx < 128) {
|
|
||||||
LOG.debug() << "Resource loaded type=" << assetTypeName(al.Type)
|
|
||||||
<< " id=" << al.Id
|
|
||||||
<< " key=" << al.Domain << ':' << al.Key
|
|
||||||
<< " size=" << al.Data.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const EnumAssets type = al.Type;
|
|
||||||
const ResourceId id = al.Id;
|
|
||||||
const std::string domain = al.Domain;
|
|
||||||
const std::string key = al.Key;
|
|
||||||
const size_t resSize = al.Data.size();
|
const size_t resSize = al.Data.size();
|
||||||
|
Resource res(std::move(al.Data));
|
||||||
|
|
||||||
AsyncContext.LoadedAssets.lock()->emplace_back(AssetEntry{
|
for(AssetLoadingEntry& entry : al.Entries) {
|
||||||
.Type = type,
|
if(entry.Domain == "test"
|
||||||
.Id = id,
|
&& (entry.Type == EnumAssets::Nodestate
|
||||||
.Domain = std::move(al.Domain),
|
|| entry.Type == EnumAssets::Model
|
||||||
.Key = std::move(al.Key),
|
|| entry.Type == EnumAssets::Texture))
|
||||||
.Res = std::move(al.Data),
|
{
|
||||||
.Hash = hash
|
uint32_t idx = debugResourceLogCount.fetch_add(1);
|
||||||
});
|
if(idx < 128) {
|
||||||
LOG.debug() << "Client received type=" << assetTypeName(type)
|
LOG.debug() << "Resource loaded type=" << assetTypeName(entry.Type)
|
||||||
<< " id=" << id
|
<< " id=" << entry.Id
|
||||||
<< " key=" << domain << ':' << key
|
<< " key=" << entry.Domain << ':' << entry.Key
|
||||||
<< " hash=" << int(hash[0]) << '.'
|
<< " size=" << resSize;
|
||||||
<< int(hash[1]) << '.'
|
}
|
||||||
<< int(hash[2]) << '.'
|
}
|
||||||
<< int(hash[3])
|
|
||||||
<< " size=" << resSize;
|
const EnumAssets type = entry.Type;
|
||||||
|
const ResourceId id = entry.Id;
|
||||||
|
const std::string domain = entry.Domain;
|
||||||
|
const std::string key = entry.Key;
|
||||||
|
|
||||||
|
AsyncContext.LoadedAssets.lock()->emplace_back(AssetEntry{
|
||||||
|
.Type = type,
|
||||||
|
.Id = id,
|
||||||
|
.Domain = std::move(entry.Domain),
|
||||||
|
.Key = std::move(entry.Key),
|
||||||
|
.Res = res,
|
||||||
|
.Hash = hash
|
||||||
|
});
|
||||||
|
LOG.debug() << "Client received type=" << assetTypeName(type)
|
||||||
|
<< " id=" << id
|
||||||
|
<< " key=" << domain << ':' << key
|
||||||
|
<< " hash=" << int(hash[0]) << '.'
|
||||||
|
<< int(hash[1]) << '.'
|
||||||
|
<< int(hash[2]) << '.'
|
||||||
|
<< int(hash[3])
|
||||||
|
<< " size=" << resSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncContext.AssetsLoading.erase(AsyncContext.AssetsLoading.find(hash));
|
AsyncContext.AssetsLoading.erase(AsyncContext.AssetsLoading.find(hash));
|
||||||
|
|||||||
@@ -79,12 +79,17 @@ private:
|
|||||||
std::unordered_map<std::string, std::pair<AssetEntry, uint64_t>> NotInUse[(int) EnumAssets::MAX_ENUM];
|
std::unordered_map<std::string, std::pair<AssetEntry, uint64_t>> NotInUse[(int) EnumAssets::MAX_ENUM];
|
||||||
} MyAssets;
|
} MyAssets;
|
||||||
|
|
||||||
struct AssetLoading {
|
struct AssetLoadingEntry {
|
||||||
EnumAssets Type;
|
EnumAssets Type;
|
||||||
ResourceId Id;
|
ResourceId Id;
|
||||||
std::string Domain, Key;
|
std::string Domain;
|
||||||
|
std::string Key;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AssetLoading {
|
||||||
|
std::vector<AssetLoadingEntry> Entries;
|
||||||
std::u8string Data;
|
std::u8string Data;
|
||||||
size_t Offset;
|
size_t Offset = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AssetBindEntry {
|
struct AssetBindEntry {
|
||||||
|
|||||||
@@ -140,6 +140,14 @@ void ChunkMeshGenerator::run(uint8_t id) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::vector<NodeStateInfo> metaStatesInfo;
|
||||||
|
{
|
||||||
|
NodeStateInfo info;
|
||||||
|
info.Name = "meta";
|
||||||
|
info.Variations = 256;
|
||||||
|
metaStatesInfo.push_back(std::move(info));
|
||||||
|
}
|
||||||
|
|
||||||
// Воксели пока не рендерим
|
// Воксели пока не рендерим
|
||||||
if(auto iterWorld = SS->Content.Worlds.find(wId); iterWorld != SS->Content.Worlds.end()) {
|
if(auto iterWorld = SS->Content.Worlds.find(wId); iterWorld != SS->Content.Worlds.end()) {
|
||||||
Pos::GlobalRegion rPos = pos >> 2;
|
Pos::GlobalRegion rPos = pos >> 2;
|
||||||
@@ -188,6 +196,38 @@ void ChunkMeshGenerator::run(uint8_t id) {
|
|||||||
return (nodeFullCuboidCache[node.Data] = true);
|
return (nodeFullCuboidCache[node.Data] = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(NSP && profile->NodestateId != 0 && NSP->hasNodestate(profile->NodestateId)) {
|
||||||
|
std::unordered_map<std::string, int32_t> states;
|
||||||
|
int32_t meta = node.Meta;
|
||||||
|
states.emplace("meta", meta);
|
||||||
|
const auto routes = NSP->getModelsForNode(profile->NodestateId, metaStatesInfo, states);
|
||||||
|
bool isFull = !routes.empty();
|
||||||
|
if(isFull) {
|
||||||
|
for(const auto& variants : routes) {
|
||||||
|
for(const auto& [weight, faces] : variants) {
|
||||||
|
(void)weight;
|
||||||
|
auto hasFace = [&](EnumFace face) -> bool {
|
||||||
|
auto iterFace = faces.find(face);
|
||||||
|
return iterFace != faces.end() && !iterFace->second.empty();
|
||||||
|
};
|
||||||
|
if(!hasFace(EnumFace::Up)
|
||||||
|
|| !hasFace(EnumFace::Down)
|
||||||
|
|| !hasFace(EnumFace::East)
|
||||||
|
|| !hasFace(EnumFace::West)
|
||||||
|
|| !hasFace(EnumFace::South)
|
||||||
|
|| !hasFace(EnumFace::North))
|
||||||
|
{
|
||||||
|
isFull = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!isFull)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (nodeFullCuboidCache[node.Data] = isFull);
|
||||||
|
}
|
||||||
|
|
||||||
return (nodeFullCuboidCache[node.Data] = false);
|
return (nodeFullCuboidCache[node.Data] = false);
|
||||||
} else {
|
} else {
|
||||||
return iterCache->second;
|
return iterCache->second;
|
||||||
@@ -325,14 +365,6 @@ void ChunkMeshGenerator::run(uint8_t id) {
|
|||||||
std::unordered_map<uint32_t, ModelCacheEntry> modelCache;
|
std::unordered_map<uint32_t, ModelCacheEntry> modelCache;
|
||||||
std::unordered_map<AssetsTexture, uint32_t> baseTextureCache;
|
std::unordered_map<AssetsTexture, uint32_t> baseTextureCache;
|
||||||
|
|
||||||
std::vector<NodeStateInfo> metaStatesInfo;
|
|
||||||
{
|
|
||||||
NodeStateInfo info;
|
|
||||||
info.Name = "meta";
|
|
||||||
info.Variations = 256;
|
|
||||||
metaStatesInfo.push_back(std::move(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto isFaceCovered = [&](EnumFace face, int covered) -> bool {
|
auto isFaceCovered = [&](EnumFace face, int covered) -> bool {
|
||||||
switch(face) {
|
switch(face) {
|
||||||
case EnumFace::Up: return covered & (1 << 2);
|
case EnumFace::Up: return covered & (1 << 2);
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include "Common/Abstract.hpp"
|
#include "Common/Abstract.hpp"
|
||||||
#include <Client/Vulkan/Vulkan.hpp>
|
#include <Client/Vulkan/Vulkan.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -134,14 +136,6 @@ public:
|
|||||||
Models.erase(iterModel);
|
Models.erase(iterModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_map<std::string, ResourceId> modelKeyToId;
|
|
||||||
if(modelAssets) {
|
|
||||||
modelKeyToId.reserve(modelAssets->size());
|
|
||||||
for(const auto& [id, entry] : *modelAssets) {
|
|
||||||
modelKeyToId.emplace(entry.Domain + ':' + entry.Key, id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const auto& [key, resource, deps] : newOrChanged) {
|
for(const auto& [key, resource, deps] : newOrChanged) {
|
||||||
result.push_back(key);
|
result.push_back(key);
|
||||||
|
|
||||||
@@ -173,19 +167,39 @@ public:
|
|||||||
return pipe;
|
return pipe;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
size_t dataSize = 0;
|
||||||
|
std::array<uint8_t, 4> prefix = {};
|
||||||
try {
|
try {
|
||||||
std::u8string_view data((const char8_t*) resource.data(), resource.size());
|
std::u8string_view data((const char8_t*) resource.data(), resource.size());
|
||||||
|
dataSize = data.size();
|
||||||
|
if(!data.empty()) {
|
||||||
|
const size_t prefixLen = std::min<size_t>(prefix.size(), data.size());
|
||||||
|
for(size_t i = 0; i < prefixLen; ++i)
|
||||||
|
prefix[i] = static_cast<uint8_t>(data[i]);
|
||||||
|
}
|
||||||
if(data.starts_with((const char8_t*) "bm")) {
|
if(data.starts_with((const char8_t*) "bm")) {
|
||||||
type = "InternalBinary";
|
type = "InternalBinary";
|
||||||
// Компилированная модель внутреннего формата
|
// Компилированная модель внутреннего формата
|
||||||
LV::PreparedModel pm((std::u8string) data);
|
HeadlessModel hm;
|
||||||
|
hm.load(data);
|
||||||
model.TextureMap.clear();
|
model.TextureMap.clear();
|
||||||
model.TextureMap.reserve(pm.CompiledTextures.size());
|
model.TextureMap.reserve(hm.Textures.size());
|
||||||
for(auto& [tkey, pipe] : pm.CompiledTextures)
|
for(const auto& [tkey, id] : hm.Textures) {
|
||||||
model.TextureMap.emplace(tkey, remapPipeline(std::move(pipe)));
|
TexturePipeline pipe;
|
||||||
|
if(header && id < header->TexturePipelines.size()) {
|
||||||
|
pipe.Pipeline = header->TexturePipelines[id];
|
||||||
|
} else {
|
||||||
|
LOG.warn() << "Model texture pipeline id out of range: model=" << key
|
||||||
|
<< " local=" << id
|
||||||
|
<< " pipelines=" << (header ? header->TexturePipelines.size() : 0);
|
||||||
|
pipe.BinTextures.push_back(id);
|
||||||
|
pipe = remapPipeline(std::move(pipe));
|
||||||
|
}
|
||||||
|
model.TextureMap.emplace(tkey, std::move(pipe));
|
||||||
|
}
|
||||||
model.TextureKeys = {};
|
model.TextureKeys = {};
|
||||||
|
|
||||||
for(const PreparedModel::Cuboid& cb : pm.Cuboids) {
|
for(const HeadlessModel::Cuboid& cb : hm.Cuboids) {
|
||||||
glm::vec3 min = glm::min(cb.From, cb.To), max = glm::max(cb.From, cb.To);
|
glm::vec3 min = glm::min(cb.From, cb.To), max = glm::max(cb.From, cb.To);
|
||||||
|
|
||||||
for(const auto& [face, params] : cb.Faces) {
|
for(const auto& [face, params] : cb.Faces) {
|
||||||
@@ -265,13 +279,16 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!pm.SubModels.empty() && modelAssets) {
|
if(!hm.SubModels.empty()) {
|
||||||
model.Depends.reserve(pm.SubModels.size());
|
model.Depends.reserve(hm.SubModels.size());
|
||||||
for(const auto& sub : pm.SubModels) {
|
for(const auto& sub : hm.SubModels) {
|
||||||
auto iter = modelKeyToId.find(sub.Domain + ':' + sub.Key);
|
if(!header || sub.Id >= header->ModelDeps.size()) {
|
||||||
if(iter == modelKeyToId.end())
|
LOG.warn() << "Model sub-model id out of range: model=" << key
|
||||||
|
<< " local=" << sub.Id
|
||||||
|
<< " deps=" << (header ? header->ModelDeps.size() : 0);
|
||||||
continue;
|
continue;
|
||||||
model.Depends.emplace_back(iter->second, Transformations{});
|
}
|
||||||
|
model.Depends.emplace_back(header->ModelDeps[sub.Id], Transformations{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,6 +314,35 @@ public:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
static std::atomic<uint32_t> debugModelLogCount = 0;
|
||||||
|
uint32_t idx = debugModelLogCount.fetch_add(1);
|
||||||
|
if(idx < 128) {
|
||||||
|
size_t vertexCount = 0;
|
||||||
|
for(const auto& [_, verts] : model.Vertecies)
|
||||||
|
vertexCount += verts.size();
|
||||||
|
size_t texDepsCount = textureDeps ? textureDeps->size() : 0;
|
||||||
|
LOG.debug() << "Model loaded id=" << key
|
||||||
|
<< " verts=" << vertexCount
|
||||||
|
<< " texKeys=" << model.TextureKeys.size()
|
||||||
|
<< " texDeps=" << texDepsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(model.Vertecies.empty()) {
|
||||||
|
static std::atomic<uint32_t> debugEmptyModelLogCount = 0;
|
||||||
|
uint32_t idx = debugEmptyModelLogCount.fetch_add(1);
|
||||||
|
if(idx < 128) {
|
||||||
|
LOG.warn() << "Model has empty geometry id=" << key
|
||||||
|
<< " type=" << type
|
||||||
|
<< " size=" << dataSize
|
||||||
|
<< " prefix=" << int(prefix[0]) << '.'
|
||||||
|
<< int(prefix[1]) << '.'
|
||||||
|
<< int(prefix[2]) << '.'
|
||||||
|
<< int(prefix[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Models.insert_or_assign(key, std::move(model));
|
Models.insert_or_assign(key, std::move(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -625,13 +671,24 @@ public:
|
|||||||
std::move(pixels)
|
std::move(pixels)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
bool animated = false;
|
||||||
if(auto anim = getDefaultAnimation(update.Key, width, height)) {
|
if(auto anim = getDefaultAnimation(update.Key, width, height)) {
|
||||||
AnimatedSources[key] = *anim;
|
AnimatedSources[key] = *anim;
|
||||||
|
animated = true;
|
||||||
} else {
|
} else {
|
||||||
AnimatedSources.erase(key);
|
AnimatedSources.erase(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
NeedsUpload = true;
|
NeedsUpload = true;
|
||||||
|
|
||||||
|
static std::atomic<uint32_t> debugTextureLogCount = 0;
|
||||||
|
uint32_t idx = debugTextureLogCount.fetch_add(1);
|
||||||
|
if(idx < 128) {
|
||||||
|
LOG.debug() << "Texture loaded id=" << key
|
||||||
|
<< " key=" << update.Domain << ':' << update.Key
|
||||||
|
<< " size=" << width << 'x' << height
|
||||||
|
<< " animated=" << (animated ? 1 : 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(AssetsTexture key : lost) {
|
for(AssetsTexture key : lost) {
|
||||||
@@ -897,7 +954,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(deps && !deps->empty()) {
|
if(deps && !deps->empty()) {
|
||||||
auto header = AssetsManager::parseHeader(EnumAssets::Model, *deps);
|
auto header = AssetsManager::parseHeader(EnumAssets::Nodestate, *deps);
|
||||||
if(header && header->Type == EnumAssets::Nodestate) {
|
if(header && header->Type == EnumAssets::Nodestate) {
|
||||||
nodestate.LocalToModel.assign(header->ModelDeps.begin(), header->ModelDeps.end());
|
nodestate.LocalToModel.assign(header->ModelDeps.begin(), header->ModelDeps.end());
|
||||||
}
|
}
|
||||||
@@ -968,6 +1025,11 @@ public:
|
|||||||
|
|
||||||
auto appendModel = [&](AssetsModel modelId, const std::vector<Transformation>& transforms, std::unordered_map<EnumFace, std::vector<NodeVertexStatic>>& out) {
|
auto appendModel = [&](AssetsModel modelId, const std::vector<Transformation>& transforms, std::unordered_map<EnumFace, std::vector<NodeVertexStatic>>& out) {
|
||||||
ModelProvider::Model model = MP.getModel(modelId);
|
ModelProvider::Model model = MP.getModel(modelId);
|
||||||
|
if(model.Vertecies.empty()) {
|
||||||
|
if(MissingModelGeometryLogged.insert(modelId).second) {
|
||||||
|
LOG.warn() << "Model has no geometry id=" << modelId;
|
||||||
|
}
|
||||||
|
}
|
||||||
Transformations trf{transforms};
|
Transformations trf{transforms};
|
||||||
|
|
||||||
for(auto& [l, r] : model.Vertecies) {
|
for(auto& [l, r] : model.Vertecies) {
|
||||||
@@ -1015,13 +1077,28 @@ public:
|
|||||||
|
|
||||||
if(const PreparedNodeState::Model* ptr = std::get_if<PreparedNodeState::Model>(&m)) {
|
if(const PreparedNodeState::Model* ptr = std::get_if<PreparedNodeState::Model>(&m)) {
|
||||||
AssetsModel modelId;
|
AssetsModel modelId;
|
||||||
if(resolveModelId(ptr->Id, modelId))
|
if(resolveModelId(ptr->Id, modelId)) {
|
||||||
appendModel(modelId, ptr->Transforms, out);
|
appendModel(modelId, ptr->Transforms, out);
|
||||||
|
} else {
|
||||||
|
uint64_t missKey = (uint64_t(id) << 32) | ptr->Id;
|
||||||
|
if(MissingLocalModelMapLogged.insert(missKey).second) {
|
||||||
|
LOG.warn() << "Missing model mapping nodestate=" << id
|
||||||
|
<< " local=" << ptr->Id
|
||||||
|
<< " locals=" << nodestate.LocalToModel.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if(const PreparedNodeState::VectorModel* ptr = std::get_if<PreparedNodeState::VectorModel>(&m)) {
|
} else if(const PreparedNodeState::VectorModel* ptr = std::get_if<PreparedNodeState::VectorModel>(&m)) {
|
||||||
for(const auto& sub : ptr->Models) {
|
for(const auto& sub : ptr->Models) {
|
||||||
AssetsModel modelId;
|
AssetsModel modelId;
|
||||||
if(!resolveModelId(sub.Id, modelId))
|
if(!resolveModelId(sub.Id, modelId)) {
|
||||||
|
uint64_t missKey = (uint64_t(id) << 32) | sub.Id;
|
||||||
|
if(MissingLocalModelMapLogged.insert(missKey).second) {
|
||||||
|
LOG.warn() << "Missing model mapping nodestate=" << id
|
||||||
|
<< " local=" << sub.Id
|
||||||
|
<< " locals=" << nodestate.LocalToModel.size();
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Transformation> transforms = sub.Transforms;
|
std::vector<Transformation> transforms = sub.Transforms;
|
||||||
transforms.insert(transforms.end(), ptr->Transforms.begin(), ptr->Transforms.end());
|
transforms.insert(transforms.end(), ptr->Transforms.begin(), ptr->Transforms.end());
|
||||||
@@ -1058,6 +1135,8 @@ private:
|
|||||||
TextureProvider& TP;
|
TextureProvider& TP;
|
||||||
std::unordered_map<AssetsNodestate, PreparedNodeState> Nodestates;
|
std::unordered_map<AssetsNodestate, PreparedNodeState> Nodestates;
|
||||||
std::unordered_set<AssetsNodestate> MissingNodestateLogged;
|
std::unordered_set<AssetsNodestate> MissingNodestateLogged;
|
||||||
|
std::unordered_set<uint64_t> MissingLocalModelMapLogged;
|
||||||
|
std::unordered_set<AssetsModel> MissingModelGeometryLogged;
|
||||||
std::unordered_set<uint64_t> EmptyRouteLogged;
|
std::unordered_set<uint64_t> EmptyRouteLogged;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1741,26 +1741,6 @@ ResourceHeader HeadlessModel::parse(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(boost::system::result<const js::value&> subModels_val = profile.try_at("sub_models")) {
|
|
||||||
const js::array& subModels = subModels_val->as_array();
|
|
||||||
|
|
||||||
for(const js::value& sub_val : subModels) {
|
|
||||||
SubModel result;
|
|
||||||
|
|
||||||
if(auto path = sub_val.try_as_string()) {
|
|
||||||
result.Id = headerResolverModel(path.value());
|
|
||||||
} else {
|
|
||||||
const js::object& sub = sub_val.as_object();
|
|
||||||
result.Id = headerResolverModel(sub.at("path").as_string());
|
|
||||||
|
|
||||||
if(boost::system::result<const js::value&> scene_val = profile.try_at("scene"))
|
|
||||||
result.Scene = scene_val->to_number<uint16_t>();
|
|
||||||
}
|
|
||||||
|
|
||||||
SubModels.emplace_back(std::move(result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Заголовок
|
// Заголовок
|
||||||
TOS::ByteBuffer rh;
|
TOS::ByteBuffer rh;
|
||||||
|
|
||||||
@@ -2004,37 +1984,6 @@ std::u8string HeadlessModel::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) {
|
|
||||||
|
|
||||||
// lr.checkUnreaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
|
|||||||
@@ -982,37 +982,6 @@ struct TexturePipeline {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PreparedModel {
|
|
||||||
struct SubModel {
|
|
||||||
std::string Domain;
|
|
||||||
std::string Key;
|
|
||||||
};
|
|
||||||
|
|
||||||
using Cuboid = HeadlessModel::Cuboid;
|
|
||||||
|
|
||||||
std::unordered_map<std::string, TexturePipeline> CompiledTextures;
|
|
||||||
std::vector<Cuboid> Cuboids;
|
|
||||||
std::vector<SubModel> SubModels;
|
|
||||||
|
|
||||||
PreparedModel() = default;
|
|
||||||
PreparedModel(const std::u8string& data) { load(data); }
|
|
||||||
PreparedModel(std::u8string_view data) { load(data); }
|
|
||||||
|
|
||||||
void load(std::u8string_view data) {
|
|
||||||
HeadlessModel model;
|
|
||||||
model.load(data);
|
|
||||||
Cuboids = model.Cuboids;
|
|
||||||
|
|
||||||
CompiledTextures.clear();
|
|
||||||
CompiledTextures.reserve(model.Textures.size());
|
|
||||||
for(const auto& [key, id] : model.Textures) {
|
|
||||||
TexturePipeline pipe;
|
|
||||||
pipe.BinTextures.push_back(id);
|
|
||||||
CompiledTextures.emplace(key, std::move(pipe));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PreparedNodeState : public HeadlessNodeState {
|
struct PreparedNodeState : public HeadlessNodeState {
|
||||||
using HeadlessNodeState::Model;
|
using HeadlessNodeState::Model;
|
||||||
using HeadlessNodeState::VectorModel;
|
using HeadlessNodeState::VectorModel;
|
||||||
@@ -1024,30 +993,6 @@ struct PreparedNodeState : public HeadlessNodeState {
|
|||||||
PreparedNodeState(const std::u8string& data) { load(data); }
|
PreparedNodeState(const std::u8string& data) { load(data); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PreparedGLTF {
|
|
||||||
std::vector<std::string> TextureKey;
|
|
||||||
std::unordered_map<std::string, uint16_t> Textures;
|
|
||||||
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 {
|
||||||
Texture, // Указание текстуры
|
Texture, // Указание текстуры
|
||||||
Combine, // Комбинирование
|
Combine, // Комбинирование
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
#include "AssetsPreloader.hpp"
|
#include "AssetsPreloader.hpp"
|
||||||
|
#include <atomic>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace LV {
|
namespace LV {
|
||||||
|
|
||||||
|
static TOS::Logger LOG = "AssetsPreloader";
|
||||||
|
|
||||||
static ResourceFile readFileBytes(const fs::path& path) {
|
static ResourceFile readFileBytes(const fs::path& path) {
|
||||||
std::ifstream file(path, std::ios::binary);
|
std::ifstream file(path, std::ios::binary);
|
||||||
if(!file)
|
if(!file)
|
||||||
@@ -155,6 +158,92 @@ AssetsPreloader::Out_reloadResources AssetsPreloader::_reloadResources(const Ass
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto resolveModelInfo = [&](std::string_view domain, std::string_view key) -> const ResourceFindInfo* {
|
||||||
|
auto& table = resourcesFirstStage[static_cast<size_t>(AssetType::Model)];
|
||||||
|
auto iterDomain = table.find(std::string(domain));
|
||||||
|
if(iterDomain == table.end())
|
||||||
|
return nullptr;
|
||||||
|
auto iterKey = iterDomain->second.find(std::string(key));
|
||||||
|
if(iterKey == iterDomain->second.end())
|
||||||
|
return nullptr;
|
||||||
|
return &iterKey->second;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::function<std::optional<js::object>(std::string_view, std::string_view, std::unordered_set<std::string>&)> loadModelProfile;
|
||||||
|
loadModelProfile = [&](std::string_view domain, std::string_view key, std::unordered_set<std::string>& visiting)
|
||||||
|
-> std::optional<js::object>
|
||||||
|
{
|
||||||
|
std::string fullKey = std::string(domain) + ':' + std::string(key);
|
||||||
|
if(!visiting.insert(fullKey).second) {
|
||||||
|
LOG.warn() << "Model parent cycle: " << fullKey;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResourceFindInfo* info = resolveModelInfo(domain, key);
|
||||||
|
if(!info) {
|
||||||
|
LOG.warn() << "Model file not found for parent: " << fullKey;
|
||||||
|
visiting.erase(fullKey);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceFile file = readFileBytes(info->Path);
|
||||||
|
std::string_view view(reinterpret_cast<const char*>(file.Data.data()), file.Data.size());
|
||||||
|
js::object obj = js::parse(view).as_object();
|
||||||
|
|
||||||
|
if(auto parentVal = obj.if_contains("parent")) {
|
||||||
|
if(parentVal->is_string()) {
|
||||||
|
std::string parentStr = std::string(parentVal->as_string());
|
||||||
|
auto [pDomain, pKeyRaw] = parseDomainKey(parentStr, domain);
|
||||||
|
fs::path pKeyPath = fs::path(pKeyRaw);
|
||||||
|
if(pKeyPath.extension().empty())
|
||||||
|
pKeyPath += ".json";
|
||||||
|
std::string pKey = pKeyPath.string();
|
||||||
|
|
||||||
|
std::optional<js::object> parent = loadModelProfile(pDomain, pKey, visiting);
|
||||||
|
if(parent) {
|
||||||
|
auto mergeFieldIfMissing = [&](const char* field) {
|
||||||
|
if(!obj.contains(field) && parent->contains(field))
|
||||||
|
obj[field] = parent->at(field);
|
||||||
|
};
|
||||||
|
|
||||||
|
mergeFieldIfMissing("cuboids");
|
||||||
|
mergeFieldIfMissing("sub_models");
|
||||||
|
mergeFieldIfMissing("gui_light");
|
||||||
|
mergeFieldIfMissing("ambient_occlusion");
|
||||||
|
|
||||||
|
if(auto parentTextures = parent->if_contains("textures"); parentTextures && parentTextures->is_object()) {
|
||||||
|
if(auto childTextures = obj.if_contains("textures"); childTextures && childTextures->is_object()) {
|
||||||
|
auto& childObj = childTextures->as_object();
|
||||||
|
const auto& parentObj = parentTextures->as_object();
|
||||||
|
for(const auto& [tkey, tval] : parentObj) {
|
||||||
|
if(!childObj.contains(tkey))
|
||||||
|
childObj.emplace(tkey, tval);
|
||||||
|
}
|
||||||
|
} else if(!obj.contains("textures")) {
|
||||||
|
obj["textures"] = parentTextures->as_object();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(auto parentDisplay = parent->if_contains("display"); parentDisplay && parentDisplay->is_object()) {
|
||||||
|
if(auto childDisplay = obj.if_contains("display"); childDisplay && childDisplay->is_object()) {
|
||||||
|
auto& childObj = childDisplay->as_object();
|
||||||
|
const auto& parentObj = parentDisplay->as_object();
|
||||||
|
for(const auto& [dkey, dval] : parentObj) {
|
||||||
|
if(!childObj.contains(dkey))
|
||||||
|
childObj.emplace(dkey, dval);
|
||||||
|
}
|
||||||
|
} else if(!obj.contains("display")) {
|
||||||
|
obj["display"] = parentDisplay->as_object();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visiting.erase(fullKey);
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
// Функция парсинга ресурсов
|
// Функция парсинга ресурсов
|
||||||
auto buildResource = [&](AssetType type, std::string_view domain, std::string_view key, const ResourceFindInfo& info) -> PendingResource {
|
auto buildResource = [&](AssetType type, std::string_view domain, std::string_view key, const ResourceFindInfo& info) -> PendingResource {
|
||||||
PendingResource out;
|
PendingResource out;
|
||||||
@@ -175,11 +264,25 @@ AssetsPreloader::Out_reloadResources AssetsPreloader::_reloadResources(const Ass
|
|||||||
return getId(AssetType::Texture, mDomain, mKey);
|
return getId(AssetType::Texture, mDomain, mKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto normalizeTexturePipelineSrc = [](std::string_view src) -> std::string {
|
||||||
|
std::string out(src);
|
||||||
|
auto isSpace = [](unsigned char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; };
|
||||||
|
size_t start = 0;
|
||||||
|
while(start < out.size() && isSpace(static_cast<unsigned char>(out[start])))
|
||||||
|
++start;
|
||||||
|
if(out.compare(start, 3, "tex") != 0) {
|
||||||
|
std::string pref = "tex ";
|
||||||
|
pref += out.substr(start);
|
||||||
|
return pref;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
std::function<std::vector<uint8_t>(const std::string_view)> textureResolver
|
std::function<std::vector<uint8_t>(const std::string_view)> textureResolver
|
||||||
= [&](const std::string_view texturePipelineSrc) -> std::vector<uint8_t>
|
= [&](const std::string_view texturePipelineSrc) -> std::vector<uint8_t>
|
||||||
{
|
{
|
||||||
TexturePipelineProgram tpp;
|
TexturePipelineProgram tpp;
|
||||||
bool flag = tpp.compile((std::string) texturePipelineSrc);
|
bool flag = tpp.compile(normalizeTexturePipelineSrc(texturePipelineSrc));
|
||||||
if(!flag)
|
if(!flag)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
@@ -200,13 +303,28 @@ AssetsPreloader::Out_reloadResources AssetsPreloader::_reloadResources(const Ass
|
|||||||
} else if (type == AssetType::Model) {
|
} else if (type == AssetType::Model) {
|
||||||
const std::string ext = info.Path.extension().string();
|
const std::string ext = info.Path.extension().string();
|
||||||
if (ext == ".json") {
|
if (ext == ".json") {
|
||||||
ResourceFile file = readFileBytes(info.Path);
|
std::unordered_set<std::string> visiting;
|
||||||
std::string_view view(reinterpret_cast<const char*>(file.Data.data()), file.Data.size());
|
std::optional<js::object> objOpt = loadModelProfile(domain, key, visiting);
|
||||||
js::object obj = js::parse(view).as_object();
|
if(!objOpt) {
|
||||||
|
LOG.warn() << "Не удалось загрузить модель: " << info.Path.string();
|
||||||
|
throw std::runtime_error("Model profile load failed");
|
||||||
|
}
|
||||||
|
js::object obj = std::move(*objOpt);
|
||||||
|
|
||||||
HeadlessModel hm;
|
HeadlessModel hm;
|
||||||
out.Header = hm.parse(obj, modelResolver, textureResolver);
|
out.Header = hm.parse(obj, modelResolver, textureResolver);
|
||||||
out.Resource = std::make_shared<std::u8string>(hm.dump());
|
std::u8string compiled = hm.dump();
|
||||||
|
if(hm.Cuboids.empty()) {
|
||||||
|
static std::atomic<uint32_t> debugEmptyModelLogCount = 0;
|
||||||
|
uint32_t idx = debugEmptyModelLogCount.fetch_add(1);
|
||||||
|
if(idx < 128) {
|
||||||
|
LOG.warn() << "Model compiled with empty cuboids: "
|
||||||
|
<< domain << ':' << key
|
||||||
|
<< " file=" << info.Path.string()
|
||||||
|
<< " size=" << compiled.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.Resource = std::make_shared<std::u8string>(std::move(compiled));
|
||||||
out.Hash = sha2::sha256((const uint8_t*) out.Resource->data(), out.Resource->size());
|
out.Hash = sha2::sha256((const uint8_t*) out.Resource->data(), out.Resource->size());
|
||||||
// } else if (ext == ".gltf" || ext == ".glb") {
|
// } else if (ext == ".gltf" || ext == ".glb") {
|
||||||
// /// TODO: добавить поддержку gltf
|
// /// TODO: добавить поддержку gltf
|
||||||
@@ -361,7 +479,7 @@ AssetsPreloader::Out_applyResourceChange AssetsPreloader::applyResourceChange(co
|
|||||||
// Не должно быть ресурсов, которые были помечены как потерянные
|
// Не должно быть ресурсов, которые были помечены как потерянные
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
std::unordered_set<uint32_t> changed;
|
std::unordered_set<uint32_t> changed;
|
||||||
for(const auto& [id, _, _] : result.NewOrChange[type])
|
for(const auto& [id, _, _2] : result.NewOrChange[type])
|
||||||
changed.insert(id);
|
changed.insert(id);
|
||||||
|
|
||||||
auto& lost = result.Lost[type];
|
auto& lost = result.Lost[type];
|
||||||
|
|||||||
Reference in New Issue
Block a user