mirror of
https://github.com/Relintai/godot_voxel.git
synced 2024-11-19 02:47:18 +01:00
A bit of reorganization and tweaks:
- Moved HermiteValue in its own file - VoxelBuffer channels are now predefined - Create DualGridGenerator for easier passing of data - Chunk size is no longer hardcoded - Respect padding when polygonizing voxels - BUG: due to the above, the way we build the octree is now breaking the result as mentionned in the TODO
This commit is contained in:
parent
35ff5fd546
commit
91a5c7ffa8
68
dmc/hermite_value.h
Normal file
68
dmc/hermite_value.h
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef HERMITE_VALUE_H
|
||||
#define HERMITE_VALUE_H
|
||||
|
||||
#include "../utility.h"
|
||||
#include "../voxel_buffer.h"
|
||||
#include <core/math/vector3.h>
|
||||
|
||||
namespace dmc {
|
||||
|
||||
struct HermiteValue {
|
||||
// TODO Rename isolevel
|
||||
float value; // Signed "distance" to surface
|
||||
Vector3 gradient; // "Normal" of the volume
|
||||
|
||||
HermiteValue() :
|
||||
value(1.0) {
|
||||
}
|
||||
};
|
||||
|
||||
inline HermiteValue get_hermite_value(const VoxelBuffer &voxels, unsigned int x, unsigned int y, unsigned int z) {
|
||||
|
||||
x = x >= voxels.get_size().x ? voxels.get_size().x - 1 : x;
|
||||
x = x >= voxels.get_size().y ? voxels.get_size().y - 1 : x;
|
||||
x = x >= voxels.get_size().z ? voxels.get_size().z - 1 : x;
|
||||
|
||||
HermiteValue v;
|
||||
|
||||
v.value = voxels.get_voxel_iso(x, y, z, VoxelBuffer::CHANNEL_ISOLEVEL);
|
||||
// TODO It looks like this gradient should not be a normalized vector!
|
||||
v.gradient.x = voxels.get_voxel_iso(x, y, z, VoxelBuffer::CHANNEL_GRADIENT_X);
|
||||
v.gradient.y = voxels.get_voxel_iso(x, y, z, VoxelBuffer::CHANNEL_GRADIENT_Y);
|
||||
v.gradient.z = voxels.get_voxel_iso(x, y, z, VoxelBuffer::CHANNEL_GRADIENT_Z);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
inline HermiteValue get_interpolated_hermite_value(const VoxelBuffer &voxels, Vector3 pos) {
|
||||
|
||||
int x0 = static_cast<int>(pos.x);
|
||||
int y0 = static_cast<int>(pos.y);
|
||||
int z0 = static_cast<int>(pos.z);
|
||||
|
||||
int x1 = static_cast<int>(Math::ceil(pos.x));
|
||||
int y1 = static_cast<int>(Math::ceil(pos.y));
|
||||
int z1 = static_cast<int>(Math::ceil(pos.z));
|
||||
|
||||
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.value = ::interpolate(v0.value, v1.value, v2.value, v3.value, v4.value, v5.value, v6.value, v7.value, 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
|
45
dmc/octree_utility.h
Normal file
45
dmc/octree_utility.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef OCTREE_UTILITY_H
|
||||
#define OCTREE_UTILITY_H
|
||||
|
||||
namespace OctreeUtility {
|
||||
|
||||
// Corners: Octants:
|
||||
//
|
||||
// 6---------------18--------------7 o---o---o
|
||||
// / / /| | 6 | 7 |
|
||||
// / / / | o---o---o Upper
|
||||
// 17--------------25--------------19 | | 5 | 4 |
|
||||
// / / / | o---o---o
|
||||
// / / / |
|
||||
// 5---------------16--------------4 | o---o---o
|
||||
// | 14--------|-----23--------|-----15 | 2 | 3 |
|
||||
// | / | / | /| o---o---o Lower Z
|
||||
// | / | / | / | | 1 | 0 | |
|
||||
// | 22-----------|--26-----------|--24 | o---o---o X---o
|
||||
// | / | / | / |
|
||||
// |/ |/ |/ |
|
||||
// 13--------------21--------------12 |
|
||||
// | 2---------|-----10--------|-----3
|
||||
// | / | / | /
|
||||
// | / | / | /
|
||||
// | 9------------|--20-----------|--11 Y
|
||||
// | / | / | / | Z
|
||||
// |/ |/ |/ |/
|
||||
// 1---------------8---------------0 X----o
|
||||
|
||||
const int g_octant_position[8][3]{
|
||||
|
||||
{ 0, 0, 0 },
|
||||
{ 1, 0, 0 },
|
||||
{ 1, 0, 1 },
|
||||
{ 0, 0, 1 },
|
||||
|
||||
{ 0, 1, 0 },
|
||||
{ 1, 1, 0 },
|
||||
{ 1, 1, 1 },
|
||||
{ 0, 1, 1 }
|
||||
};
|
||||
|
||||
} // namespace OctreeUtility
|
||||
|
||||
#endif // OCTREE_UTILITY_H
|
@ -1,34 +1,24 @@
|
||||
#include "voxel_mesher_dmc.h"
|
||||
#include "../cube_tables.h"
|
||||
#include "hermite_value.h"
|
||||
#include "marching_cubes_tables.h"
|
||||
#include "mesh_builder.h"
|
||||
#include "octree_utility.h"
|
||||
|
||||
// Dual marching cubes
|
||||
// Algorithm taken from https://www.volume-gfx.com/volume-rendering/dual-marching-cubes/
|
||||
|
||||
namespace dmc {
|
||||
|
||||
enum Channels {
|
||||
CHANNEL_VALUE = 0,
|
||||
CHANNEL_GRADIENT_X,
|
||||
CHANNEL_GRADIENT_Y,
|
||||
CHANNEL_GRADIENT_Z
|
||||
};
|
||||
// Surface is defined when isolevel crosses 0
|
||||
const float SURFACE_ISO_LEVEL = 0.0;
|
||||
|
||||
const int CHUNK_SIZE = 8;
|
||||
const float ISO_LEVEL = 0.0;
|
||||
const float NEAR_SURFACE_FACTOR = 2.0;
|
||||
const float SQRT3 = 1.7320508075688772;
|
||||
|
||||
struct HermiteValue {
|
||||
float value; // Signed "distance" to surface
|
||||
Vector3 gradient; // "Normal" of the volume
|
||||
|
||||
HermiteValue() :
|
||||
value(1.0) {
|
||||
}
|
||||
};
|
||||
|
||||
// Octree used only for dual grid construction
|
||||
struct OctreeNode {
|
||||
|
||||
Vector3i origin;
|
||||
int size; // Nodes are cubic
|
||||
HermiteValue center_value;
|
||||
@ -53,43 +43,6 @@ struct OctreeNode {
|
||||
}
|
||||
};
|
||||
|
||||
// Corners: Octants:
|
||||
//
|
||||
// 6---------------18--------------7 o---o---o
|
||||
// / / /| | 6 | 7 |
|
||||
// / / / | o---o---o Upper
|
||||
// 17--------------25--------------19 | | 5 | 4 |
|
||||
// / / / | o---o---o
|
||||
// / / / |
|
||||
// 5---------------16--------------4 | o---o---o
|
||||
// | 14--------|-----23--------|-----15 | 2 | 3 |
|
||||
// | / | / | /| o---o---o Lower Z
|
||||
// | / | / | / | | 1 | 0 | |
|
||||
// | 22-----------|--26-----------|--24 | o---o---o X---o
|
||||
// | / | / | / |
|
||||
// |/ |/ |/ |
|
||||
// 13--------------21--------------12 |
|
||||
// | 2---------|-----10--------|-----3
|
||||
// | / | / | /
|
||||
// | / | / | /
|
||||
// | 9------------|--20-----------|--11 Y
|
||||
// | / | / | / | Z
|
||||
// |/ |/ |/ |/
|
||||
// 1---------------8---------------0 X----o
|
||||
|
||||
const int g_octant_position[8][3]{
|
||||
|
||||
{ 0, 0, 0 },
|
||||
{ 1, 0, 0 },
|
||||
{ 1, 0, 1 },
|
||||
{ 0, 0, 1 },
|
||||
|
||||
{ 0, 1, 0 },
|
||||
{ 1, 1, 0 },
|
||||
{ 1, 1, 1 },
|
||||
{ 0, 1, 1 }
|
||||
};
|
||||
|
||||
void split(OctreeNode *node) {
|
||||
|
||||
CRASH_COND(node->has_children());
|
||||
@ -98,7 +51,7 @@ void split(OctreeNode *node) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
|
||||
OctreeNode *child = memnew(OctreeNode);
|
||||
const int *v = g_octant_position[i];
|
||||
const int *v = OctreeUtility::g_octant_position[i];
|
||||
child->size = node->size / 2;
|
||||
child->origin = node->origin + Vector3i(v[0], v[1], v[2]) * child->size;
|
||||
|
||||
@ -106,87 +59,6 @@ void split(OctreeNode *node) {
|
||||
}
|
||||
}
|
||||
|
||||
// Trilinear interpolation between corner values of a cube.
|
||||
// Cube points respect the same position as in the ASCII schema.
|
||||
template <typename T>
|
||||
inline T interpolate(const T v0, const T v1, const T v2, const T v3, const T v4, const T v5, const T v6, const T v7, Vector3 position) {
|
||||
|
||||
const float one_min_x = 1.f - position.x;
|
||||
const float one_min_y = 1.f - position.y;
|
||||
const float one_min_z = 1.f - position.z;
|
||||
const float one_min_x_one_min_y = one_min_x * one_min_y;
|
||||
const float x_one_min_y = position.x * one_min_y;
|
||||
|
||||
T res = one_min_z * (v0 * one_min_x_one_min_y + v1 * x_one_min_y + v4 * one_min_x * position.y);
|
||||
res += position.z * (v3 * one_min_x_one_min_y + v2 * x_one_min_y + v7 * one_min_x * position.y);
|
||||
res += position.x * position.y * (v5 * one_min_z + v6 * position.z);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
inline Vector3 interpolate(const Vector3 &v0, const Vector3 &v1, const HermiteValue &val0, const HermiteValue &val1, Vector3 &out_normal) {
|
||||
|
||||
if (Math::abs(val0.value - ISO_LEVEL) <= FLT_EPSILON) {
|
||||
out_normal = val0.gradient;
|
||||
return v0;
|
||||
}
|
||||
|
||||
if (Math::abs(val1.value - ISO_LEVEL) <= FLT_EPSILON) {
|
||||
out_normal = val1.gradient;
|
||||
return v1;
|
||||
}
|
||||
|
||||
if (Math::abs(val1.value - val0.value) <= FLT_EPSILON) {
|
||||
out_normal = val0.gradient;
|
||||
return v0;
|
||||
}
|
||||
|
||||
float mu = (ISO_LEVEL - val0.value) / (val1.value - val0.value);
|
||||
out_normal = val0.gradient + mu * (val1.gradient - val0.gradient);
|
||||
out_normal.normalize();
|
||||
|
||||
return v0 + mu * (v1 - v0);
|
||||
}
|
||||
|
||||
inline HermiteValue get_hermite_value(const VoxelBuffer &voxels, int x, int y, int z) {
|
||||
HermiteValue v;
|
||||
v.value = voxels.get_voxel_iso(x, y, z, CHANNEL_VALUE);
|
||||
// TODO It looks like this gradient should not be a normalized vector!
|
||||
v.gradient.x = voxels.get_voxel_iso(x, y, z, CHANNEL_GRADIENT_X);
|
||||
v.gradient.y = voxels.get_voxel_iso(x, y, z, CHANNEL_GRADIENT_Y);
|
||||
v.gradient.z = voxels.get_voxel_iso(x, y, z, CHANNEL_GRADIENT_Z);
|
||||
return v;
|
||||
}
|
||||
|
||||
inline HermiteValue get_interpolated_hermite_value(const VoxelBuffer &voxels, Vector3 pos) {
|
||||
|
||||
int x0 = static_cast<int>(pos.x);
|
||||
int y0 = static_cast<int>(pos.y);
|
||||
int z0 = static_cast<int>(pos.z);
|
||||
|
||||
int x1 = static_cast<int>(Math::ceil(pos.x));
|
||||
int y1 = static_cast<int>(Math::ceil(pos.y));
|
||||
int z1 = static_cast<int>(Math::ceil(pos.z));
|
||||
|
||||
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.value = interpolate(v0.value, v1.value, v2.value, v3.value, v4.value, v5.value, v6.value, v7.value, rpos);
|
||||
v.gradient = interpolate(v0.gradient, v1.gradient, v2.gradient, v3.gradient, v4.gradient, v5.gradient, v6.gradient, v7.gradient, rpos);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
bool can_split(OctreeNode *node, const VoxelBuffer &voxels, float geometric_error) {
|
||||
|
||||
if (node->size == 1) {
|
||||
@ -196,7 +68,7 @@ bool can_split(OctreeNode *node, const VoxelBuffer &voxels, float geometric_erro
|
||||
|
||||
Vector3i origin = node->origin;
|
||||
int step = node->size;
|
||||
int channel = CHANNEL_VALUE;
|
||||
int channel = VoxelBuffer::CHANNEL_ISOLEVEL;
|
||||
|
||||
// Fighting with Clang-format here /**/
|
||||
|
||||
@ -271,7 +143,7 @@ bool can_split(OctreeNode *node, const VoxelBuffer &voxels, float geometric_erro
|
||||
|
||||
HermiteValue value = get_hermite_value(voxels, pos.x, pos.y, pos.z);
|
||||
|
||||
float interpolated_value = interpolate(v0, v1, v2, v3, v4, v5, v6, v7, positions_ratio[i]);
|
||||
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) {
|
||||
@ -390,17 +262,6 @@ Ref<ArrayMesh> generate_debug_octree_mesh(OctreeNode *root) {
|
||||
return mesh;
|
||||
}
|
||||
|
||||
inline bool is_surface_near(OctreeNode *node) {
|
||||
|
||||
if (node->center_value.value == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const float sqrt3 = 1.7320508075688772;
|
||||
|
||||
return Math::abs(node->center_value.value) < node->size * sqrt3 * NEAR_SURFACE_FACTOR;
|
||||
}
|
||||
|
||||
struct DualCell {
|
||||
Vector3 corners[8];
|
||||
HermiteValue values[8];
|
||||
@ -419,7 +280,16 @@ struct DualCell {
|
||||
struct DualGrid {
|
||||
std::vector<DualCell> cells;
|
||||
|
||||
void add_cell(const Vector3 c0, const Vector3 c1, const Vector3 c2, const Vector3 c3, const Vector3 c4, const Vector3 c5, const Vector3 c6, const Vector3 c7) {
|
||||
inline void add_cell(
|
||||
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;
|
||||
@ -476,24 +346,24 @@ inline bool is_border_left(const OctreeNode *node) {
|
||||
return node->origin.x == 0;
|
||||
}
|
||||
|
||||
inline bool is_border_right(const OctreeNode *node) {
|
||||
return node->origin.x + node->size == CHUNK_SIZE;
|
||||
inline bool is_border_right(const OctreeNode *node, int rootSize) {
|
||||
return node->origin.x + node->size == rootSize;
|
||||
}
|
||||
|
||||
inline bool is_border_bottom(const OctreeNode *node) {
|
||||
return node->origin.y == 0;
|
||||
}
|
||||
|
||||
inline bool is_border_top(const OctreeNode *node) {
|
||||
return node->origin.y + node->size == CHUNK_SIZE;
|
||||
inline bool is_border_top(const OctreeNode *node, int rootSize) {
|
||||
return node->origin.y + node->size == rootSize;
|
||||
}
|
||||
|
||||
inline bool is_border_back(const OctreeNode *node) {
|
||||
return node->origin.z == 0;
|
||||
}
|
||||
|
||||
inline bool is_border_front(const OctreeNode *node) {
|
||||
return node->origin.z + node->size == CHUNK_SIZE;
|
||||
inline bool is_border_front(const OctreeNode *node, int rootSize) {
|
||||
return node->origin.z + node->size == rootSize;
|
||||
}
|
||||
|
||||
inline Vector3 get_center_back(const OctreeNode *node) {
|
||||
@ -672,7 +542,40 @@ inline Vector3 get_corner7(const OctreeNode *node) {
|
||||
return p;
|
||||
}
|
||||
|
||||
void create_border_cells(DualGrid &grid,
|
||||
class DualGridGenerator {
|
||||
public:
|
||||
DualGridGenerator(DualGrid &grid, int octreeRootSize) :
|
||||
_grid(grid),
|
||||
_octreeRootSize(octreeRootSize) {}
|
||||
|
||||
void node_proc(OctreeNode *node);
|
||||
|
||||
private:
|
||||
DualGrid &_grid;
|
||||
int _octreeRootSize;
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
void DualGridGenerator::create_border_cells(
|
||||
const OctreeNode *n0,
|
||||
const OctreeNode *n1,
|
||||
const OctreeNode *n2,
|
||||
@ -682,6 +585,8 @@ void create_border_cells(DualGrid &grid,
|
||||
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)) {
|
||||
@ -691,7 +596,7 @@ void create_border_cells(DualGrid &grid,
|
||||
get_center_back(n4), get_center_back(n5), get_center(n5), get_center(n4));
|
||||
|
||||
// Generate back edge border cells
|
||||
if (is_border_top(n4) && is_border_top(n5)) {
|
||||
if (is_border_top(n4, _octreeRootSize) && is_border_top(n5, _octreeRootSize)) {
|
||||
|
||||
grid.add_cell(
|
||||
get_center_back(n4), get_center_back(n5), get_center(n5), get_center(n4),
|
||||
@ -704,7 +609,7 @@ void create_border_cells(DualGrid &grid,
|
||||
get_corner4(n4), get_center_back_top(n4), get_center_top(n4), get_center_left_top(n4));
|
||||
}
|
||||
|
||||
if (is_border_right(n4)) {
|
||||
if (is_border_right(n4, _octreeRootSize)) {
|
||||
grid.add_cell(
|
||||
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));
|
||||
@ -723,21 +628,24 @@ void create_border_cells(DualGrid &grid,
|
||||
get_center_back_left(n0), get_center_back(n0), get_center(n0), get_center_left(n0));
|
||||
}
|
||||
|
||||
if (is_border_right(n1)) {
|
||||
if (is_border_right(n1, _octreeRootSize)) {
|
||||
grid.add_cell(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) && is_border_front(n3) && is_border_front(n6) && is_border_front(n7)) {
|
||||
if (is_border_front(n2, _octreeRootSize) &&
|
||||
is_border_front(n3, _octreeRootSize) &&
|
||||
is_border_front(n6, _octreeRootSize) &&
|
||||
is_border_front(n7, _octreeRootSize)) {
|
||||
|
||||
grid.add_cell(
|
||||
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) && is_border_top(n7)) {
|
||||
if (is_border_top(n6, _octreeRootSize) && is_border_top(n7, _octreeRootSize)) {
|
||||
|
||||
grid.add_cell(
|
||||
get_center(n7), get_center(n6), get_center_front(n6), get_center_front(n7),
|
||||
@ -750,7 +658,7 @@ void create_border_cells(DualGrid &grid,
|
||||
get_center_left_top(n7), get_center_top(n7), get_center_front_top(n7), get_corner7(n7));
|
||||
}
|
||||
|
||||
if (is_border_right(n6)) {
|
||||
if (is_border_right(n6, _octreeRootSize)) {
|
||||
grid.add_cell(
|
||||
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));
|
||||
@ -769,7 +677,7 @@ void create_border_cells(DualGrid &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)) {
|
||||
if (is_border_right(n2, _octreeRootSize)) {
|
||||
grid.add_cell(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));
|
||||
}
|
||||
@ -783,7 +691,7 @@ void create_border_cells(DualGrid &grid,
|
||||
get_center_left(n4), get_center(n4), get_center(n7), get_center_left(n7));
|
||||
|
||||
// Generate left edge border cells
|
||||
if (is_border_top(n4) && is_border_top(n7)) {
|
||||
if (is_border_top(n4, _octreeRootSize) && is_border_top(n7, _octreeRootSize)) {
|
||||
grid.add_cell(
|
||||
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));
|
||||
@ -801,21 +709,24 @@ void create_border_cells(DualGrid &grid,
|
||||
get_center_back_left(n4), get_center_back(n4), get_center(n4), get_center_left(n4));
|
||||
}
|
||||
|
||||
if (is_border_front(n3) && is_border_front(n7)) {
|
||||
if (is_border_front(n3, _octreeRootSize) && is_border_front(n7, _octreeRootSize)) {
|
||||
grid.add_cell(
|
||||
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) && is_border_right(n2) && is_border_right(n5) && is_border_right(n6)) {
|
||||
if (is_border_right(n1, _octreeRootSize) &&
|
||||
is_border_right(n2, _octreeRootSize) &&
|
||||
is_border_right(n5, _octreeRootSize) &&
|
||||
is_border_right(n6, _octreeRootSize)) {
|
||||
|
||||
grid.add_cell(
|
||||
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) && is_border_top(n6)) {
|
||||
if (is_border_top(n5, _octreeRootSize) && is_border_top(n6, _octreeRootSize)) {
|
||||
grid.add_cell(
|
||||
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));
|
||||
@ -833,14 +744,18 @@ void create_border_cells(DualGrid &grid,
|
||||
get_center_back(n5), get_center_back_right(n5), get_center_right(n5), get_center(n5));
|
||||
}
|
||||
|
||||
if (is_border_front(n2) && is_border_front(n6)) {
|
||||
if (is_border_front(n2, _octreeRootSize) && is_border_front(n6, _octreeRootSize)) {
|
||||
grid.add_cell(
|
||||
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) && is_border_top(n5) && is_border_top(n6) && is_border_top(n7)) {
|
||||
if (is_border_top(n4, _octreeRootSize) &&
|
||||
is_border_top(n5, _octreeRootSize) &&
|
||||
is_border_top(n6, _octreeRootSize) &&
|
||||
is_border_top(n7, _octreeRootSize)) {
|
||||
|
||||
grid.add_cell(
|
||||
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));
|
||||
@ -853,7 +768,22 @@ void create_border_cells(DualGrid &grid,
|
||||
}
|
||||
}
|
||||
|
||||
void vert_proc(DualGrid &grid, OctreeNode *n0, OctreeNode *n1, OctreeNode *n2, OctreeNode *n3, OctreeNode *n4, OctreeNode *n5, OctreeNode *n6, OctreeNode *n7) {
|
||||
inline bool is_surface_near(OctreeNode *node) {
|
||||
if (node->center_value.value == 0) {
|
||||
return true;
|
||||
}
|
||||
return Math::abs(node->center_value.value) < 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();
|
||||
@ -864,7 +794,9 @@ void vert_proc(DualGrid &grid, OctreeNode *n0, OctreeNode *n1, OctreeNode *n2, O
|
||||
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) {
|
||||
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;
|
||||
@ -875,11 +807,12 @@ void vert_proc(DualGrid &grid, OctreeNode *n0, OctreeNode *n1, OctreeNode *n2, O
|
||||
OctreeNode *c6 = n6_has_children ? n6->children[0] : n6;
|
||||
OctreeNode *c7 = n7_has_children ? n7->children[1] : n7;
|
||||
|
||||
vert_proc(grid, c0, c1, c2, c3, c4, c5, c6, c7);
|
||||
vert_proc(c0, c1, c2, c3, c4, c5, c6, c7);
|
||||
|
||||
} else {
|
||||
|
||||
if (!(is_surface_near(n0) ||
|
||||
if (!(
|
||||
is_surface_near(n0) ||
|
||||
is_surface_near(n1) ||
|
||||
is_surface_near(n2) ||
|
||||
is_surface_near(n3) ||
|
||||
@ -900,13 +833,13 @@ void vert_proc(DualGrid &grid, OctreeNode *n0, OctreeNode *n1, OctreeNode *n2, O
|
||||
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);
|
||||
_grid.cells.push_back(cell);
|
||||
|
||||
create_border_cells(grid, n0, n1, n2, n3, n4, n5, n6, n7);
|
||||
create_border_cells(n0, n1, n2, n3, n4, n5, n6, n7);
|
||||
}
|
||||
}
|
||||
|
||||
void edge_proc_x(DualGrid &grid, OctreeNode *n0, OctreeNode *n1, OctreeNode *n2, OctreeNode *n3) {
|
||||
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();
|
||||
@ -926,13 +859,13 @@ void edge_proc_x(DualGrid &grid, OctreeNode *n0, OctreeNode *n1, OctreeNode *n2,
|
||||
OctreeNode *c6 = n2_has_children ? n2->children[1] : n2;
|
||||
OctreeNode *c7 = n2_has_children ? n2->children[0] : n2;
|
||||
|
||||
edge_proc_x(grid, c0, c3, c7, c4);
|
||||
edge_proc_x(grid, c1, c2, c6, c5);
|
||||
edge_proc_x(c0, c3, c7, c4);
|
||||
edge_proc_x(c1, c2, c6, c5);
|
||||
|
||||
vert_proc(grid, c0, c1, c2, c3, c4, c5, c6, c7);
|
||||
vert_proc(c0, c1, c2, c3, c4, c5, c6, c7);
|
||||
}
|
||||
|
||||
void edge_proc_y(DualGrid &grid, OctreeNode *n0, OctreeNode *n1, OctreeNode *n2, OctreeNode *n3) {
|
||||
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();
|
||||
@ -952,13 +885,13 @@ void edge_proc_y(DualGrid &grid, OctreeNode *n0, OctreeNode *n1, OctreeNode *n2,
|
||||
OctreeNode *c6 = n2_has_children ? n2->children[4] : n2;
|
||||
OctreeNode *c7 = n3_has_children ? n3->children[5] : n3;
|
||||
|
||||
edge_proc_y(grid, c0, c1, c2, c3);
|
||||
edge_proc_y(grid, c4, c5, c6, c7);
|
||||
edge_proc_y(c0, c1, c2, c3);
|
||||
edge_proc_y(c4, c5, c6, c7);
|
||||
|
||||
vert_proc(grid, c0, c1, c2, c3, c4, c5, c6, c7);
|
||||
vert_proc(c0, c1, c2, c3, c4, c5, c6, c7);
|
||||
}
|
||||
|
||||
void edge_proc_z(DualGrid &grid, OctreeNode *n0, OctreeNode *n1, OctreeNode *n2, OctreeNode *n3) {
|
||||
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();
|
||||
@ -978,13 +911,13 @@ void edge_proc_z(DualGrid &grid, OctreeNode *n0, OctreeNode *n1, OctreeNode *n2,
|
||||
OctreeNode *c6 = n1_has_children ? n1->children[3] : n1;
|
||||
OctreeNode *c7 = n0_has_children ? n0->children[2] : n0;
|
||||
|
||||
edge_proc_z(grid, c7, c6, c2, c3);
|
||||
edge_proc_z(grid, c4, c5, c1, c0);
|
||||
edge_proc_z(c7, c6, c2, c3);
|
||||
edge_proc_z(c4, c5, c1, c0);
|
||||
|
||||
vert_proc(grid, c0, c1, c2, c3, c4, c5, c6, c7);
|
||||
vert_proc(c0, c1, c2, c3, c4, c5, c6, c7);
|
||||
}
|
||||
|
||||
void face_proc_xy(DualGrid &grid, OctreeNode *n0, OctreeNode *n1) {
|
||||
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();
|
||||
@ -1002,21 +935,21 @@ void face_proc_xy(DualGrid &grid, OctreeNode *n0, OctreeNode *n1) {
|
||||
OctreeNode *c6 = n1_has_children ? n1->children[5] : n1;
|
||||
OctreeNode *c7 = n1_has_children ? n1->children[4] : n1;
|
||||
|
||||
face_proc_xy(grid, c0, c3);
|
||||
face_proc_xy(grid, c1, c2);
|
||||
face_proc_xy(grid, c4, c7);
|
||||
face_proc_xy(grid, c5, c6);
|
||||
face_proc_xy(c0, c3);
|
||||
face_proc_xy(c1, c2);
|
||||
face_proc_xy(c4, c7);
|
||||
face_proc_xy(c5, c6);
|
||||
|
||||
edge_proc_x(grid, c0, c3, c7, c4);
|
||||
edge_proc_x(grid, c1, c2, c6, c5);
|
||||
edge_proc_x(c0, c3, c7, c4);
|
||||
edge_proc_x(c1, c2, c6, c5);
|
||||
|
||||
edge_proc_y(grid, c0, c1, c2, c3);
|
||||
edge_proc_y(grid, c4, c5, c6, c7);
|
||||
edge_proc_y(c0, c1, c2, c3);
|
||||
edge_proc_y(c4, c5, c6, c7);
|
||||
|
||||
vert_proc(grid, c0, c1, c2, c3, c4, c5, c6, c7);
|
||||
vert_proc(c0, c1, c2, c3, c4, c5, c6, c7);
|
||||
}
|
||||
|
||||
void face_proc_zy(DualGrid &grid, OctreeNode *n0, OctreeNode *n1) {
|
||||
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();
|
||||
@ -1034,20 +967,20 @@ void face_proc_zy(DualGrid &grid, OctreeNode *n0, OctreeNode *n1) {
|
||||
OctreeNode *c6 = n1_has_children ? n1->children[7] : n1;
|
||||
OctreeNode *c7 = n0_has_children ? n0->children[6] : n0;
|
||||
|
||||
face_proc_zy(grid, c0, c1);
|
||||
face_proc_zy(grid, c3, c2);
|
||||
face_proc_zy(grid, c4, c5);
|
||||
face_proc_zy(grid, c7, c6);
|
||||
face_proc_zy(c0, c1);
|
||||
face_proc_zy(c3, c2);
|
||||
face_proc_zy(c4, c5);
|
||||
face_proc_zy(c7, c6);
|
||||
|
||||
edge_proc_y(grid, c0, c1, c2, c3);
|
||||
edge_proc_y(grid, c4, c5, c6, c7);
|
||||
edge_proc_z(grid, c7, c6, c2, c3);
|
||||
edge_proc_z(grid, c4, c5, c1, c0);
|
||||
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(grid, c0, c1, c2, c3, c4, c5, c6, c7);
|
||||
vert_proc(c0, c1, c2, c3, c4, c5, c6, c7);
|
||||
}
|
||||
|
||||
void face_proc_xz(DualGrid &grid, OctreeNode *n0, OctreeNode *n1) {
|
||||
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();
|
||||
@ -1065,20 +998,20 @@ void face_proc_xz(DualGrid &grid, OctreeNode *n0, OctreeNode *n1) {
|
||||
OctreeNode *c6 = n0_has_children ? n0->children[2] : n0;
|
||||
OctreeNode *c7 = n0_has_children ? n0->children[3] : n0;
|
||||
|
||||
face_proc_xz(grid, c4, c0);
|
||||
face_proc_xz(grid, c5, c1);
|
||||
face_proc_xz(grid, c7, c3);
|
||||
face_proc_xz(grid, c6, c2);
|
||||
face_proc_xz(c4, c0);
|
||||
face_proc_xz(c5, c1);
|
||||
face_proc_xz(c7, c3);
|
||||
face_proc_xz(c6, c2);
|
||||
|
||||
edge_proc_x(grid, c0, c3, c7, c4);
|
||||
edge_proc_x(grid, c1, c2, c6, c5);
|
||||
edge_proc_z(grid, c7, c6, c2, c3);
|
||||
edge_proc_z(grid, c4, c5, c1, c0);
|
||||
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(grid, c0, c1, c2, c3, c4, c5, c6, c7);
|
||||
vert_proc(c0, c1, c2, c3, c4, c5, c6, c7);
|
||||
}
|
||||
|
||||
void node_proc(DualGrid &grid, OctreeNode *node) {
|
||||
void DualGridGenerator::node_proc(OctreeNode *node) {
|
||||
|
||||
if (!node->has_children()) {
|
||||
return;
|
||||
@ -1087,34 +1020,58 @@ void node_proc(DualGrid &grid, OctreeNode *node) {
|
||||
OctreeNode **children = node->children;
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
node_proc(grid, children[i]);
|
||||
node_proc(children[i]);
|
||||
}
|
||||
|
||||
face_proc_xy(grid, children[0], children[3]);
|
||||
face_proc_xy(grid, children[1], children[2]);
|
||||
face_proc_xy(grid, children[4], children[7]);
|
||||
face_proc_xy(grid, children[5], children[6]);
|
||||
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(grid, children[0], children[1]);
|
||||
face_proc_zy(grid, children[3], children[2]);
|
||||
face_proc_zy(grid, children[4], children[5]);
|
||||
face_proc_zy(grid, children[7], 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(grid, children[4], children[0]);
|
||||
face_proc_xz(grid, children[5], children[1]);
|
||||
face_proc_xz(grid, children[7], children[3]);
|
||||
face_proc_xz(grid, children[6], children[2]);
|
||||
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(grid, children[0], children[3], children[7], children[4]);
|
||||
edge_proc_x(grid, children[1], children[2], children[6], children[5]);
|
||||
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(grid, children[0], children[1], children[2], children[3]);
|
||||
edge_proc_y(grid, children[4], children[5], children[6], children[7]);
|
||||
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(grid, children[7], children[6], children[2], children[3]);
|
||||
edge_proc_z(grid, children[4], children[5], children[1], children[0]);
|
||||
edge_proc_z(children[7], children[6], children[2], children[3]);
|
||||
edge_proc_z(children[4], children[5], children[1], children[0]);
|
||||
|
||||
vert_proc(grid, children[0], children[1], children[2], children[3], children[4], children[5], children[6], children[7]);
|
||||
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.value - SURFACE_ISO_LEVEL) <= FLT_EPSILON) {
|
||||
out_normal = val0.gradient;
|
||||
return v0;
|
||||
}
|
||||
|
||||
if (Math::abs(val1.value - SURFACE_ISO_LEVEL) <= FLT_EPSILON) {
|
||||
out_normal = val1.gradient;
|
||||
return v1;
|
||||
}
|
||||
|
||||
if (Math::abs(val1.value - val0.value) <= FLT_EPSILON) {
|
||||
out_normal = val0.gradient;
|
||||
return v0;
|
||||
}
|
||||
|
||||
float mu = (SURFACE_ISO_LEVEL - val0.value) / (val1.value - val0.value);
|
||||
out_normal = val0.gradient + mu * (val1.gradient - val0.gradient);
|
||||
out_normal.normalize();
|
||||
|
||||
return v0 + mu * (v1 - v0);
|
||||
}
|
||||
|
||||
Ref<ArrayMesh> polygonize_dual_grid(const DualGrid &grid, const VoxelBuffer &voxels, bool wireframe, MeshBuilder &mesh_builder) {
|
||||
@ -1134,7 +1091,7 @@ Ref<ArrayMesh> polygonize_dual_grid(const DualGrid &grid, const VoxelBuffer &vox
|
||||
} else {
|
||||
values[i] = get_interpolated_hermite_value(voxels, corners[i]);
|
||||
}
|
||||
if (values[i].value >= ISO_LEVEL) {
|
||||
if (values[i].value >= SURFACE_ISO_LEVEL) {
|
||||
case_index |= 1 << i;
|
||||
}
|
||||
}
|
||||
@ -1206,48 +1163,55 @@ Ref<ArrayMesh> polygonize_dual_grid(const DualGrid &grid, const VoxelBuffer &vox
|
||||
return mesh_builder.commit(wireframe);
|
||||
}
|
||||
|
||||
Ref<ArrayMesh> polygonize(const VoxelBuffer &voxels, float geometric_error, VoxelMesherDMC::Mode mode, MeshBuilder &mesh_builder) {
|
||||
} // namespace dmc
|
||||
|
||||
Ref<ArrayMesh> VoxelMesherDMC::build_mesh(const VoxelBuffer &voxels, real_t geometric_error, Mode mode) {
|
||||
|
||||
// Requirements:
|
||||
// - Voxel data must be padded
|
||||
// - Gradients must be precalculated
|
||||
// - The non-padded area size is cubic and power of two
|
||||
|
||||
int padding = 1;
|
||||
int chunk_size = CHUNK_SIZE;
|
||||
// TODO Don't hardcode size. It must be next lower power of two
|
||||
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));
|
||||
|
||||
CRASH_COND(voxels.get_size().x < chunk_size + padding * 2);
|
||||
CRASH_COND(voxels.get_size().y < chunk_size + padding * 2);
|
||||
CRASH_COND(voxels.get_size().z < chunk_size + padding * 2);
|
||||
ERR_FAIL_COND_V(voxels.get_size().x < chunk_size + padding * 2, Ref<ArrayMesh>());
|
||||
ERR_FAIL_COND_V(voxels.get_size().y < chunk_size + padding * 2, Ref<ArrayMesh>());
|
||||
ERR_FAIL_COND_V(voxels.get_size().z < chunk_size + padding * 2, Ref<ArrayMesh>());
|
||||
|
||||
OctreeNode root;
|
||||
root.origin = Vector3i();
|
||||
dmc::OctreeNode root;
|
||||
root.origin = Vector3i(padding);
|
||||
root.size = chunk_size;
|
||||
generate_octree_top_down(&root, voxels, geometric_error);
|
||||
dmc::generate_octree_top_down(&root, voxels, geometric_error);
|
||||
|
||||
if (mode == VoxelMesherDMC::MODE_DEBUG_OCTREE) {
|
||||
return generate_debug_octree_mesh(&root);
|
||||
return dmc::generate_debug_octree_mesh(&root);
|
||||
}
|
||||
|
||||
DualGrid grid;
|
||||
node_proc(grid, &root);
|
||||
dmc::DualGrid grid;
|
||||
dmc::DualGridGenerator dual_grid_generator(grid, root.size);
|
||||
dual_grid_generator.node_proc(&root);
|
||||
// TODO Handle non-subdivided octree
|
||||
if (mode == VoxelMesherDMC::MODE_DEBUG_DUAL_GRID) {
|
||||
return generate_debug_dual_grid_mesh(grid);
|
||||
return dmc::generate_debug_dual_grid_mesh(grid);
|
||||
}
|
||||
|
||||
Ref<ArrayMesh> mesh = polygonize_dual_grid(grid, voxels, mode == VoxelMesherDMC::MODE_WIREFRAME, mesh_builder);
|
||||
Ref<ArrayMesh> mesh = dmc::polygonize_dual_grid(grid, voxels, mode == VoxelMesherDMC::MODE_WIREFRAME, _mesh_builder);
|
||||
// TODO Marching squares skirts
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
} // namespace dmc
|
||||
|
||||
Ref<ArrayMesh> VoxelMesherDMC::build_mesh(Ref<VoxelBuffer> voxels, real_t geometric_error, Mode mode) {
|
||||
Ref<ArrayMesh> VoxelMesherDMC::_build_mesh_b(Ref<VoxelBuffer> voxels, real_t geometric_error, Mode mode) {
|
||||
ERR_FAIL_COND_V(voxels.is_null(), Ref<ArrayMesh>());
|
||||
return dmc::polygonize(**voxels, geometric_error, mode, _mesh_builder);
|
||||
return build_mesh(**voxels, geometric_error, mode);
|
||||
}
|
||||
|
||||
void VoxelMesherDMC::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("build_mesh", "voxel_buffer", "geometric_error", "mode"), &VoxelMesherDMC::build_mesh, DEFVAL(MODE_NORMAL));
|
||||
ClassDB::bind_method(D_METHOD("build_mesh", "voxel_buffer", "geometric_error", "mode"), &VoxelMesherDMC::_build_mesh_b, DEFVAL(MODE_NORMAL));
|
||||
|
||||
BIND_ENUM_CONSTANT(MODE_NORMAL);
|
||||
BIND_ENUM_CONSTANT(MODE_WIREFRAME);
|
||||
|
@ -15,10 +15,11 @@ public:
|
||||
MODE_DEBUG_DUAL_GRID
|
||||
};
|
||||
|
||||
Ref<ArrayMesh> build_mesh(Ref<VoxelBuffer> voxels, real_t geometric_error, Mode mode);
|
||||
Ref<ArrayMesh> build_mesh(const VoxelBuffer &voxels, real_t geometric_error, Mode mode);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
Ref<ArrayMesh> _build_mesh_b(Ref<VoxelBuffer> voxels, real_t geometric_error, Mode mode);
|
||||
|
||||
private:
|
||||
dmc::MeshBuilder _mesh_builder;
|
||||
|
18
utility.h
18
utility.h
@ -53,4 +53,22 @@ void raw_copy_to(PoolVector<T> &to, const std::vector<T> &from) {
|
||||
memcpy(w.ptr(), from.data(), from.size() * sizeof(T));
|
||||
}
|
||||
|
||||
// Trilinear interpolation between corner values of a cube.
|
||||
// Cube points respect the same position as in octree_utility.h
|
||||
template <typename T>
|
||||
inline T interpolate(const T v0, const T v1, const T v2, const T v3, const T v4, const T v5, const T v6, const T v7, Vector3 position) {
|
||||
|
||||
const float one_min_x = 1.f - position.x;
|
||||
const float one_min_y = 1.f - position.y;
|
||||
const float one_min_z = 1.f - position.z;
|
||||
const float one_min_x_one_min_y = one_min_x * one_min_y;
|
||||
const float x_one_min_y = position.x * one_min_y;
|
||||
|
||||
T res = one_min_z * (v0 * one_min_x_one_min_y + v1 * x_one_min_y + v4 * one_min_x * position.y);
|
||||
res += position.z * (v3 * one_min_x_one_min_y + v2 * x_one_min_y + v7 * one_min_x * position.y);
|
||||
res += position.x * position.y * (v5 * one_min_z + v6 * position.z);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif // HEADER_VOXEL_UTILITY_H
|
||||
|
@ -39,8 +39,9 @@ void VoxelBuffer::clear() {
|
||||
|
||||
void VoxelBuffer::clear_channel(unsigned int channel_index, int clear_value) {
|
||||
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||
if (_channels[channel_index].data)
|
||||
if (_channels[channel_index].data) {
|
||||
delete_channel(channel_index);
|
||||
}
|
||||
_channels[channel_index].defval = clear_value;
|
||||
}
|
||||
|
||||
@ -81,8 +82,9 @@ void VoxelBuffer::set_voxel(int value, int x, int y, int z, unsigned int channel
|
||||
// This version does not cause errors if out of bounds. Use only if it's okay to be outside.
|
||||
void VoxelBuffer::try_set_voxel(int x, int y, int z, int value, unsigned int channel_index) {
|
||||
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||
if (!validate_pos(x, y, z))
|
||||
if (!validate_pos(x, y, z)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Channel &channel = _channels[channel_index];
|
||||
|
||||
@ -114,8 +116,9 @@ void VoxelBuffer::fill(int defval, unsigned int channel_index) {
|
||||
channel.defval = defval;
|
||||
return;
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
create_channel_noinit(channel_index, _size);
|
||||
}
|
||||
|
||||
unsigned int volume = get_volume();
|
||||
memset(channel.data, defval, volume);
|
||||
@ -130,15 +133,17 @@ void VoxelBuffer::fill_area(int defval, Vector3i min, Vector3i max, unsigned int
|
||||
max.clamp_to(Vector3i(0, 0, 0), _size + Vector3i(1, 1, 1));
|
||||
Vector3i area_size = max - min;
|
||||
|
||||
if (area_size.x == 0 || area_size.y == 0 || area_size.z == 0)
|
||||
if (area_size.x == 0 || area_size.y == 0 || area_size.z == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Channel &channel = _channels[channel_index];
|
||||
if (channel.data == NULL) {
|
||||
if (channel.defval == defval)
|
||||
if (channel.defval == defval) {
|
||||
return;
|
||||
else
|
||||
} else {
|
||||
create_channel(channel_index, _size);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3i pos;
|
||||
@ -156,9 +161,10 @@ bool VoxelBuffer::is_uniform(unsigned int channel_index) const {
|
||||
ERR_FAIL_INDEX_V(channel_index, MAX_CHANNELS, true);
|
||||
|
||||
const Channel &channel = _channels[channel_index];
|
||||
if (channel.data == NULL)
|
||||
if (channel.data == NULL) {
|
||||
// Channel has been optimized
|
||||
return true;
|
||||
}
|
||||
|
||||
// Channel isn't optimized, so must look at each voxel
|
||||
uint8_t voxel = channel.data[0];
|
||||
@ -172,6 +178,7 @@ bool VoxelBuffer::is_uniform(unsigned int channel_index) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO Rename compress_uniform_channels()
|
||||
void VoxelBuffer::optimize() {
|
||||
for (unsigned int i = 0; i < MAX_CHANNELS; ++i) {
|
||||
if (_channels[i].data && is_uniform(i)) {
|
||||
@ -272,20 +279,13 @@ void VoxelBuffer::delete_channel(int i) {
|
||||
channel.data = NULL;
|
||||
}
|
||||
|
||||
void VoxelBuffer::compute_gradients(unsigned int isolevel_channel, unsigned int first_gradient_channel) {
|
||||
void VoxelBuffer::compute_gradients() {
|
||||
|
||||
ERR_FAIL_COND(isolevel_channel >= MAX_CHANNELS);
|
||||
ERR_FAIL_COND(first_gradient_channel + 2 >= MAX_CHANNELS);
|
||||
const Channel &iso_channel = _channels[CHANNEL_ISOLEVEL];
|
||||
|
||||
int gradient_x_channel = first_gradient_channel;
|
||||
int gradient_y_channel = first_gradient_channel + 1;
|
||||
int gradient_z_channel = first_gradient_channel + 2;
|
||||
|
||||
ERR_FAIL_COND(gradient_x_channel == isolevel_channel);
|
||||
ERR_FAIL_COND(gradient_y_channel == isolevel_channel);
|
||||
ERR_FAIL_COND(gradient_z_channel == isolevel_channel);
|
||||
|
||||
const Channel &iso_channel = _channels[isolevel_channel];
|
||||
const int gradient_x_channel = CHANNEL_GRADIENT_X;
|
||||
const int gradient_y_channel = CHANNEL_GRADIENT_Y;
|
||||
const int gradient_z_channel = CHANNEL_GRADIENT_Z;
|
||||
|
||||
if (iso_channel.data == nullptr) {
|
||||
|
||||
@ -310,22 +310,29 @@ void VoxelBuffer::compute_gradients(unsigned int isolevel_channel, unsigned int
|
||||
Channel &gy_channel = _channels[gradient_y_channel];
|
||||
Channel &gz_channel = _channels[gradient_z_channel];
|
||||
|
||||
int max_z = _size.z - 1;
|
||||
int max_x = _size.x - 1;
|
||||
int max_y = _size.y - 1;
|
||||
const int padding = 1;
|
||||
|
||||
int lookup_left = -_size.x;
|
||||
int lookup_right = -lookup_left;
|
||||
int lookup_back = -_size.z * _size.x;
|
||||
int lookup_front = -lookup_back;
|
||||
int lookup_down = -1;
|
||||
int lookup_up = -lookup_down;
|
||||
const int min_x = padding;
|
||||
const int min_y = padding;
|
||||
const int min_z = padding;
|
||||
|
||||
for (int z = 1; z < max_z; ++z) {
|
||||
for (int x = 1; x < max_x; ++x) {
|
||||
for (int y = 1; y < max_y; ++y) {
|
||||
const int max_z = _size.z - padding;
|
||||
const int max_x = _size.x - padding;
|
||||
const int max_y = _size.y - padding;
|
||||
|
||||
int i = index(x, y, z);
|
||||
const int lookup_left = -_size.x;
|
||||
const int lookup_right = -lookup_left;
|
||||
const int lookup_back = -_size.z * _size.x;
|
||||
const int lookup_front = -lookup_back;
|
||||
const int lookup_down = -1;
|
||||
const int lookup_up = -lookup_down;
|
||||
|
||||
for (int z = min_z; z < max_z; ++z) {
|
||||
for (int x = min_x; x < max_x; ++x) {
|
||||
|
||||
int i = index(x, min_y, z);
|
||||
|
||||
for (int y = min_y; y < max_y; ++y) {
|
||||
|
||||
Vector3 v(
|
||||
byte_to_iso(iso_channel.data[i + lookup_right]) - byte_to_iso(iso_channel.data[i + lookup_left]),
|
||||
@ -337,6 +344,8 @@ void VoxelBuffer::compute_gradients(unsigned int isolevel_channel, unsigned int
|
||||
gx_channel.data[i] = iso_to_byte(v.x);
|
||||
gy_channel.data[i] = iso_to_byte(v.y);
|
||||
gz_channel.data[i] = iso_to_byte(v.z);
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -365,7 +374,17 @@ void VoxelBuffer::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_uniform", "channel"), &VoxelBuffer::is_uniform, DEFVAL(0));
|
||||
ClassDB::bind_method(D_METHOD("optimize"), &VoxelBuffer::optimize);
|
||||
ClassDB::bind_method(D_METHOD("compute_gradients", "isolevel_channel", "first_gradient_channel"), &VoxelBuffer::compute_gradients);
|
||||
ClassDB::bind_method(D_METHOD("compute_gradients"), &VoxelBuffer::compute_gradients);
|
||||
|
||||
BIND_ENUM_CONSTANT(CHANNEL_TYPE);
|
||||
BIND_ENUM_CONSTANT(CHANNEL_ISOLEVEL);
|
||||
BIND_ENUM_CONSTANT(CHANNEL_GRADIENT_X);
|
||||
BIND_ENUM_CONSTANT(CHANNEL_GRADIENT_Y);
|
||||
BIND_ENUM_CONSTANT(CHANNEL_GRADIENT_Z);
|
||||
BIND_ENUM_CONSTANT(CHANNEL_DATA);
|
||||
BIND_ENUM_CONSTANT(CHANNEL_DATA2);
|
||||
BIND_ENUM_CONSTANT(CHANNEL_DATA3);
|
||||
BIND_ENUM_CONSTANT(MAX_CHANNELS);
|
||||
}
|
||||
|
||||
void VoxelBuffer::_copy_from_binding(Ref<VoxelBuffer> other, unsigned int channel) {
|
||||
|
@ -13,8 +13,18 @@ class VoxelBuffer : public Reference {
|
||||
GDCLASS(VoxelBuffer, Reference)
|
||||
|
||||
public:
|
||||
// Arbitrary value, 8 should be enough. Tweak for your needs.
|
||||
static const int MAX_CHANNELS = 8;
|
||||
enum ChannelId {
|
||||
CHANNEL_TYPE = 0,
|
||||
CHANNEL_ISOLEVEL,
|
||||
CHANNEL_GRADIENT_X,
|
||||
CHANNEL_GRADIENT_Y,
|
||||
CHANNEL_GRADIENT_Z,
|
||||
CHANNEL_DATA,
|
||||
CHANNEL_DATA2,
|
||||
CHANNEL_DATA3,
|
||||
// Arbitrary value, 8 should be enough. Tweak for your needs.
|
||||
MAX_CHANNELS
|
||||
};
|
||||
|
||||
// Converts -1..1 float into 0..255 integer
|
||||
static inline int iso_to_byte(real_t iso) {
|
||||
@ -38,7 +48,7 @@ public:
|
||||
void clear();
|
||||
void clear_channel(unsigned int channel_index, int clear_value = 0);
|
||||
|
||||
_FORCE_INLINE_ Vector3i get_size() const { return _size; }
|
||||
_FORCE_INLINE_ const Vector3i &get_size() const { return _size; }
|
||||
|
||||
void set_default_values(uint8_t values[MAX_CHANNELS]);
|
||||
|
||||
@ -72,17 +82,18 @@ public:
|
||||
return (z * _size.z + x) * _size.x + y;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ unsigned int row_index(unsigned int x, unsigned int y, unsigned int z) const {
|
||||
return (z * _size.z + x) * _size.x;
|
||||
}
|
||||
// _FORCE_INLINE_ unsigned int row_index(unsigned int x, unsigned int y, unsigned int z) const {
|
||||
// return (z * _size.z + x) * _size.x;
|
||||
// }
|
||||
|
||||
// TODO Rename get_cells_count()
|
||||
_FORCE_INLINE_ unsigned int get_volume() const {
|
||||
return _size.x * _size.y * _size.z;
|
||||
}
|
||||
|
||||
uint8_t *get_channel_raw(unsigned int channel_index) const;
|
||||
|
||||
void compute_gradients(unsigned int isolevel_index, unsigned int first_gradient_channel);
|
||||
void compute_gradients();
|
||||
|
||||
private:
|
||||
void create_channel_noinit(int i, Vector3i size);
|
||||
@ -125,4 +136,6 @@ private:
|
||||
Vector3i _size;
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(VoxelBuffer::ChannelId)
|
||||
|
||||
#endif // VOXEL_BUFFER_H
|
||||
|
Loading…
Reference in New Issue
Block a user