From 5a26c26daa09db9ba0cfa5a38147eacc4760938c Mon Sep 17 00:00:00 2001 From: Xein Date: Sun, 1 Nov 2026 21:22:39 +0100 Subject: [PATCH] fixes now there are less bugs --- editor/Assets/VoxelPaletteFile.cs | 8 +-- engine/src/toast/voxel/assets/vox_import.cpp | 64 +++++++++++++++++++- 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/editor/Assets/VoxelPaletteFile.cs b/editor/Assets/VoxelPaletteFile.cs index 1fab4c1b8..72a51072d 100644 --- a/editor/Assets/VoxelPaletteFile.cs +++ b/editor/Assets/VoxelPaletteFile.cs @@ -240,12 +240,10 @@ public string Serialize() { public byte[] ToPng() { var row = new byte[1 + Size * 4]; // filter byte then pixels - for (var i = 1; i < Size; i++) { - var entry = Entries[i]; + for (var i = 0; i < Size - 1; i++) { + var entry = Entries[i + 1]; var o = 1 + i * 4; - if (i == Size - 1) { - row[o] = row[o + 1] = row[o + 2] = row[o + 3] = 255; - } else if (entry.InUse) { + if (entry.InUse) { row[o] = entry.R; row[o + 1] = entry.G; row[o + 2] = entry.B; diff --git a/engine/src/toast/voxel/assets/vox_import.cpp b/engine/src/toast/voxel/assets/vox_import.cpp index 22c63a1ce..2cbe197e1 100644 --- a/engine/src/toast/voxel/assets/vox_import.cpp +++ b/engine/src/toast/voxel/assets/vox_import.cpp @@ -316,6 +316,8 @@ auto importVox(std::span data) -> VoxScene { VoxScene scene; bool has_rgba = false; + std::array imap {}; + bool has_imap = false; std::unordered_map materials; struct RawNode { @@ -414,6 +416,11 @@ auto importVox(std::span data) -> VoxScene { } } has_rgba = true; + } else if (is("IMAP")) { + for (uint8_t& value : imap) { + value = reader.u8(); + } + has_imap = true; } else if (is("MATL")) { const int32_t index = reader.i32(); const VoxDict attributes = reader.dict(); @@ -538,7 +545,7 @@ auto importVox(std::span data) -> VoxScene { "node " + std::to_string(node_id) + " holds " + std::to_string(count) + " model frames; only the first is imported" ); } - } else if (is("PACK") || is("LAYR") || is("rOBJ") || is("rCAM") || is("NOTE") || is("IMAP")) { + } else if (is("PACK") || is("LAYR") || is("rOBJ") || is("rCAM") || is("NOTE")) { } else { scene.warnings.push_back( "unknown chunk '" + std::string(id.data(), 4) + "' skipped, which is how a newer MagicaVoxel adds things" @@ -570,12 +577,23 @@ auto importVox(std::span data) -> VoxScene { } scene.palette.max_emissive = brightest > 0.0f ? brightest : 1.0f; + std::array empty {}; + for (uint32_t i = 1; i < k_palette_size; ++i) { + const PaletteEntry& entry = scene.palette.entries[i]; + empty[i] = has_rgba && entry.albedo_r == 0 && entry.albedo_g == 0 && entry.albedo_b == 0; + } + // An index with no MATL chunk is MagicaVoxel diffuse too for (uint32_t i = 1; i < k_palette_size; ++i) { - scene.palette.entries[i].roughness = voxByteFromUnit(VoxMaterial {}.roughness); + if (!empty[i]) { + scene.palette.entries[i].roughness = voxByteFromUnit(VoxMaterial {}.roughness); + } } for (const auto& [index, material] : materials) { + if (empty[index]) { + continue; + } PaletteEntry& entry = scene.palette.entries[index]; entry.roughness = voxByteFromUnit(material.roughness); entry.metallic = voxByteFromUnit(material.metallic); @@ -590,6 +608,48 @@ auto importVox(std::span data) -> VoxScene { } } + for (uint32_t i = 1; i < k_palette_size; ++i) { + if (empty[i]) { + scene.palette.entries[i] = PaletteEntry {}; + } + } + + std::array id_of_internal {}; + for (uint32_t i = 0; i < 256; ++i) { + id_of_internal[i] = static_cast(i); + } + if (has_imap) { + std::array seen {}; + bool is_permutation = true; + for (uint8_t value : imap) { + is_permutation = is_permutation && !seen[value]; + seen[value] = true; + } + + if (is_permutation) { + for (uint32_t slot = 0; slot < 256; ++slot) { + id_of_internal[imap[slot]] = static_cast((slot + 1) & 255u); + } + } else { + scene.warnings.emplace_back("the IMAP chunk is not a permutation of 0..255; the palette order was left as stored"); + } + } + + if (has_imap) { + const auto internal_entries = scene.palette.entries; + for (uint32_t i = 1; i < k_palette_size; ++i) { + scene.palette.entries[i] = PaletteEntry {}; + } + for (uint32_t i = 1; i < k_palette_size; ++i) { + scene.palette.entries[id_of_internal[i]] = internal_entries[i]; + } + for (VoxModel& model : scene.models) { + for (VoxVoxel& voxel : model.voxels) { + voxel.palette_index = id_of_internal[voxel.palette_index]; + } + } + } + // A cycle has no root and would never finish flattening if (!node_order.empty()) { std::vector roots;