2020-09-10 16:36:23 -07:00
|
|
|
#include "voxel_mesher_cubes.h"
|
2020-09-14 11:33:02 -07:00
|
|
|
#include "../../storage/voxel_buffer.h"
|
2020-09-10 16:36:23 -07:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
// 2-----3
|
|
|
|
// | |
|
|
|
|
// | |
|
|
|
|
// 0-----1
|
|
|
|
// [axis][front/back][i]
|
|
|
|
const uint8_t g_indices_lut[3][2][6] = {
|
|
|
|
// X
|
|
|
|
{
|
|
|
|
// Front
|
|
|
|
{ 0, 3, 2, 0, 1, 3 },
|
|
|
|
// Back
|
|
|
|
{ 0, 2, 3, 0, 3, 1 },
|
|
|
|
},
|
|
|
|
// Y
|
|
|
|
{
|
|
|
|
// Front
|
|
|
|
{ 0, 2, 3, 0, 3, 1 },
|
|
|
|
// Back
|
|
|
|
{ 0, 3, 2, 0, 1, 3 },
|
|
|
|
},
|
|
|
|
// Z
|
|
|
|
{
|
|
|
|
// Front
|
|
|
|
{ 0, 3, 2, 0, 1, 3 },
|
|
|
|
// Back
|
|
|
|
{ 0, 2, 3, 0, 3, 1 },
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const uint8_t g_face_axes_lut[Vector3i::AXIS_COUNT][2] = {
|
|
|
|
// X
|
|
|
|
{ Vector3i::AXIS_Y, Vector3i::AXIS_Z },
|
|
|
|
// Y
|
|
|
|
{ Vector3i::AXIS_X, Vector3i::AXIS_Z },
|
|
|
|
// Z
|
|
|
|
{ Vector3i::AXIS_X, Vector3i::AXIS_Y }
|
|
|
|
};
|
|
|
|
|
|
|
|
enum Side {
|
|
|
|
SIDE_FRONT = 0,
|
|
|
|
SIDE_BACK,
|
|
|
|
SIDE_NONE // Either means there is no face, or it was consumed
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
// Returns:
|
|
|
|
// 0 if alpha is zero,
|
|
|
|
// 1 if alpha is neither zero neither max,
|
|
|
|
// 2 if alpha is max
|
2020-09-12 18:17:11 -07:00
|
|
|
inline uint8_t get_alpha_index(Color8 c) {
|
|
|
|
return (c.a == 0xf) + (c.a > 0);
|
2020-09-10 16:36:23 -07:00
|
|
|
}
|
|
|
|
|
2020-09-12 18:17:11 -07:00
|
|
|
template <typename Voxel_T, typename Color_F>
|
2020-09-10 16:36:23 -07:00
|
|
|
void build_voxel_mesh_as_simple_cubes(
|
|
|
|
FixedArray<VoxelMesherCubes::Arrays, VoxelMesherCubes::MATERIAL_COUNT> &out_arrays_per_material,
|
|
|
|
const ArraySlice<Voxel_T> voxel_buffer,
|
2020-09-12 18:17:11 -07:00
|
|
|
const Vector3i block_size,
|
|
|
|
Color_F color_func) {
|
2020-09-10 16:36:23 -07:00
|
|
|
|
2020-09-11 12:50:26 -07:00
|
|
|
ERR_FAIL_COND(block_size.x < static_cast<int>(2 * VoxelMesherCubes::PADDING) ||
|
|
|
|
block_size.y < static_cast<int>(2 * VoxelMesherCubes::PADDING) ||
|
|
|
|
block_size.z < static_cast<int>(2 * VoxelMesherCubes::PADDING));
|
2020-09-11 11:59:53 -07:00
|
|
|
|
2020-09-10 16:36:23 -07:00
|
|
|
const Vector3i min_pos = Vector3i(VoxelMesherCubes::PADDING);
|
|
|
|
const Vector3i max_pos = block_size - Vector3i(VoxelMesherCubes::PADDING);
|
|
|
|
const unsigned int row_size = block_size.y;
|
|
|
|
const unsigned int deck_size = block_size.x * row_size;
|
|
|
|
|
|
|
|
// Note: voxel buffers are indexed in ZXY order
|
|
|
|
FixedArray<uint32_t, Vector3i::AXIS_COUNT> neighbor_offset_d_lut;
|
|
|
|
neighbor_offset_d_lut[Vector3i::AXIS_X] = block_size.y;
|
|
|
|
neighbor_offset_d_lut[Vector3i::AXIS_Y] = 1;
|
|
|
|
neighbor_offset_d_lut[Vector3i::AXIS_Z] = block_size.x * block_size.y;
|
|
|
|
|
|
|
|
FixedArray<uint32_t, VoxelMesherCubes::MATERIAL_COUNT> index_offsets(0);
|
|
|
|
|
|
|
|
// For each axis
|
|
|
|
for (unsigned int za = 0; za < Vector3i::AXIS_COUNT; ++za) {
|
|
|
|
const unsigned int xa = g_face_axes_lut[za][0];
|
|
|
|
const unsigned int ya = g_face_axes_lut[za][1];
|
|
|
|
|
|
|
|
// For each deck
|
|
|
|
for (unsigned int d = min_pos[za] - VoxelMesherCubes::PADDING; d < (unsigned int)max_pos[za]; ++d) {
|
|
|
|
// For each cell of the deck, gather face info
|
2020-09-11 11:59:53 -07:00
|
|
|
for (unsigned int fy = min_pos[ya]; fy < (unsigned int)max_pos[ya]; ++fy) {
|
|
|
|
for (unsigned int fx = min_pos[xa]; fx < (unsigned int)max_pos[xa]; ++fx) {
|
2020-09-10 16:36:23 -07:00
|
|
|
FixedArray<unsigned int, Vector3i::AXIS_COUNT> pos;
|
|
|
|
pos[xa] = fx;
|
|
|
|
pos[ya] = fy;
|
|
|
|
pos[za] = d;
|
|
|
|
|
|
|
|
const unsigned int voxel_index = pos[Vector3i::AXIS_Y] +
|
|
|
|
pos[Vector3i::AXIS_X] * row_size +
|
|
|
|
pos[Vector3i::AXIS_Z] * deck_size;
|
|
|
|
|
2020-09-12 18:17:11 -07:00
|
|
|
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);
|
2020-09-10 16:36:23 -07:00
|
|
|
|
2020-09-12 18:17:11 -07:00
|
|
|
// TODO Change this
|
2020-09-10 16:36:23 -07:00
|
|
|
const uint8_t ai0 = get_alpha_index(color0);
|
|
|
|
const uint8_t ai1 = get_alpha_index(color1);
|
|
|
|
|
2020-09-12 18:17:11 -07:00
|
|
|
Color8 color;
|
2020-09-10 16:36:23 -07:00
|
|
|
Side side;
|
|
|
|
if (ai0 == ai1) {
|
|
|
|
continue;
|
|
|
|
} else if (ai0 > ai1) {
|
2020-09-12 18:17:11 -07:00
|
|
|
color = color0;
|
2020-09-10 16:36:23 -07:00
|
|
|
side = SIDE_BACK;
|
|
|
|
} else {
|
2020-09-12 18:17:11 -07:00
|
|
|
color = color1;
|
2020-09-10 16:36:23 -07:00
|
|
|
side = SIDE_FRONT;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Commit face to the mesh
|
|
|
|
|
2020-09-12 18:17:11 -07:00
|
|
|
const uint8_t material_index = color.a < 255;
|
2020-09-10 16:36:23 -07:00
|
|
|
VoxelMesherCubes::Arrays &arrays = out_arrays_per_material[material_index];
|
|
|
|
|
|
|
|
const int vx0 = fx - VoxelMesherCubes::PADDING;
|
|
|
|
const int vy0 = fy - VoxelMesherCubes::PADDING;
|
|
|
|
const int vx1 = vx0 + 1;
|
|
|
|
const int vy1 = vy0 + 1;
|
|
|
|
|
|
|
|
Vector3 v0;
|
|
|
|
v0[xa] = vx0;
|
|
|
|
v0[ya] = vy0;
|
|
|
|
v0[za] = d;
|
|
|
|
|
|
|
|
Vector3 v1;
|
|
|
|
v1[xa] = vx1;
|
|
|
|
v1[ya] = vy0;
|
|
|
|
v1[za] = d;
|
|
|
|
|
|
|
|
Vector3 v2;
|
|
|
|
v2[xa] = vx0;
|
|
|
|
v2[ya] = vy1;
|
|
|
|
v2[za] = d;
|
|
|
|
|
|
|
|
Vector3 v3;
|
|
|
|
v3[xa] = vx1;
|
|
|
|
v3[ya] = vy1;
|
|
|
|
v3[za] = d;
|
|
|
|
|
|
|
|
Vector3 n;
|
|
|
|
n[za] = side == SIDE_FRONT ? -1 : 1;
|
|
|
|
|
|
|
|
// 2-----3
|
|
|
|
// | |
|
|
|
|
// | |
|
|
|
|
// 0-----1
|
|
|
|
|
|
|
|
arrays.positions.push_back(v0);
|
|
|
|
arrays.positions.push_back(v1);
|
|
|
|
arrays.positions.push_back(v2);
|
|
|
|
arrays.positions.push_back(v3);
|
|
|
|
|
2020-09-12 18:17:11 -07:00
|
|
|
// 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);
|
2020-09-10 16:36:23 -07:00
|
|
|
|
|
|
|
arrays.normals.push_back(n);
|
|
|
|
arrays.normals.push_back(n);
|
|
|
|
arrays.normals.push_back(n);
|
|
|
|
arrays.normals.push_back(n);
|
|
|
|
|
|
|
|
const unsigned int index_offset = index_offsets[material_index];
|
|
|
|
CRASH_COND(za >= 3 || side >= 2);
|
|
|
|
const uint8_t *lut = g_indices_lut[za][side];
|
|
|
|
for (unsigned int i = 0; i < 6; ++i) {
|
|
|
|
arrays.indices.push_back(index_offset + lut[i]);
|
|
|
|
}
|
|
|
|
index_offsets[material_index] += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-12 18:17:11 -07:00
|
|
|
template <typename Voxel_T, typename Color_F>
|
2020-09-10 16:36:23 -07:00
|
|
|
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,
|
2020-09-12 18:17:11 -07:00
|
|
|
std::vector<uint8_t> mask_memory_pool,
|
|
|
|
Color_F color_func) {
|
2020-09-10 16:36:23 -07:00
|
|
|
|
2020-09-11 12:08:52 -07:00
|
|
|
ERR_FAIL_COND(block_size.x < static_cast<int>(2 * VoxelMesherCubes::PADDING) ||
|
|
|
|
block_size.y < static_cast<int>(2 * VoxelMesherCubes::PADDING) ||
|
|
|
|
block_size.z < static_cast<int>(2 * VoxelMesherCubes::PADDING));
|
2020-09-11 11:59:53 -07:00
|
|
|
|
2020-09-10 16:36:23 -07:00
|
|
|
struct MaskValue {
|
|
|
|
Voxel_T color;
|
|
|
|
uint8_t side;
|
|
|
|
|
|
|
|
inline bool operator==(const MaskValue &other) const {
|
|
|
|
return color == other.color && side == other.side;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool operator!=(const MaskValue &other) const {
|
|
|
|
return color != other.color || side != other.side;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const Vector3i min_pos = Vector3i(VoxelMesherCubes::PADDING);
|
|
|
|
const Vector3i max_pos = block_size - Vector3i(VoxelMesherCubes::PADDING);
|
|
|
|
const unsigned int row_size = block_size.y;
|
|
|
|
const unsigned int deck_size = block_size.x * row_size;
|
|
|
|
|
|
|
|
// Note: voxel buffers are indexed in ZXY order
|
|
|
|
FixedArray<uint32_t, Vector3i::AXIS_COUNT> neighbor_offset_d_lut;
|
|
|
|
neighbor_offset_d_lut[Vector3i::AXIS_X] = block_size.y;
|
|
|
|
neighbor_offset_d_lut[Vector3i::AXIS_Y] = 1;
|
|
|
|
neighbor_offset_d_lut[Vector3i::AXIS_Z] = block_size.x * block_size.y;
|
|
|
|
|
|
|
|
FixedArray<uint32_t, VoxelMesherCubes::MATERIAL_COUNT> index_offsets(0);
|
|
|
|
|
|
|
|
// For each axis
|
|
|
|
for (unsigned int za = 0; za < Vector3i::AXIS_COUNT; ++za) {
|
|
|
|
const unsigned int xa = g_face_axes_lut[za][0];
|
|
|
|
const unsigned int ya = g_face_axes_lut[za][1];
|
|
|
|
|
|
|
|
const unsigned int mask_size_x = (max_pos[xa] - min_pos[xa]);
|
|
|
|
const unsigned int mask_size_y = (max_pos[ya] - min_pos[ya]);
|
|
|
|
const unsigned int mask_area = mask_size_x * mask_size_y;
|
|
|
|
// Using the vector as memory pool
|
|
|
|
mask_memory_pool.resize(mask_area * sizeof(MaskValue));
|
|
|
|
ArraySlice<MaskValue> mask(reinterpret_cast<MaskValue *>(mask_memory_pool.data()), 0, mask_area);
|
|
|
|
|
|
|
|
// For each deck
|
|
|
|
for (unsigned int d = min_pos[za] - VoxelMesherCubes::PADDING; d < (unsigned int)max_pos[za]; ++d) {
|
|
|
|
// For each cell of the deck, gather face info
|
2020-09-11 11:59:53 -07:00
|
|
|
for (unsigned int fy = min_pos[ya]; fy < (unsigned int)max_pos[ya]; ++fy) {
|
|
|
|
for (unsigned int fx = min_pos[xa]; fx < (unsigned int)max_pos[xa]; ++fx) {
|
2020-09-10 16:36:23 -07:00
|
|
|
FixedArray<unsigned int, Vector3i::AXIS_COUNT> pos;
|
|
|
|
pos[xa] = fx;
|
|
|
|
pos[ya] = fy;
|
|
|
|
pos[za] = d;
|
|
|
|
|
|
|
|
const unsigned int voxel_index = pos[Vector3i::AXIS_Y] +
|
|
|
|
pos[Vector3i::AXIS_X] * row_size +
|
|
|
|
pos[Vector3i::AXIS_Z] * deck_size;
|
|
|
|
|
2020-09-12 18:17:11 -07:00
|
|
|
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);
|
2020-09-10 16:36:23 -07:00
|
|
|
|
|
|
|
const uint8_t ai0 = get_alpha_index(color0);
|
|
|
|
const uint8_t ai1 = get_alpha_index(color1);
|
|
|
|
|
|
|
|
MaskValue mv;
|
|
|
|
if (ai0 == ai1) {
|
|
|
|
mv.side = SIDE_NONE;
|
|
|
|
} else if (ai0 > ai1) {
|
2020-09-12 18:17:11 -07:00
|
|
|
mv.color = raw_color0;
|
2020-09-10 16:36:23 -07:00
|
|
|
mv.side = SIDE_BACK;
|
|
|
|
} else {
|
2020-09-12 18:17:11 -07:00
|
|
|
mv.color = raw_color1;
|
2020-09-10 16:36:23 -07:00
|
|
|
mv.side = SIDE_FRONT;
|
|
|
|
}
|
|
|
|
|
|
|
|
mask[(fx - VoxelMesherCubes::PADDING) + (fy - VoxelMesherCubes::PADDING) * mask_size_x] = mv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct L {
|
|
|
|
static inline bool is_range_equal(
|
|
|
|
const ArraySlice<MaskValue> &mask, unsigned int xmin, unsigned int xmax, MaskValue v) {
|
|
|
|
for (unsigned int x = xmin; x < xmax; ++x) {
|
|
|
|
if (mask[x] != v) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Greedy quads
|
|
|
|
for (unsigned int fy = 0; fy < mask_size_y; ++fy) {
|
|
|
|
for (unsigned int fx = 0; fx < mask_size_x; ++fx) {
|
|
|
|
const unsigned int mask_index = fx + fy * mask_size_x;
|
|
|
|
const MaskValue m = mask[mask_index];
|
|
|
|
|
|
|
|
if (m.side == SIDE_NONE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the next faces are the same along X
|
|
|
|
unsigned int rx = fx + 1;
|
|
|
|
while (rx < mask_size_x && mask[rx + fy * mask_size_x] == m) {
|
|
|
|
++rx;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the next rows of faces are the same along Y
|
|
|
|
unsigned int ry = fy + 1;
|
|
|
|
while (ry < mask_size_y &&
|
|
|
|
L::is_range_equal(mask,
|
|
|
|
fx + ry * mask_size_x,
|
|
|
|
rx + ry * mask_size_x, m)) {
|
|
|
|
++ry;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Commit face to the mesh
|
|
|
|
|
2020-09-12 18:17:11 -07:00
|
|
|
const Color colorf = color_func(m.color);
|
|
|
|
const uint8_t material_index = colorf.a < 0.999f;
|
2020-09-10 16:36:23 -07:00
|
|
|
VoxelMesherCubes::Arrays &arrays = out_arrays_per_material[material_index];
|
|
|
|
|
|
|
|
Vector3 v0;
|
|
|
|
v0[xa] = fx;
|
|
|
|
v0[ya] = fy;
|
|
|
|
v0[za] = d;
|
|
|
|
|
|
|
|
Vector3 v1;
|
|
|
|
v1[xa] = rx;
|
|
|
|
v1[ya] = fy;
|
|
|
|
v1[za] = d;
|
|
|
|
|
|
|
|
Vector3 v2;
|
|
|
|
v2[xa] = fx;
|
|
|
|
v2[ya] = ry;
|
|
|
|
v2[za] = d;
|
|
|
|
|
|
|
|
Vector3 v3;
|
|
|
|
v3[xa] = rx;
|
|
|
|
v3[ya] = ry;
|
|
|
|
v3[za] = d;
|
|
|
|
|
|
|
|
Vector3 n;
|
|
|
|
n[za] = m.side == SIDE_FRONT ? -1 : 1;
|
|
|
|
|
|
|
|
// 2-----3
|
|
|
|
// | |
|
|
|
|
// | |
|
|
|
|
// 0-----1
|
|
|
|
|
|
|
|
arrays.positions.push_back(v0);
|
|
|
|
arrays.positions.push_back(v1);
|
|
|
|
arrays.positions.push_back(v2);
|
|
|
|
arrays.positions.push_back(v3);
|
|
|
|
|
2020-09-12 18:17:11 -07:00
|
|
|
arrays.colors.push_back(colorf);
|
|
|
|
arrays.colors.push_back(colorf);
|
|
|
|
arrays.colors.push_back(colorf);
|
|
|
|
arrays.colors.push_back(colorf);
|
2020-09-10 16:36:23 -07:00
|
|
|
|
|
|
|
arrays.normals.push_back(n);
|
|
|
|
arrays.normals.push_back(n);
|
|
|
|
arrays.normals.push_back(n);
|
|
|
|
arrays.normals.push_back(n);
|
|
|
|
|
|
|
|
const unsigned int index_offset = index_offsets[material_index];
|
|
|
|
CRASH_COND(za >= 3 || m.side >= 2);
|
|
|
|
const uint8_t *lut = g_indices_lut[za][m.side];
|
|
|
|
for (unsigned int i = 0; i < 6; ++i) {
|
|
|
|
arrays.indices.push_back(index_offset + lut[i]);
|
|
|
|
}
|
|
|
|
index_offsets[material_index] += 4;
|
|
|
|
|
|
|
|
for (unsigned int j = fy; j < ry; ++j) {
|
|
|
|
for (unsigned int i = fx; i < rx; ++i) {
|
|
|
|
mask[i + j * mask_size_x].side = SIDE_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-18 13:01:50 -08:00
|
|
|
thread_local VoxelMesherCubes::Cache VoxelMesherCubes::_cache;
|
|
|
|
|
2020-09-10 16:36:23 -07:00
|
|
|
VoxelMesherCubes::VoxelMesherCubes() {
|
|
|
|
set_padding(PADDING, PADDING);
|
2020-12-18 13:01:50 -08:00
|
|
|
_parameters_lock = RWLock::create();
|
|
|
|
}
|
|
|
|
|
|
|
|
VoxelMesherCubes::~VoxelMesherCubes() {
|
|
|
|
memdelete(_parameters_lock);
|
2020-09-10 16:36:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelMesherCubes::build(VoxelMesher::Output &output, const VoxelMesher::Input &input) {
|
|
|
|
const int channel = VoxelBuffer::CHANNEL_COLOR;
|
2020-12-18 13:01:50 -08:00
|
|
|
Cache &cache = _cache;
|
2020-09-10 16:36:23 -07:00
|
|
|
|
2020-12-18 13:01:50 -08:00
|
|
|
for (unsigned int i = 0; i < cache.arrays_per_material.size(); ++i) {
|
|
|
|
Arrays &a = cache.arrays_per_material[i];
|
|
|
|
a.clear();
|
2020-09-10 16:36:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
const VoxelBuffer &voxels = input.voxels;
|
|
|
|
#ifdef TOOLS_ENABLED
|
|
|
|
if (input.lod != 0) {
|
2020-12-18 13:01:50 -08:00
|
|
|
WARN_PRINT("VoxelMesherCubes received lod != 0, it is not supported");
|
2020-09-10 16:36:23 -07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Iterate 3D padded data to extract voxel faces.
|
|
|
|
// This is the most intensive job in this class, so all required data should be as fit as possible.
|
|
|
|
|
|
|
|
// The buffer we receive MUST be dense (i.e not compressed, and channels allocated).
|
|
|
|
// That means we can use raw pointers to voxel data inside instead of using the higher-level getters,
|
|
|
|
// and then save a lot of time.
|
|
|
|
|
|
|
|
if (voxels.get_channel_compression(channel) == VoxelBuffer::COMPRESSION_UNIFORM) {
|
|
|
|
// All voxels have the same type.
|
|
|
|
// If it's all air, nothing to do. If it's all cubes, nothing to do either.
|
|
|
|
return;
|
|
|
|
|
|
|
|
} else if (voxels.get_channel_compression(channel) != VoxelBuffer::COMPRESSION_NONE) {
|
|
|
|
// No other form of compression is allowed
|
2020-12-18 13:01:50 -08:00
|
|
|
ERR_PRINT("VoxelMesherCubes received unsupported voxel compression");
|
2020-09-10 16:36:23 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ArraySlice<uint8_t> raw_channel;
|
|
|
|
if (!voxels.get_channel_raw(channel, raw_channel)) {
|
|
|
|
// Case supposedly handled before...
|
|
|
|
ERR_PRINT("Something wrong happened");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Vector3i block_size = voxels.get_size();
|
|
|
|
const VoxelBuffer::Depth channel_depth = voxels.get_channel_depth(channel);
|
|
|
|
|
2020-12-18 13:01:50 -08:00
|
|
|
Parameters params;
|
|
|
|
{
|
|
|
|
RWLockRead rlock(_parameters_lock);
|
|
|
|
params = _parameters;
|
|
|
|
}
|
|
|
|
// Note, we don't lock the palette because its data has fixed-size
|
|
|
|
|
|
|
|
switch (params.color_mode) {
|
2020-09-12 18:17:11 -07:00
|
|
|
case COLOR_RAW:
|
|
|
|
switch (channel_depth) {
|
|
|
|
case VoxelBuffer::DEPTH_8_BIT:
|
2020-12-18 13:01:50 -08:00
|
|
|
if (params.greedy_meshing) {
|
2020-09-12 18:17:11 -07:00
|
|
|
build_voxel_mesh_as_greedy_cubes(
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.arrays_per_material,
|
2020-09-12 18:17:11 -07:00
|
|
|
raw_channel,
|
|
|
|
block_size,
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.mask_memory_pool,
|
2020-09-12 18:17:11 -07:00
|
|
|
Color8::from_u8);
|
|
|
|
} else {
|
|
|
|
build_voxel_mesh_as_simple_cubes(
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.arrays_per_material,
|
2020-09-12 18:17:11 -07:00
|
|
|
raw_channel,
|
|
|
|
block_size,
|
|
|
|
Color8::from_u8);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VoxelBuffer::DEPTH_16_BIT:
|
2020-12-18 13:01:50 -08:00
|
|
|
if (params.greedy_meshing) {
|
2020-09-12 18:17:11 -07:00
|
|
|
build_voxel_mesh_as_greedy_cubes(
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.arrays_per_material,
|
2020-09-12 18:17:11 -07:00
|
|
|
raw_channel.reinterpret_cast_to<uint16_t>(),
|
|
|
|
block_size,
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.mask_memory_pool,
|
2020-09-12 18:17:11 -07:00
|
|
|
Color8::from_u16);
|
|
|
|
} else {
|
|
|
|
build_voxel_mesh_as_simple_cubes(
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.arrays_per_material,
|
2020-09-12 18:17:11 -07:00
|
|
|
raw_channel.reinterpret_cast_to<uint16_t>(),
|
|
|
|
block_size,
|
|
|
|
Color8::from_u16);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ERR_PRINT("Unsupported voxel depth");
|
|
|
|
return;
|
2020-09-10 16:36:23 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2020-09-12 18:17:11 -07:00
|
|
|
case COLOR_MESHER_PALETTE: {
|
2020-12-18 13:01:50 -08:00
|
|
|
ERR_FAIL_COND_MSG(params.palette.is_null(), "Palette mode is used but no palette was specified");
|
2020-09-12 18:17:11 -07:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
2020-12-18 13:01:50 -08:00
|
|
|
const GetColorFromPalette get_color_from_palette{ **params.palette };
|
2020-09-12 18:17:11 -07:00
|
|
|
|
|
|
|
switch (channel_depth) {
|
|
|
|
case VoxelBuffer::DEPTH_8_BIT:
|
2020-12-18 13:01:50 -08:00
|
|
|
if (params.greedy_meshing) {
|
2020-09-12 18:17:11 -07:00
|
|
|
build_voxel_mesh_as_greedy_cubes(
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.arrays_per_material,
|
2020-09-12 18:17:11 -07:00
|
|
|
raw_channel,
|
|
|
|
block_size,
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.mask_memory_pool,
|
2020-09-12 18:17:11 -07:00
|
|
|
get_color_from_palette);
|
|
|
|
} else {
|
|
|
|
build_voxel_mesh_as_simple_cubes(
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.arrays_per_material,
|
2020-09-12 18:17:11 -07:00
|
|
|
raw_channel,
|
|
|
|
block_size,
|
|
|
|
get_color_from_palette);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VoxelBuffer::DEPTH_16_BIT:
|
2020-12-18 13:01:50 -08:00
|
|
|
if (params.greedy_meshing) {
|
2020-09-12 18:17:11 -07:00
|
|
|
build_voxel_mesh_as_greedy_cubes(
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.arrays_per_material,
|
2020-09-12 18:17:11 -07:00
|
|
|
raw_channel.reinterpret_cast_to<uint16_t>(),
|
|
|
|
block_size,
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.mask_memory_pool,
|
2020-09-12 18:17:11 -07:00
|
|
|
get_color_from_palette);
|
|
|
|
} else {
|
|
|
|
build_voxel_mesh_as_simple_cubes(
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.arrays_per_material,
|
2020-09-12 18:17:11 -07:00
|
|
|
raw_channel.reinterpret_cast_to<uint16_t>(),
|
|
|
|
block_size,
|
|
|
|
get_color_from_palette);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ERR_PRINT("Unsupported voxel depth");
|
|
|
|
return;
|
2020-09-10 16:36:23 -07:00
|
|
|
}
|
2020-09-12 18:17:11 -07:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case COLOR_SHADER_PALETTE: {
|
2020-12-18 13:01:50 -08:00
|
|
|
ERR_FAIL_COND_MSG(params.palette.is_null(), "Palette mode is used but no palette was specified");
|
2020-09-12 18:17:11 -07:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
2020-12-18 13:01:50 -08:00
|
|
|
const GetIndexFromPalette get_index_from_palette{ **params.palette };
|
2020-09-12 18:17:11 -07:00
|
|
|
|
|
|
|
switch (channel_depth) {
|
|
|
|
case VoxelBuffer::DEPTH_8_BIT:
|
2020-12-18 13:01:50 -08:00
|
|
|
if (params.greedy_meshing) {
|
2020-09-12 18:17:11 -07:00
|
|
|
build_voxel_mesh_as_greedy_cubes(
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.arrays_per_material,
|
2020-09-12 18:17:11 -07:00
|
|
|
raw_channel,
|
|
|
|
block_size,
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.mask_memory_pool,
|
2020-09-12 18:17:11 -07:00
|
|
|
get_index_from_palette);
|
|
|
|
} else {
|
|
|
|
build_voxel_mesh_as_simple_cubes(
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.arrays_per_material,
|
2020-09-12 18:17:11 -07:00
|
|
|
raw_channel,
|
|
|
|
block_size,
|
|
|
|
get_index_from_palette);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VoxelBuffer::DEPTH_16_BIT:
|
2020-12-18 13:01:50 -08:00
|
|
|
if (params.greedy_meshing) {
|
2020-09-12 18:17:11 -07:00
|
|
|
build_voxel_mesh_as_greedy_cubes(
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.arrays_per_material,
|
2020-09-12 18:17:11 -07:00
|
|
|
raw_channel.reinterpret_cast_to<uint16_t>(),
|
|
|
|
block_size,
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.mask_memory_pool,
|
2020-09-12 18:17:11 -07:00
|
|
|
get_index_from_palette);
|
|
|
|
} else {
|
|
|
|
build_voxel_mesh_as_simple_cubes(
|
2020-12-18 13:01:50 -08:00
|
|
|
cache.arrays_per_material,
|
2020-09-12 18:17:11 -07:00
|
|
|
raw_channel.reinterpret_cast_to<uint16_t>(),
|
|
|
|
block_size,
|
|
|
|
get_index_from_palette);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ERR_PRINT("Unsupported voxel depth");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} break;
|
2020-09-10 16:36:23 -07:00
|
|
|
|
|
|
|
default:
|
2020-09-12 18:17:11 -07:00
|
|
|
CRASH_NOW();
|
|
|
|
break;
|
2020-09-10 16:36:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO We could return a single byte array and use Mesh::add_surface down the line?
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < MATERIAL_COUNT; ++i) {
|
2020-12-18 13:01:50 -08:00
|
|
|
const Arrays &arrays = cache.arrays_per_material[i];
|
2020-09-10 16:36:23 -07:00
|
|
|
|
|
|
|
if (arrays.positions.size() != 0) {
|
|
|
|
Array mesh_arrays;
|
|
|
|
mesh_arrays.resize(Mesh::ARRAY_MAX);
|
|
|
|
|
|
|
|
{
|
|
|
|
PoolVector<Vector3> positions;
|
|
|
|
PoolVector<Vector3> normals;
|
|
|
|
PoolVector<Color> colors;
|
|
|
|
PoolVector<int> indices;
|
|
|
|
|
|
|
|
raw_copy_to(positions, arrays.positions);
|
|
|
|
raw_copy_to(normals, arrays.normals);
|
|
|
|
raw_copy_to(colors, arrays.colors);
|
|
|
|
raw_copy_to(indices, arrays.indices);
|
|
|
|
|
|
|
|
mesh_arrays[Mesh::ARRAY_VERTEX] = positions;
|
|
|
|
mesh_arrays[Mesh::ARRAY_NORMAL] = normals;
|
|
|
|
mesh_arrays[Mesh::ARRAY_COLOR] = colors;
|
|
|
|
mesh_arrays[Mesh::ARRAY_INDEX] = indices;
|
|
|
|
}
|
|
|
|
|
|
|
|
output.surfaces.push_back(mesh_arrays);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Empty
|
|
|
|
output.surfaces.push_back(Array());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
output.primitive_type = Mesh::PRIMITIVE_TRIANGLES;
|
|
|
|
//output.compression_flags = Mesh::ARRAY_COMPRESS_COLOR;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelMesherCubes::set_greedy_meshing_enabled(bool enable) {
|
2020-12-18 13:01:50 -08:00
|
|
|
RWLockWrite wlock(_parameters_lock);
|
|
|
|
_parameters.greedy_meshing = enable;
|
2020-09-10 16:36:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool VoxelMesherCubes::is_greedy_meshing_enabled() const {
|
2020-12-18 13:01:50 -08:00
|
|
|
RWLockRead rlock(_parameters_lock);
|
|
|
|
return _parameters.greedy_meshing;
|
2020-09-10 16:36:23 -07:00
|
|
|
}
|
|
|
|
|
2020-09-12 18:17:11 -07:00
|
|
|
void VoxelMesherCubes::set_palette(Ref<VoxelColorPalette> palette) {
|
2020-12-18 13:01:50 -08:00
|
|
|
RWLockWrite wlock(_parameters_lock);
|
|
|
|
_parameters.palette = palette;
|
2020-09-12 18:17:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Ref<VoxelColorPalette> VoxelMesherCubes::get_palette() const {
|
2020-12-18 13:01:50 -08:00
|
|
|
RWLockRead rlock(_parameters_lock);
|
|
|
|
return _parameters.palette;
|
2020-09-12 18:17:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelMesherCubes::set_color_mode(ColorMode mode) {
|
|
|
|
ERR_FAIL_INDEX(mode, COLOR_MODE_COUNT);
|
2020-12-18 13:01:50 -08:00
|
|
|
RWLockWrite wlock(_parameters_lock);
|
|
|
|
_parameters.color_mode = mode;
|
2020-09-12 18:17:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
VoxelMesherCubes::ColorMode VoxelMesherCubes::get_color_mode() const {
|
2020-12-18 13:01:50 -08:00
|
|
|
RWLockRead rlock(_parameters_lock);
|
|
|
|
return _parameters.color_mode;
|
2020-09-12 18:17:11 -07:00
|
|
|
}
|
|
|
|
|
2020-12-18 13:01:50 -08:00
|
|
|
Ref<Resource> VoxelMesherCubes::duplicate(bool p_subresources) const {
|
|
|
|
Parameters params;
|
|
|
|
{
|
|
|
|
RWLockRead rlock(_parameters_lock);
|
|
|
|
params = _parameters;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p_subresources && params.palette.is_valid()) {
|
|
|
|
params.palette = params.palette->duplicate(true);
|
|
|
|
}
|
2020-09-10 16:36:23 -07:00
|
|
|
VoxelMesherCubes *d = memnew(VoxelMesherCubes);
|
2020-12-18 13:01:50 -08:00
|
|
|
d->_parameters = params;
|
|
|
|
|
2020-09-10 16:36:23 -07:00
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
2020-12-18 13:01:50 -08:00
|
|
|
int VoxelMesherCubes::get_used_channels_mask() const {
|
|
|
|
return (1 << VoxelBuffer::CHANNEL_COLOR);
|
|
|
|
}
|
|
|
|
|
2020-09-10 16:36:23 -07:00
|
|
|
void VoxelMesherCubes::_bind_methods() {
|
|
|
|
ClassDB::bind_method(D_METHOD("set_greedy_meshing_enabled", "enable"),
|
|
|
|
&VoxelMesherCubes::set_greedy_meshing_enabled);
|
|
|
|
ClassDB::bind_method(D_METHOD("is_greedy_meshing_enabled"), &VoxelMesherCubes::is_greedy_meshing_enabled);
|
|
|
|
|
2020-09-12 18:17:11 -07:00
|
|
|
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);
|
|
|
|
|
2020-09-10 16:36:23 -07:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "greedy_meshing_enabled"),
|
|
|
|
"set_greedy_meshing_enabled", "is_greedy_meshing_enabled");
|
2020-09-12 18:17:11 -07:00
|
|
|
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);
|
2020-09-10 16:36:23 -07:00
|
|
|
}
|