summaryrefslogtreecommitdiffstats
path: root/scene/3d/lightmap_gi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/3d/lightmap_gi.cpp')
-rw-r--r--scene/3d/lightmap_gi.cpp76
1 files changed, 66 insertions, 10 deletions
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index 86ff6d15dd..ef492a6994 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -397,7 +397,10 @@ int LightmapGI::_bsp_get_simplex_side(const Vector<Vector3> &p_points, const Loc
const BSPSimplex &s = p_simplices[p_simplex];
for (int i = 0; i < 4; i++) {
const Vector3 v = p_points[s.vertices[i]];
- if (p_plane.has_point(v)) {
+ // The tolerance used here comes from experiments on scenes up to
+ // 1000x1000x100 meters. If it's any smaller, some simplices will
+ // appear to self-intersect due to a lack of precision in Plane.
+ if (p_plane.has_point(v, 1.0 / (1 << 13))) {
// Coplanar.
} else if (p_plane.is_point_over(v)) {
over++;
@@ -419,7 +422,8 @@ int LightmapGI::_bsp_get_simplex_side(const Vector<Vector3> &p_points, const Loc
//#define DEBUG_BSP
int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const LocalVector<Plane> &p_planes, LocalVector<int32_t> &planes_tested, const LocalVector<BSPSimplex> &p_simplices, const LocalVector<int32_t> &p_simplex_indices, LocalVector<BSPNode> &bsp_nodes) {
- //if we reach here, it means there is more than one simplex
+ ERR_FAIL_COND_V(p_simplex_indices.size() < 2, -1);
+
int32_t node_index = (int32_t)bsp_nodes.size();
bsp_nodes.push_back(BSPNode());
@@ -477,13 +481,14 @@ int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const Loc
float score = 0; //by default, score is 0 (worst)
if (over_count > 0) {
- //give score mainly based on ratio (under / over), this means that this plane is splitting simplices a lot, but its balanced
- score = float(under_count) / over_count;
+ // Simplices that are intersected by the plane are moved into both the over
+ // and under subtrees which makes the entire tree deeper, so the best plane
+ // will have the least intersections while separating the simplices evenly.
+ float balance = float(under_count) / over_count;
+ float separation = float(over_count + under_count) / p_simplex_indices.size();
+ score = balance * separation * separation;
}
- //adjusting priority over least splits, probably not a great idea
- //score *= Math::sqrt(float(over_count + under_count) / p_simplex_indices.size()); //also multiply score
-
if (score > best_plane_score) {
best_plane = plane;
best_plane_score = score;
@@ -491,6 +496,44 @@ int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const Loc
}
}
+ // We often end up with two (or on rare occasions, three) simplices that are
+ // either disjoint or share one vertex and don't have a separating plane
+ // among their faces. The fallback is to loop through new planes created
+ // with one vertex of the first simplex and two vertices of the second until
+ // we find a winner.
+ if (best_plane_score == 0) {
+ const BSPSimplex &simplex0 = p_simplices[p_simplex_indices[0]];
+ const BSPSimplex &simplex1 = p_simplices[p_simplex_indices[1]];
+
+ for (uint32_t i = 0; i < 4 && !best_plane_score; i++) {
+ Vector3 v0 = p_points[simplex0.vertices[i]];
+ for (uint32_t j = 0; j < 3 && !best_plane_score; j++) {
+ if (simplex0.vertices[i] == simplex1.vertices[j]) {
+ break;
+ }
+ Vector3 v1 = p_points[simplex1.vertices[j]];
+ for (uint32_t k = j + 1; k < 4; k++) {
+ if (simplex0.vertices[i] == simplex1.vertices[k]) {
+ break;
+ }
+ Vector3 v2 = p_points[simplex1.vertices[k]];
+
+ Plane plane = Plane(v0, v1, v2);
+ if (plane == Plane()) { // When v0, v1, and v2 are collinear, they can't form a plane.
+ continue;
+ }
+ int32_t side0 = _bsp_get_simplex_side(p_points, p_simplices, plane, p_simplex_indices[0]);
+ int32_t side1 = _bsp_get_simplex_side(p_points, p_simplices, plane, p_simplex_indices[1]);
+ if ((side0 == 1 && side1 == -1) || (side0 == -1 && side1 == 1)) {
+ best_plane = plane;
+ best_plane_score = 1.0;
+ break;
+ }
+ }
+ }
+ }
+ }
+
LocalVector<int32_t> indices_over;
LocalVector<int32_t> indices_under;
@@ -515,8 +558,6 @@ int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const Loc
#endif
if (best_plane_score < 0.0 || indices_over.size() == p_simplex_indices.size() || indices_under.size() == p_simplex_indices.size()) {
- ERR_FAIL_COND_V(p_simplex_indices.size() <= 1, 0); //should not happen, this is a bug
-
// Failed to separate the tetrahedrons using planes
// this means Delaunay broke at some point.
// Luckily, because we are using tetrahedrons, we can resort to
@@ -1061,7 +1102,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
}
}
- Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, denoiser_strength, bounces, bounce_indirect_energy, bias, max_texture_size, directional, use_texture_for_bounces, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud, exposure_normalization);
+ Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, denoiser_strength, denoiser_range, bounces, bounce_indirect_energy, bias, max_texture_size, directional, use_texture_for_bounces, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud, exposure_normalization);
if (bake_err == Lightmapper::BAKE_ERROR_LIGHTMAP_TOO_SMALL) {
return BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL;
@@ -1409,6 +1450,14 @@ float LightmapGI::get_denoiser_strength() const {
return denoiser_strength;
}
+void LightmapGI::set_denoiser_range(int p_denoiser_range) {
+ denoiser_range = p_denoiser_range;
+}
+
+int LightmapGI::get_denoiser_range() const {
+ return denoiser_range;
+}
+
void LightmapGI::set_directional(bool p_enable) {
directional = p_enable;
}
@@ -1552,6 +1601,9 @@ void LightmapGI::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "denoiser_strength" && !use_denoiser) {
p_property.usage = PROPERTY_USAGE_NONE;
}
+ if (p_property.name == "denoiser_range" && !use_denoiser) {
+ p_property.usage = PROPERTY_USAGE_NONE;
+ }
}
void LightmapGI::_bind_methods() {
@@ -1597,6 +1649,9 @@ void LightmapGI::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_denoiser_strength", "denoiser_strength"), &LightmapGI::set_denoiser_strength);
ClassDB::bind_method(D_METHOD("get_denoiser_strength"), &LightmapGI::get_denoiser_strength);
+ ClassDB::bind_method(D_METHOD("set_denoiser_range", "denoiser_range"), &LightmapGI::set_denoiser_range);
+ ClassDB::bind_method(D_METHOD("get_denoiser_range"), &LightmapGI::get_denoiser_range);
+
ClassDB::bind_method(D_METHOD("set_interior", "enable"), &LightmapGI::set_interior);
ClassDB::bind_method(D_METHOD("is_interior"), &LightmapGI::is_interior);
@@ -1620,6 +1675,7 @@ void LightmapGI::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_interior", "is_interior");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_denoiser"), "set_use_denoiser", "is_using_denoiser");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "denoiser_strength", PROPERTY_HINT_RANGE, "0.001,0.2,0.001,or_greater"), "set_denoiser_strength", "get_denoiser_strength");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "denoiser_range", PROPERTY_HINT_RANGE, "1,20"), "set_denoiser_range", "get_denoiser_range");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bias", PROPERTY_HINT_RANGE, "0.00001,0.1,0.00001,or_greater"), "set_bias", "get_bias");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texel_scale", PROPERTY_HINT_RANGE, "0.01,100.0,0.01"), "set_texel_scale", "get_texel_scale");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_texture_size", PROPERTY_HINT_RANGE, "2048,16384,1"), "set_max_texture_size", "get_max_texture_size");