From 7064f6e471e495fc796d9b3d5e6d9582ce9cc43c Mon Sep 17 00:00:00 2001 From: Marc Gilleron Date: Sun, 13 Sep 2020 02:17:11 +0100 Subject: [PATCH] Color palette support in .vox loader and Cubes mesher --- math/color8.h | 80 +++++++ meshers/blocky/voxel_mesher_blocky.h | 3 + meshers/cubes/voxel_color_palette.cpp | 49 +++++ meshers/cubes/voxel_color_palette.h | 39 ++++ meshers/cubes/voxel_mesher_cubes.cpp | 286 ++++++++++++++++++++------ meshers/cubes/voxel_mesher_cubes.h | 37 +++- register_types.cpp | 10 +- streams/vox_loader.cpp | 84 ++++---- streams/vox_loader.h | 12 +- 9 files changed, 475 insertions(+), 125 deletions(-) create mode 100644 math/color8.h create mode 100644 meshers/cubes/voxel_color_palette.cpp create mode 100644 meshers/cubes/voxel_color_palette.h diff --git a/math/color8.h b/math/color8.h new file mode 100644 index 00000000..c18bff7a --- /dev/null +++ b/math/color8.h @@ -0,0 +1,80 @@ +#ifndef COLOR8_H + +#include + +struct Color8 { + union { + struct { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; + }; + uint8_t components[4]; + }; + + Color8() : + r(0), g(0), b(0), a(0) {} + + Color8(uint8_t p_r, uint8_t p_g, uint8_t p_b, uint8_t p_a) : + r(p_r), g(p_g), b(p_b), a(p_a) {} + + Color8(Color c) { + r = c.r * 255; + g = c.g * 255; + b = c.b * 255; + a = c.a * 255; + } + + static inline Color8 from_u8(uint8_t v) { + // rrggbbaa + return Color8( + v >> 6, + ((v >> 4) & 3), + ((v >> 2) & 3), + v & 3); + } + + static inline Color8 from_u16(uint16_t v) { + // rrrrgggg bbbbaaaa 🐐 + return Color8( + v >> 12, + ((v >> 8) & 0xf), + ((v >> 4) & 0xf), + v & 0xf); + } + + static inline Color8 from_u32(uint32_t c) { + return Color8( + c >> 24, + (c >> 16) & 0xff, + (c >> 8) & 0xff, + c & 0xff); + } + + inline uint8_t to_u8() const { + // Lossy + return ((r >> 6) << 6) | + ((g >> 6) << 4) | + ((b >> 6) << 2) | + (a >> 6); + } + + inline uint16_t to_u16() const { + // Lossy + return ((r >> 4) << 12) | + ((g >> 4) << 8) | + ((b >> 4) << 4) | + (a >> 4); + } + + inline uint32_t to_u32() const { + return (r << 24) | (g << 16) | (b << 8) | a; + } + + operator Color() const { + return Color(r / 255.f, g / 255.f, b / 255.f, a / 255.f); + } +}; + +#endif // COLOR8_H diff --git a/meshers/blocky/voxel_mesher_blocky.h b/meshers/blocky/voxel_mesher_blocky.h index b6c0eafb..88090359 100644 --- a/meshers/blocky/voxel_mesher_blocky.h +++ b/meshers/blocky/voxel_mesher_blocky.h @@ -8,6 +8,9 @@ #include // TODO Rename VoxelMesherModelBatch + +// Interprets voxel values as indexes to models in a VoxelLibrary, and batches them together. +// Overlapping faces are removed from the final mesh. class VoxelMesherBlocky : public VoxelMesher { GDCLASS(VoxelMesherBlocky, VoxelMesher) diff --git a/meshers/cubes/voxel_color_palette.cpp b/meshers/cubes/voxel_color_palette.cpp new file mode 100644 index 00000000..08f78aa0 --- /dev/null +++ b/meshers/cubes/voxel_color_palette.cpp @@ -0,0 +1,49 @@ +#include "voxel_color_palette.h" + +void VoxelColorPalette::set_color(int index, Color color) { + ERR_FAIL_INDEX(index, static_cast(_colors.size())); + _colors[index] = Color8(color); +} + +Color VoxelColorPalette::get_color(int index) const { + ERR_FAIL_INDEX_V(index, static_cast(_colors.size()), Color()); + return _colors[index]; +} + +void VoxelColorPalette::clear() { + for (size_t i = 0; i < _colors.size(); ++i) { + _colors[i] = Color8(); + } +} + +PoolIntArray VoxelColorPalette::_b_get_data() const { + PoolIntArray colors; + colors.resize(_colors.size()); + { + PoolIntArray::Write w = colors.write(); + for (size_t i = 0; i < _colors.size(); ++i) { + w[i] = _colors[i].to_u32(); + } + } + return colors; +} + +void VoxelColorPalette::_b_set_data(PoolIntArray colors) { + ERR_FAIL_COND(colors.size() > _colors.size()); + PoolIntArray::Read r = colors.read(); + for (size_t i = 0; i < colors.size(); ++i) { + _colors[i] = Color8::from_u32(r[i]); + } +} + +void VoxelColorPalette::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_color", "color"), &VoxelColorPalette::set_color); + ClassDB::bind_method(D_METHOD("get_color"), &VoxelColorPalette::get_color); + + ClassDB::bind_method(D_METHOD("set_data", "d"), &VoxelColorPalette::_b_set_data); + ClassDB::bind_method(D_METHOD("get_data"), &VoxelColorPalette::_b_get_data); + + ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "data"), "set_data", "get_data"); + + BIND_CONSTANT(MAX_COLORS); +} diff --git a/meshers/cubes/voxel_color_palette.h b/meshers/cubes/voxel_color_palette.h new file mode 100644 index 00000000..5c021f32 --- /dev/null +++ b/meshers/cubes/voxel_color_palette.h @@ -0,0 +1,39 @@ +#ifndef VOXEL_COLOR_PALETTE_H +#define VOXEL_COLOR_PALETTE_H + +#include "../../math/color8.h" +#include "../../util/fixed_array.h" +#include +#include + +// Associates small numbers to colors, so colored voxels can be specified using less memory. +class VoxelColorPalette : public Resource { + GDCLASS(VoxelColorPalette, Resource) +public: + static const unsigned int MAX_COLORS = 256; + + void set_color(int index, Color color); + Color get_color(int index) const; + + void clear(); + + // Internal + + inline void set_color8(uint8_t i, Color8 c) { + _colors[i] = c; + } + + inline Color8 get_color8(uint8_t i) const { + return _colors[i]; + } + +private: + PoolIntArray _b_get_data() const; + void _b_set_data(PoolIntArray colors); + + static void _bind_methods(); + + FixedArray _colors; +}; + +#endif // VOXEL_COLOR_PALETTE_H diff --git a/meshers/cubes/voxel_mesher_cubes.cpp b/meshers/cubes/voxel_mesher_cubes.cpp index 2ce9906d..6d259f26 100644 --- a/meshers/cubes/voxel_mesher_cubes.cpp +++ b/meshers/cubes/voxel_mesher_cubes.cpp @@ -51,39 +51,16 @@ enum Side { // 0 if alpha is zero, // 1 if alpha is neither zero neither max, // 2 if alpha is max -inline uint8_t get_alpha_index(uint8_t v) { - const uint8_t a = v & 3; - return (a == 3) + (a > 0); +inline uint8_t get_alpha_index(Color8 c) { + return (c.a == 0xf) + (c.a > 0); } -inline uint8_t get_alpha_index(uint16_t v) { - const uint16_t a = v & 0xf; - return (a == 0xf) + (a > 0); -} - -inline Color to_color(uint8_t v) { - // rrggbbaa - return Color( - (v >> 6) / 3.f, - ((v >> 4) & 3) / 3.f, - ((v >> 2) & 3) / 3.f, - (v & 3) / 3.f); -} - -inline Color to_color(uint16_t v) { - // rrrrgggg bbbbaaaa - return Color( - (v >> 12) / 15.f, - ((v >> 8) & 0xf) / 15.f, - ((v >> 4) & 0xf) / 15.f, - (v & 0xf) / 15.f); -} - -template +template void build_voxel_mesh_as_simple_cubes( FixedArray &out_arrays_per_material, const ArraySlice voxel_buffer, - const Vector3i block_size) { + const Vector3i block_size, + Color_F color_func) { ERR_FAIL_COND(block_size.x < static_cast(2 * VoxelMesherCubes::PADDING) || block_size.y < static_cast(2 * VoxelMesherCubes::PADDING) || @@ -121,28 +98,31 @@ void build_voxel_mesh_as_simple_cubes( pos[Vector3i::AXIS_X] * row_size + pos[Vector3i::AXIS_Z] * deck_size; - const Voxel_T color0 = voxel_buffer[voxel_index]; - const Voxel_T color1 = voxel_buffer[voxel_index + neighbor_offset_d_lut[za]]; + const Voxel_T raw_color0 = voxel_buffer[voxel_index]; + const Voxel_T raw_color1 = voxel_buffer[voxel_index + neighbor_offset_d_lut[za]]; + const Color8 color0 = color_func(raw_color0); + const Color8 color1 = color_func(raw_color1); + + // TODO Change this const uint8_t ai0 = get_alpha_index(color0); const uint8_t ai1 = get_alpha_index(color1); - Voxel_T color_raw; + Color8 color; Side side; if (ai0 == ai1) { continue; } else if (ai0 > ai1) { - color_raw = color0; + color = color0; side = SIDE_BACK; } else { - color_raw = color1; + color = color1; side = SIDE_FRONT; } // Commit face to the mesh - const Color color = to_color(color_raw); - const uint8_t material_index = color.a < 0.999f; + const uint8_t material_index = color.a < 255; VoxelMesherCubes::Arrays &arrays = out_arrays_per_material[material_index]; const int vx0 = fx - VoxelMesherCubes::PADDING; @@ -183,10 +163,12 @@ void build_voxel_mesh_as_simple_cubes( arrays.positions.push_back(v2); arrays.positions.push_back(v3); - arrays.colors.push_back(color); - arrays.colors.push_back(color); - arrays.colors.push_back(color); - arrays.colors.push_back(color); + // TODO Any way to not need Color anywhere? It's wasteful + const Color colorf = color; + arrays.colors.push_back(colorf); + arrays.colors.push_back(colorf); + arrays.colors.push_back(colorf); + arrays.colors.push_back(colorf); arrays.normals.push_back(n); arrays.normals.push_back(n); @@ -206,12 +188,13 @@ void build_voxel_mesh_as_simple_cubes( } } -template +template void build_voxel_mesh_as_greedy_cubes( FixedArray &out_arrays_per_material, const ArraySlice voxel_buffer, const Vector3i block_size, - std::vector mask_memory_pool) { + std::vector mask_memory_pool, + Color_F color_func) { ERR_FAIL_COND(block_size.x < static_cast(2 * VoxelMesherCubes::PADDING) || block_size.y < static_cast(2 * VoxelMesherCubes::PADDING) || @@ -269,8 +252,11 @@ void build_voxel_mesh_as_greedy_cubes( pos[Vector3i::AXIS_X] * row_size + pos[Vector3i::AXIS_Z] * deck_size; - const Voxel_T color0 = voxel_buffer[voxel_index]; - const Voxel_T color1 = voxel_buffer[voxel_index + neighbor_offset_d_lut[za]]; + const Voxel_T raw_color0 = voxel_buffer[voxel_index]; + const Voxel_T raw_color1 = voxel_buffer[voxel_index + neighbor_offset_d_lut[za]]; + + const Color8 color0 = color_func(raw_color0); + const Color8 color1 = color_func(raw_color1); const uint8_t ai0 = get_alpha_index(color0); const uint8_t ai1 = get_alpha_index(color1); @@ -279,10 +265,10 @@ void build_voxel_mesh_as_greedy_cubes( if (ai0 == ai1) { mv.side = SIDE_NONE; } else if (ai0 > ai1) { - mv.color = color0; + mv.color = raw_color0; mv.side = SIDE_BACK; } else { - mv.color = color1; + mv.color = raw_color1; mv.side = SIDE_FRONT; } @@ -329,8 +315,8 @@ void build_voxel_mesh_as_greedy_cubes( // Commit face to the mesh - const Color color = to_color(m.color); - const uint8_t material_index = color.a < 0.999f; + const Color colorf = color_func(m.color); + const uint8_t material_index = colorf.a < 0.999f; VoxelMesherCubes::Arrays &arrays = out_arrays_per_material[material_index]; Vector3 v0; @@ -366,10 +352,10 @@ void build_voxel_mesh_as_greedy_cubes( arrays.positions.push_back(v2); arrays.positions.push_back(v3); - arrays.colors.push_back(color); - arrays.colors.push_back(color); - arrays.colors.push_back(color); - arrays.colors.push_back(color); + arrays.colors.push_back(colorf); + arrays.colors.push_back(colorf); + arrays.colors.push_back(colorf); + arrays.colors.push_back(colorf); arrays.normals.push_back(n); arrays.normals.push_back(n); @@ -445,28 +431,160 @@ void VoxelMesherCubes::build(VoxelMesher::Output &output, const VoxelMesher::Inp const Vector3i block_size = voxels.get_size(); const VoxelBuffer::Depth channel_depth = voxels.get_channel_depth(channel); - switch (channel_depth) { - case VoxelBuffer::DEPTH_8_BIT: - if (_greedy_meshing) { - build_voxel_mesh_as_greedy_cubes(_arrays_per_material, raw_channel, block_size, _mask_memory_pool); - } else { - build_voxel_mesh_as_simple_cubes(_arrays_per_material, raw_channel, block_size); + switch (_color_mode) { + case COLOR_RAW: + switch (channel_depth) { + case VoxelBuffer::DEPTH_8_BIT: + if (_greedy_meshing) { + build_voxel_mesh_as_greedy_cubes( + _arrays_per_material, + raw_channel, + block_size, + _mask_memory_pool, + Color8::from_u8); + } else { + build_voxel_mesh_as_simple_cubes( + _arrays_per_material, + raw_channel, + block_size, + Color8::from_u8); + } + break; + + case VoxelBuffer::DEPTH_16_BIT: + if (_greedy_meshing) { + build_voxel_mesh_as_greedy_cubes( + _arrays_per_material, + raw_channel.reinterpret_cast_to(), + block_size, + _mask_memory_pool, + Color8::from_u16); + } else { + build_voxel_mesh_as_simple_cubes( + _arrays_per_material, + raw_channel.reinterpret_cast_to(), + block_size, + Color8::from_u16); + } + break; + + default: + ERR_PRINT("Unsupported voxel depth"); + return; } break; - case VoxelBuffer::DEPTH_16_BIT: - if (_greedy_meshing) { - build_voxel_mesh_as_greedy_cubes(_arrays_per_material, raw_channel.reinterpret_cast_to(), - block_size, _mask_memory_pool); - } else { - build_voxel_mesh_as_simple_cubes(_arrays_per_material, raw_channel.reinterpret_cast_to(), - block_size); + case COLOR_MESHER_PALETTE: { + ERR_FAIL_COND_MSG(_palette.is_null(), "Palette mode is used but no palette was specified"); + + struct GetColorFromPalette { + VoxelColorPalette &palette; + Color8 operator()(uint64_t i) const { + // Note: even though this code may run in a thread, I'm not locking the palette at all because + // it stores colors in a fixed-size array, and reading the wrong color won't cause any serious + // problem. It's not supposed to change often in game anyways. If it does, better use shader mode. + return palette.get_color8(i); + } + }; + const GetColorFromPalette get_color_from_palette{ **_palette }; + + switch (channel_depth) { + case VoxelBuffer::DEPTH_8_BIT: + if (_greedy_meshing) { + build_voxel_mesh_as_greedy_cubes( + _arrays_per_material, + raw_channel, + block_size, + _mask_memory_pool, + get_color_from_palette); + } else { + build_voxel_mesh_as_simple_cubes( + _arrays_per_material, + raw_channel, + block_size, + get_color_from_palette); + } + break; + + case VoxelBuffer::DEPTH_16_BIT: + if (_greedy_meshing) { + build_voxel_mesh_as_greedy_cubes( + _arrays_per_material, + raw_channel.reinterpret_cast_to(), + block_size, + _mask_memory_pool, + get_color_from_palette); + } else { + build_voxel_mesh_as_simple_cubes( + _arrays_per_material, + raw_channel.reinterpret_cast_to(), + block_size, + get_color_from_palette); + } + break; + + default: + ERR_PRINT("Unsupported voxel depth"); + return; } - break; + } break; + + case COLOR_SHADER_PALETTE: { + ERR_FAIL_COND_MSG(_palette.is_null(), "Palette mode is used but no palette was specified"); + + struct GetIndexFromPalette { + VoxelColorPalette &palette; + Color8 operator()(uint64_t i) const { + // Still providing alpha because it allows to separate the opaque and transparent surfaces + return Color8(i, 0, 0, palette.get_color8(i).a); + } + }; + const GetIndexFromPalette get_index_from_palette{ **_palette }; + + switch (channel_depth) { + case VoxelBuffer::DEPTH_8_BIT: + if (_greedy_meshing) { + build_voxel_mesh_as_greedy_cubes( + _arrays_per_material, + raw_channel, + block_size, + _mask_memory_pool, + get_index_from_palette); + } else { + build_voxel_mesh_as_simple_cubes( + _arrays_per_material, + raw_channel, + block_size, + get_index_from_palette); + } + break; + + case VoxelBuffer::DEPTH_16_BIT: + if (_greedy_meshing) { + build_voxel_mesh_as_greedy_cubes( + _arrays_per_material, + raw_channel.reinterpret_cast_to(), + block_size, + _mask_memory_pool, + get_index_from_palette); + } else { + build_voxel_mesh_as_simple_cubes( + _arrays_per_material, + raw_channel.reinterpret_cast_to(), + block_size, + get_index_from_palette); + } + break; + + default: + ERR_PRINT("Unsupported voxel depth"); + return; + } + } break; default: - ERR_PRINT("Unsupported voxel depth"); - return; + CRASH_NOW(); + break; } // TODO We could return a single byte array and use Mesh::add_surface down the line? @@ -515,6 +633,23 @@ bool VoxelMesherCubes::is_greedy_meshing_enabled() const { return _greedy_meshing; } +void VoxelMesherCubes::set_palette(Ref palette) { + _palette = palette; +} + +Ref VoxelMesherCubes::get_palette() const { + return _palette; +} + +void VoxelMesherCubes::set_color_mode(ColorMode mode) { + ERR_FAIL_INDEX(mode, COLOR_MODE_COUNT); + _color_mode = mode; +} + +VoxelMesherCubes::ColorMode VoxelMesherCubes::get_color_mode() const { + return _color_mode; +} + VoxelMesher *VoxelMesherCubes::clone() { VoxelMesherCubes *d = memnew(VoxelMesherCubes); d->_greedy_meshing = _greedy_meshing; @@ -526,6 +661,23 @@ void VoxelMesherCubes::_bind_methods() { &VoxelMesherCubes::set_greedy_meshing_enabled); ClassDB::bind_method(D_METHOD("is_greedy_meshing_enabled"), &VoxelMesherCubes::is_greedy_meshing_enabled); + ClassDB::bind_method(D_METHOD("set_palette", "palette"), &VoxelMesherCubes::set_palette); + ClassDB::bind_method(D_METHOD("get_palette"), &VoxelMesherCubes::get_palette); + + ClassDB::bind_method(D_METHOD("set_color_mode", "mode"), &VoxelMesherCubes::set_color_mode); + ClassDB::bind_method(D_METHOD("get_color_mode"), &VoxelMesherCubes::get_color_mode); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "greedy_meshing_enabled"), "set_greedy_meshing_enabled", "is_greedy_meshing_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "color_mode"), "set_color_mode", "get_color_mode"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "palette", PROPERTY_HINT_RESOURCE_TYPE, "VoxelColorPalette"), + "set_palette", "get_palette"); + + BIND_ENUM_CONSTANT(MATERIAL_OPAQUE); + BIND_ENUM_CONSTANT(MATERIAL_TRANSPARENT); + BIND_ENUM_CONSTANT(MATERIAL_COUNT); + + BIND_ENUM_CONSTANT(COLOR_RAW); + BIND_ENUM_CONSTANT(COLOR_MESHER_PALETTE); + BIND_ENUM_CONSTANT(COLOR_SHADER_PALETTE); } diff --git a/meshers/cubes/voxel_mesher_cubes.h b/meshers/cubes/voxel_mesher_cubes.h index 8d6af903..31bd2f9a 100644 --- a/meshers/cubes/voxel_mesher_cubes.h +++ b/meshers/cubes/voxel_mesher_cubes.h @@ -2,6 +2,7 @@ #define VOXEL_MESHER_CUBES_H #include "../voxel_mesher.h" +#include "voxel_color_palette.h" // A super simple mesher only producing colored cubes class VoxelMesherCubes : public VoxelMesher { @@ -9,6 +10,25 @@ class VoxelMesherCubes : public VoxelMesher { public: static const unsigned int PADDING = 1; + enum Materials { + MATERIAL_OPAQUE = 0, + MATERIAL_TRANSPARENT, + MATERIAL_COUNT + }; + + enum ColorMode { + // The voxel value will be treated as an RGBA color with components of equal bit depth + COLOR_RAW = 0, + // The voxel value will map to a 32bit color in the palette specified on this mesher + COLOR_MESHER_PALETTE, + // The voxel value will be copied directly to the vertex array, + // so the proper color can be selected by a shader. + // LIMITATION: only one material can be used in this mode at the moment. + COLOR_SHADER_PALETTE, + + COLOR_MODE_COUNT + }; + VoxelMesherCubes(); void build(VoxelMesher::Output &output, const VoxelMesher::Input &input) override; @@ -16,16 +36,14 @@ public: void set_greedy_meshing_enabled(bool enable); bool is_greedy_meshing_enabled() const; - // TODO Palette support + void set_color_mode(ColorMode mode); + ColorMode get_color_mode() const; + + void set_palette(Ref palette); + Ref get_palette() const; VoxelMesher *clone() override; - enum Materials { - MATERIAL_OPAQUE = 0, - MATERIAL_TRANSPARENT, - MATERIAL_COUNT - }; - // Using std::vector because they make this mesher twice as fast than Godot Vectors. // See why: https://github.com/godotengine/godot/issues/24731 struct Arrays { @@ -41,7 +59,12 @@ protected: private: FixedArray _arrays_per_material; std::vector _mask_memory_pool; + Ref _palette; bool _greedy_meshing = true; + ColorMode _color_mode = COLOR_RAW; }; +VARIANT_ENUM_CAST(VoxelMesherCubes::ColorMode); +VARIANT_ENUM_CAST(VoxelMesherCubes::Materials); + #endif // VOXEL_MESHER_CUBES_H diff --git a/register_types.cpp b/register_types.cpp index f565bf39..466db8bb 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -43,14 +43,14 @@ void register_voxel_types() { // TODO Can I prevent users from instancing it? is "register_virtual_class" correct for a class that's not abstract? ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); + // Storage ClassDB::register_class(); ClassDB::register_class(); - // Voxel types - ClassDB::register_class(); - ClassDB::register_class(); - // Nodes ClassDB::register_class(); ClassDB::register_class(); @@ -72,7 +72,7 @@ void register_voxel_types() { ClassDB::register_class(); ClassDB::register_class(); - // Helpers + // Utilities ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); diff --git a/streams/vox_loader.cpp b/streams/vox_loader.cpp index 2ef1456c..7855da1c 100644 --- a/streams/vox_loader.cpp +++ b/streams/vox_loader.cpp @@ -3,7 +3,9 @@ namespace vox { -uint32_t g_default_palette[256] = { +const uint32_t PALETTE_SIZE = 256; + +uint32_t g_default_palette[PALETTE_SIZE] = { 0x00000000, 0xffffffff, 0xffccffff, 0xff99ffff, 0xff66ffff, 0xff33ffff, 0xff00ffff, 0xffffccff, 0xffccccff, 0xff99ccff, 0xff66ccff, 0xff33ccff, 0xff00ccff, 0xffff99ff, 0xffcc99ff, 0xff9999ff, 0xff6699ff, 0xff3399ff, 0xff0099ff, 0xffff66ff, 0xffcc66ff, 0xff9966ff, 0xff6666ff, 0xff3366ff, @@ -115,21 +117,7 @@ Error load_vox(const String &fpath, Data &data) { } // namespace vox -inline uint8_t to_u8(vox::Color8 c) { - return ((c.r >> 6) << 6) | - ((c.g >> 6) << 4) | - ((c.b >> 6) << 2) | - (c.a >> 6); -} - -inline uint16_t to_u16(vox::Color8 c) { - return ((c.r >> 4) << 12) | - ((c.g >> 4) << 8) | - ((c.b >> 4) << 4) | - (c.a >> 4); -} - -Error VoxelVoxLoader::load_from_file(String fpath, Ref voxels) { +Error VoxelVoxLoader::load_from_file(String fpath, Ref voxels, Ref palette) { ERR_FAIL_COND_V(voxels.is_null(), ERR_INVALID_PARAMETER); const Error err = vox::load_vox(fpath, _data); @@ -137,10 +125,10 @@ Error VoxelVoxLoader::load_from_file(String fpath, Ref voxels) { const VoxelBuffer::ChannelId channel = VoxelBuffer::CHANNEL_COLOR; - const ArraySlice palette = + const ArraySlice src_palette = _data.has_palette ? - ArraySlice(_data.palette) : - ArraySlice(reinterpret_cast(vox::g_default_palette), 0, 256); + ArraySlice(_data.palette) : + ArraySlice(reinterpret_cast(vox::g_default_palette), 0, vox::PALETTE_SIZE); const VoxelBuffer::Depth depth = voxels->get_channel_depth(VoxelBuffer::CHANNEL_COLOR); @@ -149,25 +137,49 @@ Error VoxelVoxLoader::load_from_file(String fpath, Ref voxels) { voxels->decompress_channel(channel); CRASH_COND(!voxels->get_channel_raw(channel, dst_raw)); - switch (depth) { - case VoxelBuffer::DEPTH_8_BIT: { - for (size_t i = 0; i < dst_raw.size(); ++i) { - const uint8_t ci = _data.color_indexes[i]; - dst_raw[i] = to_u8(palette[ci]); - } - } break; + if (palette.is_valid() && _data.has_palette) { + for (size_t i = 0; i < src_palette.size(); ++i) { + palette->set_color8(i, src_palette[i]); + } - case VoxelBuffer::DEPTH_16_BIT: { - ArraySlice dst = dst_raw.reinterpret_cast_to(); - for (size_t i = 0; i < dst.size(); ++i) { - const uint8_t ci = _data.color_indexes[i]; - dst[i] = to_u16(palette[ci]); - } - } break; + switch (depth) { + case VoxelBuffer::DEPTH_8_BIT: { + memcpy(dst_raw.data(), _data.color_indexes.data(), _data.color_indexes.size()); + } break; - default: - ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Unsupported depth"); - break; + case VoxelBuffer::DEPTH_16_BIT: { + ArraySlice dst = dst_raw.reinterpret_cast_to(); + for (size_t i = 0; i < dst.size(); ++i) { + dst[i] = _data.color_indexes[i]; + } + } break; + + default: + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Unsupported depth"); + break; + } + + } else { + switch (depth) { + case VoxelBuffer::DEPTH_8_BIT: { + for (size_t i = 0; i < dst_raw.size(); ++i) { + const uint8_t ci = _data.color_indexes[i]; + dst_raw[i] = src_palette[ci].to_u8(); + } + } break; + + case VoxelBuffer::DEPTH_16_BIT: { + ArraySlice dst = dst_raw.reinterpret_cast_to(); + for (size_t i = 0; i < dst.size(); ++i) { + const uint8_t ci = _data.color_indexes[i]; + dst[i] = src_palette[ci].to_u16(); + } + } break; + + default: + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Unsupported depth"); + break; + } } return err; diff --git a/streams/vox_loader.h b/streams/vox_loader.h index 40591e81..525b3eac 100644 --- a/streams/vox_loader.h +++ b/streams/vox_loader.h @@ -1,19 +1,11 @@ #ifndef VOX_LOADER_H #define VOX_LOADER_H -#include "../math/vector3i.h" -#include "../util/fixed_array.h" +#include "../meshers/cubes/voxel_color_palette.h" #include "../voxel_buffer.h" namespace vox { -struct Color8 { - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; -}; - struct Data { Vector3i size; std::vector color_indexes; @@ -31,7 +23,7 @@ class VoxelVoxLoader : public Reference { GDCLASS(VoxelVoxLoader, Reference); public: - Error load_from_file(String fpath, Ref voxels); + Error load_from_file(String fpath, Ref voxels, Ref palette); private: static void _bind_methods();