diff --git a/SCsub b/SCsub index a74ef11..f816e6c 100644 --- a/SCsub +++ b/SCsub @@ -11,6 +11,7 @@ env.add_source_files(env.modules_sources,"data/voxel_light.cpp") env.add_source_files(env.modules_sources,"meshers/voxel_mesher.cpp") env.add_source_files(env.modules_sources,"meshers/voxel_mesher_transvoxel.cpp") +env.add_source_files(env.modules_sources,"meshers/voxel_mesher_transvoxel_terrarin.cpp") env.add_source_files(env.modules_sources,"meshers/transvoxel_tables.cpp") env.add_source_files(env.modules_sources,"world/voxel_buffer.cpp") diff --git a/meshers/dmc/hermite_value.h b/meshers/dmc/hermite_value.h new file mode 100644 index 0000000..bf125e6 --- /dev/null +++ b/meshers/dmc/hermite_value.h @@ -0,0 +1,83 @@ +#ifndef HERMITE_VALUE_H +#define HERMITE_VALUE_H + +#include "../../util/utility.h" +#include "../../voxel_buffer.h" +#include + +namespace dmc { + +struct HermiteValue { + float sdf; // Signed "distance" to surface + Vector3 gradient; // Derivation of the density + + HermiteValue() : + sdf(1.0) { + } +}; + +inline float get_isolevel_clamped(const VoxelBuffer &voxels, unsigned int x, unsigned int y, unsigned int z) { + + x = x >= voxels.get_size().x ? voxels.get_size().x - 1 : x; + y = y >= voxels.get_size().y ? voxels.get_size().y - 1 : y; + z = z >= voxels.get_size().z ? voxels.get_size().z - 1 : z; + + return voxels.get_voxel_f(x, y, z, VoxelBuffer::CHANNEL_ISOLEVEL); +} + +inline HermiteValue get_hermite_value(const VoxelBuffer &voxels, unsigned int x, unsigned int y, unsigned int z) { + + HermiteValue v; + + v.sdf = voxels.get_voxel_f(x, y, z, VoxelBuffer::CHANNEL_ISOLEVEL); + + Vector3 gradient; + + gradient.x = get_isolevel_clamped(voxels, x + 1, y, z) - get_isolevel_clamped(voxels, x - 1, y, z); + gradient.y = get_isolevel_clamped(voxels, x, y + 1, z) - get_isolevel_clamped(voxels, x, y - 1, z); + gradient.z = get_isolevel_clamped(voxels, x, y, z + 1) - get_isolevel_clamped(voxels, x, y, z - 1); + + v.gradient = gradient; + + return v; +} + +inline HermiteValue get_interpolated_hermite_value(const VoxelBuffer &voxels, Vector3 pos) { + + int x0 = static_cast(pos.x); + int y0 = static_cast(pos.y); + int z0 = static_cast(pos.z); + + int x1 = static_cast(Math::ceil(pos.x)); + int y1 = static_cast(Math::ceil(pos.y)); + int z1 = static_cast(Math::ceil(pos.z)); + + // TODO There are lots of hidden grid accesses here, could be optimized + // + // x x x: accessed once, only because of gradient computation + // x X X x X: accessed for both value and gradient, multiple times for gradient + // x X X x + // x x (and this, in 3D) + + HermiteValue v0 = get_hermite_value(voxels, x0, y0, z0); + HermiteValue v1 = get_hermite_value(voxels, x1, y0, z0); + HermiteValue v2 = get_hermite_value(voxels, x1, y0, z1); + HermiteValue v3 = get_hermite_value(voxels, x0, y0, z1); + + HermiteValue v4 = get_hermite_value(voxels, x0, y1, z0); + HermiteValue v5 = get_hermite_value(voxels, x1, y1, z0); + HermiteValue v6 = get_hermite_value(voxels, x1, y1, z1); + HermiteValue v7 = get_hermite_value(voxels, x0, y1, z1); + + Vector3 rpos = pos - Vector3(x0, y0, z0); + + HermiteValue v; + v.sdf = ::interpolate(v0.sdf, v1.sdf, v2.sdf, v3.sdf, v4.sdf, v5.sdf, v6.sdf, v7.sdf, rpos); + v.gradient = ::interpolate(v0.gradient, v1.gradient, v2.gradient, v3.gradient, v4.gradient, v5.gradient, v6.gradient, v7.gradient, rpos); + + return v; +} + +} // namespace dmc + +#endif // HERMITE_VALUE_H diff --git a/meshers/dmc/marching_cubes_tables.h b/meshers/dmc/marching_cubes_tables.h new file mode 100644 index 0000000..6c97701 --- /dev/null +++ b/meshers/dmc/marching_cubes_tables.h @@ -0,0 +1,350 @@ +#ifndef MARCHING_CUBES_TABLES_H +#define MARCHING_CUBES_TABLES_H + +namespace MarchingCubes { + +// Marching Cubes tables from the public domain code found on http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/ +// in http://paulbourke.net/geometry/polygonise/marchingsource.cpp +// Thanks! + +// Edges table +int mc_edges[256] = { + 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x099, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x033, 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0x0aa, 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x066, 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0x0ff, 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x055, 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0x0cc, 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0x0cc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x055, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0x0ff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x066, 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0x0aa, 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x033, 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x099, 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 +}; + +// Triangulation lookup table +int mc_triangles[256][16] = { + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, + { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 }, + { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 }, + { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, + { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 }, + { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 }, + { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 }, + { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, + { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 }, + { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, + { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, + { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, + { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, + { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 }, + { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, + { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 }, + { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 }, + { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, + { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, + { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, + { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 }, + { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, + { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 }, + { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, + { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, + { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 }, + { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, + { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 }, + { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, + { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 }, + { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, + { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 }, + { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 }, + { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, + { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 }, + { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, + { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 }, + { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 }, + { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, + { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, + { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 }, + { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, + { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 }, + { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, + { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 }, + { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, + { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, + { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 }, + { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, + { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 }, + { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, + { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 }, + { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 }, + { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, + { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 }, + { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, + { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, + { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, + { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 }, + { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 }, + { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, + { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 }, + { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 }, + { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, + { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 }, + { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, + { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 }, + { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, + { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 }, + { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 }, + { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, + { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, + { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 }, + { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, + { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 }, + { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, + { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, + { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, + { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 }, + { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 }, + { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 }, + { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, + { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 }, + { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 }, + { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, + { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 }, + { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 }, + { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, + { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 }, + { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, + { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 }, + { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, + { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, + { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, + { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 }, + { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, + { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 }, + { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, + { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 }, + { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, + { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 }, + { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 }, + { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, + { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 }, + { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, + { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 }, + { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 }, + { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, + { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } +}; + +// Throw in the corners inside of the volume and get a 4 bit array which edges are cutting it. +// Edge indices to check then (Edge between 0 and 4 cut == msEdges[x] & 4). +// +// 0---1---1 +// | | +// 4 2 +// | | +// 3---3---2 +// +int ms_edges[16] = { + 0x0, // 0000 Nothing intersects + 0x9, // 0001 0 + 0x3, // 0010 1 + 0xA, // 0011 0 1 + 0x6, // 0100 2 + 0xF, // 0101 0 2 + 0x5, // 0110 1 2 + 0xC, // 0111 0 1 2 + 0xC, // 1000 3 + 0x5, // 1001 0 3 + 0xF, // 1010 1 3 + 0x6, // 1011 0 1 3 + 0xA, // 1100 2 3 + 0x3, // 1101 0 2 3 + 0x9, // 1110 1 2 3 + 0x0 // 1111 0 1 2 3 +}; + +// To get a triangulation with up to 5 triangles based on the corners inside. Alternative +// ambigous cases added at the end. Indices of the counterclockwise formed triangles: +// +// 0---1---2 +// | | +// 7 3 +// | | +// 6---5---4 +// +int ms_triangles[18][13] = { + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, // Nothing intersects + { 7, 1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, // 0 + { 3, 2, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, // 1 + { 7, 2, 0, 7, 3, 2, -1, -1, -1, -1, -1, -1, -1 }, // 0 1 + { 5, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, // 2 + { 7, 1, 0, 3, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, // 0 2 + { 1, 4, 2, 1, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, // 1 2 + { 0, 7, 5, 0, 5, 4, 0, 4, 2, -1, -1, -1, -1 }, // 0 1 2 + { 7, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, // 3 + { 0, 5, 1, 0, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, // 0 3 + { 1, 3, 2, 7, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, // 1 3 + { 3, 6, 5, 3, 0, 6, 3, 2, 0, -1, -1, -1, -1 }, // 0 1 3 + { 7, 6, 4, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1 }, // 2 3 + { 3, 1, 0, 3, 0, 6, 3, 6, 4, -1, -1, -1, -1 }, // 0 2 3 + { 2, 1, 7, 2, 7, 6, 2, 6, 4, -1, -1, -1, -1 }, // 1 2 3 + { 6, 2, 0, 6, 4, 2, -1, -1, -1, -1, -1, -1, -1 }, // 0 1 2 3 + { 1, 0, 7, 1, 7, 5, 1, 5, 4, 1, 4, 3, -1 }, // 0 2 alternative + { 1, 3, 2, 1, 5, 3, 1, 6, 5, 1, 7, 6, -1 } // 1 3 alternative +}; + +} // namespace MarchingCubes + +#endif // MARCHING_CUBES_TABLES_H diff --git a/meshers/dmc/mesh_builder.cpp b/meshers/dmc/mesh_builder.cpp new file mode 100644 index 0000000..644886b --- /dev/null +++ b/meshers/dmc/mesh_builder.cpp @@ -0,0 +1,60 @@ +#include "mesh_builder.h" + +namespace dmc { + +Array MeshBuilder::commit(bool wireframe) { + + if (_positions.size() == 0) { + return Array(); + } + + ERR_FAIL_COND_V(_indices.size() % 3 != 0, Array()); + + if (wireframe) { + + // Debug purpose, no effort to be fast here + std::vector wireframe_indices; + + for (int i = 0; i < _indices.size(); i += 3) { + + wireframe_indices.push_back(_indices[i]); + wireframe_indices.push_back(_indices[i + 1]); + + wireframe_indices.push_back(_indices[i + 1]); + wireframe_indices.push_back(_indices[i + 2]); + + wireframe_indices.push_back(_indices[i + 2]); + wireframe_indices.push_back(_indices[i]); + } + + _indices = wireframe_indices; + } + + PoolVector3Array positions; + PoolVector3Array normals; + PoolIntArray indices; + + raw_copy_to(positions, _positions); + raw_copy_to(normals, _normals); + raw_copy_to(indices, _indices); + + clear(); + + Array surface; + surface.resize(Mesh::ARRAY_MAX); + surface[Mesh::ARRAY_VERTEX] = positions; + surface[Mesh::ARRAY_NORMAL] = normals; + surface[Mesh::ARRAY_INDEX] = indices; + + return surface; +} + +void MeshBuilder::clear() { + _positions.clear(); + _normals.clear(); + _indices.clear(); + _position_to_index.clear(); + _reused_vertices = 0; +} + +} // namespace dmc diff --git a/meshers/dmc/mesh_builder.h b/meshers/dmc/mesh_builder.h new file mode 100644 index 0000000..4971355 --- /dev/null +++ b/meshers/dmc/mesh_builder.h @@ -0,0 +1,55 @@ +#ifndef MESH_BUILDER_H +#define MESH_BUILDER_H + +#include "../../util/utility.h" +#include +#include +#include + +namespace dmc { + +// Faster than SurfaceTool, only does what is needed to build a smooth mesh +class MeshBuilder { +public: + MeshBuilder() : + _reused_vertices(0) {} + + inline void add_vertex(Vector3 position, Vector3 normal) { + + int i = 0; + + Map::Element *e = _position_to_index.find(position); + + if (e) { + + i = e->get(); + ++_reused_vertices; + + } else { + + i = _positions.size(); + _position_to_index.insert(position, i); + + _positions.push_back(position); + _normals.push_back(normal); + } + + _indices.push_back(i); + } + + Array commit(bool wireframe); + void clear(); + + int get_reused_vertex_count() const { return _reused_vertices; } + +private: + std::vector _positions; + std::vector _normals; + std::vector _indices; + Map _position_to_index; + int _reused_vertices; +}; + +} // namespace dmc + +#endif // MESH_BUILDER_H diff --git a/meshers/dmc/voxel_mesher_dmc.cpp b/meshers/dmc/voxel_mesher_dmc.cpp new file mode 100644 index 0000000..2a2a37e --- /dev/null +++ b/meshers/dmc/voxel_mesher_dmc.cpp @@ -0,0 +1,1640 @@ +#include "voxel_mesher_dmc.h" +#include "../../cube_tables.h" +#include "../../octree_tables.h" +#include "marching_cubes_tables.h" +#include "mesh_builder.h" +#include + +// Dual marching cubes +// Algorithm taken from https://www.volume-gfx.com/volume-rendering/dual-marching-cubes/ +// Partially based on Ogre's implementation, adapted for requirements of this module with a few extras + +namespace dmc { + +// Surface is defined when isolevel crosses 0 +const float SURFACE_ISO_LEVEL = 0.0; + +const float NEAR_SURFACE_FACTOR = 2.0; +const float SQRT3 = 1.7320508075688772; + +// Helper to access padded voxel data +struct VoxelAccess { + + const VoxelBuffer &buffer; + const Vector3i offset; + + VoxelAccess(const VoxelBuffer &p_buffer, Vector3i p_offset) : + buffer(p_buffer), + offset(p_offset) {} + + inline HermiteValue get_hermite_value(int x, int y, int z) const { + return dmc::get_hermite_value(buffer, x + offset.x, y + offset.y, z + offset.z); + } + + inline HermiteValue get_interpolated_hermite_value(Vector3 pos) const { + pos.x += offset.x; + pos.y += offset.y; + pos.z += offset.z; + return dmc::get_interpolated_hermite_value(buffer, pos); + } +}; + +bool can_split(Vector3i node_origin, int node_size, const VoxelAccess &voxels, float geometric_error) { + + if (node_size == 1) { + // Voxel resolution, can't split further + return false; + } + + Vector3i origin = node_origin + voxels.offset; + int step = node_size; + int channel = VoxelBuffer::CHANNEL_ISOLEVEL; + + // Don't split if nothing is inside, i.e isolevel distance is greater than the size of the cube we are in + Vector3i center_pos = node_origin + Vector3i(node_size / 2); + HermiteValue center_value = voxels.get_hermite_value(center_pos.x, center_pos.y, center_pos.z); + if (Math::abs(center_value.sdf) > SQRT3 * (float)node_size) { + return false; + } + + // Fighting with Clang-format here /**/ + + float v0 = voxels.buffer.get_voxel_f(origin.x, /* */ origin.y, /* */ origin.z, /* */ channel); // 0 + float v1 = voxels.buffer.get_voxel_f(origin.x + step, origin.y, /* */ origin.z, /* */ channel); // 1 + float v2 = voxels.buffer.get_voxel_f(origin.x + step, origin.y, /* */ origin.z + step, channel); // 2 + float v3 = voxels.buffer.get_voxel_f(origin.x, /* */ origin.y, /* */ origin.z + step, channel); // 3 + + float v4 = voxels.buffer.get_voxel_f(origin.x, /* */ origin.y + step, origin.z, /* */ channel); // 4 + float v5 = voxels.buffer.get_voxel_f(origin.x + step, origin.y + step, origin.z, /* */ channel); // 5 + float v6 = voxels.buffer.get_voxel_f(origin.x + step, origin.y + step, origin.z + step, channel); // 6 + float v7 = voxels.buffer.get_voxel_f(origin.x, /* */ origin.y + step, origin.z + step, channel); // 7 + + int hstep = step / 2; + + Vector3i positions[19] = { + // Starting from point 8 + Vector3i(origin.x + hstep, /**/ origin.y, /* */ origin.z), // 8 + Vector3i(origin.x + step, /* */ origin.y, /* */ origin.z + hstep), // 9 + Vector3i(origin.x + hstep, /**/ origin.y, /* */ origin.z + step), // 10 + Vector3i(origin.x, /* */ origin.y, /* */ origin.z + hstep), // 11 + + Vector3i(origin.x, /* */ origin.y + hstep, /**/ origin.z), // 12 + Vector3i(origin.x + step, /* */ origin.y + hstep, /**/ origin.z), // 13 + Vector3i(origin.x + step, /* */ origin.y + hstep, /**/ origin.z + step), // 14 + Vector3i(origin.x, /* */ origin.y + hstep, /**/ origin.z + step), // 15 + + Vector3i(origin.x + hstep, /**/ origin.y + step, /* */ origin.z), // 16 + Vector3i(origin.x + step, /* */ origin.y + step, /* */ origin.z + hstep), // 17 + Vector3i(origin.x + hstep, /**/ origin.y + step, /* */ origin.z + step), // 18 + Vector3i(origin.x, /* */ origin.y + step, /* */ origin.z + hstep), // 19 + + Vector3i(origin.x + hstep, /**/ origin.y, /* */ origin.z + hstep), // 20 + Vector3i(origin.x + hstep, /**/ origin.y + hstep, /**/ origin.z), // 21 + Vector3i(origin.x + step, /* */ origin.y + hstep, /**/ origin.z + hstep), // 22 + Vector3i(origin.x + hstep, /**/ origin.y + hstep, /**/ origin.z + step), // 23 + Vector3i(origin.x, /* */ origin.y + hstep, /**/ origin.z + hstep), // 24 + Vector3i(origin.x + hstep, /**/ origin.y + step, /* */ origin.z + hstep), // 25 + Vector3i(origin.x + hstep, /**/ origin.y + hstep, /**/ origin.z + hstep) // 26 + }; + + Vector3 positions_ratio[19] = { + Vector3(0.5, 0.0, 0.0), + Vector3(1.0, 0.0, 0.5), + Vector3(0.5, 0.0, 1.0), + Vector3(0.0, 0.0, 0.5), + + Vector3(0.0, 0.5, 0.0), + Vector3(1.0, 0.5, 0.0), + Vector3(1.0, 0.5, 1.0), + Vector3(0.0, 0.5, 1.0), + + Vector3(0.5, 1.0, 0.0), + Vector3(1.0, 1.0, 0.5), + Vector3(0.5, 1.0, 1.0), + Vector3(0.0, 1.0, 0.5), + + Vector3(0.5, 0.0, 0.5), + Vector3(0.5, 0.5, 0.0), + Vector3(1.0, 0.5, 0.5), + Vector3(0.5, 0.5, 1.0), + Vector3(0.0, 0.5, 0.5), + Vector3(0.5, 1.0, 0.5), + Vector3(0.5, 0.5, 0.5) + }; + + float error = 0.0; + + for (int i = 0; i < 19; ++i) { + + Vector3i pos = positions[i]; + + HermiteValue value = get_hermite_value(voxels.buffer, pos.x, pos.y, pos.z); + + float interpolated_value = ::interpolate(v0, v1, v2, v3, v4, v5, v6, v7, positions_ratio[i]); + + float gradient_magnitude = value.gradient.length(); + if (gradient_magnitude < FLT_EPSILON) { + gradient_magnitude = 1.0; + } + error += Math::abs(value.sdf - interpolated_value) / gradient_magnitude; + if (error >= geometric_error) { + return true; + } + } + + return false; +} + +inline Vector3 get_center(const OctreeNode *node) { + return node->origin.to_vec3() + 0.5 * Vector3(node->size, node->size, node->size); +} + +class OctreeBuilderTopDown { +public: + OctreeBuilderTopDown(const VoxelAccess &voxels, float geometry_error, OctreeNodePool &pool) : + _voxels(voxels), + _geometry_error(geometry_error), + _pool(pool) { + } + + OctreeNode *build(Vector3i origin, int size) { + OctreeNode *root = _pool.create(); + root->origin = origin; + root->size = size; + build(root); + return root; + } + +private: + void build(OctreeNode *node) { + if (can_split(node->origin, node->size, _voxels, _geometry_error)) { + split(node); + for (int i = 0; i < 8; ++i) { + build(node->children[i]); + } + } else { + node->center_value = _voxels.get_interpolated_hermite_value(get_center(node)); + } + } + + void split(OctreeNode *node) { + + CRASH_COND(node->has_children()); + CRASH_COND(node->size == 1); + + for (int i = 0; i < 8; ++i) { + + OctreeNode *child = _pool.create(); + const int *v = OctreeTables::g_octant_position[i]; + child->size = node->size / 2; + child->origin = node->origin + Vector3i(v[0], v[1], v[2]) * child->size; + + node->children[i] = child; + } + } + +private: + const VoxelAccess &_voxels; + const float _geometry_error; + OctreeNodePool &_pool; +}; + +// Builds the octree bottom-up, to ensure that no detail can be missed by a top-down approach. +class OctreeBuilderBottomUp { +public: + OctreeBuilderBottomUp(const VoxelAccess &voxels, float geometry_error, OctreeNodePool &pool) : + _voxels(voxels), + _geometry_error(geometry_error), + _pool(pool) { + } + + OctreeNode *build(Vector3i node_origin, int node_size) const { + + OctreeNode *children[8] = { nullptr }; + bool any_node = false; + + // Go all the way down, except leaves because we can't reason bottom-up on them + if (node_size > 2) { + for (int i = 0; i < 8; ++i) { + const int *dir = OctreeTables::g_octant_position[i]; + int child_size = node_size / 2; + children[i] = build(node_origin + child_size * Vector3i(dir[0], dir[1], dir[2]), child_size); + any_node |= children[i] != nullptr; + } + } + + OctreeNode *node = nullptr; + + if (!any_node) { + // No nodes, test if the 8 octants are worth existing (this could be leaves) + if (can_split(node_origin, node_size, _voxels, _geometry_error)) { + + node = _pool.create(); + node->origin = node_origin; + node->size = node_size; + + // Create all 8 children + for (int i = 0; i < 8; ++i) { + node->children[i] = create_child(node_origin, node_size, i); + } + } + // If no splitting... then we return null. + // If the parent iteration gets all children null this way, + // it will allow detail reduction recursively upwards. + + } else { + // Some child nodes were deemed worthy of existence, + // create their siblings at the same detail level + + node = _pool.create(); + node->origin = node_origin; + node->size = node_size; + + for (int i = 0; i < 8; ++i) { + if (children[i] != nullptr) { + node->children[i] = children[i]; + } else { + node->children[i] = create_child(node_origin, node_size, i); + } + } + } + + return node; + } + +private: + inline OctreeNode *create_child(Vector3i parent_origin, int parent_size, int i) const { + const int *dir = OctreeTables::g_octant_position[i]; + OctreeNode *child = _pool.create(); + child->size = parent_size / 2; + child->origin = parent_origin + child->size * Vector3i(dir[0], dir[1], dir[2]); + child->center_value = _voxels.get_interpolated_hermite_value(get_center(child)); + return child; + } + +private: + const VoxelAccess &_voxels; + const float _geometry_error; + OctreeNodePool &_pool; +}; + +template +void foreach_node(OctreeNode *root, Action_T &a, int depth = 0) { + a(root, depth); + for (int i = 0; i < 8; ++i) { + if (root->children[i]) { + foreach_node(root->children[i], a, depth + 1); + } + } +} + +Array generate_debug_octree_mesh(OctreeNode *root) { + + struct GetMaxDepth { + int max_depth; + void operator()(OctreeNode *_, int depth) { + if (depth > max_depth) { + max_depth = depth; + } + } + }; + + struct Arrays { + PoolVector3Array positions; + PoolColorArray colors; + PoolIntArray indices; + }; + + struct AddCube { + Arrays *arrays; + int max_depth; + + void operator()(OctreeNode *node, int depth) { + + float shrink = depth * 0.005; + Vector3 o = node->origin.to_vec3() + Vector3(shrink, shrink, shrink); + float s = node->size - 2.0 * shrink; + + Color col(1.0, (float)depth / (float)max_depth, 0.0); + + int vi = arrays->positions.size(); + + for (int i = 0; i < Cube::CORNER_COUNT; ++i) { + arrays->positions.push_back(o + s * Cube::g_corner_position[i]); + arrays->colors.push_back(col); + } + + for (int i = 0; i < Cube::EDGE_COUNT; ++i) { + arrays->indices.push_back(vi + Cube::g_edge_corners[i][0]); + arrays->indices.push_back(vi + Cube::g_edge_corners[i][1]); + } + } + }; + + GetMaxDepth get_max_depth; + foreach_node(root, get_max_depth); + + Arrays arrays; + AddCube add_cube; + add_cube.arrays = &arrays; + add_cube.max_depth = get_max_depth.max_depth; + foreach_node(root, add_cube); + + if (arrays.positions.size() == 0) { + return Array(); + } + + Array surface; + surface.resize(Mesh::ARRAY_MAX); + surface[Mesh::ARRAY_VERTEX] = arrays.positions; + surface[Mesh::ARRAY_COLOR] = arrays.colors; + surface[Mesh::ARRAY_INDEX] = arrays.indices; + + return surface; +} + +Array generate_debug_dual_grid_mesh(const DualGrid &grid) { + + PoolVector3Array positions; + PoolIntArray indices; + + for (int i = 0; i < grid.cells.size(); ++i) { + + const DualCell &cell = grid.cells[i]; + + int vi = positions.size(); + + for (int j = 0; j < 8; ++j) { + // Vector3 p = Vector3(g_octant_position[j][0], g_octant_position[j][1], g_octant_position[j][2]); + // Vector3 n = (Vector3(0.5, 0.5, 0.5) - p).normalized(); + positions.push_back(cell.corners[j]); // + n * 0.01); + } + + for (int j = 0; j < Cube::EDGE_COUNT; ++j) { + indices.push_back(vi + Cube::g_edge_corners[j][0]); + indices.push_back(vi + Cube::g_edge_corners[j][1]); + } + } + + if (positions.size() == 0) { + return Array(); + } + + Array surface; + surface.resize(Mesh::ARRAY_MAX); + surface[Mesh::ARRAY_VERTEX] = positions; + surface[Mesh::ARRAY_INDEX] = indices; + + return surface; +} + +inline bool is_border_left(const OctreeNode *node) { + return node->origin.x == 0; +} + +inline bool is_border_right(const OctreeNode *node, int root_size) { + return node->origin.x + node->size == root_size; +} + +inline bool is_border_bottom(const OctreeNode *node) { + return node->origin.y == 0; +} + +inline bool is_border_top(const OctreeNode *node, int root_size) { + return node->origin.y + node->size == root_size; +} + +inline bool is_border_back(const OctreeNode *node) { + return node->origin.z == 0; +} + +inline bool is_border_front(const OctreeNode *node, int root_size) { + return node->origin.z + node->size == root_size; +} + +inline Vector3 get_center_back(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size * 0.5; + p.y += node->size * 0.5; + return p; +} + +inline Vector3 get_center_front(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size * 0.5; + p.y += node->size * 0.5; + p.z += node->size; + return p; +} + +inline Vector3 get_center_left(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.y += node->size * 0.5; + p.z += node->size * 0.5; + return p; +} + +inline Vector3 get_center_right(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size; + p.y += node->size * 0.5; + p.z += node->size * 0.5; + return p; +} + +inline Vector3 get_center_top(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size * 0.5; + p.y += node->size; + p.z += node->size * 0.5; + return p; +} + +inline Vector3 get_center_bottom(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size * 0.5; + p.z += node->size * 0.5; + return p; +} + +inline Vector3 get_center_back_top(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size * 0.5; + p.y += node->size; + return p; +} + +inline Vector3 get_center_back_bottom(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size * 0.5; + return p; +} + +inline Vector3 get_center_front_top(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size * 0.5; + p.y += node->size; + p.z += node->size; + return p; +} + +inline Vector3 get_center_front_bottom(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size * 0.5; + p.z += node->size; + return p; +} + +inline Vector3 get_center_left_top(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.y += node->size; + p.z += node->size * 0.5; + return p; +} + +inline Vector3 get_center_left_bottom(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.z += node->size * 0.5; + return p; +} + +inline Vector3 get_center_right_top(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size; + p.y += node->size; + p.z += node->size * 0.5; + return p; +} + +inline Vector3 get_center_right_bottom(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size; + p.z += node->size * 0.5; + return p; +} + +inline Vector3 get_center_back_left(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.y += node->size * 0.5; + return p; +} + +inline Vector3 get_center_front_left(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.y += node->size * 0.5; + p.z += node->size; + return p; +} + +inline Vector3 get_center_back_right(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size; + p.y += node->size * 0.5; + return p; +} + +inline Vector3 get_center_front_right(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size; + p.y += node->size * 0.5; + p.z += node->size; + return p; +} + +inline Vector3 get_corner1(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size; + return p; +} + +inline Vector3 get_corner2(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size; + p.z += node->size; + return p; +} + +inline Vector3 get_corner3(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.z += node->size; + return p; +} + +inline Vector3 get_corner4(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.y += node->size; + return p; +} + +inline Vector3 get_corner5(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size; + p.y += node->size; + return p; +} + +inline Vector3 get_corner6(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.x += node->size; + p.y += node->size; + p.z += node->size; + return p; +} + +inline Vector3 get_corner7(const OctreeNode *node) { + Vector3 p = node->origin.to_vec3(); + p.y += node->size; + p.z += node->size; + return p; +} + +class DualGridGenerator { +public: + DualGridGenerator(DualGrid &grid, int octree_root_size) : + _grid(grid), + _octree_root_size(octree_root_size) {} + + void node_proc(OctreeNode *node); + +private: + DualGrid &_grid; + int _octree_root_size; + + void create_border_cells( + const OctreeNode *n0, + const OctreeNode *n1, + const OctreeNode *n2, + const OctreeNode *n3, + const OctreeNode *n4, + const OctreeNode *n5, + const OctreeNode *n6, + const OctreeNode *n7); + + void vert_proc(OctreeNode *n0, OctreeNode *n1, OctreeNode *n2, OctreeNode *n3, OctreeNode *n4, OctreeNode *n5, OctreeNode *n6, OctreeNode *n7); + + void edge_proc_x(OctreeNode *n0, OctreeNode *n1, OctreeNode *n2, OctreeNode *n3); + void edge_proc_y(OctreeNode *n0, OctreeNode *n1, OctreeNode *n2, OctreeNode *n3); + void edge_proc_z(OctreeNode *n0, OctreeNode *n1, OctreeNode *n2, OctreeNode *n3); + + void face_proc_xy(OctreeNode *n0, OctreeNode *n1); + void face_proc_zy(OctreeNode *n0, OctreeNode *n1); + void face_proc_xz(OctreeNode *n0, OctreeNode *n1); +}; + +inline void add_cell(DualGrid &grid, + const Vector3 c0, + const Vector3 c1, + const Vector3 c2, + const Vector3 c3, + const Vector3 c4, + const Vector3 c5, + const Vector3 c6, + const Vector3 c7) { + + DualCell cell; + cell.corners[0] = c0; + cell.corners[1] = c1; + cell.corners[2] = c2; + cell.corners[3] = c3; + cell.corners[4] = c4; + cell.corners[5] = c5; + cell.corners[6] = c6; + cell.corners[7] = c7; + cell.has_values = false; + grid.cells.push_back(cell); +} + +void DualGridGenerator::create_border_cells( + const OctreeNode *n0, + const OctreeNode *n1, + const OctreeNode *n2, + const OctreeNode *n3, + const OctreeNode *n4, + const OctreeNode *n5, + const OctreeNode *n6, + const OctreeNode *n7) { + + DualGrid &grid = _grid; + + // Most boring function ever + + if (is_border_back(n0) && is_border_back(n1) && is_border_back(n4) && is_border_back(n5)) { + + add_cell(grid, + get_center_back(n0), get_center_back(n1), get_center(n1), get_center(n0), + get_center_back(n4), get_center_back(n5), get_center(n5), get_center(n4)); + + // Generate back edge border cells + if (is_border_top(n4, _octree_root_size) && is_border_top(n5, _octree_root_size)) { + + add_cell(grid, + get_center_back(n4), get_center_back(n5), get_center(n5), get_center(n4), + get_center_back_top(n4), get_center_back_top(n5), get_center_top(n5), get_center_top(n4)); + + // Generate back top corner cells + if (is_border_left(n4)) { + add_cell(grid, + get_center_back_left(n4), get_center_back(n4), get_center(n4), get_center_left(n4), + get_corner4(n4), get_center_back_top(n4), get_center_top(n4), get_center_left_top(n4)); + } + + if (is_border_right(n4, _octree_root_size)) { + add_cell(grid, + get_center_back(n5), get_center_back_right(n5), get_center_right(n5), get_center(n5), + get_center_back_top(n5), get_corner5(n5), get_center_right_top(n5), get_center_top(n5)); + } + } + + if (is_border_bottom(n0) && is_border_bottom(n1)) { + + add_cell(grid, + get_center_back_bottom(n0), get_center_back_bottom(n1), get_center_bottom(n1), get_center_bottom(n0), + get_center_back(n0), get_center_back(n1), get_center(n1), get_center(n0)); + + // Generate back bottom corner cells + if (is_border_left(n0)) { + add_cell(grid, n0->origin.to_vec3(), get_center_back_bottom(n0), get_center_bottom(n0), get_center_left_bottom(n0), + get_center_back_left(n0), get_center_back(n0), get_center(n0), get_center_left(n0)); + } + + if (is_border_right(n1, _octree_root_size)) { + add_cell(grid, get_center_back_bottom(n1), get_corner1(n1), get_center_right_bottom(n1), get_center_bottom(n1), + get_center_back(n1), get_center_back_right(n1), get_center_right(n1), get_center(n1)); + } + } + } + + if (is_border_front(n2, _octree_root_size) && + is_border_front(n3, _octree_root_size) && + is_border_front(n6, _octree_root_size) && + is_border_front(n7, _octree_root_size)) { + + add_cell(grid, + get_center(n3), get_center(n2), get_center_front(n2), get_center_front(n3), + get_center(n7), get_center(n6), get_center_front(n6), get_center_front(n7)); + + // Generate front edge border cells + if (is_border_top(n6, _octree_root_size) && is_border_top(n7, _octree_root_size)) { + + add_cell(grid, + get_center(n7), get_center(n6), get_center_front(n6), get_center_front(n7), + get_center_top(n7), get_center_top(n6), get_center_front_top(n6), get_center_front_top(n7)); + + // Generate back bottom corner cells + if (is_border_left(n7)) { + add_cell(grid, + get_center_left(n7), get_center(n7), get_center_front(n7), get_center_front_left(n7), + get_center_left_top(n7), get_center_top(n7), get_center_front_top(n7), get_corner7(n7)); + } + + if (is_border_right(n6, _octree_root_size)) { + add_cell(grid, + get_center(n6), get_center_right(n6), get_center_front_right(n6), get_center_front(n6), + get_center_top(n6), get_center_right_top(n6), get_corner6(n6), get_center_front_top(n6)); + } + } + + if (is_border_bottom(n3) && is_border_bottom(n2)) { + + add_cell(grid, + get_center_bottom(n3), get_center_bottom(n2), get_center_front_bottom(n2), get_center_front_bottom(n3), + get_center(n3), get_center(n2), get_center_front(n2), get_center_front(n3)); + + // Generate back bottom corner cells + if (is_border_left(n3)) { + add_cell(grid, + get_center_left_bottom(n3), get_center_bottom(n3), get_center_front_bottom(n3), get_corner3(n3), + get_center_left(n3), get_center(n3), get_center_front(n3), get_center_front_left(n3)); + } + if (is_border_right(n2, _octree_root_size)) { + add_cell(grid, get_center_bottom(n2), get_center_right_bottom(n2), get_corner2(n2), get_center_front_bottom(n2), + get_center(n2), get_center_right(n2), get_center_front_right(n2), get_center_front(n2)); + } + } + } + + if (is_border_left(n0) && is_border_left(n3) && is_border_left(n4) && is_border_left(n7)) { + + add_cell(grid, + get_center_left(n0), get_center(n0), get_center(n3), get_center_left(n3), + get_center_left(n4), get_center(n4), get_center(n7), get_center_left(n7)); + + // Generate left edge border cells + if (is_border_top(n4, _octree_root_size) && is_border_top(n7, _octree_root_size)) { + add_cell(grid, + get_center_left(n4), get_center(n4), get_center(n7), get_center_left(n7), + get_center_left_top(n4), get_center_top(n4), get_center_top(n7), get_center_left_top(n7)); + } + + if (is_border_bottom(n0) && is_border_bottom(n3)) { + add_cell(grid, + get_center_left_bottom(n0), get_center_bottom(n0), get_center_bottom(n3), get_center_left_bottom(n3), + get_center_left(n0), get_center(n0), get_center(n3), get_center_left(n3)); + } + + if (is_border_back(n0) && is_border_back(n4)) { + add_cell(grid, + get_center_back_left(n0), get_center_back(n0), get_center(n0), get_center_left(n0), + get_center_back_left(n4), get_center_back(n4), get_center(n4), get_center_left(n4)); + } + + if (is_border_front(n3, _octree_root_size) && is_border_front(n7, _octree_root_size)) { + add_cell(grid, + get_center_left(n3), get_center(n3), get_center_front(n3), get_center_front_left(n3), + get_center_left(n7), get_center(n7), get_center_front(n7), get_center_front_left(n7)); + } + } + + if (is_border_right(n1, _octree_root_size) && + is_border_right(n2, _octree_root_size) && + is_border_right(n5, _octree_root_size) && + is_border_right(n6, _octree_root_size)) { + + add_cell(grid, + get_center(n1), get_center_right(n1), get_center_right(n2), get_center(n2), + get_center(n5), get_center_right(n5), get_center_right(n6), get_center(n6)); + + // Generate right edge border cells + if (is_border_top(n5, _octree_root_size) && is_border_top(n6, _octree_root_size)) { + add_cell(grid, + get_center(n5), get_center_right(n5), get_center_right(n6), get_center(n6), + get_center_top(n5), get_center_right_top(n5), get_center_right_top(n6), get_center_top(n6)); + } + + if (is_border_bottom(n1) && is_border_bottom(n2)) { + add_cell(grid, + get_center_bottom(n1), get_center_right_bottom(n1), get_center_right_bottom(n2), get_center_bottom(n2), + get_center(n1), get_center_right(n1), get_center_right(n2), get_center(n2)); + } + + if (is_border_back(n1) && is_border_back(n5)) { + add_cell(grid, + get_center_back(n1), get_center_back_right(n1), get_center_right(n1), get_center(n1), + get_center_back(n5), get_center_back_right(n5), get_center_right(n5), get_center(n5)); + } + + if (is_border_front(n2, _octree_root_size) && is_border_front(n6, _octree_root_size)) { + add_cell(grid, + get_center(n2), get_center_right(n2), get_center_front_right(n2), get_center_front(n2), + get_center(n6), get_center_right(n6), get_center_front_right(n6), get_center_front(n6)); + } + } + + if (is_border_top(n4, _octree_root_size) && + is_border_top(n5, _octree_root_size) && + is_border_top(n6, _octree_root_size) && + is_border_top(n7, _octree_root_size)) { + + add_cell(grid, + get_center(n4), get_center(n5), get_center(n6), get_center(n7), + get_center_top(n4), get_center_top(n5), get_center_top(n6), get_center_top(n7)); + } + + if (is_border_bottom(n0) && is_border_bottom(n1) && is_border_bottom(n2) && is_border_bottom(n3)) { + add_cell(grid, + get_center_bottom(n0), get_center_bottom(n1), get_center_bottom(n2), get_center_bottom(n3), + get_center(n0), get_center(n1), get_center(n2), get_center(n3)); + } +} + +inline bool is_surface_near(OctreeNode *node) { + if (node->center_value.sdf == 0) { + return true; + } + return Math::abs(node->center_value.sdf) < node->size * SQRT3 * NEAR_SURFACE_FACTOR; +} + +void DualGridGenerator::vert_proc( + OctreeNode *n0, + OctreeNode *n1, + OctreeNode *n2, + OctreeNode *n3, + OctreeNode *n4, + OctreeNode *n5, + OctreeNode *n6, + OctreeNode *n7) { + + const bool n0_has_children = n0->has_children(); + const bool n1_has_children = n1->has_children(); + const bool n2_has_children = n2->has_children(); + const bool n3_has_children = n3->has_children(); + const bool n4_has_children = n4->has_children(); + const bool n5_has_children = n5->has_children(); + const bool n6_has_children = n6->has_children(); + const bool n7_has_children = n7->has_children(); + + if ( + n0_has_children || n1_has_children || n2_has_children || n3_has_children || + n4_has_children || n5_has_children || n6_has_children || n7_has_children) { + + OctreeNode *c0 = n0_has_children ? n0->children[6] : n0; + OctreeNode *c1 = n1_has_children ? n1->children[7] : n1; + OctreeNode *c2 = n2_has_children ? n2->children[4] : n2; + OctreeNode *c3 = n3_has_children ? n3->children[5] : n3; + OctreeNode *c4 = n4_has_children ? n4->children[2] : n4; + OctreeNode *c5 = n5_has_children ? n5->children[3] : n5; + OctreeNode *c6 = n6_has_children ? n6->children[0] : n6; + OctreeNode *c7 = n7_has_children ? n7->children[1] : n7; + + vert_proc(c0, c1, c2, c3, c4, c5, c6, c7); + + } else { + + if (!( + is_surface_near(n0) || + is_surface_near(n1) || + is_surface_near(n2) || + is_surface_near(n3) || + is_surface_near(n4) || + is_surface_near(n5) || + is_surface_near(n6) || + is_surface_near(n7))) { + return; + } + + DualCell cell; + cell.set_corner(0, get_center(n0), n0->center_value); + cell.set_corner(1, get_center(n1), n1->center_value); + cell.set_corner(2, get_center(n2), n2->center_value); + cell.set_corner(3, get_center(n3), n3->center_value); + cell.set_corner(4, get_center(n4), n4->center_value); + cell.set_corner(5, get_center(n5), n5->center_value); + cell.set_corner(6, get_center(n6), n6->center_value); + cell.set_corner(7, get_center(n7), n7->center_value); + cell.has_values = true; + _grid.cells.push_back(cell); + + create_border_cells(n0, n1, n2, n3, n4, n5, n6, n7); + } +} + +void DualGridGenerator::edge_proc_x(OctreeNode *n0, OctreeNode *n1, OctreeNode *n2, OctreeNode *n3) { + + const bool n0_has_children = n0->has_children(); + const bool n1_has_children = n1->has_children(); + const bool n2_has_children = n2->has_children(); + const bool n3_has_children = n3->has_children(); + + if (!(n0_has_children || n1_has_children || n2_has_children || n3_has_children)) { + return; + } + + OctreeNode *c0 = n0_has_children ? n0->children[7] : n0; + OctreeNode *c1 = n0_has_children ? n0->children[6] : n0; + OctreeNode *c2 = n1_has_children ? n1->children[5] : n1; + OctreeNode *c3 = n1_has_children ? n1->children[4] : n1; + OctreeNode *c4 = n3_has_children ? n3->children[3] : n3; + OctreeNode *c5 = n3_has_children ? n3->children[2] : n3; + OctreeNode *c6 = n2_has_children ? n2->children[1] : n2; + OctreeNode *c7 = n2_has_children ? n2->children[0] : n2; + + edge_proc_x(c0, c3, c7, c4); + edge_proc_x(c1, c2, c6, c5); + + vert_proc(c0, c1, c2, c3, c4, c5, c6, c7); +} + +void DualGridGenerator::edge_proc_y(OctreeNode *n0, OctreeNode *n1, OctreeNode *n2, OctreeNode *n3) { + + const bool n0_has_children = n0->has_children(); + const bool n1_has_children = n1->has_children(); + const bool n2_has_children = n2->has_children(); + const bool n3_has_children = n3->has_children(); + + if (!(n0_has_children || n1_has_children || n2_has_children || n3_has_children)) { + return; + } + + OctreeNode *c0 = n0_has_children ? n0->children[2] : n0; + OctreeNode *c1 = n1_has_children ? n1->children[3] : n1; + OctreeNode *c2 = n2_has_children ? n2->children[0] : n2; + OctreeNode *c3 = n3_has_children ? n3->children[1] : n3; + OctreeNode *c4 = n0_has_children ? n0->children[6] : n0; + OctreeNode *c5 = n1_has_children ? n1->children[7] : n1; + OctreeNode *c6 = n2_has_children ? n2->children[4] : n2; + OctreeNode *c7 = n3_has_children ? n3->children[5] : n3; + + edge_proc_y(c0, c1, c2, c3); + edge_proc_y(c4, c5, c6, c7); + + vert_proc(c0, c1, c2, c3, c4, c5, c6, c7); +} + +void DualGridGenerator::edge_proc_z(OctreeNode *n0, OctreeNode *n1, OctreeNode *n2, OctreeNode *n3) { + + const bool n0_has_children = n0->has_children(); + const bool n1_has_children = n1->has_children(); + const bool n2_has_children = n2->has_children(); + const bool n3_has_children = n3->has_children(); + + if (!(n0_has_children || n1_has_children || n2_has_children || n3_has_children)) { + return; + } + + OctreeNode *c0 = n3_has_children ? n3->children[5] : n3; + OctreeNode *c1 = n2_has_children ? n2->children[4] : n2; + OctreeNode *c2 = n2_has_children ? n2->children[7] : n2; + OctreeNode *c3 = n3_has_children ? n3->children[6] : n3; + OctreeNode *c4 = n0_has_children ? n0->children[1] : n0; + OctreeNode *c5 = n1_has_children ? n1->children[0] : n1; + OctreeNode *c6 = n1_has_children ? n1->children[3] : n1; + OctreeNode *c7 = n0_has_children ? n0->children[2] : n0; + + edge_proc_z(c7, c6, c2, c3); + edge_proc_z(c4, c5, c1, c0); + + vert_proc(c0, c1, c2, c3, c4, c5, c6, c7); +} + +void DualGridGenerator::face_proc_xy(OctreeNode *n0, OctreeNode *n1) { + + const bool n0_has_children = n0->has_children(); + const bool n1_has_children = n1->has_children(); + + if (!(n0_has_children || n1_has_children)) { + return; + } + + OctreeNode *c0 = n0_has_children ? n0->children[3] : n0; + OctreeNode *c1 = n0_has_children ? n0->children[2] : n0; + OctreeNode *c2 = n1_has_children ? n1->children[1] : n1; + OctreeNode *c3 = n1_has_children ? n1->children[0] : n1; + OctreeNode *c4 = n0_has_children ? n0->children[7] : n0; + OctreeNode *c5 = n0_has_children ? n0->children[6] : n0; + OctreeNode *c6 = n1_has_children ? n1->children[5] : n1; + OctreeNode *c7 = n1_has_children ? n1->children[4] : n1; + + face_proc_xy(c0, c3); + face_proc_xy(c1, c2); + face_proc_xy(c4, c7); + face_proc_xy(c5, c6); + + edge_proc_x(c0, c3, c7, c4); + edge_proc_x(c1, c2, c6, c5); + + edge_proc_y(c0, c1, c2, c3); + edge_proc_y(c4, c5, c6, c7); + + vert_proc(c0, c1, c2, c3, c4, c5, c6, c7); +} + +void DualGridGenerator::face_proc_zy(OctreeNode *n0, OctreeNode *n1) { + + const bool n0_has_children = n0->has_children(); + const bool n1_has_children = n1->has_children(); + + if (!(n0_has_children || n1_has_children)) { + return; + } + + OctreeNode *c0 = n0_has_children ? n0->children[1] : n0; + OctreeNode *c1 = n1_has_children ? n1->children[0] : n1; + OctreeNode *c2 = n1_has_children ? n1->children[3] : n1; + OctreeNode *c3 = n0_has_children ? n0->children[2] : n0; + OctreeNode *c4 = n0_has_children ? n0->children[5] : n0; + OctreeNode *c5 = n1_has_children ? n1->children[4] : n1; + OctreeNode *c6 = n1_has_children ? n1->children[7] : n1; + OctreeNode *c7 = n0_has_children ? n0->children[6] : n0; + + face_proc_zy(c0, c1); + face_proc_zy(c3, c2); + face_proc_zy(c4, c5); + face_proc_zy(c7, c6); + + edge_proc_y(c0, c1, c2, c3); + edge_proc_y(c4, c5, c6, c7); + edge_proc_z(c7, c6, c2, c3); + edge_proc_z(c4, c5, c1, c0); + + vert_proc(c0, c1, c2, c3, c4, c5, c6, c7); +} + +void DualGridGenerator::face_proc_xz(OctreeNode *n0, OctreeNode *n1) { + + const bool n0_has_children = n0->has_children(); + const bool n1_has_children = n1->has_children(); + + if (!(n0_has_children || n1_has_children)) { + return; + } + + OctreeNode *c0 = n1_has_children ? n1->children[4] : n1; + OctreeNode *c1 = n1_has_children ? n1->children[5] : n1; + OctreeNode *c2 = n1_has_children ? n1->children[6] : n1; + OctreeNode *c3 = n1_has_children ? n1->children[7] : n1; + OctreeNode *c4 = n0_has_children ? n0->children[0] : n0; + OctreeNode *c5 = n0_has_children ? n0->children[1] : n0; + OctreeNode *c6 = n0_has_children ? n0->children[2] : n0; + OctreeNode *c7 = n0_has_children ? n0->children[3] : n0; + + face_proc_xz(c4, c0); + face_proc_xz(c5, c1); + face_proc_xz(c7, c3); + face_proc_xz(c6, c2); + + edge_proc_x(c0, c3, c7, c4); + edge_proc_x(c1, c2, c6, c5); + edge_proc_z(c7, c6, c2, c3); + edge_proc_z(c4, c5, c1, c0); + + vert_proc(c0, c1, c2, c3, c4, c5, c6, c7); +} + +void DualGridGenerator::node_proc(OctreeNode *node) { + + if (!node->has_children()) { + return; + } + + OctreeNode **children = node->children; + + for (int i = 0; i < 8; ++i) { + node_proc(children[i]); + } + + face_proc_xy(children[0], children[3]); + face_proc_xy(children[1], children[2]); + face_proc_xy(children[4], children[7]); + face_proc_xy(children[5], children[6]); + + face_proc_zy(children[0], children[1]); + face_proc_zy(children[3], children[2]); + face_proc_zy(children[4], children[5]); + face_proc_zy(children[7], children[6]); + + face_proc_xz(children[4], children[0]); + face_proc_xz(children[5], children[1]); + face_proc_xz(children[7], children[3]); + face_proc_xz(children[6], children[2]); + + edge_proc_x(children[0], children[3], children[7], children[4]); + edge_proc_x(children[1], children[2], children[6], children[5]); + + edge_proc_y(children[0], children[1], children[2], children[3]); + edge_proc_y(children[4], children[5], children[6], children[7]); + + edge_proc_z(children[7], children[6], children[2], children[3]); + edge_proc_z(children[4], children[5], children[1], children[0]); + + vert_proc(children[0], children[1], children[2], children[3], children[4], children[5], children[6], children[7]); +} + +inline Vector3 interpolate(const Vector3 &v0, const Vector3 &v1, const HermiteValue &val0, const HermiteValue &val1, Vector3 &out_normal) { + + if (Math::abs(val0.sdf - SURFACE_ISO_LEVEL) <= FLT_EPSILON) { + out_normal = val0.gradient; + return v0; + } + + if (Math::abs(val1.sdf - SURFACE_ISO_LEVEL) <= FLT_EPSILON) { + out_normal = val1.gradient; + return v1; + } + + if (Math::abs(val1.sdf - val0.sdf) <= FLT_EPSILON) { + out_normal = val0.gradient; + return v0; + } + + float mu = (SURFACE_ISO_LEVEL - val0.sdf) / (val1.sdf - val0.sdf); + out_normal = val0.gradient + mu * (val1.gradient - val0.gradient); + out_normal.normalize(); + + return v0 + mu * (v1 - v0); +} + +void polygonize_cell_marching_squares(const Vector3 *cube_corners, const HermiteValue *cube_values, float max_distance, MeshBuilder &mesh_builder, const int *corner_map) { + + // Note: + // Using Ogre's implementation directly resulted in inverted result, because it expects density values instead of SDF, + // So I had to flip a few things around in order to make it work + + unsigned char square_index = 0; + HermiteValue values[4]; + + // Find out the case. + for (size_t i = 0; i < 4; ++i) { + values[i] = cube_values[corner_map[i]]; + if (values[i].sdf <= SURFACE_ISO_LEVEL) { + square_index |= 1 << i; + } + } + + // Don't generate triangles if we are completely inside and far enough away from the surface + max_distance = -max_distance; + if (square_index == 15 && + values[0].sdf <= max_distance && + values[1].sdf <= max_distance && + values[2].sdf <= max_distance && + values[3].sdf <= max_distance) { + return; + } + + int edge = MarchingCubes::ms_edges[square_index]; + + // Find the intersection vertices. + Vector3 intersection_points[8]; + Vector3 intersection_normals[8]; + + intersection_points[0] = cube_corners[corner_map[0]]; + intersection_points[2] = cube_corners[corner_map[1]]; + intersection_points[4] = cube_corners[corner_map[2]]; + intersection_points[6] = cube_corners[corner_map[3]]; + + HermiteValue inner_val; + + inner_val = values[0]; // mSrc->getValueAndGradient(intersection_points[0]); + intersection_normals[0] = inner_val.gradient.normalized(); // * (inner_val.value + 1.0); + + inner_val = values[1]; // mSrc->getValueAndGradient(intersection_points[2]); + intersection_normals[2] = inner_val.gradient.normalized(); // * (inner_val.value + 1.0); + + inner_val = values[2]; // mSrc->getValueAndGradient(intersection_points[4]); + intersection_normals[4] = inner_val.gradient.normalized(); // * (inner_val.value + 1.0); + + inner_val = values[3]; // mSrc->getValueAndGradient(intersection_points[6]); + intersection_normals[6] = inner_val.gradient.normalized(); // * (inner_val.value + 1.0); + + if (edge & 1) { + intersection_points[1] = interpolate(cube_corners[corner_map[0]], cube_corners[corner_map[1]], values[0], values[1], intersection_normals[1]); + } + if (edge & 2) { + intersection_points[3] = interpolate(cube_corners[corner_map[1]], cube_corners[corner_map[2]], values[1], values[2], intersection_normals[3]); + } + if (edge & 4) { + intersection_points[5] = interpolate(cube_corners[corner_map[2]], cube_corners[corner_map[3]], values[2], values[3], intersection_normals[5]); + } + if (edge & 8) { + intersection_points[7] = interpolate(cube_corners[corner_map[3]], cube_corners[corner_map[0]], values[3], values[0], intersection_normals[7]); + } + + // Ambigous case handling, 5 = 0 2 and 10 = 1 3 + /*if (squareIndex == 5 || squareIndex == 10) + { + Vector3 avg = (corners[corner_map[0]] + corners[corner_map[1]] + corners[corner_map[2]] + corners[corner_map[3]]) / (Real)4.0; + // Lets take the alternative. + if (mSrc->getValue(avg) >= ISO_LEVEL) + { + squareIndex = squareIndex == 5 ? 16 : 17; + } + }*/ + + // Create the triangles according to the table. + for (int i = 0; MarchingCubes::mc_triangles[square_index][i] != -1; i += 3) { + + mesh_builder.add_vertex( + intersection_points[MarchingCubes::ms_triangles[square_index][i]], + intersection_normals[MarchingCubes::ms_triangles[square_index][i]]); + + mesh_builder.add_vertex( + intersection_points[MarchingCubes::ms_triangles[square_index][i + 2]], + intersection_normals[MarchingCubes::ms_triangles[square_index][i + 2]]); + + mesh_builder.add_vertex( + intersection_points[MarchingCubes::ms_triangles[square_index][i + 1]], + intersection_normals[MarchingCubes::ms_triangles[square_index][i + 1]]); + } +} + +namespace MarchingSquares { + +static const int g_corner_map_front[4] = { 7, 6, 2, 3 }; +static const int g_corner_map_back[4] = { 5, 4, 0, 1 }; +static const int g_corner_map_left[4] = { 4, 7, 3, 0 }; +static const int g_corner_map_right[4] = { 6, 5, 1, 2 }; +static const int g_corner_map_top[4] = { 4, 5, 6, 7 }; +static const int g_corner_map_bottom[4] = { 3, 2, 1, 0 }; + +} // namespace MarchingSquares + +void add_marching_squares_skirts(const Vector3 *corners, const HermiteValue *values, MeshBuilder &mesh_builder, Vector3 min_pos, Vector3 max_pos) { + + float max_distance = 0.2f; // Max distance to the isosurface + + if (corners[0].z == min_pos.z) { + polygonize_cell_marching_squares(corners, values, max_distance, mesh_builder, MarchingSquares::g_corner_map_back); + } + if (corners[2].z == max_pos.z) { + polygonize_cell_marching_squares(corners, values, max_distance, mesh_builder, MarchingSquares::g_corner_map_front); + } + if (corners[0].x == min_pos.x) { + polygonize_cell_marching_squares(corners, values, max_distance, mesh_builder, MarchingSquares::g_corner_map_left); + } + if (corners[1].x == max_pos.x) { + polygonize_cell_marching_squares(corners, values, max_distance, mesh_builder, MarchingSquares::g_corner_map_right); + } + if (corners[5].y == max_pos.y) { + polygonize_cell_marching_squares(corners, values, max_distance, mesh_builder, MarchingSquares::g_corner_map_top); + } + if (corners[0].y == min_pos.y) { + polygonize_cell_marching_squares(corners, values, max_distance, mesh_builder, MarchingSquares::g_corner_map_bottom); + } +} + +void polygonize_cell_marching_cubes(const Vector3 *corners, const HermiteValue *values, MeshBuilder &mesh_builder) { + + unsigned char case_index = 0; + + for (int i = 0; i < 8; ++i) { + if (values[i].sdf >= SURFACE_ISO_LEVEL) { + case_index |= 1 << i; + } + } + + int edge = MarchingCubes::mc_edges[case_index]; + + if (!edge) { + // Nothing intersects + return; + } + + // Find the intersection vertices + Vector3 intersection_points[12]; + Vector3 intersection_normals[12]; + if (edge & 1) { + intersection_points[0] = interpolate(corners[0], corners[1], values[0], values[1], intersection_normals[0]); + } + if (edge & 2) { + intersection_points[1] = interpolate(corners[1], corners[2], values[1], values[2], intersection_normals[1]); + } + if (edge & 4) { + intersection_points[2] = interpolate(corners[2], corners[3], values[2], values[3], intersection_normals[2]); + } + if (edge & 8) { + intersection_points[3] = interpolate(corners[3], corners[0], values[3], values[0], intersection_normals[3]); + } + if (edge & 16) { + intersection_points[4] = interpolate(corners[4], corners[5], values[4], values[5], intersection_normals[4]); + } + if (edge & 32) { + intersection_points[5] = interpolate(corners[5], corners[6], values[5], values[6], intersection_normals[5]); + } + if (edge & 64) { + intersection_points[6] = interpolate(corners[6], corners[7], values[6], values[7], intersection_normals[6]); + } + if (edge & 128) { + intersection_points[7] = interpolate(corners[7], corners[4], values[7], values[4], intersection_normals[7]); + } + if (edge & 256) { + intersection_points[8] = interpolate(corners[0], corners[4], values[0], values[4], intersection_normals[8]); + } + if (edge & 512) { + intersection_points[9] = interpolate(corners[1], corners[5], values[1], values[5], intersection_normals[9]); + } + if (edge & 1024) { + intersection_points[10] = interpolate(corners[2], corners[6], values[2], values[6], intersection_normals[10]); + } + if (edge & 2048) { + intersection_points[11] = interpolate(corners[3], corners[7], values[3], values[7], intersection_normals[11]); + } + + // Create the triangles according to the table. + for (int i = 0; MarchingCubes::mc_triangles[case_index][i] != -1; i += 3) { + + mesh_builder.add_vertex( + intersection_points[MarchingCubes::mc_triangles[case_index][i]], + intersection_normals[MarchingCubes::mc_triangles[case_index][i]]); + + mesh_builder.add_vertex( + intersection_points[MarchingCubes::mc_triangles[case_index][i + 1]], + intersection_normals[MarchingCubes::mc_triangles[case_index][i + 1]]); + + mesh_builder.add_vertex( + intersection_points[MarchingCubes::mc_triangles[case_index][i + 2]], + intersection_normals[MarchingCubes::mc_triangles[case_index][i + 2]]); + } + + return; +} + +void polygonize_dual_cell(const DualCell &cell, const VoxelAccess &voxels, MeshBuilder &mesh_builder, bool skirts_enabled) { + + const Vector3 *corners = cell.corners; + HermiteValue values[8]; + + if (cell.has_values) { + memcpy(values, cell.values, 8 * sizeof(HermiteValue)); + } else { + for (int i = 0; i < 8; ++i) { + values[i] = voxels.get_interpolated_hermite_value(corners[i]); + } + } + + polygonize_cell_marching_cubes(corners, values, mesh_builder); + + if (skirts_enabled) { + add_marching_squares_skirts(corners, values, mesh_builder, Vector3(), (voxels.buffer.get_size() + voxels.offset).to_vec3()); + } +} + +inline void polygonize_dual_grid(const DualGrid &grid, const VoxelAccess &voxels, MeshBuilder &mesh_builder, bool skirts_enabled) { + + for (int i = 0; i < grid.cells.size(); ++i) { + polygonize_dual_cell(grid.cells[i], voxels, mesh_builder, skirts_enabled); + } +} + +void polygonize_volume_directly(const VoxelBuffer &voxels, Vector3i min, Vector3i size, MeshBuilder &mesh_builder, bool skirts_enabled) { + + Vector3 corners[8]; + HermiteValue values[8]; + + const Vector3i max = min + size; + const Vector3 minf = min.to_vec3(); + + const Vector3 min_vertex_pos = Vector3(); + const Vector3 max_vertex_pos = (voxels.get_size() - 2 * min).to_vec3(); + + for (int z = min.z; z < max.z; ++z) { + for (int x = min.x; x < max.x; ++x) { + for (int y = min.y; y < max.y; ++y) { + + values[0] = get_hermite_value(voxels, x, y, z); + values[1] = get_hermite_value(voxels, x + 1, y, z); + values[2] = get_hermite_value(voxels, x + 1, y, z + 1); + values[3] = get_hermite_value(voxels, x, y, z + 1); + values[4] = get_hermite_value(voxels, x, y + 1, z); + values[5] = get_hermite_value(voxels, x + 1, y + 1, z); + values[6] = get_hermite_value(voxels, x + 1, y + 1, z + 1); + values[7] = get_hermite_value(voxels, x, y + 1, z + 1); + + corners[0] = Vector3(x, y, z); + corners[1] = Vector3(x + 1, y, z); + corners[2] = Vector3(x + 1, y, z + 1); + corners[3] = Vector3(x, y, z + 1); + corners[4] = Vector3(x, y + 1, z); + corners[5] = Vector3(x + 1, y + 1, z); + corners[6] = Vector3(x + 1, y + 1, z + 1); + corners[7] = Vector3(x, y + 1, z + 1); + + for (int i = 0; i < 8; ++i) { + corners[i] -= minf; + } + + polygonize_cell_marching_cubes(corners, values, mesh_builder); + + if (skirts_enabled) { + add_marching_squares_skirts(corners, values, mesh_builder, min_vertex_pos, max_vertex_pos); + } + } + } + } +} + +} // namespace dmc + +#define BUILD_OCTREE_BOTTOM_UP + +VoxelMesherDMC::VoxelMesherDMC() { +} + +void VoxelMesherDMC::set_mesh_mode(MeshMode mode) { + _mesh_mode = mode; +} + +VoxelMesherDMC::MeshMode VoxelMesherDMC::get_mesh_mode() const { + return _mesh_mode; +} + +void VoxelMesherDMC::set_simplify_mode(SimplifyMode mode) { + _simplify_mode = mode; +} + +VoxelMesherDMC::SimplifyMode VoxelMesherDMC::get_simplify_mode() const { + return _simplify_mode; +} + +void VoxelMesherDMC::set_geometric_error(real_t geometric_error) { + _geometric_error = geometric_error; +} + +float VoxelMesherDMC::get_geometric_error() const { + return _geometric_error; +} + +void VoxelMesherDMC::set_seam_mode(SeamMode mode) { + _seam_mode = mode; +} + +VoxelMesherDMC::SeamMode VoxelMesherDMC::get_seam_mode() const { + return _seam_mode; +} + +void VoxelMesherDMC::build(VoxelMesher::Output &output, const VoxelBuffer &voxels, int padding) { + + // Requirements: + // - Voxel data must be padded + // - The non-padded area size is cubic and power of two + + _stats = { 0 }; + + if (voxels.is_uniform(VoxelBuffer::CHANNEL_ISOLEVEL)) { + // That won't produce any polygon + return; + } + + ERR_FAIL_COND(padding < MINIMUM_PADDING); + + const Vector3i buffer_size = voxels.get_size(); + // Taking previous power of two because the algorithm uses an integer cubic octree, and data should be padded + int chunk_size = previous_power_of_2(MIN(MIN(buffer_size.x, buffer_size.y), buffer_size.z)); + + ERR_FAIL_COND(voxels.get_size().x < chunk_size + padding * 2); + ERR_FAIL_COND(voxels.get_size().y < chunk_size + padding * 2); + ERR_FAIL_COND(voxels.get_size().z < chunk_size + padding * 2); + + // TODO Option for this in case LOD is not used + bool skirts_enabled = _seam_mode == SEAM_MARCHING_SQUARE_SKIRTS; + // Marching square skirts are a cheap way to hide LOD cracks, + // however they might still be visible because of shadow mapping, and cause potential issues when used for physics. + // Maybe a shader with a `light()` function can prevent shadows from being applied to these, + // but in longer term, proper seams remain a better solution. + // Unfortunately, such seams require the ability to quickly swap index buffers of the mesh using OpenGL/Vulkan, + // which is not possible with current Godot's VisualServer without forking the whole lot (dang!), + // and we are forced to at least re-upload the mesh entirely or have 16 versions of it just swapping seams... + // So we can't improve this further until Godot's API gives us that possibility, or other approaches like skirts need to be taken. + + // Construct an intermediate to handle padding transparently + dmc::VoxelAccess voxels_access(voxels, Vector3i(padding)); + + real_t time_before = OS::get_singleton()->get_ticks_usec(); + + // In an ideal world, a tiny sphere placed in the middle of an empty SDF volume will + // cause corners data to change so that they indicate distance to it. + // That means we could build our meshing octree top-down efficiently because corners of the volume will tell if the + // distance to any surface is varying. + // + // For large terrains, 8-bit isolevels with quantification of -1..1 could be enough to represent surfaces. + // It compresses well, and we don't need to propagate distance changes to the whole volume as we edit it. + // Finally, it's easy to find uniform locations, they will be filled with 0 or 255, and discarded, + // or possibly stored in a data octree already. + // + // Problem: + // If you try building the meshing octree top-down in that case, it could see corners all have value of 1, + // and will skip everything, assuming the volume contains nothing. + // Building the octree bottom-up ensures to always catch voxels of any size, but will be a bit slower + // because all voxels are queried. + // + // TODO This option might disappear once I find a good enough solution + dmc::OctreeNode *root = nullptr; + if (_simplify_mode == SIMPLIFY_OCTREE_BOTTOM_UP) { + + dmc::OctreeBuilderBottomUp octree_builder(voxels_access, _geometric_error, _octree_node_pool); + root = octree_builder.build(Vector3i(), chunk_size); + + } else if (_simplify_mode == SIMPLIFY_OCTREE_TOP_DOWN) { + + dmc::OctreeBuilderTopDown octree_builder(voxels_access, _geometric_error, _octree_node_pool); + root = octree_builder.build(Vector3i(), chunk_size); + } + + _stats.octree_build_time = OS::get_singleton()->get_ticks_usec() - time_before; + + Array surface; + + if (root != nullptr) { + + if (_mesh_mode == MESH_DEBUG_OCTREE) { + surface = dmc::generate_debug_octree_mesh(root); + + } else { + + time_before = OS::get_singleton()->get_ticks_usec(); + + dmc::DualGridGenerator dual_grid_generator(_dual_grid, root->size); + dual_grid_generator.node_proc(root); + // TODO Handle non-subdivided octree + + _stats.dualgrid_derivation_time = OS::get_singleton()->get_ticks_usec() - time_before; + + if (_mesh_mode == MESH_DEBUG_DUAL_GRID) { + surface = dmc::generate_debug_dual_grid_mesh(_dual_grid); + + } else { + + time_before = OS::get_singleton()->get_ticks_usec(); + dmc::polygonize_dual_grid(_dual_grid, voxels_access, _mesh_builder, skirts_enabled); + _stats.meshing_time = OS::get_singleton()->get_ticks_usec() - time_before; + } + + _dual_grid.cells.clear(); + } + + root->recycle(_octree_node_pool); + + } else if (_simplify_mode == SIMPLIFY_NONE) { + + // We throw away adaptivity for meshing speed. + // This is essentially regular marching cubes. + + time_before = OS::get_singleton()->get_ticks_usec(); + dmc::polygonize_volume_directly(voxels, Vector3i(padding), Vector3i(chunk_size), _mesh_builder, skirts_enabled); + _stats.meshing_time = OS::get_singleton()->get_ticks_usec() - time_before; + } + + if (surface.empty()) { + time_before = OS::get_singleton()->get_ticks_usec(); + surface = _mesh_builder.commit(_mesh_mode == MESH_WIREFRAME); + _stats.commit_time = OS::get_singleton()->get_ticks_usec() - time_before; + } + + // surfaces[material][array_type], for now single material + output.surfaces.push_back(surface); + + if (_mesh_mode == MESH_NORMAL) { + output.primitive_type = Mesh::PRIMITIVE_TRIANGLES; + } else { + output.primitive_type = Mesh::PRIMITIVE_LINES; + } +} + +int VoxelMesherDMC::get_minimum_padding() const { + return MINIMUM_PADDING; +} + +VoxelMesher *VoxelMesherDMC::clone() { + VoxelMesherDMC *c = memnew(VoxelMesherDMC); + c->set_mesh_mode(_mesh_mode); + c->set_simplify_mode(_simplify_mode); + c->set_geometric_error(_geometric_error); + c->set_seam_mode(_seam_mode); + return c; +} + +Dictionary VoxelMesherDMC::get_stats() const { + Dictionary d; + d["octree_build_time"] = _stats.octree_build_time; + d["dualgrid_derivation_time"] = _stats.dualgrid_derivation_time; + d["meshing_time"] = _stats.meshing_time; + d["commit_time"] = _stats.commit_time; + return d; +} + +void VoxelMesherDMC::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_mesh_mode", "mode"), &VoxelMesherDMC::set_mesh_mode); + ClassDB::bind_method(D_METHOD("get_mesh_mode"), &VoxelMesherDMC::get_mesh_mode); + + ClassDB::bind_method(D_METHOD("set_simplify_mode", "mode"), &VoxelMesherDMC::set_simplify_mode); + ClassDB::bind_method(D_METHOD("get_simplify_mode"), &VoxelMesherDMC::get_simplify_mode); + + ClassDB::bind_method(D_METHOD("set_geometric_error", "error"), &VoxelMesherDMC::set_geometric_error); + ClassDB::bind_method(D_METHOD("get_geometric_error"), &VoxelMesherDMC::get_geometric_error); + + ClassDB::bind_method(D_METHOD("set_seam_mode", "mode"), &VoxelMesherDMC::set_seam_mode); + ClassDB::bind_method(D_METHOD("get_seam_mode"), &VoxelMesherDMC::get_seam_mode); + + ClassDB::bind_method(D_METHOD("get_stats"), &VoxelMesherDMC::get_stats); + + BIND_ENUM_CONSTANT(MESH_NORMAL); + BIND_ENUM_CONSTANT(MESH_WIREFRAME); + BIND_ENUM_CONSTANT(MESH_DEBUG_OCTREE); + BIND_ENUM_CONSTANT(MESH_DEBUG_DUAL_GRID); + + BIND_ENUM_CONSTANT(SIMPLIFY_OCTREE_BOTTOM_UP); + BIND_ENUM_CONSTANT(SIMPLIFY_OCTREE_TOP_DOWN); + BIND_ENUM_CONSTANT(SIMPLIFY_NONE); + + BIND_ENUM_CONSTANT(SEAM_NONE); + BIND_ENUM_CONSTANT(SEAM_MARCHING_SQUARE_SKIRTS); +} diff --git a/meshers/dmc/voxel_mesher_dmc.h b/meshers/dmc/voxel_mesher_dmc.h new file mode 100644 index 0000000..df77f29 --- /dev/null +++ b/meshers/dmc/voxel_mesher_dmc.h @@ -0,0 +1,137 @@ +#ifndef VOXEL_MESHER_DMC_H +#define VOXEL_MESHER_DMC_H + +#include "../../util/object_pool.h" +#include "../voxel_mesher.h" +#include "hermite_value.h" +#include "mesh_builder.h" +#include + +namespace dmc { + +struct OctreeNode; +typedef ObjectPool OctreeNodePool; + +// Octree used only for dual grid construction +struct OctreeNode { + + Vector3i origin; + int size; // Nodes are cubic + HermiteValue center_value; + OctreeNode *children[8]; + + OctreeNode() { + init(); + } + + inline void init() { + for (int i = 0; i < 8; ++i) { + children[i] = nullptr; + } + } + + void recycle(OctreeNodePool &pool) { + for (int i = 0; i < 8; ++i) { + if (children[i]) { + pool.recycle(children[i]); + } + } + } + + inline bool has_children() const { + return children[0] != nullptr; + } +}; + +struct DualCell { + Vector3 corners[8]; + HermiteValue values[8]; + bool has_values = false; + + inline void set_corner(int i, Vector3 vertex, HermiteValue value) { + CRASH_COND(i < 0 || i >= 8); + corners[i] = vertex; + values[i] = value; + } +}; + +struct DualGrid { + std::vector cells; +}; + +} // namespace dmc + +// Mesher extending Marching Cubes using a dual grid. +class VoxelMesherDMC : public VoxelMesher { + GDCLASS(VoxelMesherDMC, VoxelMesher) +public: + static const int MINIMUM_PADDING = 2; + + enum MeshMode { + MESH_NORMAL, + MESH_WIREFRAME, + MESH_DEBUG_OCTREE, + MESH_DEBUG_DUAL_GRID + }; + + enum SimplifyMode { + SIMPLIFY_OCTREE_BOTTOM_UP, + SIMPLIFY_OCTREE_TOP_DOWN, + SIMPLIFY_NONE + }; + + enum SeamMode { + SEAM_NONE, // No seam management + SEAM_MARCHING_SQUARE_SKIRTS, + // SEAM_OVERLAP // Polygonize extra voxels with lower isolevel + // SEAM_JUNCTION_TRIANGLES // Extra triangles for alternate borders, requires index buffer switching + }; + + VoxelMesherDMC(); + + void set_mesh_mode(MeshMode mode); + MeshMode get_mesh_mode() const; + + void set_simplify_mode(SimplifyMode mode); + SimplifyMode get_simplify_mode() const; + + void set_geometric_error(real_t geometric_error); + float get_geometric_error() const; + + void set_seam_mode(SeamMode mode); + SeamMode get_seam_mode() const; + + void build(VoxelMesher::Output &output, const VoxelBuffer &voxels, int padding) override; + int get_minimum_padding() const override; + + Dictionary get_stats() const; + + VoxelMesher *clone() override; + +protected: + static void _bind_methods(); + +private: + dmc::MeshBuilder _mesh_builder; + dmc::DualGrid _dual_grid; + dmc::OctreeNodePool _octree_node_pool; + real_t _geometric_error = 0.1; + MeshMode _mesh_mode = MESH_NORMAL; + SimplifyMode _simplify_mode = SIMPLIFY_OCTREE_BOTTOM_UP; + SeamMode _seam_mode = SEAM_NONE; + + struct Stats { + real_t octree_build_time = 0; + real_t dualgrid_derivation_time = 0; + real_t meshing_time = 0; + real_t commit_time = 0; + }; + + Stats _stats; +}; + +VARIANT_ENUM_CAST(VoxelMesherDMC::SimplifyMode) +VARIANT_ENUM_CAST(VoxelMesherDMC::MeshMode) +VARIANT_ENUM_CAST(VoxelMesherDMC::SeamMode) + +#endif // VOXEL_MESHER_DMC_H diff --git a/meshers/voxel_mesher.h b/meshers/voxel_mesher.h index b80a435..4be4f5d 100644 --- a/meshers/voxel_mesher.h +++ b/meshers/voxel_mesher.h @@ -78,7 +78,6 @@ public: protected: static void _bind_methods(); -private: Vector _vertices; Vector _normals; Vector _colors; diff --git a/meshers/voxel_mesher_transvoxel_terrarin.cpp b/meshers/voxel_mesher_transvoxel_terrarin.cpp new file mode 100644 index 0000000..453f8ad --- /dev/null +++ b/meshers/voxel_mesher_transvoxel_terrarin.cpp @@ -0,0 +1,354 @@ + +#include "voxel_mesher_transvoxel_terrarin.h" +#include + +// This is a very partial implementation, it is barely equivalent to marching cubes. +// It doesn't include transition cells. + +namespace { + +inline float tof(int8_t v) { + return static_cast(v) / 256.f; +} + +inline int8_t tos(uint8_t v) { + return v - 128; +} + +// Values considered negative have a sign bit of 1 +inline uint8_t sign(int8_t v) { + return (v >> 7) & 1; +} + +// +// 6-------7 +// /| /| +// / | / | Corners +// 4-------5 | +// | 2----|--3 +// | / | / z y +// |/ |/ |/ +// 0-------1 o--x +// + +const Vector3i g_corner_dirs[8] = { + Vector3i(0, 0, 0), + Vector3i(1, 0, 0), + Vector3i(0, 1, 0), + Vector3i(1, 1, 0), + Vector3i(0, 0, 1), + Vector3i(1, 0, 1), + Vector3i(0, 1, 1), + Vector3i(1, 1, 1) +}; + +inline Vector3i dir_to_prev_vec(uint8_t dir) { + //return g_corner_dirs[mask] - Vector3(1,1,1); + return Vector3i( + -(dir & 1), + -((dir >> 1) & 1), + -((dir >> 2) & 1)); +} + +template +void copy_to(PoolVector &to, Vector &from) { + + to.resize(from.size()); + + typename PoolVector::Write w = to.write(); + + for (unsigned int i = 0; i < from.size(); ++i) { + w[i] = from[i]; + } +} + +} // namespace + +VoxelMesherTransvoxelTerrarin::ReuseCell::ReuseCell() { + case_index = 0; + for (unsigned int i = 0; i < 4; ++i) { + vertices[i] = -1; + } +} +/* +int VoxelMesherTransvoxelTerrarin::get_minimum_padding() const { + return MINIMUM_PADDING; +}*/ + +void VoxelMesherTransvoxelTerrarin::_add_buffer(Ref buffer) { + + ERR_FAIL_COND(padding < MINIMUM_PADDING); + + int channel = VoxelBuffer::CHANNEL_ISOLEVEL; + + build_internal(buffer, channel); +} + +void VoxelMesherTransvoxelTerrarin::build_internal(Ref buffer, unsigned int channel) { + + // Each 2x2 voxel group is a "cell" + + const VoxelBuffer &voxels = *(buffer.ptr()); + + if (voxels.is_uniform(channel)) { + // Nothing to extract, because constant isolevels never cross the threshold and describe no surface + return; + } + + const Vector3i block_size = voxels.get_size(); + // TODO No lod yet, but it's planned + const int lod_index = 0; + const int lod_scale = 1 << lod_index; + + // Prepare vertex reuse cache + m_block_size = block_size; + unsigned int deck_area = block_size.x * block_size.y; + for (int i = 0; i < 2; ++i) { + if (m_cache[i].size() != deck_area) { + m_cache[i].clear(); // Clear any previous data + m_cache[i].resize(deck_area); + } + } + + // Iterate all cells with padding (expected to be neighbors) + Vector3i pos; + for (pos.z = PAD.z; pos.z < block_size.z - 2; ++pos.z) { + for (pos.y = PAD.y; pos.y < block_size.y - 2; ++pos.y) { + for (pos.x = PAD.x; pos.x < block_size.x - 2; ++pos.x) { + + // Get the value of cells. + // Negative values are "solid" and positive are "air". + // Due to raw cells being unsigned 8-bit, they get converted to signed. + int8_t cell_samples[8] = { + tos(voxels.get_voxel(pos.x, pos.y, pos.z, channel)), + tos(voxels.get_voxel(pos.x + 1, pos.y, pos.z, channel)), + tos(voxels.get_voxel(pos.x, pos.y + 1, pos.z, channel)), + tos(voxels.get_voxel(pos.x + 1, pos.y + 1, pos.z, channel)), + tos(voxels.get_voxel(pos.x, pos.y, pos.z + 1, channel)), + tos(voxels.get_voxel(pos.x + 1, pos.y, pos.z + 1, channel)), + tos(voxels.get_voxel(pos.x, pos.y + 1, pos.z + 1, channel)), + tos(voxels.get_voxel(pos.x + 1, pos.y + 1, pos.z + 1, channel)) + }; + + // Concatenate the sign of cell values to obtain the case code. + // Index 0 is the less significant bit, and index 7 is the most significant bit. + uint8_t case_code = sign(cell_samples[0]); + case_code |= (sign(cell_samples[1]) << 1); + case_code |= (sign(cell_samples[2]) << 2); + case_code |= (sign(cell_samples[3]) << 3); + case_code |= (sign(cell_samples[4]) << 4); + case_code |= (sign(cell_samples[5]) << 5); + case_code |= (sign(cell_samples[6]) << 6); + case_code |= (sign(cell_samples[7]) << 7); + + { + ReuseCell &rc = get_reuse_cell(pos); + rc.case_index = case_code; + } + + if (case_code == 0 || case_code == 255) { + // If the case_code is 0 or 255, there is no triangulation to do + continue; + } + + // TODO We might not always need all of them + // Compute normals + Vector3 corner_normals[8]; + for (unsigned int i = 0; i < 8; ++i) { + + Vector3i p = pos + g_corner_dirs[i]; + + float nx = tof(tos(voxels.get_voxel(p - Vector3i(1, 0, 0), channel))) - tof(tos(voxels.get_voxel(p + Vector3i(1, 0, 0), channel))); + float ny = tof(tos(voxels.get_voxel(p - Vector3i(0, 1, 0), channel))) - tof(tos(voxels.get_voxel(p + Vector3i(0, 1, 0), channel))); + float nz = tof(tos(voxels.get_voxel(p - Vector3i(0, 0, 1), channel))) - tof(tos(voxels.get_voxel(p + Vector3i(0, 0, 1), channel))); + + corner_normals[i] = Vector3(nx, ny, nz); + corner_normals[i].normalize(); + } + + // For cells occurring along the minimal boundaries of a block, + // the preceding cells needed for vertex reuse may not exist. + // In these cases, we allow new vertex creation on additional edges of a cell. + // While iterating through the cells in a block, a 3-bit mask is maintained whose bits indicate + // whether corresponding bits in a direction code are valid + uint8_t direction_validity_mask = + (pos.x > 1 ? 1 : 0) | ((pos.y > 1 ? 1 : 0) << 1) | ((pos.z > 1 ? 1 : 0) << 2); + + uint8_t regular_cell_class_index = Transvoxel::regularCellClass[case_code]; + Transvoxel::RegularCellData regular_cell_class = Transvoxel::regularCellData[regular_cell_class_index]; + uint8_t triangle_count = regular_cell_class.geometryCounts & 0x0f; + uint8_t vertex_count = (regular_cell_class.geometryCounts & 0xf0) >> 4; + + int cell_mesh_indices[12]; + + // For each vertex in the case + for (unsigned int i = 0; i < vertex_count; ++i) { + + // The case index maps to a list of 16-bit codes providing information about the edges on which the vertices lie. + // The low byte of each 16-bit code contains the corner indexes of the edge’s endpoints in one nibble each, + // and the high byte contains the mapping code shown in Figure 3.8(b) + unsigned short rvd = Transvoxel::regularVertexData[case_code][i]; + unsigned short edge_code_low = rvd & 0xff; + unsigned short edge_code_high = (rvd >> 8) & 0xff; + + // Get corner indexes in the low nibble (always ordered so the higher comes last) + uint8_t v0 = (edge_code_low >> 4) & 0xf; + uint8_t v1 = edge_code_low & 0xf; + + ERR_FAIL_COND(v1 <= v0); + + // Get voxel values at the corners + int sample0 = cell_samples[v0]; // called d0 in the paper + int sample1 = cell_samples[v1]; // called d1 in the paper + + // TODO Zero-division is not mentionned in the paper?? + ERR_FAIL_COND(sample1 == sample0); + ERR_FAIL_COND(sample1 == 0 && sample0 == 0); + + // Get interpolation position + // We use an 8-bit fraction, allowing the new vertex to be located at one of 257 possible + // positions along the edge when both endpoints are included. + int t = (sample1 << 8) / (sample1 - sample0); + + float t0 = static_cast(t) / 256.f; + float t1 = static_cast(0x0100 - t) / 256.f; + + Vector3i p0 = pos + g_corner_dirs[v0]; + Vector3i p1 = pos + g_corner_dirs[v1]; + + if (t & 0xff) { + //OS::get_singleton()->print("A"); + // Vertex lies in the interior of the edge. + + // Each edge of a cell is assigned an 8-bit code, as shown in Figure 3.8(b), + // that provides a mapping to a preceding cell and the coincident edge on that preceding cell + // for which new vertex creation was allowed. + // The high nibble of this code indicates which direction to go in order to reach the correct preceding cell. + // The bit values 1, 2, and 4 in this nibble indicate that we must subtract one + // from the x, y, and/or z coordinate, respectively. + uint8_t reuse_dir = (edge_code_high >> 4) & 0xf; + uint8_t reuse_vertex_index = edge_code_high & 0xf; + + bool can_reuse = (reuse_dir & direction_validity_mask) == reuse_dir; + + if (can_reuse) { + Vector3i cache_pos = pos + dir_to_prev_vec(reuse_dir); + ReuseCell &prev_cell = get_reuse_cell(cache_pos); + + if (prev_cell.case_index == 0 || prev_cell.case_index == 255) { + // TODO I don't think this can happen for non-corner vertices. + cell_mesh_indices[i] = -1; + } else { + // Will reuse a previous vertice + cell_mesh_indices[i] = prev_cell.vertices[reuse_vertex_index]; + } + } + + if (!can_reuse || cell_mesh_indices[i] == -1) { + // Going to create a new vertice + + cell_mesh_indices[i] = _vertices.size(); + + Vector3 pi = p0.to_vec3() * t0 + p1.to_vec3() * t1; + + Vector3 primary = pi; //pos.to_vec3() + pi; + Vector3 normal = corner_normals[v0] * t0 + corner_normals[v1] * t1; + + emit_vertex(primary, normal); + + if (reuse_dir & 8) { + // Store the generated vertex so that other cells can reuse it. + ReuseCell &rc = get_reuse_cell(pos); + rc.vertices[reuse_vertex_index] = cell_mesh_indices[i]; + } + } + + } else if (t == 0 && v1 == 7) { + + //OS::get_singleton()->print("B"); + // This cell owns the vertex, so it should be created. + + cell_mesh_indices[i] = _vertices.size(); + + Vector3 pi = p0.to_vec3() * t0 + p1.to_vec3() * t1; + Vector3 primary = pi; //pos.to_vec3() + pi; + Vector3 normal = corner_normals[v0] * t0 + corner_normals[v1] * t1; + + emit_vertex(primary, normal); + + ReuseCell &rc = get_reuse_cell(pos); + rc.vertices[0] = cell_mesh_indices[i]; + + } else { + // Always try to reuse previous vertices in these cases + + //OS::get_singleton()->print("C"); + // A 3-bit direction code leading to the proper cell can easily be obtained by + // inverting the 3-bit corner index (bitwise, by exclusive ORing with the number 7). + // The corner index depends on the value of t, t = 0 means that we're at the higher + // numbered endpoint. + uint8_t reuse_dir = (t == 0 ? v1 ^ 7 : v0 ^ 7); + bool can_reuse = (reuse_dir & direction_validity_mask) == reuse_dir; + + // Note: the only difference with similar code above is that we take vertice 0 in the `else` + if (can_reuse) { + Vector3i cache_pos = pos + dir_to_prev_vec(reuse_dir); + ReuseCell prev_cell = get_reuse_cell(cache_pos); + + // The previous cell might not have any geometry, and we + // might therefore have to create a new vertex anyway. + if (prev_cell.case_index == 0 || prev_cell.case_index == 255) { + cell_mesh_indices[i] = -1; + } else { + cell_mesh_indices[i] = prev_cell.vertices[0]; + } + } + + if (!can_reuse || cell_mesh_indices[i] < 0) { + cell_mesh_indices[i] = _vertices.size(); + + Vector3 pi = p0.to_vec3() * t0 + p1.to_vec3() * t1; + Vector3 primary = pi; //pos.to_vec3() + pi; + Vector3 normal = corner_normals[v0] * t0 + corner_normals[v1] * t1; + + emit_vertex(primary, normal); + } + } + + } // for each cell vertice + + //OS::get_singleton()->print("_"); + + for (int t = 0; t < triangle_count; ++t) { + for (int i = 0; i < 3; ++i) { + int index = cell_mesh_indices[regular_cell_class.vertexIndex[t * 3 + i]]; + _indices.push_back(index); + } + } + + } // x + } // y + } // z + + //OS::get_singleton()->print("\n"); +} + +VoxelMesherTransvoxelTerrarin::ReuseCell &VoxelMesherTransvoxelTerrarin::get_reuse_cell(Vector3i pos) { + int j = pos.z & 1; + int i = pos.y * m_block_size.y + pos.x; + return m_cache[j].write[i]; +} + +void VoxelMesherTransvoxelTerrarin::emit_vertex(Vector3 primary, Vector3 normal) { + _vertices.push_back(primary - PAD.to_vec3()); + _normals.push_back(normal); +} + +VoxelMesherTransvoxelTerrarin::VoxelMesherTransvoxelTerrarin() { + padding = MINIMUM_PADDING; +} + + +void VoxelMesherTransvoxelTerrarin::_bind_methods() { + ClassDB::bind_method(D_METHOD("_add_buffer", "buffer"), &VoxelMesherTransvoxelTerrarin::_add_buffer); +} diff --git a/meshers/voxel_mesher_transvoxel_terrarin.h b/meshers/voxel_mesher_transvoxel_terrarin.h new file mode 100644 index 0000000..621e990 --- /dev/null +++ b/meshers/voxel_mesher_transvoxel_terrarin.h @@ -0,0 +1,43 @@ +#ifndef VOXEL_MESHER_TRANSVOXEL_TERRARIN_H +#define VOXEL_MESHER_TRANSVOXEL_TERRARIN_H + +#include + +#include "voxel_mesher.h" +#include "transvoxel_tables.h" + + +class VoxelMesherTransvoxelTerrarin : public VoxelMesher { + GDCLASS(VoxelMesherTransvoxelTerrarin, VoxelMesher) + +public: + static const int MINIMUM_PADDING = 2; + + void _add_buffer(Ref buffer); + //int get_minimum_padding() const override; + + VoxelMesherTransvoxelTerrarin(); + +protected: + static void _bind_methods(); + +private: + struct ReuseCell { + int vertices[4]; + int case_index; + ReuseCell(); + }; + + void build_internal(Ref buffer, unsigned int channel); + ReuseCell &get_reuse_cell(Vector3i pos); + void emit_vertex(Vector3 primary, Vector3 normal); + +private: + int padding; + const Vector3i PAD = Vector3i(1, 1, 1); + + Vector m_cache[2]; + Vector3i m_block_size; +}; + +#endif // VOXEL_MESHER_TRANSVOXEL_TERRARIN_H diff --git a/register_types.cpp b/register_types.cpp index c84a5fd..c6eb908 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -6,6 +6,7 @@ #include "data/voxel_light.h" #include "meshers/voxel_mesher.h" #include "meshers/voxel_mesher_transvoxel.h" +#include "meshers/voxel_mesher_transvoxel_terrarin.h" #include "world/voxel_buffer.h" #include "world/voxel_world.h" @@ -14,6 +15,7 @@ void register_voxelman_types() { ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class();