Color palette support in .vox loader and Cubes mesher
parent
7f89f2db76
commit
7064f6e471
|
@ -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
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue