mirror of
https://github.com/godotengine/godot.git
synced 2024-11-27 09:16:35 +08:00
Merge pull request #90701 from permelin/improve-delaunay3d-triangulation
Delaunay3D/LightmapGI: Improve triangulation
This commit is contained in:
commit
4b6629978e
@ -46,7 +46,8 @@ class Delaunay3D {
|
|||||||
struct Simplex;
|
struct Simplex;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
ACCEL_GRID_SIZE = 16
|
ACCEL_GRID_SIZE = 16,
|
||||||
|
QUANTIZATION_MAX = 1 << 16 // A power of two smaller than the 23 bit significand of a float.
|
||||||
};
|
};
|
||||||
struct GridPos {
|
struct GridPos {
|
||||||
Vector3i pos;
|
Vector3i pos;
|
||||||
@ -173,38 +174,25 @@ class Delaunay3D {
|
|||||||
|
|
||||||
R128 radius2 = rel2_x * rel2_x + rel2_y * rel2_y + rel2_z * rel2_z;
|
R128 radius2 = rel2_x * rel2_x + rel2_y * rel2_y + rel2_z * rel2_z;
|
||||||
|
|
||||||
return radius2 < (p_simplex.circum_r2 - R128(0.00001));
|
return radius2 < (p_simplex.circum_r2 - R128(0.0000000001));
|
||||||
|
// When this tolerance is too big, it can result in overlapping simplices.
|
||||||
|
// When it's too small, large amounts of planar simplices are created.
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool simplex_is_coplanar(const Vector3 *p_points, const Simplex &p_simplex) {
|
static bool simplex_is_coplanar(const Vector3 *p_points, const Simplex &p_simplex) {
|
||||||
Plane p(p_points[p_simplex.points[0]], p_points[p_simplex.points[1]], p_points[p_simplex.points[2]]);
|
// Checking every possible distance like this is overkill, but only checking
|
||||||
if (ABS(p.distance_to(p_points[p_simplex.points[3]])) < CMP_EPSILON) {
|
// one is not enough. If the simplex is almost planar then the vectors p1-p2
|
||||||
return true;
|
// and p1-p3 can be practically collinear, which makes Plane unreliable.
|
||||||
|
for (uint32_t i = 0; i < 4; i++) {
|
||||||
|
Plane p(p_points[p_simplex.points[i]], p_points[p_simplex.points[(i + 1) % 4]], p_points[p_simplex.points[(i + 2) % 4]]);
|
||||||
|
// This tolerance should not be smaller than the one used with
|
||||||
|
// Plane::has_point() when creating the LightmapGI probe BSP tree.
|
||||||
|
if (ABS(p.distance_to(p_points[p_simplex.points[(i + 3) % 4]])) < 0.001) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Projection cm;
|
return false;
|
||||||
|
|
||||||
cm.columns[0][0] = p_points[p_simplex.points[0]].x;
|
|
||||||
cm.columns[0][1] = p_points[p_simplex.points[1]].x;
|
|
||||||
cm.columns[0][2] = p_points[p_simplex.points[2]].x;
|
|
||||||
cm.columns[0][3] = p_points[p_simplex.points[3]].x;
|
|
||||||
|
|
||||||
cm.columns[1][0] = p_points[p_simplex.points[0]].y;
|
|
||||||
cm.columns[1][1] = p_points[p_simplex.points[1]].y;
|
|
||||||
cm.columns[1][2] = p_points[p_simplex.points[2]].y;
|
|
||||||
cm.columns[1][3] = p_points[p_simplex.points[3]].y;
|
|
||||||
|
|
||||||
cm.columns[2][0] = p_points[p_simplex.points[0]].z;
|
|
||||||
cm.columns[2][1] = p_points[p_simplex.points[1]].z;
|
|
||||||
cm.columns[2][2] = p_points[p_simplex.points[2]].z;
|
|
||||||
cm.columns[2][3] = p_points[p_simplex.points[3]].z;
|
|
||||||
|
|
||||||
cm.columns[3][0] = 1.0;
|
|
||||||
cm.columns[3][1] = 1.0;
|
|
||||||
cm.columns[3][2] = 1.0;
|
|
||||||
cm.columns[3][3] = 1.0;
|
|
||||||
|
|
||||||
return ABS(cm.determinant()) <= CMP_EPSILON;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -215,9 +203,10 @@ public:
|
|||||||
static Vector<OutputSimplex> tetrahedralize(const Vector<Vector3> &p_points) {
|
static Vector<OutputSimplex> tetrahedralize(const Vector<Vector3> &p_points) {
|
||||||
uint32_t point_count = p_points.size();
|
uint32_t point_count = p_points.size();
|
||||||
Vector3 *points = (Vector3 *)memalloc(sizeof(Vector3) * (point_count + 4));
|
Vector3 *points = (Vector3 *)memalloc(sizeof(Vector3) * (point_count + 4));
|
||||||
|
const Vector3 *src_points = p_points.ptr();
|
||||||
|
Vector3 proportions;
|
||||||
|
|
||||||
{
|
{
|
||||||
const Vector3 *src_points = p_points.ptr();
|
|
||||||
AABB rect;
|
AABB rect;
|
||||||
for (uint32_t i = 0; i < point_count; i++) {
|
for (uint32_t i = 0; i < point_count; i++) {
|
||||||
Vector3 point = src_points[i];
|
Vector3 point = src_points[i];
|
||||||
@ -226,17 +215,25 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
rect.expand_to(point);
|
rect.expand_to(point);
|
||||||
}
|
}
|
||||||
points[i] = point;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
real_t longest_axis = rect.size[rect.get_longest_axis_index()];
|
||||||
|
proportions = Vector3(longest_axis, longest_axis, longest_axis) / rect.size;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < point_count; i++) {
|
for (uint32_t i = 0; i < point_count; i++) {
|
||||||
points[i] = (points[i] - rect.position) / rect.size;
|
// Scale points to the unit cube to better utilize R128 precision
|
||||||
|
// and quantize to stabilize triangulation over a wide range of
|
||||||
|
// distances.
|
||||||
|
points[i] = Vector3(Vector3i((src_points[i] - rect.position) / longest_axis * QUANTIZATION_MAX)) / QUANTIZATION_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
const real_t delta_max = Math::sqrt(2.0) * 20.0;
|
const real_t delta_max = Math::sqrt(2.0) * 100.0;
|
||||||
Vector3 center = Vector3(0.5, 0.5, 0.5);
|
Vector3 center = Vector3(0.5, 0.5, 0.5);
|
||||||
|
|
||||||
// any simplex that contains everything is good
|
// The larger the root simplex is, the more likely it is that the
|
||||||
|
// triangulation is convex. If it's not absolutely huge, there can
|
||||||
|
// be missing simplices that are not created for the outermost faces
|
||||||
|
// of the point cloud if the point density is very low there.
|
||||||
points[point_count + 0] = center + Vector3(0, 1, 0) * delta_max;
|
points[point_count + 0] = center + Vector3(0, 1, 0) * delta_max;
|
||||||
points[point_count + 1] = center + Vector3(0, -1, 1) * delta_max;
|
points[point_count + 1] = center + Vector3(0, -1, 1) * delta_max;
|
||||||
points[point_count + 2] = center + Vector3(1, -1, -1) * delta_max;
|
points[point_count + 2] = center + Vector3(1, -1, -1) * delta_max;
|
||||||
@ -271,7 +268,7 @@ public:
|
|||||||
for (uint32_t i = 0; i < point_count; i++) {
|
for (uint32_t i = 0; i < point_count; i++) {
|
||||||
bool unique = true;
|
bool unique = true;
|
||||||
for (uint32_t j = i + 1; j < point_count; j++) {
|
for (uint32_t j = i + 1; j < point_count; j++) {
|
||||||
if (points[i].is_equal_approx(points[j])) {
|
if (points[i] == points[j]) {
|
||||||
unique = false;
|
unique = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -280,7 +277,7 @@ public:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3i grid_pos = Vector3i(points[i] * ACCEL_GRID_SIZE);
|
Vector3i grid_pos = Vector3i(points[i] * proportions * ACCEL_GRID_SIZE);
|
||||||
grid_pos = grid_pos.clamp(Vector3i(), Vector3i(ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1));
|
grid_pos = grid_pos.clamp(Vector3i(), Vector3i(ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1));
|
||||||
|
|
||||||
for (List<Simplex *>::Element *E = acceleration_grid[grid_pos.x][grid_pos.y][grid_pos.z].front(); E;) {
|
for (List<Simplex *>::Element *E = acceleration_grid[grid_pos.x][grid_pos.y][grid_pos.z].front(); E;) {
|
||||||
@ -300,6 +297,9 @@ public:
|
|||||||
Triangle t = Triangle(simplex->points[triangle_order[k][0]], simplex->points[triangle_order[k][1]], simplex->points[triangle_order[k][2]]);
|
Triangle t = Triangle(simplex->points[triangle_order[k][0]], simplex->points[triangle_order[k][1]], simplex->points[triangle_order[k][2]]);
|
||||||
uint32_t *p = triangles_inserted.lookup_ptr(t);
|
uint32_t *p = triangles_inserted.lookup_ptr(t);
|
||||||
if (p) {
|
if (p) {
|
||||||
|
// This Delaunay implementation uses the Bowyer-Watson algorithm.
|
||||||
|
// The rule is that you don't reuse any triangles that were
|
||||||
|
// shared by any of the retriangulated simplices.
|
||||||
triangles[*p].bad = true;
|
triangles[*p].bad = true;
|
||||||
} else {
|
} else {
|
||||||
triangles_inserted.insert(t, triangles.size());
|
triangles_inserted.insert(t, triangles.size());
|
||||||
@ -307,7 +307,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//remove simplex and continue
|
|
||||||
simplex_list.erase(simplex->SE);
|
simplex_list.erase(simplex->SE);
|
||||||
|
|
||||||
for (const GridPos &gp : simplex->grid_positions) {
|
for (const GridPos &gp : simplex->grid_positions) {
|
||||||
@ -334,8 +333,8 @@ public:
|
|||||||
|
|
||||||
const real_t radius2 = Math::sqrt(double(new_simplex->circum_r2)) + 0.0001;
|
const real_t radius2 = Math::sqrt(double(new_simplex->circum_r2)) + 0.0001;
|
||||||
Vector3 extents = Vector3(radius2, radius2, radius2);
|
Vector3 extents = Vector3(radius2, radius2, radius2);
|
||||||
Vector3i from = Vector3i((center - extents) * ACCEL_GRID_SIZE);
|
Vector3i from = Vector3i((center - extents) * proportions * ACCEL_GRID_SIZE);
|
||||||
Vector3i to = Vector3i((center + extents) * ACCEL_GRID_SIZE);
|
Vector3i to = Vector3i((center + extents) * proportions * ACCEL_GRID_SIZE);
|
||||||
from = from.clamp(Vector3i(), Vector3i(ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1));
|
from = from.clamp(Vector3i(), Vector3i(ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1));
|
||||||
to = to.clamp(Vector3i(), Vector3i(ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1));
|
to = to.clamp(Vector3i(), Vector3i(ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1));
|
||||||
|
|
||||||
@ -370,7 +369,7 @@ public:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (invalid || simplex_is_coplanar(points, *simplex)) {
|
if (invalid || simplex_is_coplanar(src_points, *simplex)) {
|
||||||
memdelete(simplex);
|
memdelete(simplex);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user