2103 lines
70 KiB
C++
2103 lines
70 KiB
C++
#include "Abstract.hpp"
|
||
#include "Common/Net.hpp"
|
||
#include "TOSLib.hpp"
|
||
#include <boost/interprocess/file_mapping.hpp>
|
||
#include <boost/interprocess/mapped_region.hpp>
|
||
#include "boost/json.hpp"
|
||
#include "sha2.hpp"
|
||
#include <algorithm>
|
||
#include <boost/iostreams/filtering_streambuf.hpp>
|
||
#include <boost/iostreams/copy.hpp>
|
||
#include <boost/iostreams/filter/zlib.hpp>
|
||
#include <cstddef>
|
||
#include <endian.h>
|
||
#include <print>
|
||
#include <sstream>
|
||
#include <string>
|
||
#include <string_view>
|
||
#include <utility>
|
||
|
||
|
||
namespace LV {
|
||
|
||
namespace fs = std::filesystem;
|
||
|
||
|
||
CompressedVoxels compressVoxels_byte(const std::vector<VoxelCube>& voxels) {
|
||
std::u8string compressed;
|
||
std::vector<DefVoxelId> defines;
|
||
DefVoxelId maxValue = 0;
|
||
defines.reserve(voxels.size());
|
||
|
||
compressed.push_back(1);
|
||
|
||
assert(voxels.size() <= 65535);
|
||
|
||
for(const VoxelCube& cube : voxels) {
|
||
defines.push_back(cube.VoxelId);
|
||
if(cube.VoxelId > maxValue)
|
||
maxValue = cube.VoxelId;
|
||
}
|
||
|
||
{
|
||
std::sort(defines.begin(), defines.end());
|
||
auto last = std::unique(defines.begin(), defines.end());
|
||
defines.erase(last, defines.end());
|
||
defines.shrink_to_fit();
|
||
}
|
||
|
||
// Количество байт на идентификатор в сыром виде
|
||
uint8_t bytes_raw = std::ceil(std::log2(maxValue)/8);
|
||
assert(bytes_raw >= 1 && bytes_raw <= 3);
|
||
// Количество байт без таблицы индексов
|
||
size_t size_in_raw = (bytes_raw+6)*voxels.size();
|
||
// Количество байт на индекс
|
||
uint8_t bytes_per_define = std::ceil(std::log2(defines.size())/8);
|
||
assert(bytes_per_define == 1 || bytes_per_define == 2);
|
||
// Количество байт после таблицы индексов
|
||
size_t size_after_indices = (bytes_per_define+6)*voxels.size();
|
||
// Размер таблицы индексов
|
||
size_t indeces_size = 2+bytes_raw*defines.size();
|
||
|
||
if(indeces_size+size_after_indices < size_in_raw) {
|
||
// Выгодней писать с таблицей индексов
|
||
|
||
// Индексы, размер идентификатора ключа к таблице, размер значения таблицы
|
||
compressed.push_back(1 | (bytes_per_define << 1) | (bytes_raw << 2));
|
||
compressed.push_back(defines.size() & 0xff);
|
||
compressed.push_back((defines.size() >> 8) & 0xff);
|
||
|
||
// Таблица
|
||
for(DefVoxelId id : defines) {
|
||
compressed.push_back(id & 0xff);
|
||
if(bytes_raw > 1)
|
||
compressed.push_back((id >> 8) & 0xff);
|
||
if(bytes_raw > 2)
|
||
compressed.push_back((id >> 16) & 0xff);
|
||
}
|
||
|
||
compressed.push_back(voxels.size() & 0xff);
|
||
compressed.push_back((voxels.size() >> 8) & 0xff);
|
||
|
||
for(const VoxelCube& cube : voxels) {
|
||
size_t index = std::binary_search(defines.begin(), defines.end(), cube.VoxelId);
|
||
compressed.push_back(index & 0xff);
|
||
if(bytes_per_define > 1)
|
||
compressed.push_back((index >> 8) & 0xff);
|
||
|
||
compressed.push_back(cube.Meta);
|
||
compressed.push_back(cube.Pos.x);
|
||
compressed.push_back(cube.Pos.y);
|
||
compressed.push_back(cube.Pos.z);
|
||
compressed.push_back(cube.Size.x);
|
||
compressed.push_back(cube.Size.y);
|
||
compressed.push_back(cube.Size.z);
|
||
}
|
||
} else {
|
||
compressed.push_back(0 | (0 << 1) | (bytes_raw << 2));
|
||
|
||
compressed.push_back(voxels.size() & 0xff);
|
||
compressed.push_back((voxels.size() >> 8) & 0xff);
|
||
|
||
for(const VoxelCube& cube : voxels) {
|
||
compressed.push_back(cube.VoxelId & 0xff);
|
||
if(bytes_raw > 1)
|
||
compressed.push_back((cube.VoxelId >> 8) & 0xff);
|
||
if(bytes_raw > 2)
|
||
compressed.push_back((cube.VoxelId >> 16) & 0xff);
|
||
|
||
compressed.push_back(cube.Meta);
|
||
compressed.push_back(cube.Pos.x);
|
||
compressed.push_back(cube.Pos.y);
|
||
compressed.push_back(cube.Pos.z);
|
||
compressed.push_back(cube.Size.x);
|
||
compressed.push_back(cube.Size.y);
|
||
compressed.push_back(cube.Size.z);
|
||
}
|
||
}
|
||
|
||
return {compressLinear(compressed), defines};
|
||
}
|
||
|
||
CompressedVoxels compressVoxels_bit(const std::vector<VoxelCube>& voxels) {
|
||
std::vector<DefVoxelId> profile;
|
||
std::vector<DefVoxelId> one_byte[7];
|
||
|
||
DefVoxelId maxValueProfile = 0;
|
||
DefVoxelId maxValues[7] = {0};
|
||
|
||
profile.reserve(voxels.size());
|
||
for(int iter = 0; iter < 7; iter++)
|
||
one_byte[iter].reserve(voxels.size());
|
||
|
||
assert(voxels.size() <= 65535);
|
||
for(const VoxelCube& cube : voxels) {
|
||
profile.push_back(cube.VoxelId);
|
||
|
||
one_byte[0].push_back(cube.Meta);
|
||
one_byte[1].push_back(cube.Pos.x);
|
||
one_byte[2].push_back(cube.Pos.y);
|
||
one_byte[3].push_back(cube.Pos.z);
|
||
one_byte[4].push_back(cube.Size.x);
|
||
one_byte[5].push_back(cube.Size.y);
|
||
one_byte[6].push_back(cube.Size.z);
|
||
|
||
if(cube.VoxelId > maxValueProfile)
|
||
maxValueProfile = cube.VoxelId;
|
||
if(cube.Meta > maxValues[0])
|
||
maxValues[0] = cube.Meta;
|
||
if(cube.Pos.x > maxValues[1])
|
||
maxValues[1] = cube.Pos.x;
|
||
if(cube.Pos.y > maxValues[2])
|
||
maxValues[2] = cube.Pos.y;
|
||
if(cube.Pos.z > maxValues[3])
|
||
maxValues[3] = cube.Pos.z;
|
||
if(cube.Size.x > maxValues[4])
|
||
maxValues[4] = cube.Size.x;
|
||
if(cube.Size.y > maxValues[5])
|
||
maxValues[5] = cube.Size.y;
|
||
if(cube.Size.z > maxValues[6])
|
||
maxValues[6] = cube.Size.z;
|
||
}
|
||
|
||
{
|
||
std::sort(profile.begin(), profile.end());
|
||
auto last = std::unique(profile.begin(), profile.end());
|
||
profile.erase(last, profile.end());
|
||
profile.shrink_to_fit();
|
||
}
|
||
|
||
for (int iter = 0; iter < 7; iter++) {
|
||
std::sort(one_byte[iter].begin(), one_byte[iter].end());
|
||
auto last = std::unique(one_byte[iter].begin(), one_byte[iter].end());
|
||
one_byte[iter].erase(last, one_byte[iter].end());
|
||
}
|
||
|
||
// Количество бит на идентификатор в сыром виде
|
||
size_t bits_raw_profile = std::ceil(std::log2(maxValueProfile));
|
||
assert(bits_raw_profile >= 1 && bits_raw_profile <= 24);
|
||
size_t bits_index_profile = std::ceil(std::log2(profile.size()));
|
||
bool indices_profile = 16+bits_raw_profile*profile.size()+bits_index_profile*voxels.size() < bits_raw_profile*voxels.size();
|
||
|
||
size_t bits_raw[7];
|
||
size_t bits_index[7];
|
||
bool indices[7];
|
||
|
||
for(int iter = 0; iter < 7; iter++) {
|
||
bits_raw[iter] = std::ceil(std::log2(maxValues[iter]));
|
||
assert(bits_raw[iter] >= 1 && bits_raw[iter] <= 8);
|
||
bits_index[iter] = std::ceil(std::log2(one_byte[iter].size()));
|
||
}
|
||
|
||
std::vector<bool> buff;
|
||
|
||
buff.push_back(indices_profile);
|
||
for(int iter = 0; iter < 7; iter++)
|
||
buff.push_back(indices[iter]);
|
||
|
||
auto write = [&](size_t value, int count) {
|
||
for(int iter = 0; iter < count; iter++)
|
||
buff.push_back((value >> iter) & 0x1);
|
||
};
|
||
|
||
write(0, 8);
|
||
|
||
// Таблицы
|
||
if(indices_profile) {
|
||
write(profile.size(), 16);
|
||
write(bits_raw_profile, 5);
|
||
write(bits_index_profile, 4);
|
||
|
||
for(DefNodeId id : profile)
|
||
write(id, bits_raw_profile);
|
||
} else {
|
||
write(bits_raw_profile, 5);
|
||
}
|
||
|
||
for(int iter = 0; iter < 7; iter++) {
|
||
if(indices[iter]) {
|
||
write(one_byte[iter].size(), 16);
|
||
write(bits_raw[iter], 3);
|
||
write(bits_index[iter], 3);
|
||
|
||
for(uint8_t id : one_byte[iter])
|
||
write(id, bits_raw[iter]);
|
||
} else {
|
||
write(bits_raw[iter], 3);
|
||
}
|
||
}
|
||
|
||
// Данные
|
||
|
||
write(voxels.size(), 16);
|
||
|
||
for(const VoxelCube& cube : voxels) {
|
||
if(indices_profile)
|
||
write(std::binary_search(profile.begin(), profile.end(), cube.VoxelId), bits_index_profile);
|
||
else
|
||
write(cube.VoxelId, bits_raw_profile);
|
||
|
||
for(int iter = 0; iter < 7; iter++) {
|
||
uint8_t val;
|
||
if(iter == 0) val = cube.Meta;
|
||
else if(iter == 1) val = cube.Pos.x;
|
||
else if(iter == 2) val = cube.Pos.y;
|
||
else if(iter == 3) val = cube.Pos.z;
|
||
else if(iter == 4) val = cube.Size.x;
|
||
else if(iter == 5) val = cube.Size.y;
|
||
else if(iter == 6) val = cube.Size.z;
|
||
|
||
if(indices[iter])
|
||
write(std::binary_search(one_byte[iter].begin(), one_byte[iter].end(), val), bits_index[iter]);
|
||
else
|
||
write(val, bits_raw[iter]);
|
||
}
|
||
}
|
||
|
||
std::u8string compressed((buff.size()+7)/8, '\0');
|
||
|
||
for(int begin = 0, end = compressed.size()*8-buff.size(); begin < end; begin++)
|
||
compressed.push_back(0);
|
||
|
||
for(size_t iter = 0; iter < buff.size(); iter++)
|
||
compressed[iter / 8] |= (buff[iter] << (iter % 8));
|
||
|
||
return {compressLinear(compressed), profile};
|
||
}
|
||
|
||
CompressedVoxels compressVoxels(const std::vector<VoxelCube>& voxels, bool fast) {
|
||
if(fast)
|
||
return compressVoxels_byte(voxels);
|
||
else
|
||
return compressVoxels_bit(voxels);
|
||
}
|
||
|
||
std::vector<VoxelCube> unCompressVoxels_byte(const std::u8string& compressed) {
|
||
size_t pos = 1;
|
||
|
||
auto read = [&]() -> size_t {
|
||
assert(pos < compressed.size());
|
||
return compressed[pos++];
|
||
};
|
||
|
||
uint8_t cmd = read();
|
||
|
||
if(cmd & 0x1) {
|
||
// Таблица
|
||
uint8_t bytes_per_define = (cmd >> 1) & 0x1;
|
||
uint8_t bytes_raw = (cmd >> 2) & 0x3;
|
||
|
||
std::vector<DefVoxelId> defines;
|
||
defines.resize(read() | (read() << 8));
|
||
|
||
for(size_t iter = 0; iter < defines.size(); iter++) {
|
||
DefVoxelId id = read();
|
||
if(bytes_raw > 1)
|
||
id |= read() << 8;
|
||
if(bytes_raw > 2)
|
||
id |= read() << 16;
|
||
}
|
||
|
||
std::vector<VoxelCube> voxels;
|
||
voxels.resize(read() | (read() << 8));
|
||
|
||
for(size_t iter = 0; iter < voxels.size(); iter++) {
|
||
size_t index = read();
|
||
|
||
if(bytes_per_define > 1)
|
||
index |= read() << 8;
|
||
|
||
VoxelCube &cube = voxels[iter];
|
||
|
||
assert(index < defines.size());
|
||
|
||
cube.VoxelId = defines[index];
|
||
cube.Meta = read();
|
||
cube.Pos.x = read();
|
||
cube.Pos.y = read();
|
||
cube.Pos.z = read();
|
||
cube.Size.x = read();
|
||
cube.Size.y = read();
|
||
cube.Size.z = read();
|
||
}
|
||
|
||
return voxels;
|
||
} else {
|
||
uint8_t bytes_raw = (cmd >> 2) & 0x3;
|
||
|
||
std::vector<VoxelCube> voxels;
|
||
voxels.resize(read() | (read() << 8));
|
||
|
||
for(size_t iter = 0; iter < voxels.size(); iter++) {
|
||
VoxelCube &cube = voxels[iter];
|
||
|
||
cube.VoxelId = read();
|
||
if(bytes_raw > 1)
|
||
cube.VoxelId |= read() << 8;
|
||
if(bytes_raw > 2)
|
||
cube.VoxelId |= read() << 16;
|
||
|
||
cube.Meta = read();
|
||
cube.Pos.x = read();
|
||
cube.Pos.y = read();
|
||
cube.Pos.z = read();
|
||
cube.Size.x = read();
|
||
cube.Size.y = read();
|
||
cube.Size.z = read();
|
||
}
|
||
|
||
return voxels;
|
||
}
|
||
}
|
||
|
||
std::vector<VoxelCube> unCompressVoxels_bit(const std::u8string& compressed) {
|
||
size_t pos = 1;
|
||
|
||
auto read = [&](int bits) -> size_t {
|
||
size_t out = 0;
|
||
for(int iter = 0; iter < bits; iter++, pos++) {
|
||
assert(pos < compressed.size()*8);
|
||
|
||
out |= (compressed[pos / 8] >> (pos % 8)) << iter;
|
||
}
|
||
|
||
return out;
|
||
};
|
||
|
||
bool indices_profile = read(1);
|
||
bool indices[7];
|
||
|
||
for(int iter = 0; iter < 7; iter++)
|
||
indices[iter] = read(1);
|
||
|
||
std::vector<DefVoxelId> profile;
|
||
std::vector<DefVoxelId> one_byte[7];
|
||
uint8_t bits_raw_profile;
|
||
uint8_t bits_index_profile;
|
||
size_t bits_raw[7];
|
||
size_t bits_index[7];
|
||
|
||
// Таблицы
|
||
if(indices_profile) {
|
||
profile.resize(read(16));
|
||
bits_raw_profile = read(5);
|
||
bits_index_profile = read(4);
|
||
|
||
for(size_t iter = 0; iter < profile.size(); iter++)
|
||
profile[iter] = read(bits_raw_profile);
|
||
} else {
|
||
bits_raw_profile = read(5);
|
||
}
|
||
|
||
for(int iter = 0; iter < 7; iter++) {
|
||
if(indices[iter]) {
|
||
one_byte[iter].resize(read(16));
|
||
bits_raw[iter] = read(3);
|
||
bits_index[iter] = read(3);
|
||
|
||
for(size_t iter2 = 0; iter2 < one_byte[iter].size(); iter2++)
|
||
one_byte[iter][iter2] = read(bits_raw[iter]);
|
||
} else {
|
||
bits_raw[iter] = read(3);
|
||
}
|
||
}
|
||
|
||
// Данные
|
||
std::vector<VoxelCube> voxels;
|
||
voxels.resize(read(16));
|
||
|
||
for(size_t iter = 0; iter < voxels.size(); iter++) {
|
||
VoxelCube &cube = voxels[iter];
|
||
|
||
if(indices_profile)
|
||
cube.VoxelId = profile[read(bits_index_profile)];
|
||
else
|
||
cube.VoxelId = read(bits_raw_profile);
|
||
|
||
for(int iter = 0; iter < 7; iter++) {
|
||
uint8_t val;
|
||
|
||
if(indices[iter])
|
||
val = one_byte[iter][read(bits_index[iter])];
|
||
else
|
||
val = read(bits_raw[iter]);
|
||
|
||
if(iter == 0) cube.Meta = val;
|
||
else if(iter == 1) cube.Pos.x = val;
|
||
else if(iter == 2) cube.Pos.y = val;
|
||
else if(iter == 3) cube.Pos.z = val;
|
||
else if(iter == 4) cube.Size.x = val;
|
||
else if(iter == 5) cube.Size.y = val;
|
||
else if(iter == 6) cube.Size.z = val;
|
||
}
|
||
}
|
||
|
||
return voxels;
|
||
}
|
||
|
||
std::vector<VoxelCube> unCompressVoxels(const std::u8string& compressed) {
|
||
const std::u8string& next = unCompressLinear(compressed);
|
||
if(next.front())
|
||
return unCompressVoxels_byte(next);
|
||
else
|
||
return unCompressVoxels_bit(next);
|
||
}
|
||
|
||
|
||
|
||
CompressedNodes compressNodes_byte(const Node* nodes) {
|
||
std::u8string compressed;
|
||
|
||
std::vector<DefNodeId> profiles;
|
||
|
||
profiles.reserve(16*16*16);
|
||
|
||
compressed.push_back(1);
|
||
|
||
DefNodeId maxValueProfile = 0;
|
||
|
||
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||
const Node &node = nodes[iter];
|
||
|
||
profiles.push_back(node.NodeId);
|
||
|
||
if(node.NodeId > maxValueProfile)
|
||
maxValueProfile = node.NodeId;
|
||
}
|
||
|
||
{
|
||
std::sort(profiles.begin(), profiles.end());
|
||
auto last = std::unique(profiles.begin(), profiles.end());
|
||
profiles.erase(last, profiles.end());
|
||
profiles.shrink_to_fit();
|
||
}
|
||
|
||
|
||
// Количество байт на идентификатор в сыром виде
|
||
uint8_t bytes_raw_profile = std::ceil(std::log2(maxValueProfile+1)/8);
|
||
assert(bytes_raw_profile >= 0 && bytes_raw_profile <= 3);
|
||
// Количество байт на индекс
|
||
uint8_t bytes_indices_profile = std::ceil(std::log2(profiles.size())/8);
|
||
assert(bytes_indices_profile >= 0 && bytes_indices_profile <= 2);
|
||
|
||
bool indices_profile = 3+bytes_raw_profile*profiles.size()+bytes_indices_profile*16*16*16 < bytes_raw_profile*16*16*16;
|
||
|
||
compressed.push_back(indices_profile | (bytes_raw_profile << 1) | (bytes_indices_profile << 3));
|
||
|
||
if(indices_profile) {
|
||
// Таблица
|
||
compressed.push_back(profiles.size() & 0xff);
|
||
compressed.push_back((profiles.size() >> 8) & 0xff);
|
||
compressed.push_back((profiles.size() >> 16) & 0xff);
|
||
|
||
for(DefNodeId id : profiles) {
|
||
if(bytes_raw_profile > 0)
|
||
compressed.push_back(id & 0xff);
|
||
if(bytes_raw_profile > 1)
|
||
compressed.push_back((id >> 8) & 0xff);
|
||
if(bytes_raw_profile > 2)
|
||
compressed.push_back((id >> 16) & 0xff);
|
||
}
|
||
|
||
// Данные
|
||
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||
const Node &node = nodes[iter];
|
||
|
||
size_t index = std::binary_search(profiles.begin(), profiles.end(), node.NodeId);
|
||
|
||
compressed.push_back(index & 0xff);
|
||
if(bytes_indices_profile > 1)
|
||
compressed.push_back((index >> 8) & 0xff);
|
||
|
||
compressed.push_back(node.Meta);
|
||
}
|
||
} else {
|
||
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||
const Node &node = nodes[iter];
|
||
|
||
if(bytes_raw_profile > 0)
|
||
compressed.push_back(node.NodeId & 0xff);
|
||
if(bytes_raw_profile > 1)
|
||
compressed.push_back((node.NodeId >> 8) & 0xff);
|
||
if(bytes_raw_profile > 2)
|
||
compressed.push_back((node.NodeId >> 8) & 0xff);
|
||
|
||
compressed.push_back(node.Meta);
|
||
}
|
||
}
|
||
|
||
profiles.shrink_to_fit();
|
||
|
||
return {compressLinear(compressed), profiles};
|
||
}
|
||
|
||
CompressedNodes compressNodes_bit(const Node* nodes) {
|
||
std::u8string compressed;
|
||
|
||
std::vector<DefNodeId> profiles;
|
||
std::vector<DefNodeId> meta;
|
||
|
||
profiles.reserve(16*16*16);
|
||
meta.reserve(16*16*16);
|
||
|
||
compressed.push_back(1);
|
||
|
||
DefNodeId maxValueProfile = 0,
|
||
maxValueMeta = 0;
|
||
|
||
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||
const Node &node = nodes[iter];
|
||
|
||
profiles.push_back(node.NodeId);
|
||
meta.push_back(node.Meta);
|
||
|
||
if(node.NodeId > maxValueProfile)
|
||
maxValueProfile = node.NodeId;
|
||
|
||
if(node.Meta > maxValueMeta)
|
||
maxValueMeta = node.Meta;
|
||
}
|
||
|
||
{
|
||
std::sort(profiles.begin(), profiles.end());
|
||
auto last = std::unique(profiles.begin(), profiles.end());
|
||
profiles.erase(last, profiles.end());
|
||
profiles.shrink_to_fit();
|
||
}
|
||
|
||
{
|
||
std::sort(meta.begin(), meta.end());
|
||
auto last = std::unique(meta.begin(), meta.end());
|
||
meta.erase(last, meta.end());
|
||
meta.shrink_to_fit();
|
||
}
|
||
|
||
|
||
// Количество бит на идентификатор в сыром виде
|
||
uint8_t bits_raw_profile = std::ceil(std::log2(maxValueProfile+1));
|
||
assert(bits_raw_profile >= 1 && bits_raw_profile <= 24);
|
||
// Количество бит на индекс
|
||
uint8_t bits_indices_profile = std::ceil(std::log2(profiles.size()));
|
||
assert(bits_indices_profile >= 1 && bits_indices_profile <= 16);
|
||
|
||
bool indices_profile = 3*8+bits_raw_profile*profiles.size()+bits_indices_profile*16*16*16 < bits_raw_profile*16*16*16;
|
||
|
||
|
||
std::vector<bool> buff;
|
||
|
||
auto write = [&](size_t value, int count) {
|
||
for(int iter = 0; iter < count; iter++)
|
||
buff.push_back((value >> iter) & 0x1);
|
||
};
|
||
|
||
|
||
write(indices_profile, 1);
|
||
write(bits_raw_profile, 5);
|
||
write(bits_indices_profile, 4);
|
||
|
||
// Количество бит на идентификатор в сыром виде
|
||
uint8_t bits_raw_meta = std::ceil(std::log2(maxValueMeta+1));
|
||
assert(bits_raw_meta >= 1 && bits_raw_meta <= 8);
|
||
// Количество бит на индекс
|
||
uint8_t bits_indices_meta = std::ceil(std::log2(meta.size()));
|
||
assert(bits_indices_meta >= 1 && bits_indices_meta <= 8);
|
||
|
||
bool indices_meta = 3*8+bits_raw_meta*profiles.size()+bits_indices_meta*16*16*16 < bits_raw_meta*16*16*16;
|
||
|
||
write(indices_meta, 1);
|
||
write(bits_raw_meta, 3);
|
||
write(bits_indices_meta, 3);
|
||
|
||
// Таблицы
|
||
if(indices_profile) {
|
||
write(profiles.size(), 12);
|
||
|
||
for(DefNodeId id : profiles) {
|
||
write(id, bits_raw_profile);
|
||
}
|
||
}
|
||
|
||
|
||
if(indices_meta) {
|
||
write(meta.size(), 8);
|
||
|
||
for(DefNodeId id : meta) {
|
||
write(id, bits_raw_meta);
|
||
}
|
||
}
|
||
|
||
|
||
// Данные
|
||
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||
const Node &node = nodes[iter];
|
||
|
||
if(indices_profile) {
|
||
size_t index = std::binary_search(profiles.begin(), profiles.end(), node.NodeId);
|
||
write(index, bits_indices_profile);
|
||
} else {
|
||
write(node.NodeId, bits_raw_profile);
|
||
}
|
||
|
||
if(indices_meta) {
|
||
size_t index = std::binary_search(meta.begin(), meta.end(), node.Meta);
|
||
write(index, bits_indices_meta);
|
||
} else {
|
||
write(node.Meta, bits_raw_meta);
|
||
}
|
||
}
|
||
|
||
return {compressLinear(compressed), profiles};
|
||
}
|
||
|
||
CompressedNodes compressNodes(const Node* nodes, bool fast) {
|
||
std::u8string data(16*16*16*sizeof(Node), '\0');
|
||
const char8_t *ptr = (const char8_t*) nodes;
|
||
std::copy(ptr, ptr+16*16*16*4, data.data());
|
||
|
||
std::vector<DefNodeId> node(16*16*16);
|
||
for(int iter = 0; iter < 16*16*16; iter++) {
|
||
node[iter] = nodes[iter].NodeId;
|
||
}
|
||
|
||
{
|
||
std::sort(node.begin(), node.end());
|
||
auto last = std::unique(node.begin(), node.end());
|
||
node.erase(last, node.end());
|
||
node.shrink_to_fit();
|
||
}
|
||
|
||
return {compressLinear(data), std::move(node)};
|
||
|
||
// if(fast)
|
||
// return compressNodes_byte(nodes);
|
||
// else
|
||
// return compressNodes_bit(nodes);
|
||
}
|
||
|
||
void unCompressNodes_byte(const std::u8string& compressed, Node* ptr) {
|
||
size_t pos = 1;
|
||
|
||
auto read = [&]() -> size_t {
|
||
assert(pos < compressed.size());
|
||
return compressed[pos++];
|
||
};
|
||
|
||
uint8_t value = read();
|
||
|
||
uint8_t bytes_raw_profile = (value >> 1) & 0x3;
|
||
uint8_t bytes_indices_profile = (value >> 3) & 0x3;
|
||
bool indices_profile = value & 0x1;
|
||
|
||
if(indices_profile) {
|
||
std::vector<DefNodeId> profiles;
|
||
profiles.resize(read() | (read() << 8) | (read() << 16));
|
||
|
||
for(size_t iter = 0; iter < profiles.size(); iter++) {
|
||
DefNodeId id = 0;
|
||
|
||
if(bytes_raw_profile > 0)
|
||
id = read();
|
||
if(bytes_raw_profile > 1)
|
||
id |= read() << 8;
|
||
if(bytes_raw_profile > 2)
|
||
id |= read() << 16;
|
||
}
|
||
|
||
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||
Node &node = ptr[iter];
|
||
|
||
DefNodeId index = read();
|
||
if(bytes_indices_profile > 1)
|
||
index |= read() << 8;
|
||
|
||
node.NodeId = profiles[index];
|
||
node.Meta = read();
|
||
}
|
||
} else {
|
||
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||
Node &node = ptr[iter];
|
||
|
||
node.NodeId = 0;
|
||
|
||
if(bytes_raw_profile > 0)
|
||
node.NodeId = read();
|
||
if(bytes_raw_profile > 1)
|
||
node.NodeId |= read() << 8;
|
||
if(bytes_raw_profile > 2)
|
||
node.NodeId |= read() << 16;
|
||
|
||
node.Meta = read();
|
||
}
|
||
}
|
||
}
|
||
|
||
void unCompressNodes_bit(const std::u8string& compressed, Node* ptr) {
|
||
size_t pos = 1;
|
||
|
||
auto read = [&](int bits) -> size_t {
|
||
size_t out = 0;
|
||
for(int iter = 0; iter < bits; iter++, pos++) {
|
||
assert(pos < compressed.size()*8);
|
||
|
||
out |= (compressed[pos / 8] >> (pos % 8)) << iter;
|
||
}
|
||
|
||
return out;
|
||
};
|
||
|
||
std::vector<DefNodeId> meta;
|
||
|
||
|
||
bool indices_profile = read(1);
|
||
uint8_t bits_raw_profile = read(5);
|
||
uint8_t bits_indices_profile = read(4);
|
||
|
||
bool indices_meta = read(1);
|
||
uint8_t bits_raw_meta = read(3);
|
||
uint8_t bits_indices_meta = read(3);
|
||
|
||
std::vector<DefNodeId> profiles;
|
||
|
||
// Таблицы
|
||
if(indices_profile) {
|
||
profiles.resize(read(12));
|
||
|
||
for(size_t iter = 0; iter < profiles.size(); iter++) {
|
||
profiles[iter] = read(bits_raw_profile);
|
||
}
|
||
}
|
||
|
||
|
||
if(indices_meta) {
|
||
meta.resize(read(8));
|
||
|
||
for(size_t iter = 0; iter < meta.size(); iter++) {
|
||
meta[iter] = read(bits_raw_meta);
|
||
}
|
||
}
|
||
|
||
|
||
// Данные
|
||
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||
Node &node = ptr[iter];
|
||
|
||
if(indices_profile) {
|
||
node.NodeId = profiles[read(bits_indices_profile)];
|
||
} else {
|
||
node.NodeId = read(bits_raw_profile);
|
||
}
|
||
|
||
if(indices_meta) {
|
||
node.Meta = meta[read(bits_indices_meta)];
|
||
} else {
|
||
node.Meta = read(bits_raw_meta);
|
||
}
|
||
}
|
||
}
|
||
|
||
void unCompressNodes(const std::u8string& compressed, Node* ptr) {
|
||
const std::u8string& next = unCompressLinear(compressed);
|
||
const Node *lPtr = (const Node*) next.data();
|
||
std::copy(lPtr, lPtr+16*16*16, ptr);
|
||
|
||
// if(next.front())
|
||
// return unCompressNodes_byte(next, ptr);
|
||
// else
|
||
// return unCompressNodes_bit(next, ptr);
|
||
}
|
||
|
||
std::u8string compressLinear(const std::u8string& data) {
|
||
std::stringstream in;
|
||
in.write((const char*) data.data(), data.size());
|
||
|
||
boost::iostreams::filtering_streambuf<boost::iostreams::input> out;
|
||
out.push(boost::iostreams::zlib_compressor());
|
||
out.push(in);
|
||
|
||
std::stringstream compressed;
|
||
boost::iostreams::copy(out, compressed);
|
||
std::string outString = compressed.str();
|
||
|
||
return *(std::u8string*) &outString;
|
||
}
|
||
|
||
std::u8string unCompressLinear(const std::u8string& data) {
|
||
std::stringstream in;
|
||
in.write((const char*) data.data(), data.size());
|
||
|
||
boost::iostreams::filtering_streambuf<boost::iostreams::input> out;
|
||
out.push(boost::iostreams::zlib_decompressor());
|
||
out.push(in);
|
||
|
||
std::stringstream compressed;
|
||
boost::iostreams::copy(out, compressed);
|
||
std::string outString = compressed.str();
|
||
|
||
return *(std::u8string*) &outString;
|
||
}
|
||
|
||
PreparedNodeState::PreparedNodeState(const std::string_view modid, const js::object& profile) {
|
||
for(auto& [condition, variability] : profile) {
|
||
// Распарсить условие
|
||
uint16_t node = parseCondition(condition);
|
||
|
||
boost::container::small_vector<
|
||
std::pair<float, std::variant<Model, VectorModel>>,
|
||
1
|
||
> models;
|
||
|
||
if(variability.is_array()) {
|
||
// Варианты условия
|
||
for(const js::value& model : variability.as_array()) {
|
||
models.push_back(parseModel(modid, model.as_object()));
|
||
}
|
||
|
||
HasVariability = true;
|
||
} else if (variability.is_object()) {
|
||
// Один список моделей на условие
|
||
models.push_back(parseModel(modid, variability.as_object()));
|
||
} else {
|
||
MAKE_ERROR("Условию должен соответствовать список или объект");
|
||
}
|
||
|
||
Routes.emplace_back(node, std::move(models));
|
||
}
|
||
}
|
||
|
||
PreparedNodeState::PreparedNodeState(const std::string_view modid, const sol::table& profile) {
|
||
|
||
}
|
||
|
||
PreparedNodeState::PreparedNodeState(const std::u8string& data) {
|
||
Net::LinearReader lr(data);
|
||
|
||
uint16_t size;
|
||
lr >> size;
|
||
|
||
ModelToLocalId.reserve(size);
|
||
for(int counter = 0; counter < size; counter++) {
|
||
std::string domain, key;
|
||
lr >> domain >> key;
|
||
ModelToLocalId.emplace_back(std::move(domain), std::move(key));
|
||
}
|
||
|
||
lr >> size;
|
||
Nodes.reserve(size);
|
||
for(int counter = 0; counter < size; counter++) {
|
||
uint8_t index = lr.read<uint8_t>();
|
||
Node node;
|
||
|
||
switch(index) {
|
||
case 0: node.v = Node::Num(lr.read<int>()); break;
|
||
case 1: node.v = Node::Var(lr.read<std::string>()); break;
|
||
case 2: {
|
||
Node::Unary unary;
|
||
unary.op = Op(lr.read<uint8_t>());
|
||
unary.rhs = lr.read<uint16_t>();
|
||
node.v = unary;
|
||
break;
|
||
}
|
||
case 3: {
|
||
Node::Binary binary;
|
||
binary.op = Op(lr.read<uint8_t>());
|
||
binary.lhs = lr.read<uint16_t>();
|
||
binary.rhs = lr.read<uint16_t>();
|
||
node.v = binary;
|
||
break;
|
||
}
|
||
default:
|
||
MAKE_ERROR("Ошибка формата данных");
|
||
}
|
||
|
||
Nodes.emplace_back(std::move(node));
|
||
}
|
||
|
||
lr >> size;
|
||
Routes.reserve(size);
|
||
for(int counter = 0; counter < size; counter++) {
|
||
uint16_t nodeId, variantsSize;
|
||
lr >> nodeId >> variantsSize;
|
||
|
||
boost::container::small_vector<
|
||
std::pair<float, std::variant<Model, VectorModel>>,
|
||
1
|
||
> variants;
|
||
variants.reserve(variantsSize);
|
||
|
||
for(int counter2 = 0; counter2 < variantsSize; counter2++) {
|
||
float weight;
|
||
lr >> weight;
|
||
|
||
if(lr.read<uint8_t>()) {
|
||
VectorModel mod;
|
||
uint16_t modelsSize;
|
||
lr >> modelsSize;
|
||
mod.Models.reserve(modelsSize);
|
||
|
||
for(int counter3 = 0; counter3 < modelsSize; counter3++) {
|
||
Model mod2;
|
||
lr >> mod2.Id;
|
||
mod2.UVLock = lr.read<uint8_t>();
|
||
|
||
uint16_t transformsSize;
|
||
lr >> transformsSize;
|
||
|
||
mod2.Transforms.reserve(transformsSize);
|
||
for(int counter4 = 0; counter4 < transformsSize; counter4++) {
|
||
Transformation tr;
|
||
tr.Op = Transformation::EnumTransform(lr.read<uint8_t>());
|
||
mod2.Transforms.push_back(tr);
|
||
}
|
||
}
|
||
|
||
mod.UVLock = lr.read<uint8_t>();
|
||
|
||
uint16_t transformsSize;
|
||
lr >> transformsSize;
|
||
|
||
mod.Transforms.reserve(transformsSize);
|
||
for(int counter3 = 0; counter3 < transformsSize; counter3++) {
|
||
Transformation tr;
|
||
tr.Op = Transformation::EnumTransform(lr.read<uint8_t>());
|
||
mod.Transforms.push_back(tr);
|
||
}
|
||
|
||
variants.emplace_back(weight, std::move(mod));
|
||
} else {
|
||
Model mod;
|
||
lr >> mod.Id;
|
||
mod.UVLock = lr.read<uint8_t>();
|
||
uint16_t transformsSize;
|
||
lr >> transformsSize;
|
||
|
||
mod.Transforms.reserve(transformsSize);
|
||
for(int counter3 = 0; counter3 < transformsSize; counter3++) {
|
||
Transformation tr;
|
||
tr.Op = Transformation::EnumTransform(lr.read<uint8_t>());
|
||
mod.Transforms.push_back(tr);
|
||
}
|
||
|
||
variants.emplace_back(weight, std::move(mod));
|
||
}
|
||
}
|
||
}
|
||
|
||
lr.checkUnreaded();
|
||
}
|
||
|
||
std::u8string PreparedNodeState::dump() const {
|
||
Net::Packet result;
|
||
|
||
// ResourceToLocalId
|
||
assert(ModelToLocalId.size() < (1 << 16));
|
||
result << uint16_t(ModelToLocalId.size());
|
||
|
||
for(const auto& [domain, key] : ModelToLocalId) {
|
||
assert(domain.size() < 32);
|
||
result << domain;
|
||
assert(key.size() < 32);
|
||
result << key;
|
||
}
|
||
|
||
// Nodes
|
||
assert(Nodes.size() < (1 << 16));
|
||
result << uint16_t(Nodes.size());
|
||
|
||
for(const Node& node : Nodes) {
|
||
result << uint8_t(node.v.index());
|
||
|
||
if(const Node::Num* val = std::get_if<Node::Num>(&node.v)) {
|
||
result << val->v;
|
||
} else if(const Node::Var* val = std::get_if<Node::Var>(&node.v)) {
|
||
assert(val->name.size() < 32);
|
||
result << val->name;
|
||
} else if(const Node::Unary* val = std::get_if<Node::Unary>(&node.v)) {
|
||
result << uint8_t(val->op) << val->rhs;
|
||
} else if(const Node::Binary* val = std::get_if<Node::Binary>(&node.v)) {
|
||
result << uint8_t(val->op) << val->lhs << val->rhs;
|
||
} else {
|
||
std::unreachable();
|
||
}
|
||
}
|
||
|
||
// Routes
|
||
assert(Routes.size() < (1 << 16));
|
||
result << uint16_t(Routes.size());
|
||
|
||
for(const auto& [nodeId, variants] : Routes) {
|
||
result << nodeId;
|
||
|
||
assert(variants.size() < (1 << 16));
|
||
result << uint16_t(variants.size());
|
||
|
||
for(const auto& [weight, model] : variants) {
|
||
result << weight << uint8_t(model.index());
|
||
|
||
if(const Model* val = std::get_if<Model>(&model)) {
|
||
result << val->Id << uint8_t(val->UVLock);
|
||
|
||
assert(val->Transforms.size() < (1 << 16));
|
||
result << uint16_t(val->Transforms.size());
|
||
|
||
for(const Transformation& val : val->Transforms) {
|
||
result << uint8_t(val.Op) << val.Value;
|
||
}
|
||
} else if(const VectorModel* val = std::get_if<VectorModel>(&model)) {
|
||
assert(val->Models.size() < (1 << 16));
|
||
result << uint16_t(val->Models.size());
|
||
|
||
for(const Model& subModel : val->Models) {
|
||
result << subModel.Id << uint8_t(subModel.UVLock);
|
||
|
||
assert(subModel.Transforms.size() < (1 << 16));
|
||
result << uint16_t(subModel.Transforms.size());
|
||
|
||
for(const Transformation& val : subModel.Transforms)
|
||
result << uint8_t(val.Op) << val.Value;
|
||
}
|
||
|
||
result << uint8_t(val->UVLock);
|
||
|
||
assert(val->Transforms.size() < (1 << 16));
|
||
result << uint16_t(val->Transforms.size());
|
||
|
||
for(const Transformation& val : val->Transforms)
|
||
result << uint8_t(val.Op) << val.Value;
|
||
}
|
||
}
|
||
}
|
||
|
||
return result.complite();
|
||
}
|
||
|
||
uint16_t PreparedNodeState::parseCondition(const std::string_view expression) {
|
||
enum class EnumTokenKind {
|
||
LParen, RParen,
|
||
Plus, Minus, Star, Slash, Percent,
|
||
Not, And, Or,
|
||
LT, LE, GT, GE, EQ, NE
|
||
};
|
||
|
||
std::vector<std::variant<EnumTokenKind, std::string_view, int, uint16_t>> tokens;
|
||
ssize_t pos = 0;
|
||
auto skipWS = [&](){ while(pos<expression.size() && std::isspace((unsigned char) expression[pos])) ++pos; };
|
||
|
||
for(; pos < expression.size(); pos++) {
|
||
skipWS();
|
||
|
||
char c = expression[pos];
|
||
|
||
// Числа
|
||
if(std::isdigit(c)) {
|
||
ssize_t npos = pos;
|
||
for(; npos < expression.size() && std::isdigit(expression[npos]); npos++);
|
||
int value;
|
||
std::string_view value_view = expression.substr(pos, npos-pos);
|
||
auto [partial_ptr, partial_ec] = std::from_chars(value_view.data(), value_view.data() + value_view.size(), value);
|
||
|
||
if(partial_ec == std::errc{} && partial_ptr != value_view.data() + value_view.size()) {
|
||
MAKE_ERROR("Converted part of the string: " << value << " (remaining: " << std::string_view(partial_ptr, value_view.data() + value_view.size() - partial_ptr) << ")");
|
||
} else if(partial_ec != std::errc{}) {
|
||
MAKE_ERROR("Error converting partial string: " << value);
|
||
}
|
||
|
||
tokens.push_back(value);
|
||
continue;
|
||
}
|
||
|
||
// Переменные
|
||
if(std::isalpha(c) || c == ':') {
|
||
ssize_t npos = pos;
|
||
for(; npos < expression.size() && std::isalpha(expression[npos]); npos++);
|
||
std::string_view value = expression.substr(pos, npos-pos);
|
||
pos += value.size();
|
||
if(value == "true")
|
||
tokens.push_back(1);
|
||
else if(value == "false")
|
||
tokens.push_back(0);
|
||
else
|
||
tokens.push_back(value);
|
||
continue;
|
||
}
|
||
|
||
// Двойные операторы
|
||
if(pos-1 < expression.size()) {
|
||
char n = expression[pos+1];
|
||
|
||
if(c == '<' && n == '=') {
|
||
tokens.push_back(EnumTokenKind::LE);
|
||
pos++;
|
||
continue;
|
||
} else if(c == '>' && n == '=') {
|
||
tokens.push_back(EnumTokenKind::GE);
|
||
pos++;
|
||
continue;
|
||
} else if(c == '=' && n == '=') {
|
||
tokens.push_back(EnumTokenKind::EQ);
|
||
pos++;
|
||
continue;
|
||
} else if(c == '!' && n == '=') {
|
||
tokens.push_back(EnumTokenKind::NE);
|
||
pos++;
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// Операторы
|
||
switch(c) {
|
||
case '(': tokens.push_back(EnumTokenKind::LParen);
|
||
case ')': tokens.push_back(EnumTokenKind::RParen);
|
||
case '+': tokens.push_back(EnumTokenKind::Plus);
|
||
case '-': tokens.push_back(EnumTokenKind::Minus);
|
||
case '*': tokens.push_back(EnumTokenKind::Star);
|
||
case '/': tokens.push_back(EnumTokenKind::Slash);
|
||
case '%': tokens.push_back(EnumTokenKind::Percent);
|
||
case '!': tokens.push_back(EnumTokenKind::Not);
|
||
case '&': tokens.push_back(EnumTokenKind::And);
|
||
case '|': tokens.push_back(EnumTokenKind::Or);
|
||
case '<': tokens.push_back(EnumTokenKind::LT);
|
||
case '>': tokens.push_back(EnumTokenKind::GT);
|
||
}
|
||
|
||
MAKE_ERROR("Недопустимый символ: " << c);
|
||
}
|
||
|
||
|
||
for(size_t index = 0; index < tokens.size(); index++) {
|
||
auto &token = tokens[index];
|
||
|
||
if(std::string_view* value = std::get_if<std::string_view>(&token)) {
|
||
if(*value == "false") {
|
||
token = 0;
|
||
} else if(*value == "true") {
|
||
token = 1;
|
||
} else {
|
||
Node node;
|
||
node.v = Node::Var((std::string) *value);
|
||
Nodes.emplace_back(std::move(node));
|
||
assert(Nodes.size() < std::pow(2, 16)-64);
|
||
token = uint16_t(Nodes.size()-1);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Рекурсивный разбор выражений в скобках
|
||
std::function<uint16_t(size_t pos)> lambdaParse = [&](size_t pos) -> uint16_t {
|
||
size_t end = tokens.size();
|
||
|
||
// Парсим выражения в скобках
|
||
for(size_t index = pos; index < tokens.size(); index++) {
|
||
if(EnumTokenKind* kind = std::get_if<EnumTokenKind>(&tokens[index])) {
|
||
if(*kind == EnumTokenKind::LParen) {
|
||
uint16_t node = lambdaParse(index+1);
|
||
tokens.insert(tokens.begin()+index, node);
|
||
tokens.erase(tokens.begin()+index+1, tokens.begin()+index+3);
|
||
end = tokens.size();
|
||
} else if(*kind == EnumTokenKind::RParen) {
|
||
end = index;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Обрабатываем унарные операции
|
||
for(ssize_t index = end-1; index >= (ssize_t) pos; index--) {
|
||
if(EnumTokenKind *kind = std::get_if<EnumTokenKind>(&tokens[index])) {
|
||
if(*kind != EnumTokenKind::Not && *kind != EnumTokenKind::Plus && *kind != EnumTokenKind::Minus)
|
||
continue;
|
||
|
||
if(index+1 >= end)
|
||
MAKE_ERROR("Отсутствует операнд");
|
||
|
||
auto rightToken = tokens[index+1];
|
||
if(std::holds_alternative<EnumTokenKind>(rightToken))
|
||
MAKE_ERROR("Недопустимый операнд");
|
||
|
||
if(int* value = std::get_if<int>(&rightToken)) {
|
||
if(*kind == EnumTokenKind::Not)
|
||
tokens[index] = *value ? 0 : 1;
|
||
else if(*kind == EnumTokenKind::Plus)
|
||
tokens[index] = +*value;
|
||
else if(*kind == EnumTokenKind::Minus)
|
||
tokens[index] = -*value;
|
||
|
||
} else if(uint16_t* value = std::get_if<uint16_t>(&rightToken)) {
|
||
Node node;
|
||
Node::Unary un;
|
||
un.rhs = *value;
|
||
|
||
if(*kind == EnumTokenKind::Not)
|
||
un.op = Op::Not;
|
||
else if(*kind == EnumTokenKind::Plus)
|
||
un.op = Op::Pos;
|
||
else if(*kind == EnumTokenKind::Minus)
|
||
un.op = Op::Neg;
|
||
|
||
node.v = un;
|
||
Nodes.emplace_back(std::move(node));
|
||
assert(Nodes.size() < std::pow(2, 16)-64);
|
||
tokens[index] = uint16_t(Nodes.size()-1);
|
||
}
|
||
|
||
end--;
|
||
tokens.erase(tokens.begin()+index+1);
|
||
}
|
||
}
|
||
|
||
// Бинарные в порядке приоритета
|
||
for(int priority = 0; priority < 6; priority++)
|
||
for(size_t index = pos; index < end; index++) {
|
||
EnumTokenKind *kind = std::get_if<EnumTokenKind>(&tokens[index]);
|
||
|
||
if(!kind)
|
||
continue;
|
||
|
||
if(priority == 0 && *kind != EnumTokenKind::Star && *kind != EnumTokenKind::Slash && *kind != EnumTokenKind::Percent)
|
||
continue;
|
||
if(priority == 1 && *kind != EnumTokenKind::Plus && *kind != EnumTokenKind::Minus)
|
||
continue;
|
||
if(priority == 2 && *kind != EnumTokenKind::LT && *kind != EnumTokenKind::GT && *kind != EnumTokenKind::LE && *kind != EnumTokenKind::GE)
|
||
continue;
|
||
if(priority == 3 && *kind != EnumTokenKind::EQ && *kind != EnumTokenKind::NE)
|
||
continue;
|
||
if(priority == 4 && *kind != EnumTokenKind::And)
|
||
continue;
|
||
if(priority == 5 && *kind != EnumTokenKind::Or)
|
||
continue;
|
||
|
||
if(index == pos)
|
||
MAKE_ERROR("Отсутствует операнд перед");
|
||
else if(index == end-1)
|
||
MAKE_ERROR("Отсутствует операнд после");
|
||
|
||
auto &leftToken = tokens[index-1];
|
||
auto &rightToken = tokens[index+1];
|
||
|
||
if(std::holds_alternative<EnumTokenKind>(leftToken))
|
||
MAKE_ERROR("Недопустимый операнд");
|
||
|
||
if(std::holds_alternative<EnumTokenKind>(rightToken))
|
||
MAKE_ERROR("Недопустимый операнд");
|
||
|
||
if(std::holds_alternative<int>(leftToken) && std::holds_alternative<int>(rightToken)) {
|
||
int value;
|
||
|
||
switch(*kind) {
|
||
case EnumTokenKind::Plus: value = std::get<int>(leftToken) + std::get<int>(rightToken); break;
|
||
case EnumTokenKind::Minus: value = std::get<int>(leftToken) - std::get<int>(rightToken); break;
|
||
case EnumTokenKind::Star: value = std::get<int>(leftToken) * std::get<int>(rightToken); break;
|
||
case EnumTokenKind::Slash: value = std::get<int>(leftToken) / std::get<int>(rightToken); break;
|
||
case EnumTokenKind::Percent: value = std::get<int>(leftToken) % std::get<int>(rightToken); break;
|
||
case EnumTokenKind::And: value = std::get<int>(leftToken) && std::get<int>(rightToken); break;
|
||
case EnumTokenKind::Or: value = std::get<int>(leftToken) || std::get<int>(rightToken); break;
|
||
case EnumTokenKind::LT: value = std::get<int>(leftToken) < std::get<int>(rightToken); break;
|
||
case EnumTokenKind::LE: value = std::get<int>(leftToken) <= std::get<int>(rightToken); break;
|
||
case EnumTokenKind::GT: value = std::get<int>(leftToken) > std::get<int>(rightToken); break;
|
||
case EnumTokenKind::GE: value = std::get<int>(leftToken) >= std::get<int>(rightToken); break;
|
||
case EnumTokenKind::EQ: value = std::get<int>(leftToken) == std::get<int>(rightToken); break;
|
||
case EnumTokenKind::NE: value = std::get<int>(leftToken) != std::get<int>(rightToken); break;
|
||
|
||
default: std::unreachable();
|
||
}
|
||
|
||
|
||
leftToken = value;
|
||
} else {
|
||
Node node;
|
||
Node::Binary bin;
|
||
|
||
switch(*kind) {
|
||
case EnumTokenKind::Plus: bin.op = Op::Add; break;
|
||
case EnumTokenKind::Minus: bin.op = Op::Sub; break;
|
||
case EnumTokenKind::Star: bin.op = Op::Mul; break;
|
||
case EnumTokenKind::Slash: bin.op = Op::Div; break;
|
||
case EnumTokenKind::Percent: bin.op = Op::Mod; break;
|
||
case EnumTokenKind::And: bin.op = Op::And; break;
|
||
case EnumTokenKind::Or: bin.op = Op::Or; break;
|
||
case EnumTokenKind::LT: bin.op = Op::LT; break;
|
||
case EnumTokenKind::LE: bin.op = Op::LE; break;
|
||
case EnumTokenKind::GT: bin.op = Op::GT; break;
|
||
case EnumTokenKind::GE: bin.op = Op::GE; break;
|
||
case EnumTokenKind::EQ: bin.op = Op::EQ; break;
|
||
case EnumTokenKind::NE: bin.op = Op::NE; break;
|
||
|
||
default: std::unreachable();
|
||
}
|
||
|
||
if(int* value = std::get_if<int>(&leftToken)) {
|
||
Node valueNode;
|
||
valueNode.v = Node::Num(*value);
|
||
Nodes.emplace_back(std::move(valueNode));
|
||
assert(Nodes.size() < std::pow(2, 16)-64);
|
||
bin.lhs = uint16_t(Nodes.size()-1);
|
||
} else if(uint16_t* nodeId = std::get_if<uint16_t>(&leftToken)) {
|
||
bin.lhs = *nodeId;
|
||
}
|
||
|
||
if(int* value = std::get_if<int>(&rightToken)) {
|
||
Node valueNode;
|
||
valueNode.v = Node::Num(*value);
|
||
Nodes.emplace_back(std::move(valueNode));
|
||
assert(Nodes.size() < std::pow(2, 16)-64);
|
||
bin.rhs = uint16_t(Nodes.size()-1);
|
||
} else if(uint16_t* nodeId = std::get_if<uint16_t>(&rightToken)) {
|
||
bin.rhs = *nodeId;
|
||
}
|
||
|
||
Nodes.emplace_back(std::move(node));
|
||
assert(Nodes.size() < std::pow(2, 16)-64);
|
||
leftToken = uint16_t(Nodes.size()-1);
|
||
}
|
||
|
||
tokens.erase(tokens.begin()+index, tokens.begin()+index+2);
|
||
end -= 2;
|
||
index--;
|
||
}
|
||
|
||
if(tokens.size() != 1)
|
||
MAKE_ERROR("Выражение не корректно");
|
||
|
||
if(uint16_t* nodeId = std::get_if<uint16_t>(&tokens[0])) {
|
||
return *nodeId;
|
||
} else if(int* value = std::get_if<int>(&tokens[0])) {
|
||
Node node;
|
||
node.v = Node::Num(*value);
|
||
Nodes.emplace_back(std::move(node));
|
||
assert(Nodes.size() < std::pow(2, 16)-64);
|
||
return uint16_t(Nodes.size()-1);
|
||
} else {
|
||
MAKE_ERROR("Выражение не корректно");
|
||
}
|
||
};
|
||
|
||
return lambdaParse(0);
|
||
|
||
// std::unordered_map<std::string, int> vars;
|
||
// std::function<int(uint16_t)> lambdaCalcNode = [&](uint16_t nodeId) -> int {
|
||
// const Node& node = Nodes[nodeId];
|
||
// if(const Node::Num* value = std::get_if<Node::Num>(&node.v)) {
|
||
// return value->v;
|
||
// } else if(const Node::Var* value = std::get_if<Node::Var>(&node.v)) {
|
||
// auto iter = vars.find(value->name);
|
||
// if(iter == vars.end())
|
||
// MAKE_ERROR("Неопознанное состояние");
|
||
|
||
// return iter->second;
|
||
// } else if(const Node::Unary* value = std::get_if<Node::Unary>(&node.v)) {
|
||
// int rNodeValue = lambdaCalcNode(value->rhs);
|
||
// switch(value->op) {
|
||
// case Op::Not: return !rNodeValue;
|
||
// case Op::Pos: return +rNodeValue;
|
||
// case Op::Neg: return -rNodeValue;
|
||
// default:
|
||
// std::unreachable();
|
||
// }
|
||
// } else if(const Node::Binary* value = std::get_if<Node::Binary>(&node.v)) {
|
||
// int lNodeValue = lambdaCalcNode(value->lhs);
|
||
// int rNodeValue = lambdaCalcNode(value->rhs);
|
||
|
||
// switch(value->op) {
|
||
// case Op::Add: return lNodeValue+rNodeValue;
|
||
// case Op::Sub: return lNodeValue-rNodeValue;
|
||
// case Op::Mul: return lNodeValue*rNodeValue;
|
||
// case Op::Div: return lNodeValue/rNodeValue;
|
||
// case Op::Mod: return lNodeValue%rNodeValue;
|
||
// case Op::LT: return lNodeValue<rNodeValue;
|
||
// case Op::LE: return lNodeValue<=rNodeValue;
|
||
// case Op::GT: return lNodeValue>rNodeValue;
|
||
// case Op::GE: return lNodeValue>=rNodeValue;
|
||
// case Op::EQ: return lNodeValue==rNodeValue;
|
||
// case Op::NE: return lNodeValue!=rNodeValue;
|
||
// case Op::And: return lNodeValue&&rNodeValue;
|
||
// case Op::Or: return lNodeValue||rNodeValue;
|
||
// default:
|
||
// std::unreachable();
|
||
// }
|
||
// } else {
|
||
// std::unreachable();
|
||
// }
|
||
// };
|
||
}
|
||
|
||
std::pair<float, std::variant<PreparedNodeState::Model, PreparedNodeState::VectorModel>> PreparedNodeState::parseModel(const std::string_view modid, const js::object& obj) {
|
||
// ModelToLocalId
|
||
|
||
bool uvlock;
|
||
float weight = 1;
|
||
std::vector<Transformation> transforms;
|
||
|
||
if(const auto weight_val = obj.try_at("weight")) {
|
||
weight = weight_val->to_number<float>();
|
||
}
|
||
|
||
if(const auto uvlock_val = obj.try_at("uvlock")) {
|
||
uvlock = uvlock_val->as_bool();
|
||
}
|
||
|
||
if(const auto transformations_val = obj.try_at("transformations")) {
|
||
transforms = parseTransormations(transformations_val->as_array());
|
||
}
|
||
|
||
const js::value& model = obj.at("model");
|
||
if(const auto model_key = model.try_as_string()) {
|
||
// Одна модель
|
||
Model result;
|
||
result.UVLock = false;
|
||
result.Transforms = std::move(transforms);
|
||
|
||
auto [domain, key] = parseDomainKey((std::string) *model_key, modid);
|
||
|
||
uint16_t resId = 0;
|
||
for(auto& [lDomain, lKey] : ModelToLocalId) {
|
||
if(lDomain == domain && lKey == key)
|
||
break;
|
||
|
||
resId++;
|
||
}
|
||
|
||
if(resId == ModelToLocalId.size()) {
|
||
ModelToLocalId.emplace_back(domain, key);
|
||
}
|
||
|
||
result.Id = resId;
|
||
|
||
return {weight, result};
|
||
} else if(model.is_array()) {
|
||
// Множество моделей
|
||
VectorModel result;
|
||
result.UVLock = uvlock;
|
||
result.Transforms = std::move(transforms);
|
||
|
||
for(const js::value& js_value : model.as_array()) {
|
||
const js::object& js_obj = js_value.as_object();
|
||
|
||
Model subModel;
|
||
if(const auto uvlock_val = js_obj.try_at("uvlock")) {
|
||
subModel.UVLock = uvlock_val->as_bool();
|
||
}
|
||
|
||
if(const auto transformations_val = js_obj.try_at("transformations")) {
|
||
subModel.Transforms = parseTransormations(transformations_val->as_array());
|
||
}
|
||
|
||
auto [domain, key] = parseDomainKey((std::string) js_obj.at("model").as_string(), modid);
|
||
|
||
uint16_t resId = 0;
|
||
for(auto& [lDomain, lKey] : ModelToLocalId) {
|
||
if(lDomain == domain && lKey == key)
|
||
break;
|
||
|
||
resId++;
|
||
}
|
||
|
||
if(resId == ModelToLocalId.size()) {
|
||
ModelToLocalId.emplace_back(domain, key);
|
||
}
|
||
|
||
subModel.Id = resId;
|
||
result.Models.push_back(std::move(subModel));
|
||
}
|
||
|
||
return {weight, result};
|
||
} else {
|
||
MAKE_ERROR("");
|
||
}
|
||
}
|
||
|
||
std::vector<Transformation> PreparedNodeState::parseTransormations(const js::array& arr) {
|
||
std::vector<Transformation> result;
|
||
|
||
for(const js::value& js_value : arr) {
|
||
const js::string_view transform = js_value.as_string();
|
||
|
||
auto pos = transform.find('=');
|
||
std::string_view key = transform.substr(0, pos);
|
||
std::string_view value = transform.substr(pos+1);
|
||
|
||
float f_value;
|
||
auto [partial_ptr, partial_ec] = std::from_chars(value.data(), value.data() + value.size(), f_value);
|
||
|
||
if(partial_ec == std::errc{} && partial_ptr != value.data() + value.size()) {
|
||
MAKE_ERROR("Converted part of the string: " << value << " (remaining: " << std::string_view(partial_ptr, value.data() + value.size() - partial_ptr) << ")");
|
||
} else if(partial_ec != std::errc{}) {
|
||
MAKE_ERROR("Error converting partial string: " << value);
|
||
}
|
||
|
||
if(key == "x")
|
||
result.emplace_back(Transformation::MoveX, f_value);
|
||
else if(key == "y")
|
||
result.emplace_back(Transformation::MoveY, f_value);
|
||
else if(key == "z")
|
||
result.emplace_back(Transformation::MoveZ, f_value);
|
||
else if(key == "rx")
|
||
result.emplace_back(Transformation::RotateX, f_value);
|
||
else if(key == "ry")
|
||
result.emplace_back(Transformation::RotateY, f_value);
|
||
else if(key == "rz")
|
||
result.emplace_back(Transformation::RotateZ, f_value);
|
||
else
|
||
MAKE_ERROR("Неизвестный ключ трансформации");
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
|
||
PreparedModel::PreparedModel(const std::string_view modid, const js::object& profile) {
|
||
if(profile.contains("gui_light")) {
|
||
std::string_view gui_light = profile.at("gui_light").as_string();
|
||
|
||
GuiLight = EnumGuiLight::Default;
|
||
}
|
||
|
||
if(profile.contains("ambient_occlusion")) {
|
||
AmbientOcclusion = profile.at("ambient_occlusion").as_bool();
|
||
}
|
||
|
||
if(profile.contains("display")) {
|
||
const js::object& list = profile.at("display").as_object();
|
||
for(auto& [key, value] : list) {
|
||
FullTransformation result;
|
||
|
||
const js::object& display_value = value.as_object();
|
||
|
||
if(boost::system::result<const js::value&> arr_val = display_value.try_at("rotation")) {
|
||
const js::array& arr = arr_val->as_array();
|
||
if(arr.size() != 3)
|
||
MAKE_ERROR("3");
|
||
|
||
for(int iter = 0; iter < 3; iter++)
|
||
result.Rotation[iter] = arr[iter].to_number<float>();
|
||
}
|
||
|
||
if(boost::system::result<const js::value&> arr_val = display_value.try_at("translation")) {
|
||
const js::array& arr = arr_val->as_array();
|
||
if(arr.size() != 3)
|
||
MAKE_ERROR("3");
|
||
|
||
for(int iter = 0; iter < 3; iter++)
|
||
result.Translation[iter] = arr[iter].to_number<float>();
|
||
}
|
||
|
||
if(boost::system::result<const js::value&> arr_val = display_value.try_at("scale")) {
|
||
const js::array& arr = arr_val->as_array();
|
||
if(arr.size() != 3)
|
||
MAKE_ERROR("3");
|
||
|
||
for(int iter = 0; iter < 3; iter++)
|
||
result.Scale[iter] = arr[iter].to_number<float>();
|
||
}
|
||
|
||
Display[key] = result;
|
||
}
|
||
}
|
||
|
||
if(boost::system::result<const js::value&> textures_val = profile.try_at("textures")) {
|
||
const js::object& textures = textures_val->as_object();
|
||
|
||
for(const auto& [key, value] : textures) {
|
||
Textures[key] = compileTexturePipeline((std::string) value.as_string(), modid);
|
||
}
|
||
}
|
||
|
||
if(boost::system::result<const js::value&> cuboids_val = profile.try_at("cuboids")) {
|
||
const js::array& cuboids = cuboids_val->as_array();
|
||
|
||
for(const auto& value : cuboids) {
|
||
const js::object& cuboid = value.as_object();
|
||
|
||
Cuboid result;
|
||
|
||
if(boost::system::result<const js::value&> shade_val = cuboid.try_at("shade")) {
|
||
result.Shade = shade_val->as_bool();
|
||
} else {
|
||
result.Shade = true;
|
||
}
|
||
|
||
{
|
||
const js::array& from = cuboid.at("from").as_array();
|
||
if(from.size() != 3)
|
||
MAKE_ERROR("3");
|
||
|
||
for(int iter = 0; iter < 3; iter++)
|
||
result.From[iter] = from[iter].to_number<float>();
|
||
}
|
||
|
||
{
|
||
const js::array& to = cuboid.at("to").as_array();
|
||
if(to.size() != 3)
|
||
MAKE_ERROR("3");
|
||
|
||
for(int iter = 0; iter < 3; iter++)
|
||
result.To[iter] = to[iter].to_number<float>();
|
||
}
|
||
|
||
{
|
||
const js::object& faces = cuboid.at("faces").as_object();
|
||
|
||
for(const auto& [key, value] : faces) {
|
||
EnumFace type;
|
||
|
||
if(key == "down")
|
||
type = EnumFace::Down;
|
||
else if(key == "up")
|
||
type = EnumFace::Up;
|
||
else if(key == "north")
|
||
type = EnumFace::North;
|
||
else if(key == "south")
|
||
type = EnumFace::South;
|
||
else if(key == "west")
|
||
type = EnumFace::West;
|
||
else if(key == "east")
|
||
type = EnumFace::East;
|
||
else
|
||
MAKE_ERROR("Unknown face");
|
||
|
||
const js::object& js_face = value.as_object();
|
||
Cuboid::Face face;
|
||
|
||
if(boost::system::result<const js::value&> uv_val = js_face.try_at("uv")) {
|
||
const js::array& arr = uv_val->as_array();
|
||
if(arr.size() != 4)
|
||
MAKE_ERROR(4);
|
||
|
||
for(int iter = 0; iter < 4; iter++)
|
||
face.UV[iter] = arr[iter].to_number<float>();
|
||
}
|
||
|
||
if(boost::system::result<const js::value&> texture_val = js_face.try_at("texture")) {
|
||
face.Texture = texture_val->as_string();
|
||
}
|
||
|
||
if(boost::system::result<const js::value&> cullface_val = js_face.try_at("cullface")) {
|
||
const std::string_view cullface = cullface_val->as_string();
|
||
|
||
if(cullface == "down")
|
||
face.Cullface = EnumFace::Down;
|
||
else if(cullface == "up")
|
||
face.Cullface = EnumFace::Up;
|
||
else if(cullface == "north")
|
||
face.Cullface = EnumFace::North;
|
||
else if(cullface == "south")
|
||
face.Cullface = EnumFace::South;
|
||
else if(cullface == "west")
|
||
face.Cullface = EnumFace::West;
|
||
else if(cullface == "east")
|
||
face.Cullface = EnumFace::East;
|
||
else
|
||
MAKE_ERROR("Unknown face");
|
||
}
|
||
|
||
if(boost::system::result<const js::value&> tintindex_val = js_face.try_at("tintindex")) {
|
||
face.TintIndex = tintindex_val->to_number<int>();
|
||
} else
|
||
face.TintIndex = -1;
|
||
|
||
if(boost::system::result<const js::value&> rotation_val = js_face.try_at("rotation")) {
|
||
face.Rotation = rotation_val->to_number<int16_t>();
|
||
} else
|
||
face.Rotation = 0;
|
||
|
||
|
||
result.Faces[type] = face;
|
||
}
|
||
}
|
||
|
||
if(boost::system::result<const js::value&> transformations_val = cuboid.try_at("transformations")) {
|
||
const js::array& transformations = transformations_val->as_array();
|
||
|
||
for(const js::value& tobj : transformations) {
|
||
const js::string_view transform = tobj.as_string();
|
||
|
||
auto pos = transform.find('=');
|
||
std::string_view key = transform.substr(0, pos);
|
||
std::string_view value = transform.substr(pos+1);
|
||
|
||
float f_value;
|
||
auto [partial_ptr, partial_ec] = std::from_chars(value.data(), value.data() + value.size(), f_value);
|
||
|
||
if(partial_ec == std::errc{} && partial_ptr != value.data() + value.size()) {
|
||
MAKE_ERROR("Converted part of the string: " << value << " (remaining: " << std::string_view(partial_ptr, value.data() + value.size() - partial_ptr) << ")");
|
||
} else if(partial_ec != std::errc{}) {
|
||
MAKE_ERROR("Error converting partial string: " << value);
|
||
}
|
||
|
||
if(key == "x")
|
||
result.Trs.OPs.emplace_back(Transformation::MoveX, f_value);
|
||
else if(key == "y")
|
||
result.Trs.OPs.emplace_back(Transformation::MoveY, f_value);
|
||
else if(key == "z")
|
||
result.Trs.OPs.emplace_back(Transformation::MoveZ, f_value);
|
||
else if(key == "rx")
|
||
result.Trs.OPs.emplace_back(Transformation::RotateX, f_value);
|
||
else if(key == "ry")
|
||
result.Trs.OPs.emplace_back(Transformation::RotateY, f_value);
|
||
else if(key == "rz")
|
||
result.Trs.OPs.emplace_back(Transformation::RotateZ, f_value);
|
||
else
|
||
MAKE_ERROR("Неизвестный ключ трансформации");
|
||
}
|
||
}
|
||
|
||
Cuboids.emplace_back(std::move(result));
|
||
}
|
||
}
|
||
|
||
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()) {
|
||
auto [domain, key] = parseDomainKey((std::string) path.value(), modid);
|
||
result.Domain = std::move(domain);
|
||
result.Key = std::move(key);
|
||
} else {
|
||
const js::object& sub = sub_val.as_object();
|
||
auto [domain, key] = parseDomainKey((std::string) sub.at("path").as_string(), modid);
|
||
result.Domain = std::move(domain);
|
||
result.Key = std::move(key);
|
||
|
||
if(boost::system::result<const js::value&> scene_val = profile.try_at("scene"))
|
||
result.Scene = scene_val->to_number<uint16_t>();
|
||
}
|
||
|
||
SubModels.emplace_back(std::move(result));
|
||
}
|
||
}
|
||
}
|
||
|
||
PreparedModel::PreparedModel(const std::string_view modid, const sol::table& profile) {
|
||
std::unreachable();
|
||
}
|
||
|
||
PreparedModel::PreparedModel(const std::u8string& data) {
|
||
Net::LinearReader lr(data);
|
||
|
||
lr.read<uint16_t>();
|
||
|
||
if(lr.read<uint8_t>()) {
|
||
GuiLight = (EnumGuiLight) lr.read<uint8_t>();
|
||
}
|
||
|
||
{
|
||
uint8_t val;
|
||
lr >> val;
|
||
|
||
if(val == 2)
|
||
AmbientOcclusion = true;
|
||
else if(val == 1)
|
||
AmbientOcclusion = false;
|
||
else if(val != 0)
|
||
MAKE_ERROR("Ошибка формата данных");
|
||
}
|
||
|
||
uint16_t size;
|
||
lr >> size;
|
||
Display.reserve(size);
|
||
|
||
for(int counter = 0; counter < size; counter++) {
|
||
std::string key;
|
||
lr >> key;
|
||
|
||
FullTransformation tsf;
|
||
for(int iter = 0; iter < 3; iter++)
|
||
lr >> tsf.Rotation[iter];
|
||
|
||
for(int iter = 0; iter < 3; iter++)
|
||
lr >> tsf.Translation[iter];
|
||
|
||
for(int iter = 0; iter < 3; iter++)
|
||
lr >> tsf.Scale[iter];
|
||
|
||
Display.insert({key, tsf});
|
||
}
|
||
|
||
lr >> size;
|
||
Textures.reserve(size);
|
||
for(int counter = 0; counter < size; counter++) {
|
||
std::string tkey;
|
||
lr >> tkey;
|
||
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;
|
||
Cuboids.reserve(size);
|
||
for(int counter = 0; counter < size; counter++) {
|
||
Cuboid cuboid;
|
||
cuboid.Shade = lr.read<uint8_t>();
|
||
|
||
for(int iter = 0; iter < 3; iter++)
|
||
lr >> cuboid.From[iter];
|
||
|
||
for(int iter = 0; iter < 3; iter++)
|
||
lr >> cuboid.To[iter];
|
||
|
||
uint16_t facesSize;
|
||
lr >> facesSize;
|
||
cuboid.Faces.reserve(facesSize);
|
||
|
||
for(int counter2 = 0; counter2 < facesSize; counter2++) {
|
||
Cuboid::Face face;
|
||
uint8_t type;
|
||
lr >> type;
|
||
|
||
for(int iter = 0; iter < 4; iter++)
|
||
lr >> face.UV[iter];
|
||
|
||
lr >> face.Texture;
|
||
uint8_t val = lr.read<uint8_t>();
|
||
face.Cullface = EnumFace(val);
|
||
if((int) face.Cullface > (int) EnumFace::None)
|
||
MAKE_ERROR("Unknown face");
|
||
|
||
lr >> face.TintIndex >> face.Rotation;
|
||
|
||
cuboid.Faces.insert({(EnumFace) type, face});
|
||
}
|
||
|
||
uint16_t transformationsSize;
|
||
lr >> transformationsSize;
|
||
cuboid.Trs.OPs.reserve(transformationsSize);
|
||
|
||
for(int counter2 = 0; counter2 < transformationsSize; counter2++) {
|
||
Transformation tsf;
|
||
tsf.Op = (Transformation::EnumTransform) lr.read<uint8_t>();
|
||
lr >> tsf.Value;
|
||
cuboid.Trs.OPs.emplace_back(tsf);
|
||
}
|
||
|
||
Cuboids.emplace_back(std::move(cuboid));
|
||
}
|
||
|
||
uint8_t size8;
|
||
lr >> size8;
|
||
SubModels.reserve(size8);
|
||
for(int counter = 0; counter < size8; counter++) {
|
||
SubModel sub;
|
||
lr >> sub.Domain >> sub.Key;
|
||
uint16_t val = lr.read<uint16_t>();
|
||
if(val != uint16_t(-1)) {
|
||
sub.Scene = val;
|
||
}
|
||
|
||
SubModels.push_back(std::move(sub));
|
||
}
|
||
|
||
lr.checkUnreaded();
|
||
}
|
||
|
||
std::u8string PreparedModel::dump() const {
|
||
Net::Packet result;
|
||
|
||
result << 'b' << 'm';
|
||
|
||
if(GuiLight.has_value()) {
|
||
result << uint8_t(1);
|
||
result << uint8_t(GuiLight.value());
|
||
} else
|
||
result << uint8_t(0);
|
||
|
||
if(AmbientOcclusion.has_value()) {
|
||
if(*AmbientOcclusion)
|
||
result << uint8_t(2);
|
||
else
|
||
result << uint8_t(1);
|
||
} else
|
||
result << uint8_t(0);
|
||
|
||
assert(Display.size() < (1 << 16));
|
||
result << uint16_t(Display.size());
|
||
|
||
for(const auto& [key, tsf] : Display) {
|
||
assert(key.size() < (1 << 16));
|
||
result << key;
|
||
|
||
for(int iter = 0; iter < 3; iter++)
|
||
result << tsf.Rotation[iter];
|
||
|
||
for(int iter = 0; iter < 3; iter++)
|
||
result << tsf.Translation[iter];
|
||
|
||
for(int iter = 0; iter < 3; iter++)
|
||
result << tsf.Scale[iter];
|
||
}
|
||
|
||
assert(Textures.size() < (1 << 16));
|
||
result << uint16_t(Textures.size());
|
||
|
||
assert(CompiledTextures.size() == Textures.size());
|
||
|
||
for(const auto& [tkey, dk] : CompiledTextures) {
|
||
assert(tkey.size() < 32);
|
||
result << tkey;
|
||
|
||
assert(dk.BinTextures.size() < 512);
|
||
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));
|
||
result << uint16_t(Cuboids.size());
|
||
|
||
for(const Cuboid& cuboid : Cuboids) {
|
||
result << uint8_t(cuboid.Shade);
|
||
|
||
for(int iter = 0; iter < 3; iter++)
|
||
result << cuboid.From[iter];
|
||
|
||
for(int iter = 0; iter < 3; iter++)
|
||
result << cuboid.To[iter];
|
||
|
||
assert(cuboid.Faces.size() < 7);
|
||
result << uint8_t(cuboid.Faces.size());
|
||
for(const auto& [type, face] : cuboid.Faces) {
|
||
result << uint8_t(type);
|
||
|
||
for(int iter = 0; iter < 4; iter++)
|
||
result << face.UV[iter];
|
||
|
||
result << face.Texture;
|
||
result << uint8_t(face.Cullface);
|
||
|
||
result << face.TintIndex << face.Rotation;
|
||
}
|
||
|
||
assert(cuboid.Trs.OPs.size() < 256);
|
||
result << uint8_t(cuboid.Trs.OPs.size());
|
||
for(const auto& [op, value] : cuboid.Trs.OPs) {
|
||
result << uint8_t(op) << value;
|
||
}
|
||
}
|
||
|
||
assert(SubModels.size() < 256);
|
||
result << uint8_t(SubModels.size());
|
||
for(const SubModel& model : SubModels) {
|
||
assert(model.Domain.size() < 32);
|
||
assert(model.Key.size() < 32);
|
||
|
||
result << model.Domain << model.Key;
|
||
if(model.Scene)
|
||
result << uint16_t(*model.Scene);
|
||
else
|
||
result << uint16_t(-1);
|
||
}
|
||
|
||
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 {
|
||
boost::interprocess::file_mapping MMap;
|
||
boost::interprocess::mapped_region Region;
|
||
Hash_t Hash;
|
||
|
||
InlineMMap(fs::path path)
|
||
: MMap(path.c_str(), boost::interprocess::read_only),
|
||
Region(MMap, boost::interprocess::read_only)
|
||
{
|
||
Hash = sha2::sha256((const uint8_t*) Region.get_address(), Region.get_size());
|
||
}
|
||
|
||
const std::byte* data() const { return (const std::byte*) Region.get_address(); }
|
||
size_t size() const { return Region.get_size(); }
|
||
};
|
||
|
||
struct Resource::InlinePtr {
|
||
std::u8string Data;
|
||
Hash_t Hash;
|
||
|
||
InlinePtr(const uint8_t* data, size_t size) {
|
||
Data.resize(size);
|
||
std::copy(data, data+size, Data.data());
|
||
Hash = sha2::sha256(data, size);
|
||
}
|
||
|
||
InlinePtr(std::u8string&& data) {
|
||
Data = std::move(data);
|
||
Hash = sha2::sha256((const uint8_t*) Data.data(), Data.size());
|
||
}
|
||
|
||
const std::byte* data() const { return (const std::byte*) Data.data(); }
|
||
size_t size() const { return Data.size(); }
|
||
};
|
||
|
||
|
||
Resource::Resource(fs::path path)
|
||
: In(std::make_shared<std::variant<InlineMMap, InlinePtr>>(InlineMMap(path)))
|
||
{}
|
||
|
||
Resource::Resource(const uint8_t* data, size_t size)
|
||
: In(std::make_shared<std::variant<InlineMMap, InlinePtr>>(InlinePtr(data, size)))
|
||
{}
|
||
|
||
Resource::Resource(const std::u8string& data)
|
||
: In(std::make_shared<std::variant<InlineMMap, InlinePtr>>(InlinePtr((const uint8_t*) data.data(), data.size())))
|
||
{}
|
||
|
||
Resource::Resource(std::u8string&& data)
|
||
: In(std::make_shared<std::variant<InlineMMap, InlinePtr>>(InlinePtr(std::move(data))))
|
||
{}
|
||
|
||
const std::byte* Resource::data() const { assert(In); return std::visit<const std::byte*>([](auto& obj){ return obj.data(); }, *In); }
|
||
size_t Resource::size() const { assert(In); return std::visit<size_t>([](auto& obj){ return obj.size(); }, *In); }
|
||
Hash_t Resource::hash() const { assert(In); return std::visit<Hash_t>([](auto& obj){ return obj.Hash; }, *In); }
|
||
|
||
Resource Resource::convertToMem() const {
|
||
if(InlineMMap* ptr = std::get_if<InlineMMap>(&*In)) {
|
||
std::u8string data(ptr->size(), '\0');
|
||
std::copy(ptr->data(), ptr->data()+ptr->size(), (std::byte*) data.data());
|
||
return Resource(std::move(data));
|
||
} else {
|
||
return *this;
|
||
}
|
||
}
|
||
|
||
auto Resource::operator<=>(const Resource&) const = default;
|
||
|
||
} |