game: fix loading schematic models with blocks larger than 8 bits

This commit is contained in:
Crypto City 2021-09-20 20:17:11 +00:00
parent 25cc1bf99a
commit e9955db73e
2 changed files with 85 additions and 8 deletions

View File

@ -5237,11 +5237,26 @@ bool CryptoCityUrho3D::LoadModelData(const std::shared_ptr<Flag> &flag, const st
}
else if (!mm.front().imported_palette.empty())
{
// remove unused palette entries in case the imported model is heavy
bool used[256];
memset(used, 0, sizeof(used));
const auto *tiles = flag->get_raw_tiles();
const size_t n_tiles = (flag->x1 - flag->x0 + 1) * (flag->y1 - flag->y0 + 1);
for (size_t i = 0; i < n_tiles; ++i, ++tiles)
{
const uint32_t height = tiles->get_height();
for (uint32_t h = 0; h < height; ++h)
used[tiles->get_type(h)] = true;
}
for (size_t i = flag->palette.size() - 1; i > 0; --i)
if (!used[i])
flag->palette.erase(flag->palette.begin() + i);
std::map<uint8_t, uint8_t> remap;
for (auto &e: mm.front().imported_palette)
{
// the empty block
if (e.second == "minecraft:air")
if (e.second == "minecraft:air" || e.second == "minecraft::structure_void")
{
remap[e.first] = 0;
continue;

View File

@ -295,9 +295,10 @@ bool load_schematic_data(std::list<VoxelModel> &sm, const std::string &compresse
m.depth = 0;
m.height = 0;
std::map<int32_t, uint16_t> palette_map;
int32_t palette_index = -1;
if (!load_schematic_data(compressed_data, [&m, &palette_index, &palette_map](uint8_t type, const char *name, const void *data, size_t bytes) {
std::map<uint32_t, std::string> imported_palette;
bool in_palette = false;
if (!load_schematic_data(compressed_data, [&m, &in_palette, &imported_palette](uint8_t type, const char *name, const void *data, size_t bytes) {
if (type == tag_short && !strcmp(name, "Width"))
m.width = *(const uint16_t*)data;
else if (type == tag_short && !strcmp(name, "Height"))
@ -307,11 +308,11 @@ bool load_schematic_data(std::list<VoxelModel> &sm, const std::string &compresse
else if (type == tag_byte_array && !strcmp(name, "BlockData"))
m.data.assign((const uint8_t*)data, (const uint8_t*)data + bytes);
else if (type == tag_compound && !strcmp(name, "Palette"))
palette_index = 0;
in_palette = true;
else if (type == tag_end)
palette_index = -1;
else if (type == tag_int && palette_index >= 0)
m.imported_palette[*(const int32_t*)data] = name;
in_palette = false;
else if (type == tag_int && in_palette)
imported_palette[*(const int32_t*)data] = name;
}))
return false;
@ -336,6 +337,67 @@ bool load_schematic_data(std::list<VoxelModel> &sm, const std::string &compresse
return false;
}
// these files have a 32 bit palette, we have a 8 bit palette,
// but they have a lot of variations on a smaller number of basic
// materials, so we can merge all the variations and hopefully
// fit in 8 bits
std::map<uint32_t, uint8_t> reduced_palette_map;
std::map<uint8_t, std::string> reduced_imported_palette;
if (imported_palette.size() > 256 || (!imported_palette.empty() && imported_palette.rbegin()->first >= 256))
{
std::map<std::string, uint8_t> material_map;
size_t palette_entries = 1; // reserve empty/0
material_map["minecraft:air"] = 0;
material_map["minecraft:structure_void"] = 0; // some test model has that, wiki says empty
for (const auto &e: imported_palette)
{
std::string material = e.second;
const char *ptr = strchr(material.c_str(), '[');
if (ptr)
material.resize(ptr - material.c_str());
const auto i = material_map.find(material);
if (i == material_map.end())
{
if (palette_entries == 256)
{
MERROR("Too many materials to fit in palette");
return false;
}
material_map[material] = palette_entries;
reduced_palette_map[e.first] = palette_entries;
reduced_imported_palette[palette_entries] = material;
++palette_entries;
}
}
}
else
{
for (auto &e: imported_palette)
m.imported_palette[e.first] = std::move(e.second);
}
const size_t n_blocks = m.width * m.height * (size_t)m.depth;
std::vector<uint8_t> blocks;
blocks.resize(n_blocks);
const uint8_t *begin = m.data.data();
const uint8_t *end = m.data.data() + m.data.size();
for (size_t i = 0; i < n_blocks; ++i)
{
uint32_t v;
const int read = tools::read_varint(begin, end, v);
if (read <= 0)
{
MERROR("Failed to decode block data");
return false;
}
if (!reduced_palette_map.empty())
v = reduced_palette_map[v];
blocks[i] = v;
}
std::swap(m.data, blocks);
if (!reduced_imported_palette.empty())
std::swap(m.imported_palette, reduced_imported_palette);
sm.push_back(std::move(m));
return true;
}