Browse Source

Make navmesh rasterization errors more lenient

Make navmesh rasterization on the navigation regions and map more lenient by starting out with a lower internal cell scale by default and changing the merge error to just warning that can be toggled.
smix8 2 weeks ago
parent
commit
19df15f1dc

+ 8 - 0
core/config/project_settings.cpp

@@ -1733,6 +1733,14 @@ ProjectSettings::ProjectSettings() {
 	GLOBAL_DEF("navigation/baking/thread_model/baking_use_multiple_threads", true);
 	GLOBAL_DEF("navigation/baking/thread_model/baking_use_high_priority_threads", true);
 #endif // !defined(NAVIGATION_2D_DISABLED) || !defined(NAVIGATION_3D_DISABLED)
+#ifndef NAVIGATION_2D_DISABLED
+	GLOBAL_DEF("navigation/2d/warnings/navmesh_edge_merge_errors", true);
+	GLOBAL_DEF("navigation/2d/warnings/navmesh_cell_size_mismatch", true);
+#endif // NAVIGATION_2D_DISABLED
+#ifndef NAVIGATION_3D_DISABLED
+	GLOBAL_DEF("navigation/3d/warnings/navmesh_edge_merge_errors", true);
+	GLOBAL_DEF("navigation/3d/warnings/navmesh_cell_size_mismatch", true);
+#endif // NAVIGATION_3D_DISABLED
 
 	ProjectSettings::get_singleton()->add_hidden_prefix("input/");
 }

+ 12 - 0
doc/classes/ProjectSettings.xml

@@ -2314,6 +2314,12 @@
 		<member name="navigation/2d/use_edge_connections" type="bool" setter="" getter="" default="true">
 			If enabled 2D navigation regions will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. This setting only affects World2D default navigation maps.
 		</member>
+		<member name="navigation/2d/warnings/navmesh_cell_size_mismatch" type="bool" setter="" getter="" default="true">
+			If [code]true[/code], the navigation system will print warnings when a navigation mesh with a small cell size is used on a navigation map with a larger size as this commonly causes rasterization errors.
+		</member>
+		<member name="navigation/2d/warnings/navmesh_edge_merge_errors" type="bool" setter="" getter="" default="true">
+			If [code]true[/code], the navigation system will print warnings about navigation mesh edge merge errors occurring in navigation regions or maps.
+		</member>
 		<member name="navigation/3d/default_cell_height" type="float" setter="" getter="" default="0.25">
 			Default cell height for 3D navigation maps. See [method NavigationServer3D.map_set_cell_height].
 		</member>
@@ -2335,6 +2341,12 @@
 		<member name="navigation/3d/use_edge_connections" type="bool" setter="" getter="" default="true">
 			If enabled 3D navigation regions will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. This setting only affects World3D default navigation maps.
 		</member>
+		<member name="navigation/3d/warnings/navmesh_cell_size_mismatch" type="bool" setter="" getter="" default="true">
+			If [code]true[/code], the navigation system will print warnings when a navigation mesh with a small cell size (or in 3D height) is used on a navigation map with a larger size as this commonly causes rasterization errors.
+		</member>
+		<member name="navigation/3d/warnings/navmesh_edge_merge_errors" type="bool" setter="" getter="" default="true">
+			If [code]true[/code], the navigation system will print warnings about navigation mesh edge merge errors occurring in navigation regions or maps.
+		</member>
 		<member name="navigation/avoidance/thread_model/avoidance_use_high_priority_threads" type="bool" setter="" getter="" default="true">
 			If enabled and avoidance calculations use multiple threads the threads run with high priority.
 		</member>

+ 8 - 1
modules/navigation_2d/2d/nav_map_builder_2d.cpp

@@ -37,6 +37,8 @@
 #include "nav_map_iteration_2d.h"
 #include "nav_region_iteration_2d.h"
 
