Color palette support in .vox loader and Cubes mesher

master
Marc Gilleron 2020-09-13 02:17:11 +01:00
parent 7f89f2db76
commit 7064f6e471
9 changed files with 475 additions and 125 deletions

80
math/color8.h Normal file
View File

@ -0,0 +1,80 @@
#ifndef COLOR8_H
#include <core/color.h>
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

View File

@ -8,6 +8,9 @@
#include <vector>
// 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)

View File

@ -0,0 +1,49 @@
#include "voxel_color_palette.h"
void VoxelColorPalette::set_color(int index, Color color) {
ERR_FAIL_INDEX(index, static_cast<int>(_colors.size()));
_colors[index] = Color8(color);
}
Color VoxelColorPalette::get_color(int index) const {
ERR_FAIL_INDEX_V(index, static_cast<int>(_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);
}

View File

@ -0,0 +1,39 @@
#ifndef VOXEL_COLOR_PALETTE_H
#define VOXEL_COLOR_PALETTE_H
#include "../../math/color8.h"
#include "../../util/fixed_array.h"
#include <core/resource.h>
#include <vector>
// 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<Color8, MAX_COLORS> _colors;
};
#endif // VOXEL_COLOR_PALETTE_H

View File

@ -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 <typename Voxel_T>
template <typename Voxel_T, typename Color_F>
void build_voxel_mesh_as_simple_cubes(
FixedArray<VoxelMesherCubes::Arrays, VoxelMesherCubes::MATERIAL_COUNT> &out_arrays_per_material,
const ArraySlice<Voxel_T> voxel_buffer,
const Vector3i block_size) {
const Vector3i block_size,
Color_F color_func) {
ERR_FAIL_COND(block_size.x < static_cast<int>(2 * VoxelMesherCubes::PADDING) ||
block_size.y < static_cast<int>(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 <typename Voxel_T>
template <typename Voxel_T, typename Color_F>
void build_voxel_mesh_as_greedy_cubes(
FixedArray<VoxelMesherCubes::Arrays, VoxelMesherCubes::MATERIAL_COUNT> &out_arrays_per_material,
const ArraySlice<Voxel_T> voxel_buffer,
const Vector3i block_size,
std::vector<uint8_t> mask_memory_pool) {
std::vector<uint8_t> mask_memory_pool,
Color_F color_func) {
ERR_FAIL_COND(block_size.x < static_cast<int>(2 * VoxelMesherCubes::PADDING) ||
block_size.y < static_cast<int>(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<uint16_t>(),
block_size,
_mask_memory_pool,
Color8::from_u16);
} else {
build_voxel_mesh_as_simple_cubes(
_arrays_per_material,
raw_channel.reinterpret_cast_to<uint16_t>(),
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<uint16_t>(),
block_size, _mask_memory_pool);
} else {
build_voxel_mesh_as_simple_cubes(_arrays_per_material, raw_channel.reinterpret_cast_to<uint16_t>(),
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<uint16_t>(),
block_size,
_mask_memory_pool,
get_color_from_palette);
} else {
build_voxel_mesh_as_simple_cubes(
_arrays_per_material,
raw_channel.reinterpret_cast_to<uint16_t>(),
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<uint16_t>(),
block_size,
_mask_memory_pool,
get_index_from_palette);
} else {
build_voxel_mesh_as_simple_cubes(
_arrays_per_material,
raw_channel.reinterpret_cast_to<uint16_t>(),
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<VoxelColorPalette> palette) {
_palette = palette;
}
Ref<VoxelColorPalette> 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);
}

View File

@ -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<VoxelColorPalette> palette);
Ref<VoxelColorPalette> 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, MATERIAL_COUNT> _arrays_per_material;
std::vector<uint8_t> _mask_memory_pool;
Ref<VoxelColorPalette> _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

View File

@ -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<VoxelServer>();
ClassDB::register_class<Voxel>();
ClassDB::register_class<VoxelLibrary>();
ClassDB::register_class<VoxelColorPalette>();
// Storage
ClassDB::register_class<VoxelBuffer>();
ClassDB::register_class<VoxelMap>();
// Voxel types
ClassDB::register_class<Voxel>();
ClassDB::register_class<VoxelLibrary>();
// Nodes
ClassDB::register_class<VoxelTerrain>();
ClassDB::register_class<VoxelLodTerrain>();
@ -72,7 +72,7 @@ void register_voxel_types() {
ClassDB::register_class<VoxelGeneratorNoise>();
ClassDB::register_class<VoxelGeneratorGraph>();
// Helpers
// Utilities
ClassDB::register_class<VoxelBoxMover>();
ClassDB::register_class<VoxelRaycastResult>();
ClassDB::register_class<VoxelTool>();

View File

@ -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<VoxelBuffer> voxels) {
Error VoxelVoxLoader::load_from_file(String fpath, Ref<VoxelBuffer> voxels, Ref<VoxelColorPalette> 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<VoxelBuffer> voxels) {
const VoxelBuffer::ChannelId channel = VoxelBuffer::CHANNEL_COLOR;
const ArraySlice<vox::Color8> palette =
const ArraySlice<Color8> src_palette =
_data.has_palette ?
ArraySlice<vox::Color8>(_data.palette) :
ArraySlice<vox::Color8>(reinterpret_cast<vox::Color8 *>(vox::g_default_palette), 0, 256);
ArraySlice<Color8>(_data.palette) :
ArraySlice<Color8>(reinterpret_cast<Color8 *>(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<VoxelBuffer> 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<uint16_t> dst = dst_raw.reinterpret_cast_to<uint16_t>();
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<uint16_t> dst = dst_raw.reinterpret_cast_to<uint16_t>();
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<uint16_t> dst = dst_raw.reinterpret_cast_to<uint16_t>();
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;

View File

@ -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<uint8_t> color_indexes;
@ -31,7 +23,7 @@ class VoxelVoxLoader : public Reference {
GDCLASS(VoxelVoxLoader, Reference);
public:
Error load_from_file(String fpath, Ref<VoxelBuffer> voxels);
Error load_from_file(String fpath, Ref<VoxelBuffer> voxels, Ref<VoxelColorPalette> palette);
private:
static void _bind_methods();