+#include "core/config/project_settings.h"
+
 using namespace Nav2D;
 
 PointKey NavMapBuilder2D::get_point_key(const Vector2 &p_pos, const Vector2 &p_cell_size) {
@@ -110,6 +112,7 @@ void NavMapBuilder2D::_build_step_find_edge_connection_pairs(NavMapIterationBuil
 	connection_pairs_map.clear();
 	connection_pairs_map.reserve(polygon_count);
 	int free_edges_count = 0; // How many ConnectionPairs have only one Connection.
+	int edge_merge_error_count = 0;
 
 	for (const Ref<NavRegionIteration2D> &region : map_iteration->region_iterations) {
 		for (const ConnectableEdge &connectable_edge : region->get_external_edges()) {
@@ -138,11 +141,15 @@ void NavMapBuilder2D::_build_step_find_edge_connection_pairs(NavMapIterationBuil
 
 			} else {
 				// The edge is already connected with another edge, skip.
-				ERR_PRINT_ONCE("Navigation map synchronization error. Attempted to merge a navigation mesh polygon edge with another already-merged edge. This is usually caused by crossing edges, overlapping polygons, or a mismatch of the NavigationMesh / NavigationPolygon baked 'cell_size' and navigation map 'cell_size'. If you're certain none of above is the case, change 'navigation/2d/merge_rasterizer_cell_scale' to 0.001.");
+				edge_merge_error_count++;
 			}
 		}
 	}
 
+	if (edge_merge_error_count > 0 && GLOBAL_GET_CACHED(bool, "navigation/2d/warnings/navmesh_edge_merge_errors")) {
+		WARN_PRINT("Navigation map synchronization had " + itos(edge_merge_error_count) + " edge error(s).\nMore than 2 edges tried to occupy the same map rasterization space.\nThis causes a logical error in the navigation mesh geometry and is commonly caused by overlap or too densely placed edges.\nConsider baking with a higher 'cell_size', greater geometry margin, and less detailed bake objects to cause fewer edges.\nConsider lowering the 'navigation/2d/merge_rasterizer_cell_scale' in the project settings.\nThis warning can be toggled under 'navigation/2d/warnings/navmesh_edge_merge_errors' in the project settings.");
+	}
+
 	r_build.free_edge_count = free_edges_count;
 }
 

+ 8 - 1
modules/navigation_2d/2d/nav_region_builder_2d.cpp

@@ -35,6 +35,8 @@
 #include "../triangle2.h"
 #include "nav_region_iteration_2d.h"
 
+#include "core/config/project_settings.h"
+
 using namespace Nav2D;
 
 void NavRegionBuilder2D::build_iteration(NavRegionIterationBuild2D &r_build) {
@@ -179,6 +181,7 @@ void NavRegionBuilder2D::_build_step_find_edge_connection_pairs(NavRegionIterati
 	region_iteration->external_edges.clear();
 
 	int free_edges_count = 0;
+	int edge_merge_error_count = 0;
 
 	for (Polygon &poly : region_iteration->navmesh_polygons) {
 		for (uint32_t p = 0; p < poly.vertices.size(); p++) {
@@ -208,11 +211,15 @@ void NavRegionBuilder2D::_build_step_find_edge_connection_pairs(NavRegionIterati
 
 			} else {
 				// The edge is already connected with another edge, skip.
-				ERR_FAIL_COND_MSG(pair.size >= 2, "Navigation region synchronization error. More than 2 edges tried to occupy the same map rasterization space. This is a logical error in the navigation mesh caused by overlap or too densely placed edges.");
+				edge_merge_error_count++;
 			}
 		}
 	}
 
+	if (edge_merge_error_count > 0 && GLOBAL_GET_CACHED(bool, "navigation/2d/warnings/navmesh_edge_merge_errors")) {
+		WARN_PRINT("Navigation region synchronization had " + itos(edge_merge_error_count) + " edge error(s).\nMore than 2 edges tried to occupy the same map rasterization space.\nThis causes a logical error in the navigation mesh geometry and is commonly caused by overlap or too densely placed edges.\nConsider baking with a higher 'cell_size', greater geometry margin, and less detailed bake objects to cause fewer edges.\nConsider lowering the 'navigation/2d/merge_rasterizer_cell_scale' in the project settings.\nThis warning can be toggled under 'navigation/2d/warnings/navmesh_edge_merge_errors' in the project settings.");
+	}
+
 	performance_data.pm_edge_free_count = free_edges_count;
 }
 

+ 1 - 1
modules/navigation_2d/nav_map_2d.cpp

@@ -80,7 +80,7 @@ void NavMap2D::set_merge_rasterizer_cell_scale(float p_value) {
 	if (merge_rasterizer_cell_scale == p_value) {
 		return;
 	}
-	merge_rasterizer_cell_scale = MAX(p_value, NavigationDefaults2D::NAV_MESH_CELL_SIZE_MIN);
+	merge_rasterizer_cell_scale = MAX(MIN(p_value, 0.1), NavigationDefaults2D::NAV_MESH_CELL_SIZE_MIN);
 	_update_merge_rasterizer_cell_dimensions();
 	map_settings_dirty = true;
 }

+ 1 - 1
modules/navigation_2d/nav_map_2d.h

@@ -56,7 +56,7 @@ class NavMap2D : public NavRid2D {
 	Vector2 merge_rasterizer_cell_size = Vector2(cell_size, cell_size);
 
 	// This value is used to control sensitivity of internal rasterizer.
-	float merge_rasterizer_cell_scale = 1.0;
+	float merge_rasterizer_cell_scale = 0.1;
 
 	bool use_edge_connections = true;
 	/// This value is used to detect the near edges to connect.

+ 7 - 2
modules/navigation_2d/nav_region_2d.cpp

@@ -94,8 +94,13 @@ void NavRegion2D::set_transform(Transform2D p_transform) {
 
 void NavRegion2D::set_navigation_mesh(Ref<NavigationPolygon> p_navigation_mesh) {
 #ifdef DEBUG_ENABLED
-	if (map && p_navigation_mesh.is_valid() && !Math::is_equal_approx(double(map->get_cell_size()), double(p_navigation_mesh->get_cell_size()))) {
-		ERR_PRINT_ONCE(vformat("Attempted to update a navigation region with a navigation mesh that uses a `cell_size` of %s while assigned to a navigation map set to a `cell_size` of %s. The cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function. The cell size for default navigation maps can also be changed in the ProjectSettings.", double(p_navigation_mesh->get_cell_size()), double(map->get_cell_size())));
+	if (map && p_navigation_mesh.is_valid() && GLOBAL_GET_CACHED(bool, "navigation/2d/warnings/navmesh_cell_size_mismatch")) {
+		const double map_cell_size = double(map->get_cell_size());
+		const double navmesh_cell_size = double(p_navigation_mesh->get_cell_size());
+
+		if (map_cell_size > navmesh_cell_size) {
+			WARN_PRINT(vformat("A navigation mesh that uses a `cell_size` of %s was assigned to a navigation map set to a larger `cell_size` of %s.\nThis mismatch in cell size can cause rasterization errors with navigation mesh edges on the navigation map.\nThe cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function.\nThe cell size for default navigation maps can also be changed in the project settings.\nThis warning can be toggled under 'navigation/2d/warnings/navmesh_cell_size_mismatch' in the project settings.", navmesh_cell_size, map_cell_size));
+		}
 	}
 #endif // DEBUG_ENABLED
 

+ 8 - 1
modules/navigation_3d/3d/nav_map_builder_3d.cpp

@@ -36,6 +36,8 @@
 #include "nav_map_iteration_3d.h"
 #include "nav_region_iteration_3d.h"
 
+#include "core/config/project_settings.h"
+
 using namespace Nav3D;
 
 PointKey NavMapBuilder3D::get_point_key(const Vector3 &p_pos, const Vector3 &p_cell_size) {
@@ -111,6 +113,7 @@ void NavMapBuilder3D::_build_step_find_edge_connection_pairs(NavMapIterationBuil
 	connection_pairs_map.clear();
 	connection_pairs_map.reserve(polygon_count);
 	int free_edges_count = 0; // How many ConnectionPairs have only one Connection.
+	int edge_merge_error_count = 0;
 
 	for (const Ref<NavRegionIteration3D> &region : map_iteration->region_iterations) {
 		for (const ConnectableEdge &connectable_edge : region->get_external_edges()) {
@@ -139,11 +142,15 @@ void NavMapBuilder3D::_build_step_find_edge_connection_pairs(NavMapIterationBuil
 
 			} else {
 				// The edge is already connected with another edge, skip.
-				ERR_PRINT_ONCE("Navigation map synchronization error. Attempted to merge a navigation mesh polygon edge with another already-merged edge. This is usually caused by crossing edges, overlapping polygons, or a mismatch of the NavigationMesh / NavigationPolygon baked 'cell_size' and navigation map 'cell_size'. If you're certain none of above is the case, change 'navigation/3d/merge_rasterizer_cell_scale' to 0.001.");
+				edge_merge_error_count++;
 			}
 		}
 	}
 
+	if (edge_merge_error_count > 0 && GLOBAL_GET_CACHED(bool, "navigation/3d/warnings/navmesh_edge_merge_errors")) {
+		WARN_PRINT("Navigation map synchronization had " + itos(edge_merge_error_count) + " edge error(s).\nMore than 2 edges tried to occupy the same map rasterization space.\nThis causes a logical error in the navigation mesh geometry and is commonly caused by overlap or too densely placed edges.\nConsider baking with a higher 'cell_size', greater geometry margin, and less detailed bake objects to cause fewer edges.\nConsider lowering the 'navigation/3d/merge_rasterizer_cell_scale' in the project settings.\nThis warning can be toggled under 'navigation/3d/warnings/navmesh_edge_merge_errors' in the project settings.");
+	}
+
 	r_build.free_edge_count = free_edges_count;
 }
 

+ 8 - 1
modules/navigation_3d/3d/nav_region_builder_3d.cpp

@@ -34,6 +34,8 @@
 #include "../nav_region_3d.h"
 #include "nav_region_iteration_3d.h"
 
+#include "core/config/project_settings.h"
+
 using namespace Nav3D;
 
 void NavRegionBuilder3D::build_iteration(NavRegionIterationBuild3D &r_build) {
@@ -180,6 +182,7 @@ void NavRegionBuilder3D::_build_step_find_edge_connection_pairs(NavRegionIterati
 	region_iteration->external_edges.clear();
 
 	int free_edges_count = 0;
+	int edge_merge_error_count = 0;
 
 	for (Polygon &poly : region_iteration->navmesh_polygons) {
 		for (uint32_t p = 0; p < poly.vertices.size(); p++) {
@@ -209,11 +212,15 @@ void NavRegionBuilder3D::_build_step_find_edge_connection_pairs(NavRegionIterati
 
 			} else {
 				// The edge is already connected with another edge, skip.
-				ERR_FAIL_COND_MSG(pair.size >= 2, "Navigation region synchronization error. More than 2 edges tried to occupy the same map rasterization space. This is a logical error in the navigation mesh caused by overlap or too densely placed edges.");
+				edge_merge_error_count++;
 			}
 		}
 	}
 
+	if (edge_merge_error_count > 0 && GLOBAL_GET_CACHED(bool, "navigation/3d/warnings/navmesh_edge_merge_errors")) {
+		WARN_PRINT("Navigation region synchronization had " + itos(edge_merge_error_count) + " edge error(s).\nMore than 2 edges tried to occupy the same map rasterization space.\nThis causes a logical error in the navigation mesh geometry and is commonly caused by overlap or too densely placed edges.\nConsider baking with a higher 'cell_size', greater geometry margin, and less detailed bake objects to cause fewer edges.\nConsider lowering the 'navigation/3d/merge_rasterizer_cell_scale' in the project settings.\nThis warning can be toggled under 'navigation/3d/warnings/navmesh_edge_merge_errors' in the project settings.");
+	}
+
 	performance_data.pm_edge_free_count = free_edges_count;
 }
 

+ 1 - 1
modules/navigation_3d/nav_map_3d.cpp

@@ -97,7 +97,7 @@ void NavMap3D::set_merge_rasterizer_cell_scale(float p_value) {
 	if (merge_rasterizer_cell_scale == p_value) {
 		return;
 	}
-	merge_rasterizer_cell_scale = MAX(p_value, NavigationDefaults3D::NAV_MESH_CELL_SIZE_MIN);
+	merge_rasterizer_cell_scale = MAX(MIN(p_value, 0.1), NavigationDefaults3D::NAV_MESH_CELL_SIZE_MIN);
 	_update_merge_rasterizer_cell_dimensions();
 	map_settings_dirty = true;
 }

+ 1 - 1
modules/navigation_3d/nav_map_3d.h

@@ -62,7 +62,7 @@ class NavMap3D : public NavRid3D {
 	Vector3 merge_rasterizer_cell_size = Vector3(cell_size, cell_height, cell_size);
 
 	// This value is used to control sensitivity of internal rasterizer.
-	float merge_rasterizer_cell_scale = 1.0;
+	float merge_rasterizer_cell_scale = 0.1;
 
 	bool use_edge_connections = true;
 	/// This value is used to detect the near edges to connect.

+ 12 - 6
modules/navigation_3d/nav_region_3d.cpp

@@ -100,12 +100,18 @@ void NavRegion3D::set_transform(Transform3D p_transform) {
 
 void NavRegion3D::set_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) {
 #ifdef DEBUG_ENABLED
-	if (map && p_navigation_mesh.is_valid() && !Math::is_equal_approx(double(map->get_cell_size()), double(p_navigation_mesh->get_cell_size()))) {
-		ERR_PRINT_ONCE(vformat("Attempted to update a navigation region with a navigation mesh that uses a `cell_size` of %s while assigned to a navigation map set to a `cell_size` of %s. The cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function. The cell size for default navigation maps can also be changed in the ProjectSettings.", double(p_navigation_mesh->get_cell_size()), double(map->get_cell_size())));
-	}
-
-	if (map && p_navigation_mesh.is_valid() && !Math::is_equal_approx(double(map->get_cell_height()), double(p_navigation_mesh->get_cell_height()))) {
-		ERR_PRINT_ONCE(vformat("Attempted to update a navigation region with a navigation mesh that uses a `cell_height` of %s while assigned to a navigation map set to a `cell_height` of %s. The cell height for navigation maps can be changed by using the NavigationServer map_set_cell_height() function. The cell height for default navigation maps can also be changed in the ProjectSettings.", double(p_navigation_mesh->get_cell_height()), double(map->get_cell_height())));
+	if (map && p_navigation_mesh.is_valid() && GLOBAL_GET_CACHED(bool, "navigation/3d/warnings/navmesh_cell_size_mismatch")) {
+		const double map_cell_size = double(map->get_cell_size());
+		const double map_cell_height = double(map->get_cell_height());
+		const double navmesh_cell_size = double(p_navigation_mesh->get_cell_size());
+		const double navmesh_cell_height = double(p_navigation_mesh->get_cell_height());
+
+		if (map_cell_size > navmesh_cell_size) {
+			WARN_PRINT(vformat("A navigation mesh that uses a `cell_size` of %s was assigned to a navigation map set to a larger `cell_size` of %s.\nThis mismatch in cell size can cause rasterization errors with navigation mesh edges on the navigation map.\nThe cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function.\nThe cell size for default navigation maps can also be changed in the project settings.\nThis warning can be toggled under 'navigation/3d/warnings/navmesh_cell_size_mismatch' in the project settings.", navmesh_cell_size, map_cell_size));
+		}
+		if (map_cell_height > navmesh_cell_height) {
+			WARN_PRINT(vformat("A navigation mesh that uses a `cell_height` of %s was assigned to a navigation map set to a larger `cell_height` of %s.\nThis mismatch in cell height can cause rasterization errors with navigation mesh edges on the navigation map.\nThe cell height for navigation maps can be changed by using the NavigationServer map_set_cell_height() function.\nThe cell height for default navigation maps can also be changed in the project settings.\nThis warning can be toggled under 'navigation/3d/warnings/navmesh_cell_size_mismatch' in the project settings.", navmesh_cell_height, map_cell_height));
+		}
 	}
 #endif // DEBUG_ENABLED