Browse Source

Merge pull request #106670 from smix8/ref_iterations

Change navigation region and link updates to an async process
Rémi Verschelde 2 months ago
parent
commit
74f5b863bc

+ 1 - 0
core/config/project_settings.cpp

@@ -1707,6 +1707,7 @@ ProjectSettings::ProjectSettings() {
 
 
 #if !defined(NAVIGATION_2D_DISABLED) || !defined(NAVIGATION_3D_DISABLED)
 #if !defined(NAVIGATION_2D_DISABLED) || !defined(NAVIGATION_3D_DISABLED)
 	GLOBAL_DEF("navigation/world/map_use_async_iterations", true);
 	GLOBAL_DEF("navigation/world/map_use_async_iterations", true);
+	GLOBAL_DEF("navigation/world/region_use_async_iterations", true);
 
 
 	GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_multiple_threads", true);
 	GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_multiple_threads", true);
 	GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_high_priority_threads", true);
 	GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_high_priority_threads", true);

+ 15 - 0
doc/classes/NavigationServer3D.xml

@@ -1058,6 +1058,13 @@
 				Returns the travel cost of this [param region].
 				Returns the travel cost of this [param region].
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="region_get_use_async_iterations" qualifiers="const">
+			<return type="bool" />
+			<param index="0" name="region" type="RID" />
+			<description>
+				Returns [code]true[/code] if the [param region] uses an async synchronization process that runs on a background thread.
+			</description>
+		</method>
 		<method name="region_get_use_edge_connections" qualifiers="const">
 		<method name="region_get_use_edge_connections" qualifiers="const">
 			<return type="bool" />
 			<return type="bool" />
 			<param index="0" name="region" type="RID" />
 			<param index="0" name="region" type="RID" />
@@ -1139,6 +1146,14 @@
 				Sets the [param travel_cost] for this [param region].
 				Sets the [param travel_cost] for this [param region].
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="region_set_use_async_iterations">
+			<return type="void" />
+			<param index="0" name="region" type="RID" />
+			<param index="1" name="enabled" type="bool" />
+			<description>
+				If [param enabled] is [code]true[/code] the [param region] uses an async synchronization process that runs on a background thread.
+			</description>
+		</method>
 		<method name="region_set_use_edge_connections">
 		<method name="region_set_use_edge_connections">
 			<return type="void" />
 			<return type="void" />
 			<param index="0" name="region" type="RID" />
 			<param index="0" name="region" type="RID" />

+ 3 - 0
doc/classes/ProjectSettings.xml

@@ -2341,6 +2341,9 @@
 		<member name="navigation/world/map_use_async_iterations" type="bool" setter="" getter="" default="true">
 		<member name="navigation/world/map_use_async_iterations" type="bool" setter="" getter="" default="true">
 			If enabled, navigation map synchronization uses an async process that runs on a background thread. This avoids stalling the main thread but adds an additional delay to any navigation map change.
 			If enabled, navigation map synchronization uses an async process that runs on a background thread. This avoids stalling the main thread but adds an additional delay to any navigation map change.
 		</member>
 		</member>
+		<member name="navigation/world/region_use_async_iterations" type="bool" setter="" getter="" default="true">
+			If enabled, navigation region synchronization uses an async process that runs on a background thread. This avoids stalling the main thread but adds an additional delay to any navigation region change.
+		</member>
 		<member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="32768">
 		<member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="32768">
 			Maximum number of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
 			Maximum number of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
 		</member>
 		</member>

+ 13 - 0
modules/navigation_3d/3d/godot_navigation_server_3d.cpp

@@ -397,6 +397,19 @@ uint32_t GodotNavigationServer3D::region_get_iteration_id(RID p_region) const {
 	return region->get_iteration_id();
 	return region->get_iteration_id();
 }
 }
 
 
+COMMAND_2(region_set_use_async_iterations, RID, p_region, bool, p_enabled) {
+	NavRegion3D *region = region_owner.get_or_null(p_region);
+	ERR_FAIL_NULL(region);
+	region->set_use_async_iterations(p_enabled);
+}
+
+bool GodotNavigationServer3D::region_get_use_async_iterations(RID p_region) const {
+	NavRegion3D *region = region_owner.get_or_null(p_region);
+	ERR_FAIL_NULL_V(region, false);
+
+	return region->get_use_async_iterations();
+}
+
 COMMAND_2(region_set_enabled, RID, p_region, bool, p_enabled) {
 COMMAND_2(region_set_enabled, RID, p_region, bool, p_enabled) {
 	NavRegion3D *region = region_owner.get_or_null(p_region);
 	NavRegion3D *region = region_owner.get_or_null(p_region);
 	ERR_FAIL_NULL(region);
 	ERR_FAIL_NULL(region);

+ 3 - 0
modules/navigation_3d/3d/godot_navigation_server_3d.h

@@ -149,6 +149,9 @@ public:
 	virtual RID region_create() override;
 	virtual RID region_create() override;
 	virtual uint32_t region_get_iteration_id(RID p_region) const override;
 	virtual uint32_t region_get_iteration_id(RID p_region) const override;
 
 
+	COMMAND_2(region_set_use_async_iterations, RID, p_region, bool, p_enabled);
+	virtual bool region_get_use_async_iterations(RID p_region) const override;
+
 	COMMAND_2(region_set_enabled, RID, p_region, bool, p_enabled);
 	COMMAND_2(region_set_enabled, RID, p_region, bool, p_enabled);
 	virtual bool region_get_enabled(RID p_region) const override;
 	virtual bool region_get_enabled(RID p_region) const override;
 
 

+ 12 - 2
modules/navigation_3d/3d/nav_base_iteration_3d.h

@@ -32,10 +32,13 @@
 
 
 #include "../nav_utils_3d.h"
 #include "../nav_utils_3d.h"
 
 
+#include "core/object/ref_counted.h"
 #include "servers/navigation/navigation_utilities.h"
 #include "servers/navigation/navigation_utilities.h"
 
 
-struct NavBaseIteration3D {
-	uint32_t id = UINT32_MAX;
+class NavBaseIteration3D : public RefCounted {
+	GDCLASS(NavBaseIteration3D, RefCounted);
+
+public:
 	bool enabled = true;
 	bool enabled = true;
 	uint32_t navigation_layers = 1;
 	uint32_t navigation_layers = 1;
 	real_t enter_cost = 0.0;
 	real_t enter_cost = 0.0;
@@ -45,6 +48,7 @@ struct NavBaseIteration3D {
 	RID owner_rid;
 	RID owner_rid;
 	bool owner_use_edge_connections = false;
 	bool owner_use_edge_connections = false;
 	LocalVector<Nav3D::Polygon> navmesh_polygons;
 	LocalVector<Nav3D::Polygon> navmesh_polygons;
+	LocalVector<LocalVector<Nav3D::Connection>> internal_connections;
 
 
 	bool get_enabled() const { return enabled; }
 	bool get_enabled() const { return enabled; }
 	NavigationUtilities::PathSegmentType get_type() const { return owner_type; }
 	NavigationUtilities::PathSegmentType get_type() const { return owner_type; }
@@ -55,4 +59,10 @@ struct NavBaseIteration3D {
 	real_t get_travel_cost() const { return travel_cost; }
 	real_t get_travel_cost() const { return travel_cost; }
 	bool get_use_edge_connections() const { return owner_use_edge_connections; }
 	bool get_use_edge_connections() const { return owner_use_edge_connections; }
 	const LocalVector<Nav3D::Polygon> &get_navmesh_polygons() const { return navmesh_polygons; }
 	const LocalVector<Nav3D::Polygon> &get_navmesh_polygons() const { return navmesh_polygons; }
+	const LocalVector<LocalVector<Nav3D::Connection>> &get_internal_connections() const { return internal_connections; }
+
+	virtual ~NavBaseIteration3D() {
+		navmesh_polygons.clear();
+		internal_connections.clear();
+	}
 };
 };

+ 119 - 97
modules/navigation_3d/3d/nav_map_builder_3d.cpp

@@ -77,26 +77,23 @@ void NavMapBuilder3D::_build_step_gather_region_polygons(NavMapIterationBuild3D
 	PerformanceData &performance_data = r_build.performance_data;
 	PerformanceData &performance_data = r_build.performance_data;
 	NavMapIteration3D *map_iteration = r_build.map_iteration;
 	NavMapIteration3D *map_iteration = r_build.map_iteration;
 
 
-	LocalVector<NavRegionIteration3D> &regions = map_iteration->region_iterations;
-	HashMap<uint32_t, LocalVector<Edge::Connection>> &region_external_connections = map_iteration->external_region_connections;
+	const LocalVector<Ref<NavRegionIteration3D>> &regions = map_iteration->region_iterations;
+	HashMap<const NavBaseIteration3D *, LocalVector<Connection>> &region_external_connections = map_iteration->external_region_connections;
+
+	map_iteration->navbases_polygons_external_connections.clear();
 
 
 	// Remove regions connections.
 	// Remove regions connections.
 	region_external_connections.clear();
 	region_external_connections.clear();
-	for (const NavRegionIteration3D &region : regions) {
-		region_external_connections[region.id] = LocalVector<Edge::Connection>();
-	}
 
 
 	// Copy all region polygons in the map.
 	// Copy all region polygons in the map.
 	int polygon_count = 0;
 	int polygon_count = 0;
-	for (NavRegionIteration3D &region : regions) {
-		if (!region.get_enabled()) {
-			continue;
-		}
-		LocalVector<Polygon> &polygons_source = region.navmesh_polygons;
-		for (uint32_t n = 0; n < polygons_source.size(); n++) {
-			polygons_source[n].id = polygon_count;
-			polygon_count++;
-		}
+	for (const Ref<NavRegionIteration3D> &region : regions) {
+		const uint32_t polygons_size = region->navmesh_polygons.size();
+		polygon_count += polygons_size;
+
+		region_external_connections[region.ptr()] = LocalVector<Connection>();
+		map_iteration->navbases_polygons_external_connections[region.ptr()] = LocalVector<LocalVector<Connection>>();
+		map_iteration->navbases_polygons_external_connections[region.ptr()].resize(polygons_size);
 	}
 	}
 
 
 	performance_data.pm_polygon_count = polygon_count;
 	performance_data.pm_polygon_count = polygon_count;
@@ -107,7 +104,7 @@ void NavMapBuilder3D::_build_step_find_edge_connection_pairs(NavMapIterationBuil
 	PerformanceData &performance_data = r_build.performance_data;
 	PerformanceData &performance_data = r_build.performance_data;
 	NavMapIteration3D *map_iteration = r_build.map_iteration;
 	NavMapIteration3D *map_iteration = r_build.map_iteration;
 	int polygon_count = r_build.polygon_count;
 	int polygon_count = r_build.polygon_count;
-	const Vector3 merge_rasterizer_cell_size = r_build.merge_rasterizer_cell_size;
+
 	HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
 	HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
 
 
 	// Group all edges per key.
 	// Group all edges per key.
@@ -115,41 +112,33 @@ void NavMapBuilder3D::_build_step_find_edge_connection_pairs(NavMapIterationBuil
 	connection_pairs_map.reserve(polygon_count);
 	connection_pairs_map.reserve(polygon_count);
 	int free_edges_count = 0; // How many ConnectionPairs have only one Connection.
 	int free_edges_count = 0; // How many ConnectionPairs have only one Connection.
 
 
-	for (NavRegionIteration3D &region : map_iteration->region_iterations) {
-		if (!region.get_enabled()) {
-			continue;
-		}
-
-		for (Polygon &poly : region.navmesh_polygons) {
-			for (uint32_t p = 0; p < poly.vertices.size(); p++) {
-				const int next_point = (p + 1) % poly.vertices.size();
-				const EdgeKey ek(get_point_key(poly.vertices[p], merge_rasterizer_cell_size), get_point_key(poly.vertices[next_point], merge_rasterizer_cell_size));
+	for (const Ref<NavRegionIteration3D> &region : map_iteration->region_iterations) {
+		for (const ConnectableEdge &connectable_edge : region->get_external_edges()) {
+			const EdgeKey &ek = connectable_edge.ek;
 
 
-				HashMap<EdgeKey, EdgeConnectionPair, EdgeKey>::Iterator pair_it = connection_pairs_map.find(ek);
-				if (!pair_it) {
-					pair_it = connection_pairs_map.insert(ek, EdgeConnectionPair());
-					performance_data.pm_edge_count += 1;
-					++free_edges_count;
+			HashMap<EdgeKey, EdgeConnectionPair, EdgeKey>::Iterator pair_it = connection_pairs_map.find(ek);
+			if (!pair_it) {
+				pair_it = connection_pairs_map.insert(ek, EdgeConnectionPair());
+				performance_data.pm_edge_count += 1;
+				++free_edges_count;
+			}
+			EdgeConnectionPair &pair = pair_it->value;
+			if (pair.size < 2) {
+				// Add the polygon/edge tuple to this key.
+				Connection new_connection;
+				new_connection.polygon = &region->navmesh_polygons[connectable_edge.polygon_index];
+				new_connection.pathway_start = connectable_edge.pathway_start;
+				new_connection.pathway_end = connectable_edge.pathway_end;
+
+				pair.connections[pair.size] = new_connection;
+				++pair.size;
+				if (pair.size == 2) {
+					--free_edges_count;
 				}
 				}
-				EdgeConnectionPair &pair = pair_it->value;
-				if (pair.size < 2) {
-					// Add the polygon/edge tuple to this key.
-					Edge::Connection new_connection;
-					new_connection.polygon = &poly;
-					new_connection.edge = p;
-					new_connection.pathway_start = poly.vertices[p];
-					new_connection.pathway_end = poly.vertices[next_point];
-
-					pair.connections[pair.size] = new_connection;
-					++pair.size;
-					if (pair.size == 2) {
-						--free_edges_count;
-					}
 
 
-				} 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.");
-				}
+			} 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.");
 			}
 			}
 		}
 		}
 	}
 	}
@@ -161,23 +150,28 @@ void NavMapBuilder3D::_build_step_merge_edge_connection_pairs(NavMapIterationBui
 	PerformanceData &performance_data = r_build.performance_data;
 	PerformanceData &performance_data = r_build.performance_data;
 
 
 	HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
 	HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
-	LocalVector<Edge::Connection> &free_edges = r_build.iter_free_edges;
+	LocalVector<Connection> &free_edges = r_build.iter_free_edges;
 	int free_edges_count = r_build.free_edge_count;
 	int free_edges_count = r_build.free_edge_count;
 	bool use_edge_connections = r_build.use_edge_connections;
 	bool use_edge_connections = r_build.use_edge_connections;
 
 
 	free_edges.clear();
 	free_edges.clear();
 	free_edges.reserve(free_edges_count);
 	free_edges.reserve(free_edges_count);
 
 
+	NavMapIteration3D *map_iteration = r_build.map_iteration;
+
+	HashMap<const NavBaseIteration3D *, LocalVector<LocalVector<Nav3D::Connection>>> &navbases_polygons_external_connections = map_iteration->navbases_polygons_external_connections;
+
 	for (const KeyValue<EdgeKey, EdgeConnectionPair> &pair_it : connection_pairs_map) {
 	for (const KeyValue<EdgeKey, EdgeConnectionPair> &pair_it : connection_pairs_map) {
 		const EdgeConnectionPair &pair = pair_it.value;
 		const EdgeConnectionPair &pair = pair_it.value;
 		if (pair.size == 2) {
 		if (pair.size == 2) {
 			// Connect edge that are shared in different polygons.
 			// Connect edge that are shared in different polygons.
-			const Edge::Connection &c1 = pair.connections[0];
-			const Edge::Connection &c2 = pair.connections[1];
-			c1.polygon->edges[c1.edge].connections.push_back(c2);
-			c2.polygon->edges[c2.edge].connections.push_back(c1);
-			// Note: The pathway_start/end are full for those connection and do not need to be modified.
-			performance_data.pm_edge_merge_count += 1;
+			const Connection &c1 = pair.connections[0];
+			const Connection &c2 = pair.connections[1];
+
+			navbases_polygons_external_connections[c1.polygon->owner][c1.polygon->id].push_back(c2);
+			navbases_polygons_external_connections[c2.polygon->owner][c2.polygon->id].push_back(c1);
+			performance_data.pm_edge_connection_count += 1;
+
 		} else {
 		} else {
 			CRASH_COND_MSG(pair.size != 1, vformat("Number of connection != 1. Found: %d", pair.size));
 			CRASH_COND_MSG(pair.size != 1, vformat("Number of connection != 1. Found: %d", pair.size));
 			if (use_edge_connections && pair.connections[0].polygon->owner->get_use_edge_connections()) {
 			if (use_edge_connections && pair.connections[0].polygon->owner->get_use_edge_connections()) {
@@ -192,8 +186,11 @@ void NavMapBuilder3D::_build_step_edge_connection_margin_connections(NavMapItera
 	NavMapIteration3D *map_iteration = r_build.map_iteration;
 	NavMapIteration3D *map_iteration = r_build.map_iteration;
 
 
 	real_t edge_connection_margin = r_build.edge_connection_margin;
 	real_t edge_connection_margin = r_build.edge_connection_margin;
-	LocalVector<Edge::Connection> &free_edges = r_build.iter_free_edges;
-	HashMap<uint32_t, LocalVector<Edge::Connection>> &region_external_connections = map_iteration->external_region_connections;
+
+	LocalVector<Connection> &free_edges = r_build.iter_free_edges;
+	HashMap<const NavBaseIteration3D *, LocalVector<Connection>> &region_external_connections = map_iteration->external_region_connections;
+
+	HashMap<const NavBaseIteration3D *, LocalVector<LocalVector<Nav3D::Connection>>> &navbases_polygons_external_connections = map_iteration->navbases_polygons_external_connections;
 
 
 	// Find the compatible near edges.
 	// Find the compatible near edges.
 	//
 	//
@@ -207,18 +204,18 @@ void NavMapBuilder3D::_build_step_edge_connection_margin_connections(NavMapItera
 	const real_t edge_connection_margin_squared = edge_connection_margin * edge_connection_margin;
 	const real_t edge_connection_margin_squared = edge_connection_margin * edge_connection_margin;
 
 
 	for (uint32_t i = 0; i < free_edges.size(); i++) {
 	for (uint32_t i = 0; i < free_edges.size(); i++) {
-		const Edge::Connection &free_edge = free_edges[i];
-		Vector3 edge_p1 = free_edge.polygon->vertices[free_edge.edge];
-		Vector3 edge_p2 = free_edge.polygon->vertices[(free_edge.edge + 1) % free_edge.polygon->vertices.size()];
+		const Connection &free_edge = free_edges[i];
+		const Vector3 &edge_p1 = free_edge.pathway_start;
+		const Vector3 &edge_p2 = free_edge.pathway_end;
 
 
 		for (uint32_t j = 0; j < free_edges.size(); j++) {
 		for (uint32_t j = 0; j < free_edges.size(); j++) {
-			const Edge::Connection &other_edge = free_edges[j];
+			const Connection &other_edge = free_edges[j];
 			if (i == j || free_edge.polygon->owner == other_edge.polygon->owner) {
 			if (i == j || free_edge.polygon->owner == other_edge.polygon->owner) {
 				continue;
 				continue;
 			}
 			}
 
 
-			Vector3 other_edge_p1 = other_edge.polygon->vertices[other_edge.edge];
-			Vector3 other_edge_p2 = other_edge.polygon->vertices[(other_edge.edge + 1) % other_edge.polygon->vertices.size()];
+			const Vector3 &other_edge_p1 = other_edge.pathway_start;
+			const Vector3 &other_edge_p2 = other_edge.pathway_end;
 
 
 			// Compute the projection of the opposite edge on the current one
 			// Compute the projection of the opposite edge on the current one
 			Vector3 edge_vector = edge_p2 - edge_p1;
 			Vector3 edge_vector = edge_p2 - edge_p1;
@@ -252,13 +249,14 @@ void NavMapBuilder3D::_build_step_edge_connection_margin_connections(NavMapItera
 			}
 			}
 
 
 			// The edges can now be connected.
 			// The edges can now be connected.
-			Edge::Connection new_connection = other_edge;
+			Connection new_connection = other_edge;
 			new_connection.pathway_start = (self1 + other1) / 2.0;
 			new_connection.pathway_start = (self1 + other1) / 2.0;
 			new_connection.pathway_end = (self2 + other2) / 2.0;
 			new_connection.pathway_end = (self2 + other2) / 2.0;
-			free_edge.polygon->edges[free_edge.edge].connections.push_back(new_connection);
+			//free_edge.polygon->connections.push_back(new_connection);
 
 
 			// Add the connection to the region_connection map.
 			// Add the connection to the region_connection map.
-			region_external_connections[(uint32_t)free_edge.polygon->owner->id].push_back(new_connection);
+			region_external_connections[free_edge.polygon->owner].push_back(new_connection);
+			navbases_polygons_external_connections[free_edge.polygon->owner][free_edge.polygon->id].push_back(new_connection);
 			performance_data.pm_edge_connection_count += 1;
 			performance_data.pm_edge_connection_count += 1;
 		}
 		}
 	}
 	}
@@ -269,18 +267,28 @@ void NavMapBuilder3D::_build_step_navlink_connections(NavMapIterationBuild3D &r_
 
 
 	real_t link_connection_radius = r_build.link_connection_radius;
 	real_t link_connection_radius = r_build.link_connection_radius;
 
 
-	LocalVector<NavLinkIteration3D> &links = map_iteration->link_iterations;
+	const LocalVector<Ref<NavLinkIteration3D>> &links = map_iteration->link_iterations;
+
 	int polygon_count = r_build.polygon_count;
 	int polygon_count = r_build.polygon_count;
 
 
 	real_t link_connection_radius_sqr = link_connection_radius * link_connection_radius;
 	real_t link_connection_radius_sqr = link_connection_radius * link_connection_radius;
 
 
+	HashMap<const NavBaseIteration3D *, LocalVector<LocalVector<Nav3D::Connection>>> &navbases_polygons_external_connections = map_iteration->navbases_polygons_external_connections;
+	LocalVector<Nav3D::Polygon> &navlink_polygons = map_iteration->navlink_polygons;
+	navlink_polygons.clear();
+	navlink_polygons.resize(links.size());
+	uint32_t navlink_index = 0;
+
 	// Search for polygons within range of a nav link.
 	// Search for polygons within range of a nav link.
-	for (NavLinkIteration3D &link : links) {
-		if (!link.get_enabled()) {
-			continue;
-		}
-		const Vector3 link_start_pos = link.get_start_position();
-		const Vector3 link_end_pos = link.get_end_position();
+	for (const Ref<NavLinkIteration3D> &link : links) {
+		polygon_count++;
+		Polygon &new_polygon = navlink_polygons[navlink_index++];
+
+		new_polygon.id = 0;
+		new_polygon.owner = link.ptr();
+
+		const Vector3 link_start_pos = link->get_start_position();
+		const Vector3 link_end_pos = link->get_end_position();
 
 
 		Polygon *closest_start_polygon = nullptr;
 		Polygon *closest_start_polygon = nullptr;
 		real_t closest_start_sqr_dist = link_connection_radius_sqr;
 		real_t closest_start_sqr_dist = link_connection_radius_sqr;
@@ -290,16 +298,13 @@ void NavMapBuilder3D::_build_step_navlink_connections(NavMapIterationBuild3D &r_
 		real_t closest_end_sqr_dist = link_connection_radius_sqr;
 		real_t closest_end_sqr_dist = link_connection_radius_sqr;
 		Vector3 closest_end_point;
 		Vector3 closest_end_point;
 
 
-		for (NavRegionIteration3D &region : map_iteration->region_iterations) {
-			if (!region.get_enabled()) {
-				continue;
-			}
-			AABB region_bounds = region.get_bounds().grow(link_connection_radius);
+		for (const Ref<NavRegionIteration3D> &region : map_iteration->region_iterations) {
+			AABB region_bounds = region->get_bounds().grow(link_connection_radius);
 			if (!region_bounds.has_point(link_start_pos) && !region_bounds.has_point(link_end_pos)) {
 			if (!region_bounds.has_point(link_start_pos) && !region_bounds.has_point(link_end_pos)) {
 				continue;
 				continue;
 			}
 			}
 
 
-			for (Polygon &polyon : region.navmesh_polygons) {
+			for (Polygon &polyon : region->navmesh_polygons) {
 				for (uint32_t point_id = 2; point_id < polyon.vertices.size(); point_id += 1) {
 				for (uint32_t point_id = 2; point_id < polyon.vertices.size(); point_id += 1) {
 					const Face3 face(polyon.vertices[0], polyon.vertices[point_id - 1], polyon.vertices[point_id]);
 					const Face3 face(polyon.vertices[0], polyon.vertices[point_id - 1], polyon.vertices[point_id]);
 
 
@@ -332,14 +337,6 @@ void NavMapBuilder3D::_build_step_navlink_connections(NavMapIterationBuild3D &r_
 
 
 		// If we have both a start and end point, then create a synthetic polygon to route through.
 		// If we have both a start and end point, then create a synthetic polygon to route through.
 		if (closest_start_polygon && closest_end_polygon) {
 		if (closest_start_polygon && closest_end_polygon) {
-			link.navmesh_polygons.clear();
-			link.navmesh_polygons.resize(1);
-			Polygon &new_polygon = link.navmesh_polygons[0];
-			new_polygon.id = polygon_count++;
-			new_polygon.owner = &link;
-
-			new_polygon.edges.clear();
-			new_polygon.edges.resize(4);
 			new_polygon.vertices.resize(4);
 			new_polygon.vertices.resize(4);
 
 
 			// Build a set of vertices that create a thin polygon going from the start to the end point.
 			// Build a set of vertices that create a thin polygon going from the start to the end point.
@@ -350,36 +347,38 @@ void NavMapBuilder3D::_build_step_navlink_connections(NavMapIterationBuild3D &r_
 
 
 			// Setup connections to go forward in the link.
 			// Setup connections to go forward in the link.
 			{
 			{
-				Edge::Connection entry_connection;
+				Connection entry_connection;
 				entry_connection.polygon = &new_polygon;
 				entry_connection.polygon = &new_polygon;
 				entry_connection.edge = -1;
 				entry_connection.edge = -1;
 				entry_connection.pathway_start = new_polygon.vertices[0];
 				entry_connection.pathway_start = new_polygon.vertices[0];
 				entry_connection.pathway_end = new_polygon.vertices[1];
 				entry_connection.pathway_end = new_polygon.vertices[1];
-				closest_start_polygon->edges[0].connections.push_back(entry_connection);
+				navbases_polygons_external_connections[closest_start_polygon->owner][closest_start_polygon->id].push_back(entry_connection);
 
 
-				Edge::Connection exit_connection;
+				Connection exit_connection;
 				exit_connection.polygon = closest_end_polygon;
 				exit_connection.polygon = closest_end_polygon;
 				exit_connection.edge = -1;
 				exit_connection.edge = -1;
 				exit_connection.pathway_start = new_polygon.vertices[2];
 				exit_connection.pathway_start = new_polygon.vertices[2];
 				exit_connection.pathway_end = new_polygon.vertices[3];
 				exit_connection.pathway_end = new_polygon.vertices[3];
-				new_polygon.edges[2].connections.push_back(exit_connection);
+				navbases_polygons_external_connections[link.ptr()].push_back(LocalVector<Nav3D::Connection>());
+				navbases_polygons_external_connections[link.ptr()][new_polygon.id].push_back(exit_connection);
 			}
 			}
 
 
 			// If the link is bi-directional, create connections from the end to the start.
 			// If the link is bi-directional, create connections from the end to the start.
-			if (link.is_bidirectional()) {
-				Edge::Connection entry_connection;
+			if (link->is_bidirectional()) {
+				Connection entry_connection;
 				entry_connection.polygon = &new_polygon;
 				entry_connection.polygon = &new_polygon;
 				entry_connection.edge = -1;
 				entry_connection.edge = -1;
 				entry_connection.pathway_start = new_polygon.vertices[2];
 				entry_connection.pathway_start = new_polygon.vertices[2];
 				entry_connection.pathway_end = new_polygon.vertices[3];
 				entry_connection.pathway_end = new_polygon.vertices[3];
-				closest_end_polygon->edges[0].connections.push_back(entry_connection);
+				navbases_polygons_external_connections[closest_end_polygon->owner][closest_end_polygon->id].push_back(entry_connection);
 
 
-				Edge::Connection exit_connection;
+				Connection exit_connection;
 				exit_connection.polygon = closest_start_polygon;
 				exit_connection.polygon = closest_start_polygon;
 				exit_connection.edge = -1;
 				exit_connection.edge = -1;
 				exit_connection.pathway_start = new_polygon.vertices[0];
 				exit_connection.pathway_start = new_polygon.vertices[0];
 				exit_connection.pathway_end = new_polygon.vertices[1];
 				exit_connection.pathway_end = new_polygon.vertices[1];
-				new_polygon.edges[0].connections.push_back(exit_connection);
+				navbases_polygons_external_connections[link.ptr()].push_back(LocalVector<Nav3D::Connection>());
+				navbases_polygons_external_connections[link.ptr()][new_polygon.id].push_back(exit_connection);
 			}
 			}
 		}
 		}
 	}
 	}
@@ -392,12 +391,35 @@ void NavMapBuilder3D::_build_update_map_iteration(NavMapIterationBuild3D &r_buil
 
 
 	map_iteration->navmesh_polygon_count = r_build.polygon_count;
 	map_iteration->navmesh_polygon_count = r_build.polygon_count;
 
 
+	uint32_t navmesh_polygon_count = r_build.polygon_count;
+	uint32_t total_polygon_count = navmesh_polygon_count;
+
 	map_iteration->path_query_slots_mutex.lock();
 	map_iteration->path_query_slots_mutex.lock();
 	for (NavMeshQueries3D::PathQuerySlot &p_path_query_slot : map_iteration->path_query_slots) {
 	for (NavMeshQueries3D::PathQuerySlot &p_path_query_slot : map_iteration->path_query_slots) {
 		p_path_query_slot.traversable_polys.clear();
 		p_path_query_slot.traversable_polys.clear();
-		p_path_query_slot.traversable_polys.reserve(map_iteration->navmesh_polygon_count * 0.25);
+		p_path_query_slot.traversable_polys.reserve(navmesh_polygon_count * 0.25);
 		p_path_query_slot.path_corridor.clear();
 		p_path_query_slot.path_corridor.clear();
-		p_path_query_slot.path_corridor.resize(map_iteration->navmesh_polygon_count);
+
+		p_path_query_slot.path_corridor.resize(total_polygon_count);
+
+		p_path_query_slot.poly_to_id.clear();
+		p_path_query_slot.poly_to_id.reserve(total_polygon_count);
+
+		int polygon_id = 0;
+		for (Ref<NavRegionIteration3D> &region : map_iteration->region_iterations) {
+			for (const Polygon &polygon : region->navmesh_polygons) {
+				p_path_query_slot.poly_to_id[&polygon] = polygon_id;
+				polygon_id++;
+			}
+		}
+
+		for (const Polygon &polygon : map_iteration->navlink_polygons) {
+			p_path_query_slot.poly_to_id[&polygon] = polygon_id;
+			polygon_id++;
+		}
+
+		DEV_ASSERT(p_path_query_slot.path_corridor.size() == p_path_query_slot.poly_to_id.size());
 	}
 	}
+
 	map_iteration->path_query_slots_mutex.unlock();
 	map_iteration->path_query_slots_mutex.unlock();
 }
 }

+ 22 - 7
modules/navigation_3d/3d/nav_map_iteration_3d.h

@@ -37,9 +37,9 @@
 #include "core/math/math_defs.h"
 #include "core/math/math_defs.h"
 #include "core/os/semaphore.h"
 #include "core/os/semaphore.h"
 
 
-struct NavLinkIteration3D;
+class NavLinkIteration3D;
 class NavRegion3D;
 class NavRegion3D;
-struct NavRegionIteration3D;
+class NavRegionIteration3D;
 struct NavMapIteration3D;
 struct NavMapIteration3D;
 
 
 struct NavMapIterationBuild3D {
 struct NavMapIterationBuild3D {
@@ -52,7 +52,7 @@ struct NavMapIterationBuild3D {
 	int free_edge_count = 0;
 	int free_edge_count = 0;
 
 
 	HashMap<Nav3D::EdgeKey, Nav3D::EdgeConnectionPair, Nav3D::EdgeKey> iter_connection_pairs_map;
 	HashMap<Nav3D::EdgeKey, Nav3D::EdgeConnectionPair, Nav3D::EdgeKey> iter_connection_pairs_map;
-	LocalVector<Nav3D::Edge::Connection> iter_free_edges;
+	LocalVector<Nav3D::Connection> iter_free_edges;
 
 
 	NavMapIteration3D *map_iteration = nullptr;
 	NavMapIteration3D *map_iteration = nullptr;
 
 
@@ -76,19 +76,34 @@ struct NavMapIteration3D {
 
 
 	Vector3 map_up;
 	Vector3 map_up;
 
 
-	LocalVector<NavRegionIteration3D> region_iterations;
-	LocalVector<NavLinkIteration3D> link_iterations;
+	LocalVector<Ref<NavRegionIteration3D>> region_iterations;
+	LocalVector<Ref<NavLinkIteration3D>> link_iterations;
 
 
 	int navmesh_polygon_count = 0;
 	int navmesh_polygon_count = 0;
 
 
 	// The edge connections that the map builds on top with the edge connection margin.
 	// The edge connections that the map builds on top with the edge connection margin.
-	HashMap<uint32_t, LocalVector<Nav3D::Edge::Connection>> external_region_connections;
+	HashMap<const NavBaseIteration3D *, LocalVector<Nav3D::Connection>> external_region_connections;
+	HashMap<const NavBaseIteration3D *, LocalVector<LocalVector<Nav3D::Connection>>> navbases_polygons_external_connections;
 
 
-	HashMap<NavRegion3D *, uint32_t> region_ptr_to_region_id;
+	LocalVector<Nav3D::Polygon> navlink_polygons;
+
+	HashMap<NavRegion3D *, Ref<NavRegionIteration3D>> region_ptr_to_region_iteration;
 
 
 	LocalVector<NavMeshQueries3D::PathQuerySlot> path_query_slots;
 	LocalVector<NavMeshQueries3D::PathQuerySlot> path_query_slots;
 	Mutex path_query_slots_mutex;
 	Mutex path_query_slots_mutex;
 	Semaphore path_query_slots_semaphore;
 	Semaphore path_query_slots_semaphore;
+
+	void clear() {
+		map_up = Vector3();
+		navmesh_polygon_count = 0;
+
+		region_iterations.clear();
+		link_iterations.clear();
+		external_region_connections.clear();
+		navbases_polygons_external_connections.clear();
+		navlink_polygons.clear();
+		region_ptr_to_region_iteration.clear();
+	}
 };
 };
 
 
 class NavMapIterationRead3D {
 class NavMapIterationRead3D {

+ 95 - 70
modules/navigation_3d/3d/nav_mesh_queries_3d.cpp

@@ -233,22 +233,15 @@ void NavMeshQueries3D::_query_task_find_start_end_positions(NavMeshPathQueryTask
 	real_t begin_d = FLT_MAX;
 	real_t begin_d = FLT_MAX;
 	real_t end_d = FLT_MAX;
 	real_t end_d = FLT_MAX;
 
 
-	const LocalVector<NavRegionIteration3D> &regions = p_map_iteration.region_iterations;
+	const LocalVector<Ref<NavRegionIteration3D>> &regions = p_map_iteration.region_iterations;
 
 
-	for (const NavRegionIteration3D &region : regions) {
-		if (!region.get_enabled()) {
-			continue;
-		}
-
-		if (p_query_task.exclude_regions && p_query_task.excluded_regions.has(region.get_self())) {
-			continue;
-		}
-		if (p_query_task.include_regions && !p_query_task.included_regions.has(region.get_self())) {
+	for (const Ref<NavRegionIteration3D> &region : regions) {
+		if (!_query_task_is_connection_owner_usable(p_query_task, region.ptr())) {
 			continue;
 			continue;
 		}
 		}
 
 
 		// Find the initial poly and the end poly on this map.
 		// Find the initial poly and the end poly on this map.
-		for (const Polygon &p : region.get_navmesh_polygons()) {
+		for (const Polygon &p : region->get_navmesh_polygons()) {
 			// Only consider the polygon if it in a region with compatible layers.
 			// Only consider the polygon if it in a region with compatible layers.
 			if ((p_query_task.navigation_layers & p.owner->get_navigation_layers()) == 0) {
 			if ((p_query_task.navigation_layers & p.owner->get_navigation_layers()) == 0) {
 				continue;
 				continue;
@@ -278,7 +271,47 @@ void NavMeshQueries3D::_query_task_find_start_end_positions(NavMeshPathQueryTask
 	}
 	}
 }
 }
 
 
-void NavMeshQueries3D::_query_task_build_path_corridor(NavMeshPathQueryTask3D &p_query_task) {
+void NavMeshQueries3D::_query_task_search_polygon_connections(NavMeshPathQueryTask3D &p_query_task, const Connection &p_connection, uint32_t p_least_cost_id, const NavigationPoly &p_least_cost_poly, real_t p_poly_enter_cost, const Vector3 &p_end_point) {
+	const NavBaseIteration3D *connection_owner = p_connection.polygon->owner;
+	ERR_FAIL_NULL(connection_owner);
+	const bool owner_is_usable = _query_task_is_connection_owner_usable(p_query_task, connection_owner);
+	if (!owner_is_usable) {
+		return;
+	}
+
+	Heap<NavigationPoly *, NavPolyTravelCostGreaterThan, NavPolyHeapIndexer>
+			&traversable_polys = p_query_task.path_query_slot->traversable_polys;
+	LocalVector<NavigationPoly> &navigation_polys = p_query_task.path_query_slot->path_corridor;
+
+	real_t poly_travel_cost = p_least_cost_poly.poly->owner->get_travel_cost();
+
+	Vector3 new_entry = Geometry3D::get_closest_point_to_segment(p_least_cost_poly.entry, p_connection.pathway_start, p_connection.pathway_end);
+	real_t new_traveled_distance = p_least_cost_poly.entry.distance_to(new_entry) * poly_travel_cost + p_poly_enter_cost + p_least_cost_poly.traveled_distance;
+
+	// Check if the neighbor polygon has already been processed.
+	NavigationPoly &neighbor_poly = navigation_polys[p_query_task.path_query_slot->poly_to_id[p_connection.polygon]];
+	if (new_traveled_distance < neighbor_poly.traveled_distance) {
+		// Add the polygon to the heap of polygons to traverse next.
+		neighbor_poly.back_navigation_poly_id = p_least_cost_id;
+		neighbor_poly.back_navigation_edge = p_connection.edge;
+		neighbor_poly.back_navigation_edge_pathway_start = p_connection.pathway_start;
+		neighbor_poly.back_navigation_edge_pathway_end = p_connection.pathway_end;
+		neighbor_poly.traveled_distance = new_traveled_distance;
+		neighbor_poly.distance_to_destination =
+				new_entry.distance_to(p_end_point) *
+				connection_owner->get_travel_cost();
+		neighbor_poly.entry = new_entry;
+
+		if (neighbor_poly.traversable_poly_index != traversable_polys.INVALID_INDEX) {
+			traversable_polys.shift(neighbor_poly.traversable_poly_index);
+		} else {
+			neighbor_poly.poly = p_connection.polygon;
+			traversable_polys.push(&neighbor_poly);
+		}
+	}
+}
+
+void NavMeshQueries3D::_query_task_build_path_corridor(NavMeshPathQueryTask3D &p_query_task, const NavMapIteration3D &p_map_iteration) {
 	const Vector3 p_target_position = p_query_task.target_position;
 	const Vector3 p_target_position = p_query_task.target_position;
 	const Polygon *begin_poly = p_query_task.begin_polygon;
 	const Polygon *begin_poly = p_query_task.begin_polygon;
 	const Polygon *end_poly = p_query_task.end_polygon;
 	const Polygon *end_poly = p_query_task.end_polygon;
@@ -296,7 +329,7 @@ void NavMeshQueries3D::_query_task_build_path_corridor(NavMeshPathQueryTask3D &p
 	}
 	}
 
 
 	// Initialize the matching navigation polygon.
 	// Initialize the matching navigation polygon.
-	NavigationPoly &begin_navigation_poly = navigation_polys[begin_poly->id];
+	NavigationPoly &begin_navigation_poly = navigation_polys[p_query_task.path_query_slot->poly_to_id[begin_poly]];
 	begin_navigation_poly.poly = begin_poly;
 	begin_navigation_poly.poly = begin_poly;
 	begin_navigation_poly.entry = begin_point;
 	begin_navigation_poly.entry = begin_point;
 	begin_navigation_poly.back_navigation_edge_pathway_start = begin_point;
 	begin_navigation_poly.back_navigation_edge_pathway_start = begin_point;
@@ -304,7 +337,7 @@ void NavMeshQueries3D::_query_task_build_path_corridor(NavMeshPathQueryTask3D &p
 	begin_navigation_poly.traveled_distance = 0.f;
 	begin_navigation_poly.traveled_distance = 0.f;
 
 
 	// This is an implementation of the A* algorithm.
 	// This is an implementation of the A* algorithm.
-	uint32_t least_cost_id = begin_poly->id;
+	uint32_t least_cost_id = p_query_task.path_query_slot->poly_to_id[begin_poly];
 	bool found_route = false;
 	bool found_route = false;
 
 
 	const Polygon *reachable_end = nullptr;
 	const Polygon *reachable_end = nullptr;
@@ -312,49 +345,29 @@ void NavMeshQueries3D::_query_task_build_path_corridor(NavMeshPathQueryTask3D &p
 	bool is_reachable = true;
 	bool is_reachable = true;
 	real_t poly_enter_cost = 0.0;
 	real_t poly_enter_cost = 0.0;
 
 
+	const HashMap<const NavBaseIteration3D *, LocalVector<LocalVector<Nav3D::Connection>>> &navbases_polygons_external_connections = p_map_iteration.navbases_polygons_external_connections;
+
 	while (true) {
 	while (true) {
 		const NavigationPoly &least_cost_poly = navigation_polys[least_cost_id];
 		const NavigationPoly &least_cost_poly = navigation_polys[least_cost_id];
-		real_t poly_travel_cost = least_cost_poly.poly->owner->get_travel_cost();
-
-		// Takes the current least_cost_poly neighbors (iterating over its edges) and compute the traveled_distance.
-		for (const Edge &edge : least_cost_poly.poly->edges) {
-			// Iterate over connections in this edge, then compute the new optimized travel distance assigned to this polygon.
-			for (uint32_t connection_index = 0; connection_index < edge.connections.size(); connection_index++) {
-				const Edge::Connection &connection = edge.connections[connection_index];
-
-				const NavBaseIteration3D *connection_owner = connection.polygon->owner;
-				const bool owner_is_usable = _query_task_is_connection_owner_usable(p_query_task, connection_owner);
-				if (!owner_is_usable) {
-					continue;
-				}
 
 
-				const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly.entry, connection.pathway_start, connection.pathway_end);
-				const real_t new_traveled_distance = least_cost_poly.entry.distance_to(new_entry) * poly_travel_cost + poly_enter_cost + least_cost_poly.traveled_distance;
-
-				// Check if the neighbor polygon has already been processed.
-				NavigationPoly &neighbor_poly = navigation_polys[connection.polygon->id];
-				if (new_traveled_distance < neighbor_poly.traveled_distance) {
-					// Add the polygon to the heap of polygons to traverse next.
-					neighbor_poly.back_navigation_poly_id = least_cost_id;
-					neighbor_poly.back_navigation_edge = connection.edge;
-					neighbor_poly.back_navigation_edge_pathway_start = connection.pathway_start;
-					neighbor_poly.back_navigation_edge_pathway_end = connection.pathway_end;
-					neighbor_poly.traveled_distance = new_traveled_distance;
-					neighbor_poly.distance_to_destination =
-							new_entry.distance_to(end_point) *
-							connection_owner->get_travel_cost();
-					neighbor_poly.entry = new_entry;
-
-					if (neighbor_poly.traversable_poly_index != traversable_polys.INVALID_INDEX) {
-						traversable_polys.shift(neighbor_poly.traversable_poly_index);
-					} else {
-						neighbor_poly.poly = connection.polygon;
-						traversable_polys.push(&neighbor_poly);
-					}
-				}
+		const NavBaseIteration3D *least_cost_navbase = least_cost_poly.poly->owner;
+
+		const uint32_t navbase_local_polygon_id = least_cost_poly.poly->id;
+		const LocalVector<LocalVector<Connection>> &navbase_polygons_to_connections = least_cost_poly.poly->owner->get_internal_connections();
+
+		if (navbase_polygons_to_connections.size() > 0) {
+			const LocalVector<Connection> &polygon_connections = navbase_polygons_to_connections[navbase_local_polygon_id];
+
+			for (const Connection &connection : polygon_connections) {
+				_query_task_search_polygon_connections(p_query_task, connection, least_cost_id, least_cost_poly, poly_enter_cost, end_point);
 			}
 			}
 		}
 		}
 
 
+		// Search region external navmesh polygon connections, aka connections to other regions created by outline edge merge or links.
+		for (const Connection &connection : navbases_polygons_external_connections[least_cost_navbase][navbase_local_polygon_id]) {
+			_query_task_search_polygon_connections(p_query_task, connection, least_cost_id, least_cost_poly, poly_enter_cost, end_point);
+		}
+
 		poly_enter_cost = 0;
 		poly_enter_cost = 0;
 		// When the heap of traversable polygons is empty at this point it means the end polygon is
 		// When the heap of traversable polygons is empty at this point it means the end polygon is
 		// unreachable.
 		// unreachable.
@@ -370,6 +383,7 @@ void NavMeshQueries3D::_query_task_build_path_corridor(NavMeshPathQueryTask3D &p
 			// Set as end point the furthest reachable point.
 			// Set as end point the furthest reachable point.
 			end_poly = reachable_end;
 			end_poly = reachable_end;
 			real_t end_d = FLT_MAX;
 			real_t end_d = FLT_MAX;
+
 			for (uint32_t point_id = 2; point_id < end_poly->vertices.size(); point_id++) {
 			for (uint32_t point_id = 2; point_id < end_poly->vertices.size(); point_id++) {
 				Face3 f(end_poly->vertices[0], end_poly->vertices[point_id - 1], end_poly->vertices[point_id]);
 				Face3 f(end_poly->vertices[0], end_poly->vertices[point_id - 1], end_poly->vertices[point_id]);
 				Vector3 spoint = f.get_closest_point_to(p_target_position);
 				Vector3 spoint = f.get_closest_point_to(p_target_position);
@@ -382,6 +396,7 @@ void NavMeshQueries3D::_query_task_build_path_corridor(NavMeshPathQueryTask3D &p
 
 
 			// Search all faces of start polygon as well.
 			// Search all faces of start polygon as well.
 			bool closest_point_on_start_poly = false;
 			bool closest_point_on_start_poly = false;
+
 			for (uint32_t point_id = 2; point_id < begin_poly->vertices.size(); point_id++) {
 			for (uint32_t point_id = 2; point_id < begin_poly->vertices.size(); point_id++) {
 				Face3 f(begin_poly->vertices[0], begin_poly->vertices[point_id - 1], begin_poly->vertices[point_id]);
 				Face3 f(begin_poly->vertices[0], begin_poly->vertices[point_id - 1], begin_poly->vertices[point_id]);
 				Vector3 spoint = f.get_closest_point_to(p_target_position);
 				Vector3 spoint = f.get_closest_point_to(p_target_position);
@@ -407,13 +422,14 @@ void NavMeshQueries3D::_query_task_build_path_corridor(NavMeshPathQueryTask3D &p
 				nav_poly.poly = nullptr;
 				nav_poly.poly = nullptr;
 				nav_poly.traveled_distance = FLT_MAX;
 				nav_poly.traveled_distance = FLT_MAX;
 			}
 			}
-			navigation_polys[begin_poly->id].poly = begin_poly;
-			navigation_polys[begin_poly->id].traveled_distance = 0;
-			least_cost_id = begin_poly->id;
+			uint32_t _bp_id = p_query_task.path_query_slot->poly_to_id[begin_poly];
+			navigation_polys[_bp_id].poly = begin_poly;
+			navigation_polys[_bp_id].traveled_distance = 0;
+			least_cost_id = _bp_id;
 			reachable_end = nullptr;
 			reachable_end = nullptr;
 		} else {
 		} else {
 			// Pop the polygon with the lowest travel cost from the heap of traversable polygons.
 			// Pop the polygon with the lowest travel cost from the heap of traversable polygons.
-			least_cost_id = traversable_polys.pop()->poly->id;
+			least_cost_id = p_query_task.path_query_slot->poly_to_id[traversable_polys.pop()->poly];
 
 
 			// Store the farthest reachable end polygon in case our goal is not reachable.
 			// Store the farthest reachable end polygon in case our goal is not reachable.
 			if (is_reachable) {
 			if (is_reachable) {
@@ -431,6 +447,7 @@ void NavMeshQueries3D::_query_task_build_path_corridor(NavMeshPathQueryTask3D &p
 			}
 			}
 
 
 			if (navigation_polys[least_cost_id].poly->owner->get_self() != least_cost_poly.poly->owner->get_self()) {
 			if (navigation_polys[least_cost_id].poly->owner->get_self() != least_cost_poly.poly->owner->get_self()) {
+				ERR_FAIL_NULL(least_cost_poly.poly->owner);
 				poly_enter_cost = least_cost_poly.poly->owner->get_enter_cost();
 				poly_enter_cost = least_cost_poly.poly->owner->get_enter_cost();
 			}
 			}
 		}
 		}
@@ -441,6 +458,7 @@ void NavMeshQueries3D::_query_task_build_path_corridor(NavMeshPathQueryTask3D &p
 	if (!found_route) {
 	if (!found_route) {
 		real_t end_d = FLT_MAX;
 		real_t end_d = FLT_MAX;
 		// Search all faces of the start polygon for the closest point to our target position.
 		// Search all faces of the start polygon for the closest point to our target position.
+
 		for (uint32_t point_id = 2; point_id < begin_poly->vertices.size(); point_id++) {
 		for (uint32_t point_id = 2; point_id < begin_poly->vertices.size(); point_id++) {
 			Face3 f(begin_poly->vertices[0], begin_poly->vertices[point_id - 1], begin_poly->vertices[point_id]);
 			Face3 f(begin_poly->vertices[0], begin_poly->vertices[point_id - 1], begin_poly->vertices[point_id]);
 			Vector3 spoint = f.get_closest_point_to(p_target_position);
 			Vector3 spoint = f.get_closest_point_to(p_target_position);
@@ -483,7 +501,7 @@ void NavMeshQueries3D::query_task_map_iteration_get_path(NavMeshPathQueryTask3D
 		return;
 		return;
 	}
 	}
 
 
-	_query_task_build_path_corridor(p_query_task);
+	_query_task_build_path_corridor(p_query_task, p_map_iteration);
 
 
 	if (p_query_task.status == NavMeshPathQueryTask3D::TaskStatus::QUERY_FINISHED || p_query_task.status == NavMeshPathQueryTask3D::TaskStatus::QUERY_FAILED) {
 	if (p_query_task.status == NavMeshPathQueryTask3D::TaskStatus::QUERY_FINISHED || p_query_task.status == NavMeshPathQueryTask3D::TaskStatus::QUERY_FAILED) {
 		return;
 		return;
@@ -722,9 +740,9 @@ Vector3 NavMeshQueries3D::map_iteration_get_closest_point_to_segment(const NavMa
 	Vector3 closest_point;
 	Vector3 closest_point;
 	real_t closest_point_distance = FLT_MAX;
 	real_t closest_point_distance = FLT_MAX;
 
 
-	const LocalVector<NavRegionIteration3D> &regions = p_map_iteration.region_iterations;
-	for (const NavRegionIteration3D &region : regions) {
-		for (const Polygon &polygon : region.get_navmesh_polygons()) {
+	const LocalVector<Ref<NavRegionIteration3D>> &regions = p_map_iteration.region_iterations;
+	for (const Ref<NavRegionIteration3D> &region : regions) {
+		for (const Polygon &polygon : region->get_navmesh_polygons()) {
 			// For each face check the distance to the segment.
 			// For each face check the distance to the segment.
 			for (uint32_t point_id = 2; point_id < polygon.vertices.size(); point_id += 1) {
 			for (uint32_t point_id = 2; point_id < polygon.vertices.size(); point_id += 1) {
 				const Face3 face(polygon.vertices[0], polygon.vertices[point_id - 1], polygon.vertices[point_id]);
 				const Face3 face(polygon.vertices[0], polygon.vertices[point_id - 1], polygon.vertices[point_id]);
@@ -802,9 +820,9 @@ ClosestPointQueryResult NavMeshQueries3D::map_iteration_get_closest_point_info(c
 	ClosestPointQueryResult result;
 	ClosestPointQueryResult result;
 	real_t closest_point_distance_squared = FLT_MAX;
 	real_t closest_point_distance_squared = FLT_MAX;
 
 
-	const LocalVector<NavRegionIteration3D> &regions = p_map_iteration.region_iterations;
-	for (const NavRegionIteration3D &region : regions) {
-		for (const Polygon &polygon : region.get_navmesh_polygons()) {
+	const LocalVector<Ref<NavRegionIteration3D>> &regions = p_map_iteration.region_iterations;
+	for (const Ref<NavRegionIteration3D> &region : regions) {
+		for (const Polygon &polygon : region->get_navmesh_polygons()) {
 			Vector3 plane_normal = (polygon.vertices[1] - polygon.vertices[0]).cross(polygon.vertices[2] - polygon.vertices[0]);
 			Vector3 plane_normal = (polygon.vertices[1] - polygon.vertices[0]).cross(polygon.vertices[2] - polygon.vertices[0]);
 			Vector3 closest_on_polygon;
 			Vector3 closest_on_polygon;
 			real_t closest = FLT_MAX;
 			real_t closest = FLT_MAX;
@@ -881,8 +899,8 @@ Vector3 NavMeshQueries3D::map_iteration_get_random_point(const NavMapIteration3D
 	accessible_regions.reserve(p_map_iteration.region_iterations.size());
 	accessible_regions.reserve(p_map_iteration.region_iterations.size());
 
 
 	for (uint32_t i = 0; i < p_map_iteration.region_iterations.size(); i++) {
 	for (uint32_t i = 0; i < p_map_iteration.region_iterations.size(); i++) {
-		const NavRegionIteration3D &region = p_map_iteration.region_iterations[i];
-		if (!region.enabled || (p_navigation_layers & region.navigation_layers) == 0) {
+		const Ref<NavRegionIteration3D> &region = p_map_iteration.region_iterations[i];
+		if (!region->get_enabled() || (p_navigation_layers & region->get_navigation_layers()) == 0) {
 			continue;
 			continue;
 		}
 		}
 		accessible_regions.push_back(i);
 		accessible_regions.push_back(i);
@@ -898,9 +916,9 @@ Vector3 NavMeshQueries3D::map_iteration_get_random_point(const NavMapIteration3D
 		RBMap<real_t, uint32_t> accessible_regions_area_map;
 		RBMap<real_t, uint32_t> accessible_regions_area_map;
 
 
 		for (uint32_t accessible_region_index = 0; accessible_region_index < accessible_regions.size(); accessible_region_index++) {
 		for (uint32_t accessible_region_index = 0; accessible_region_index < accessible_regions.size(); accessible_region_index++) {
-			const NavRegionIteration3D &region = p_map_iteration.region_iterations[accessible_regions[accessible_region_index]];
+			const Ref<NavRegionIteration3D> &region = p_map_iteration.region_iterations[accessible_regions[accessible_region_index]];
 
 
-			real_t region_surface_area = region.surface_area;
+			real_t region_surface_area = region->surface_area;
 
 
 			if (region_surface_area == 0.0f) {
 			if (region_surface_area == 0.0f) {
 				continue;
 				continue;
@@ -921,16 +939,16 @@ Vector3 NavMeshQueries3D::map_iteration_get_random_point(const NavMapIteration3D
 		uint32_t random_region_index = E->value;
 		uint32_t random_region_index = E->value;
 		ERR_FAIL_UNSIGNED_INDEX_V(random_region_index, accessible_regions.size(), Vector3());
 		ERR_FAIL_UNSIGNED_INDEX_V(random_region_index, accessible_regions.size(), Vector3());
 
 
-		const NavRegionIteration3D &random_region = p_map_iteration.region_iterations[accessible_regions[random_region_index]];
+		const Ref<NavRegionIteration3D> &random_region = p_map_iteration.region_iterations[accessible_regions[random_region_index]];
 
 
-		return NavMeshQueries3D::polygons_get_random_point(random_region.navmesh_polygons, p_navigation_layers, p_uniformly);
+		return NavMeshQueries3D::polygons_get_random_point(random_region->navmesh_polygons, p_navigation_layers, p_uniformly);
 
 
 	} else {
 	} else {
 		uint32_t random_region_index = Math::random(int(0), accessible_regions.size() - 1);
 		uint32_t random_region_index = Math::random(int(0), accessible_regions.size() - 1);
 
 
-		const NavRegionIteration3D &random_region = p_map_iteration.region_iterations[accessible_regions[random_region_index]];
+		const Ref<NavRegionIteration3D> &random_region = p_map_iteration.region_iterations[accessible_regions[random_region_index]];
 
 
-		return NavMeshQueries3D::polygons_get_random_point(random_region.navmesh_polygons, p_navigation_layers, p_uniformly);
+		return NavMeshQueries3D::polygons_get_random_point(random_region->navmesh_polygons, p_navigation_layers, p_uniformly);
 	}
 	}
 }
 }
 
 
@@ -1119,8 +1137,15 @@ void NavMeshQueries3D::_query_task_clip_path(NavMeshPathQueryTask3D &p_query_tas
 }
 }
 
 
 bool NavMeshQueries3D::_query_task_is_connection_owner_usable(const NavMeshPathQueryTask3D &p_query_task, const NavBaseIteration3D *p_owner) {
 bool NavMeshQueries3D::_query_task_is_connection_owner_usable(const NavMeshPathQueryTask3D &p_query_task, const NavBaseIteration3D *p_owner) {
+	ERR_FAIL_NULL_V(p_owner, false);
+
 	bool owner_usable = true;
 	bool owner_usable = true;
 
 
+	if (!p_owner->get_enabled()) {
+		owner_usable = false;
+		return owner_usable;
+	}
+
 	if ((p_query_task.navigation_layers & p_owner->get_navigation_layers()) == 0) {
 	if ((p_query_task.navigation_layers & p_owner->get_navigation_layers()) == 0) {
 		// Not usable. No matching bit between task filter bitmask and owner bitmask.
 		// Not usable. No matching bit between task filter bitmask and owner bitmask.
 		owner_usable = false;
 		owner_usable = false;

+ 6 - 1
modules/navigation_3d/3d/nav_mesh_queries_3d.h

@@ -32,6 +32,8 @@
 
 
 #include "../nav_utils_3d.h"
 #include "../nav_utils_3d.h"
 
 
+#include "core/templates/a_hash_map.h"
+
 #include "servers/navigation/navigation_path_query_parameters_3d.h"
 #include "servers/navigation/navigation_path_query_parameters_3d.h"
 #include "servers/navigation/navigation_path_query_result_3d.h"
 #include "servers/navigation/navigation_path_query_result_3d.h"
 #include "servers/navigation/navigation_utilities.h"
 #include "servers/navigation/navigation_utilities.h"
@@ -48,6 +50,7 @@ public:
 		Heap<Nav3D::NavigationPoly *, Nav3D::NavPolyTravelCostGreaterThan, Nav3D::NavPolyHeapIndexer> traversable_polys;
 		Heap<Nav3D::NavigationPoly *, Nav3D::NavPolyTravelCostGreaterThan, Nav3D::NavPolyHeapIndexer> traversable_polys;
 		bool in_use = false;
 		bool in_use = false;
 		uint32_t slot_index = 0;
 		uint32_t slot_index = 0;
+		AHashMap<const Nav3D::Polygon *, uint32_t> poly_to_id;
 	};
 	};
 
 
 	struct NavMeshPathQueryTask3D {
 	struct NavMeshPathQueryTask3D {
@@ -133,7 +136,7 @@ public:
 	static void query_task_map_iteration_get_path(NavMeshPathQueryTask3D &p_query_task, const NavMapIteration3D &p_map_iteration);
 	static void query_task_map_iteration_get_path(NavMeshPathQueryTask3D &p_query_task, const NavMapIteration3D &p_map_iteration);
 	static void _query_task_push_back_point_with_metadata(NavMeshPathQueryTask3D &p_query_task, const Vector3 &p_point, const Nav3D::Polygon *p_point_polygon);
 	static void _query_task_push_back_point_with_metadata(NavMeshPathQueryTask3D &p_query_task, const Vector3 &p_point, const Nav3D::Polygon *p_point_polygon);
 	static void _query_task_find_start_end_positions(NavMeshPathQueryTask3D &p_query_task, const NavMapIteration3D &p_map_iteration);
 	static void _query_task_find_start_end_positions(NavMeshPathQueryTask3D &p_query_task, const NavMapIteration3D &p_map_iteration);
-	static void _query_task_build_path_corridor(NavMeshPathQueryTask3D &p_query_task);
+	static void _query_task_build_path_corridor(NavMeshPathQueryTask3D &p_query_task, const NavMapIteration3D &p_map_iteration);
 	static void _query_task_post_process_corridorfunnel(NavMeshPathQueryTask3D &p_query_task);
 	static void _query_task_post_process_corridorfunnel(NavMeshPathQueryTask3D &p_query_task);
 	static void _query_task_post_process_edgecentered(NavMeshPathQueryTask3D &p_query_task);
 	static void _query_task_post_process_edgecentered(NavMeshPathQueryTask3D &p_query_task);
 	static void _query_task_post_process_nopostprocessing(NavMeshPathQueryTask3D &p_query_task);
 	static void _query_task_post_process_nopostprocessing(NavMeshPathQueryTask3D &p_query_task);
@@ -141,6 +144,8 @@ public:
 	static void _query_task_simplified_path_points(NavMeshPathQueryTask3D &p_query_task);
 	static void _query_task_simplified_path_points(NavMeshPathQueryTask3D &p_query_task);
 	static bool _query_task_is_connection_owner_usable(const NavMeshPathQueryTask3D &p_query_task, const NavBaseIteration3D *p_owner);
 	static bool _query_task_is_connection_owner_usable(const NavMeshPathQueryTask3D &p_query_task, const NavBaseIteration3D *p_owner);
 
 
+	static void _query_task_search_polygon_connections(NavMeshPathQueryTask3D &p_query_task, const Nav3D::Connection &p_connection, uint32_t p_least_cost_id, const Nav3D::NavigationPoly &p_least_cost_poly, real_t p_poly_enter_cost, const Vector3 &p_end_point);
+
 	static void simplify_path_segment(int p_start_inx, int p_end_inx, const LocalVector<Vector3> &p_points, real_t p_epsilon, LocalVector<uint32_t> &r_simplified_path_indices);
 	static void simplify_path_segment(int p_start_inx, int p_end_inx, const LocalVector<Vector3> &p_points, real_t p_epsilon, LocalVector<uint32_t> &r_simplified_path_indices);
 	static LocalVector<uint32_t> get_simplified_path_indices(const LocalVector<Vector3> &p_path, real_t p_epsilon);
 	static LocalVector<uint32_t> get_simplified_path_indices(const LocalVector<Vector3> &p_path, real_t p_epsilon);
 };
 };

+ 256 - 0
modules/navigation_3d/3d/nav_region_builder_3d.cpp

@@ -0,0 +1,256 @@
+/**************************************************************************/
+/*  nav_region_builder_3d.cpp                                             */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#include "nav_region_builder_3d.h"
+
+#include "../nav_map_3d.h"
+#include "../nav_region_3d.h"
+#include "nav_region_iteration_3d.h"
+
+using namespace Nav3D;
+
+void NavRegionBuilder3D::build_iteration(NavRegionIterationBuild3D &r_build) {
+	PerformanceData &performance_data = r_build.performance_data;
+
+	performance_data.pm_polygon_count = 0;
+	performance_data.pm_edge_count = 0;
+	performance_data.pm_edge_merge_count = 0;
+	performance_data.pm_edge_connection_count = 0;
+	performance_data.pm_edge_free_count = 0;
+
+	_build_step_process_navmesh_data(r_build);
+
+	_build_step_find_edge_connection_pairs(r_build);
+
+	_build_step_merge_edge_connection_pairs(r_build);
+
+	_build_update_iteration(r_build);
+}
+
+void NavRegionBuilder3D::_build_step_process_navmesh_data(NavRegionIterationBuild3D &r_build) {
+	Vector<Vector3> _navmesh_vertices = r_build.navmesh_data.vertices;
+	Vector<Vector<int>> _navmesh_polygons = r_build.navmesh_data.polygons;
+
+	if (_navmesh_vertices.is_empty() || _navmesh_polygons.is_empty()) {
+		return;
+	}
+
+	PerformanceData &performance_data = r_build.performance_data;
+	Ref<NavRegionIteration3D> region_iteration = r_build.region_iteration;
+
+	const Transform3D &region_transform = region_iteration->transform;
+	LocalVector<Nav3D::Polygon> &navmesh_polygons = region_iteration->navmesh_polygons;
+
+	const int vertex_count = _navmesh_vertices.size();
+
+	const Vector3 *vertices_ptr = _navmesh_vertices.ptr();
+	const Vector<int> *polygons_ptr = _navmesh_polygons.ptr();
+
+	navmesh_polygons.resize(_navmesh_polygons.size());
+
+	real_t _new_region_surface_area = 0.0;
+	AABB _new_region_bounds;
+
+	bool first_vertex = true;
+
+	for (uint32_t i = 0; i < navmesh_polygons.size(); i++) {
+		Polygon &polygon = navmesh_polygons[i];
+		polygon.id = i;
+		polygon.owner = region_iteration.ptr();
+		polygon.surface_area = 0.0;
+
+		Vector<int> polygon_indices = polygons_ptr[i];
+
+		int polygon_size = polygon_indices.size();
+		if (polygon_size < 3) {
+			continue;
+		}
+
+		const int *indices_ptr = polygon_indices.ptr();
+
+		bool polygon_valid = true;
+
+		polygon.vertices.resize(polygon_size);
+
+		{
+			real_t _new_polygon_surface_area = 0.0;
+
+			for (int j(2); j < polygon_size; j++) {
+				const Face3 face = Face3(
+						region_transform.xform(vertices_ptr[indices_ptr[0]]),
+						region_transform.xform(vertices_ptr[indices_ptr[j - 1]]),
+						region_transform.xform(vertices_ptr[indices_ptr[j]]));
+
+				_new_polygon_surface_area += face.get_area();
+			}
+
+			polygon.surface_area = _new_polygon_surface_area;
+			_new_region_surface_area += _new_polygon_surface_area;
+		}
+
+		for (int j(0); j < polygon_size; j++) {
+			int vertex_index = indices_ptr[j];
+			if (vertex_index < 0 || vertex_index >= vertex_count) {
+				polygon_valid = false;
+				break;
+			}
+
+			const Vector3 point_position = region_transform.xform(vertices_ptr[vertex_index]);
+			polygon.vertices[j] = point_position;
+
+			if (first_vertex) {
+				first_vertex = false;
+				_new_region_bounds.position = point_position;
+			} else {
+				_new_region_bounds.expand_to(point_position);
+			}
+		}
+
+		if (!polygon_valid) {
+			polygon.surface_area = 0.0;
+			polygon.vertices.clear();
+			ERR_FAIL_COND_MSG(!polygon_valid, "Corrupted navigation mesh set on region. The indices of a polygon are out of range.");
+		}
+	}
+
+	region_iteration->surface_area = _new_region_surface_area;
+	region_iteration->bounds = _new_region_bounds;
+
+	performance_data.pm_polygon_count = navmesh_polygons.size();
+}
+
+Nav3D::PointKey NavRegionBuilder3D::get_point_key(const Vector3 &p_pos, const Vector3 &p_cell_size) {
+	const int x = static_cast<int>(Math::floor(p_pos.x / p_cell_size.x));
+	const int y = static_cast<int>(Math::floor(p_pos.y / p_cell_size.y));
+	const int z = static_cast<int>(Math::floor(p_pos.z / p_cell_size.z));
+
+	PointKey p;
+	p.key = 0;
+	p.x = x;
+	p.y = y;
+	p.z = z;
+	return p;
+}
+
+Nav3D::EdgeKey NavRegionBuilder3D::get_edge_key(const Vector3 &p_vertex1, const Vector3 &p_vertex2, const Vector3 &p_cell_size) {
+	EdgeKey ek(get_point_key(p_vertex1, p_cell_size), get_point_key(p_vertex2, p_cell_size));
+	return ek;
+}
+
+void NavRegionBuilder3D::_build_step_find_edge_connection_pairs(NavRegionIterationBuild3D &r_build) {
+	PerformanceData &performance_data = r_build.performance_data;
+
+	const Vector3 &map_cell_size = r_build.map_cell_size;
+	Ref<NavRegionIteration3D> region_iteration = r_build.region_iteration;
+	LocalVector<Nav3D::Polygon> &navmesh_polygons = region_iteration->navmesh_polygons;
+
+	HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
+	connection_pairs_map.clear();
+
+	region_iteration->internal_connections.clear();
+	region_iteration->internal_connections.resize(navmesh_polygons.size());
+
+	region_iteration->external_edges.clear();
+
+	int free_edges_count = 0;
+
+	for (Polygon &poly : region_iteration->navmesh_polygons) {
+		for (uint32_t p = 0; p < poly.vertices.size(); p++) {
+			const int next_point = (p + 1) % poly.vertices.size();
+			const EdgeKey ek = get_edge_key(poly.vertices[p], poly.vertices[next_point], map_cell_size);
+
+			HashMap<EdgeKey, EdgeConnectionPair, EdgeKey>::Iterator pair_it = connection_pairs_map.find(ek);
+			if (!pair_it) {
+				pair_it = connection_pairs_map.insert(ek, EdgeConnectionPair());
+				performance_data.pm_edge_count += 1;
+				++free_edges_count;
+			}
+			EdgeConnectionPair &pair = pair_it->value;
+			if (pair.size < 2) {
+				// Add the polygon/edge tuple to this key.
+				Connection new_connection;
+				new_connection.polygon = &poly;
+				new_connection.edge = p;
+				new_connection.pathway_start = poly.vertices[p];
+				new_connection.pathway_end = poly.vertices[next_point];
+
+				pair.connections[pair.size] = new_connection;
+				++pair.size;
+				if (pair.size == 2) {
+					--free_edges_count;
+				}
+
+			} 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.");
+			}
+		}
+	}
+
+	performance_data.pm_edge_free_count = free_edges_count;
+}
+
+void NavRegionBuilder3D::_build_step_merge_edge_connection_pairs(NavRegionIterationBuild3D &r_build) {
+	PerformanceData &performance_data = r_build.performance_data;
+
+	Ref<NavRegionIteration3D> region_iteration = r_build.region_iteration;
+
+	HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
+
+	for (const KeyValue<EdgeKey, EdgeConnectionPair> &pair_it : connection_pairs_map) {
+		const EdgeConnectionPair &pair = pair_it.value;
+		if (pair.size == 2) {
+			// Connect edge that are shared in different polygons.
+			const Connection &c1 = pair.connections[0];
+			const Connection &c2 = pair.connections[1];
+			region_iteration->internal_connections[c1.polygon->id].push_back(c2);
+			region_iteration->internal_connections[c2.polygon->id].push_back(c1);
+			performance_data.pm_edge_merge_count += 1;
+
+		} else {
+			ERR_FAIL_COND_MSG(pair.size != 1, vformat("Number of connection != 1. Found: %d", pair.size));
+
+			const Connection &connection = pair.connections[0];
+
+			ConnectableEdge ce;
+			ce.ek = pair_it.key;
+			ce.polygon_index = connection.polygon->id;
+			ce.pathway_start = connection.pathway_start;
+			ce.pathway_end = connection.pathway_end;
+
+			region_iteration->external_edges.push_back(ce);
+		}
+	}
+}
+
+void NavRegionBuilder3D::_build_update_iteration(NavRegionIterationBuild3D &r_build) {
+	ERR_FAIL_NULL(r_build.region);
+	// Stub. End of the build.
+}

+ 48 - 0
modules/navigation_3d/3d/nav_region_builder_3d.h

@@ -0,0 +1,48 @@
+/**************************************************************************/
+/*  nav_region_builder_3d.h                                               */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#pragma once
+
+#include "../nav_utils_3d.h"
+
+struct NavRegionIterationBuild3D;
+
+class NavRegionBuilder3D {
+	static void _build_step_process_navmesh_data(NavRegionIterationBuild3D &r_build);
+	static void _build_step_find_edge_connection_pairs(NavRegionIterationBuild3D &r_build);
+	static void _build_step_merge_edge_connection_pairs(NavRegionIterationBuild3D &r_build);
+	static void _build_update_iteration(NavRegionIterationBuild3D &r_build);
+
+public:
+	static Nav3D::PointKey get_point_key(const Vector3 &p_pos, const Vector3 &p_cell_size);
+	static Nav3D::EdgeKey get_edge_key(const Vector3 &p_vertex1, const Vector3 &p_vertex2, const Vector3 &p_cell_size);
+
+	static void build_iteration(NavRegionIterationBuild3D &r_build);
+};

+ 47 - 1
modules/navigation_3d/3d/nav_region_iteration_3d.h

@@ -32,15 +32,61 @@
 
 
 #include "../nav_utils_3d.h"
 #include "../nav_utils_3d.h"
 #include "nav_base_iteration_3d.h"
 #include "nav_base_iteration_3d.h"
+#include "scene/resources/navigation_mesh.h"
 
 
 #include "core/math/aabb.h"
 #include "core/math/aabb.h"
 
 
-struct NavRegionIteration3D : NavBaseIteration3D {
+class NavRegion3D;
+class NavRegionIteration3D;
+
+struct NavRegionIterationBuild3D {
+	Nav3D::PerformanceData performance_data;
+
+	NavRegion3D *region = nullptr;
+
+	Vector3 map_cell_size;
+	Transform3D region_transform;
+
+	struct NavMeshData {
+		Vector<Vector3> vertices;
+		Vector<Vector<int>> polygons;
+
+		void clear() {
+			vertices.clear();
+			polygons.clear();
+		}
+	} navmesh_data;
+
+	Ref<NavRegionIteration3D> region_iteration;
+
+	HashMap<Nav3D::EdgeKey, Nav3D::EdgeConnectionPair, Nav3D::EdgeKey> iter_connection_pairs_map;
+
+	void reset() {
+		performance_data.reset();
+
+		navmesh_data.clear();
+		region_iteration = Ref<NavRegionIteration3D>();
+		iter_connection_pairs_map.clear();
+	}
+};
+
+class NavRegionIteration3D : public NavBaseIteration3D {
+	GDCLASS(NavRegionIteration3D, NavBaseIteration3D);
+
+public:
 	Transform3D transform;
 	Transform3D transform;
 	real_t surface_area = 0.0;
 	real_t surface_area = 0.0;
 	AABB bounds;
 	AABB bounds;
+	LocalVector<Nav3D::ConnectableEdge> external_edges;
 
 
 	const Transform3D &get_transform() const { return transform; }
 	const Transform3D &get_transform() const { return transform; }
 	real_t get_surface_area() const { return surface_area; }
 	real_t get_surface_area() const { return surface_area; }
 	AABB get_bounds() const { return bounds; }
 	AABB get_bounds() const { return bounds; }
+	const LocalVector<Nav3D::ConnectableEdge> &get_external_edges() const { return external_edges; }
+
+	virtual ~NavRegionIteration3D() override {
+		external_edges.clear();
+		navmesh_polygons.clear();
+		internal_connections.clear();
+	}
 };
 };

+ 60 - 32
modules/navigation_3d/nav_link_3d.cpp

@@ -44,7 +44,7 @@ void NavLink3D::set_map(NavMap3D *p_map) {
 	}
 	}
 
 
 	map = p_map;
 	map = p_map;
-	link_dirty = true;
+	iteration_dirty = true;
 
 
 	if (map) {
 	if (map) {
 		map->add_link(this);
 		map->add_link(this);
@@ -57,9 +57,7 @@ void NavLink3D::set_enabled(bool p_enabled) {
 		return;
 		return;
 	}
 	}
 	enabled = p_enabled;
 	enabled = p_enabled;
-
-	// TODO: This should not require a full rebuild as the link has not really changed.
-	link_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 }
 }
@@ -69,7 +67,7 @@ void NavLink3D::set_bidirectional(bool p_bidirectional) {
 		return;
 		return;
 	}
 	}
 	bidirectional = p_bidirectional;
 	bidirectional = p_bidirectional;
-	link_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 }
 }
@@ -79,7 +77,7 @@ void NavLink3D::set_start_position(const Vector3 p_position) {
 		return;
 		return;
 	}
 	}
 	start_position = p_position;
 	start_position = p_position;
-	link_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 }
 }
@@ -89,7 +87,7 @@ void NavLink3D::set_end_position(const Vector3 p_position) {
 		return;
 		return;
 	}
 	}
 	end_position = p_position;
 	end_position = p_position;
-	link_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 }
 }
@@ -99,7 +97,7 @@ void NavLink3D::set_navigation_layers(uint32_t p_navigation_layers) {
 		return;
 		return;
 	}
 	}
 	navigation_layers = p_navigation_layers;
 	navigation_layers = p_navigation_layers;
-	link_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 }
 }
@@ -110,7 +108,7 @@ void NavLink3D::set_enter_cost(real_t p_enter_cost) {
 		return;
 		return;
 	}
 	}
 	enter_cost = new_enter_cost;
 	enter_cost = new_enter_cost;
-	link_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 }
 }
@@ -121,7 +119,7 @@ void NavLink3D::set_travel_cost(real_t p_travel_cost) {
 		return;
 		return;
 	}
 	}
 	travel_cost = new_travel_cost;
 	travel_cost = new_travel_cost;
-	link_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 }
 }
@@ -131,21 +129,59 @@ void NavLink3D::set_owner_id(ObjectID p_owner_id) {
 		return;
 		return;
 	}
 	}
 	owner_id = p_owner_id;
 	owner_id = p_owner_id;
-	link_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 }
 }
 
 
-bool NavLink3D::is_dirty() const {
-	return link_dirty;
+bool NavLink3D::sync() {
+	bool requires_map_update = false;
+	if (!map) {
+		return requires_map_update;
+	}
+
+	if (iteration_dirty && !iteration_building && !iteration_ready) {
+		_build_iteration();
+		iteration_ready = false;
+		requires_map_update = true;
+	}
+
+	return requires_map_update;
 }
 }
 
 
-void NavLink3D::sync() {
-	if (link_dirty) {
-		iteration_id = iteration_id % UINT32_MAX + 1;
+void NavLink3D::_build_iteration() {
+	if (!iteration_dirty || iteration_building || iteration_ready) {
+		return;
 	}
 	}
 
 
-	link_dirty = false;
+	iteration_dirty = false;
+	iteration_building = true;
+	iteration_ready = false;
+
+	Ref<NavLinkIteration3D> new_iteration;
+	new_iteration.instantiate();
+
+	new_iteration->navigation_layers = get_navigation_layers();
+	new_iteration->enter_cost = get_enter_cost();
+	new_iteration->travel_cost = get_travel_cost();
+	new_iteration->owner_object_id = get_owner_id();
+	new_iteration->owner_type = get_type();
+	new_iteration->owner_rid = get_self();
+
+	new_iteration->enabled = get_enabled();
+	new_iteration->start_position = get_start_position();
+	new_iteration->end_position = get_end_position();
+	new_iteration->bidirectional = is_bidirectional();
+
+	RWLockWrite write_lock(iteration_rwlock);
+	ERR_FAIL_COND(iteration.is_null());
+	iteration = Ref<NavLinkIteration3D>();
+	DEV_ASSERT(iteration.is_null());
+	iteration = new_iteration;
+	iteration_id = iteration_id % UINT32_MAX + 1;
+
+	iteration_building = false;
+	iteration_ready = true;
 }
 }
 
 
 void NavLink3D::request_sync() {
 void NavLink3D::request_sync() {
@@ -160,27 +196,19 @@ void NavLink3D::cancel_sync_request() {
 	}
 	}
 }
 }
 
 
+Ref<NavLinkIteration3D> NavLink3D::get_iteration() {
+	RWLockRead read_lock(iteration_rwlock);
+	return iteration;
+}
+
 NavLink3D::NavLink3D() :
 NavLink3D::NavLink3D() :
 		sync_dirty_request_list_element(this) {
 		sync_dirty_request_list_element(this) {
 	type = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_LINK;
 	type = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_LINK;
+	iteration.instantiate();
 }
 }
 
 
 NavLink3D::~NavLink3D() {
 NavLink3D::~NavLink3D() {
 	cancel_sync_request();
 	cancel_sync_request();
-}
-
-void NavLink3D::get_iteration_update(NavLinkIteration3D &r_iteration) {
-	r_iteration.navigation_layers = get_navigation_layers();
-	r_iteration.enter_cost = get_enter_cost();
-	r_iteration.travel_cost = get_travel_cost();
-	r_iteration.owner_object_id = get_owner_id();
-	r_iteration.owner_type = get_type();
-	r_iteration.owner_rid = get_self();
-
-	r_iteration.enabled = get_enabled();
-	r_iteration.start_position = get_start_position();
-	r_iteration.end_position = get_end_position();
-	r_iteration.bidirectional = is_bidirectional();
 
 
-	r_iteration.navmesh_polygons.clear();
+	iteration = Ref<NavLinkIteration3D>();
 }
 }

+ 21 - 6
modules/navigation_3d/nav_link_3d.h

@@ -34,7 +34,10 @@
 #include "nav_base_3d.h"
 #include "nav_base_3d.h"
 #include "nav_utils_3d.h"
 #include "nav_utils_3d.h"
 
 
-struct NavLinkIteration3D : NavBaseIteration3D {
+class NavLinkIteration3D : public NavBaseIteration3D {
+	GDCLASS(NavLinkIteration3D, NavBaseIteration3D);
+
+public:
 	bool bidirectional = true;
 	bool bidirectional = true;
 	Vector3 start_position;
 	Vector3 start_position;
 	Vector3 end_position;
 	Vector3 end_position;
@@ -42,6 +45,11 @@ struct NavLinkIteration3D : NavBaseIteration3D {
 	Vector3 get_start_position() const { return start_position; }
 	Vector3 get_start_position() const { return start_position; }
 	Vector3 get_end_position() const { return end_position; }
 	Vector3 get_end_position() const { return end_position; }
 	bool is_bidirectional() const { return bidirectional; }
 	bool is_bidirectional() const { return bidirectional; }
+
+	virtual ~NavLinkIteration3D() override {
+		navmesh_polygons.clear();
+		internal_connections.clear();
+	}
 };
 };
 
 
 #include "core/templates/self_list.h"
 #include "core/templates/self_list.h"
@@ -53,12 +61,20 @@ class NavLink3D : public NavBase3D {
 	Vector3 end_position;
 	Vector3 end_position;
 	bool enabled = true;
 	bool enabled = true;
 
 
-	bool link_dirty = true;
-
 	SelfList<NavLink3D> sync_dirty_request_list_element;
 	SelfList<NavLink3D> sync_dirty_request_list_element;
 
 
 	uint32_t iteration_id = 0;
 	uint32_t iteration_id = 0;
 
 
+	mutable RWLock iteration_rwlock;
+	Ref<NavLinkIteration3D> iteration;
+
+	bool iteration_dirty = true;
+	bool iteration_building = false;
+	bool iteration_ready = false;
+
+	void _build_iteration();
+	void _sync_iteration();
+
 public:
 public:
 	NavLink3D();
 	NavLink3D();
 	~NavLink3D();
 	~NavLink3D();
@@ -94,10 +110,9 @@ public:
 	virtual void set_travel_cost(real_t p_travel_cost) override;
 	virtual void set_travel_cost(real_t p_travel_cost) override;
 	virtual void set_owner_id(ObjectID p_owner_id) override;
 	virtual void set_owner_id(ObjectID p_owner_id) override;
 
 
-	bool is_dirty() const;
-	void sync();
+	bool sync();
 	void request_sync();
 	void request_sync();
 	void cancel_sync_request();
 	void cancel_sync_request();
 
 
-	void get_iteration_update(NavLinkIteration3D &r_iteration);
+	Ref<NavLinkIteration3D> get_iteration();
 };
 };

+ 91 - 68
modules/navigation_3d/nav_map_3d.cpp

@@ -231,6 +231,8 @@ ClosestPointQueryResult NavMap3D::get_closest_point_info(const Vector3 &p_point)
 }
 }
 
 
 void NavMap3D::add_region(NavRegion3D *p_region) {
 void NavMap3D::add_region(NavRegion3D *p_region) {
+	DEV_ASSERT(!regions.has(p_region));
+
 	regions.push_back(p_region);
 	regions.push_back(p_region);
 	iteration_dirty = true;
 	iteration_dirty = true;
 }
 }
@@ -242,6 +244,8 @@ void NavMap3D::remove_region(NavRegion3D *p_region) {
 }
 }
 
 
 void NavMap3D::add_link(NavLink3D *p_link) {
 void NavMap3D::add_link(NavLink3D *p_link) {
+	DEV_ASSERT(!links.has(p_link));
+
 	links.push_back(p_link);
 	links.push_back(p_link);
 	iteration_dirty = true;
 	iteration_dirty = true;
 }
 }
@@ -365,49 +369,22 @@ void NavMap3D::_build_iteration() {
 	iteration_build.edge_connection_margin = get_edge_connection_margin();
 	iteration_build.edge_connection_margin = get_edge_connection_margin();
 	iteration_build.link_connection_radius = get_link_connection_radius();
 	iteration_build.link_connection_radius = get_link_connection_radius();
 
 
-	uint32_t enabled_region_count = 0;
-	uint32_t enabled_link_count = 0;
-
-	for (NavRegion3D *region : regions) {
-		if (!region->get_enabled()) {
-			continue;
-		}
-		enabled_region_count++;
-	}
-	for (NavLink3D *link : links) {
-		if (!link->get_enabled()) {
-			continue;
-		}
-		enabled_link_count++;
-	}
+	next_map_iteration.clear();
 
 
-	next_map_iteration.region_ptr_to_region_id.clear();
-
-	next_map_iteration.region_iterations.clear();
-	next_map_iteration.link_iterations.clear();
-
-	next_map_iteration.region_iterations.resize(enabled_region_count);
-	next_map_iteration.link_iterations.resize(enabled_link_count);
+	next_map_iteration.region_iterations.resize(regions.size());
+	next_map_iteration.link_iterations.resize(links.size());
 
 
 	uint32_t region_id_count = 0;
 	uint32_t region_id_count = 0;
 	uint32_t link_id_count = 0;
 	uint32_t link_id_count = 0;
 
 
 	for (NavRegion3D *region : regions) {
 	for (NavRegion3D *region : regions) {
-		if (!region->get_enabled()) {
-			continue;
-		}
-		NavRegionIteration3D &region_iteration = next_map_iteration.region_iterations[region_id_count];
-		region_iteration.id = region_id_count++;
-		region->get_iteration_update(region_iteration);
-		next_map_iteration.region_ptr_to_region_id[region] = (uint32_t)region_iteration.id;
+		const Ref<NavRegionIteration3D> region_iteration = region->get_iteration();
+		next_map_iteration.region_iterations[region_id_count++] = region_iteration;
+		next_map_iteration.region_ptr_to_region_iteration[region] = region_iteration;
 	}
 	}
 	for (NavLink3D *link : links) {
 	for (NavLink3D *link : links) {
-		if (!link->get_enabled()) {
-			continue;
-		}
-		NavLinkIteration3D &link_iteration = next_map_iteration.link_iterations[link_id_count];
-		link_iteration.id = link_id_count++;
-		link->get_iteration_update(link_iteration);
+		const Ref<NavLinkIteration3D> link_iteration = link->get_iteration();
+		next_map_iteration.link_iterations[link_id_count++] = link_iteration;
 	}
 	}
 
 
 	next_map_iteration.map_up = get_up();
 	next_map_iteration.map_up = get_up();
@@ -435,9 +412,6 @@ void NavMap3D::_sync_iteration() {
 		return;
 		return;
 	}
 	}
 
 
-	performance_data.pm_polygon_count = iteration_build.performance_data.pm_polygon_count;
-	performance_data.pm_edge_count = iteration_build.performance_data.pm_edge_count;
-	performance_data.pm_edge_merge_count = iteration_build.performance_data.pm_edge_merge_count;
 	performance_data.pm_edge_connection_count = iteration_build.performance_data.pm_edge_connection_count;
 	performance_data.pm_edge_connection_count = iteration_build.performance_data.pm_edge_connection_count;
 	performance_data.pm_edge_free_count = iteration_build.performance_data.pm_edge_free_count;
 	performance_data.pm_edge_free_count = iteration_build.performance_data.pm_edge_free_count;
 
 
@@ -459,6 +433,8 @@ void NavMap3D::sync() {
 	performance_data.pm_link_count = links.size();
 	performance_data.pm_link_count = links.size();
 	performance_data.pm_obstacle_count = obstacles.size();
 	performance_data.pm_obstacle_count = obstacles.size();
 
 
+	_sync_async_tasks();
+
 	_sync_dirty_map_update_requests();
 	_sync_dirty_map_update_requests();
 
 
 	if (iteration_dirty && !iteration_building && !iteration_ready) {
 	if (iteration_dirty && !iteration_building && !iteration_ready) {
@@ -482,6 +458,16 @@ void NavMap3D::sync() {
 	map_settings_dirty = false;
 	map_settings_dirty = false;
 
 
 	_sync_avoidance();
 	_sync_avoidance();
+
+	performance_data.pm_polygon_count = 0;
+	performance_data.pm_edge_count = 0;
+	performance_data.pm_edge_merge_count = 0;
+
+	for (NavRegion3D *region : regions) {
+		performance_data.pm_polygon_count += region->get_pm_polygon_count();
+		performance_data.pm_edge_count += region->get_pm_edge_count();
+		performance_data.pm_edge_merge_count += region->get_pm_edge_merge_count();
+	}
 }
 }
 
 
 void NavMap3D::_sync_avoidance() {
 void NavMap3D::_sync_avoidance() {
@@ -671,9 +657,9 @@ int NavMap3D::get_region_connections_count(NavRegion3D *p_region) const {
 
 
 	GET_MAP_ITERATION_CONST();
 	GET_MAP_ITERATION_CONST();
 
 
-	HashMap<NavRegion3D *, uint32_t>::ConstIterator found_id = map_iteration.region_ptr_to_region_id.find(p_region);
+	HashMap<NavRegion3D *, Ref<NavRegionIteration3D>>::ConstIterator found_id = map_iteration.region_ptr_to_region_iteration.find(p_region);
 	if (found_id) {
 	if (found_id) {
-		HashMap<uint32_t, LocalVector<Edge::Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value);
+		HashMap<const NavBaseIteration3D *, LocalVector<Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value.ptr());
 		if (found_connections) {
 		if (found_connections) {
 			return found_connections->value.size();
 			return found_connections->value.size();
 		}
 		}
@@ -687,9 +673,9 @@ Vector3 NavMap3D::get_region_connection_pathway_start(NavRegion3D *p_region, int
 
 
 	GET_MAP_ITERATION_CONST();
 	GET_MAP_ITERATION_CONST();
 
 
-	HashMap<NavRegion3D *, uint32_t>::ConstIterator found_id = map_iteration.region_ptr_to_region_id.find(p_region);
+	HashMap<NavRegion3D *, Ref<NavRegionIteration3D>>::ConstIterator found_id = map_iteration.region_ptr_to_region_iteration.find(p_region);
 	if (found_id) {
 	if (found_id) {
-		HashMap<uint32_t, LocalVector<Edge::Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value);
+		HashMap<const NavBaseIteration3D *, LocalVector<Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value.ptr());
 		if (found_connections) {
 		if (found_connections) {
 			ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector3());
 			ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector3());
 			return found_connections->value[p_connection_id].pathway_start;
 			return found_connections->value[p_connection_id].pathway_start;
@@ -704,9 +690,9 @@ Vector3 NavMap3D::get_region_connection_pathway_end(NavRegion3D *p_region, int p
 
 
 	GET_MAP_ITERATION_CONST();
 	GET_MAP_ITERATION_CONST();
 
 
-	HashMap<NavRegion3D *, uint32_t>::ConstIterator found_id = map_iteration.region_ptr_to_region_id.find(p_region);
+	HashMap<NavRegion3D *, Ref<NavRegionIteration3D>>::ConstIterator found_id = map_iteration.region_ptr_to_region_iteration.find(p_region);
 	if (found_id) {
 	if (found_id) {
-		HashMap<uint32_t, LocalVector<Edge::Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value);
+		HashMap<const NavBaseIteration3D *, LocalVector<Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value.ptr());
 		if (found_connections) {
 		if (found_connections) {
 			ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector3());
 			ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector3());
 			return found_connections->value[p_connection_id].pathway_end;
 			return found_connections->value[p_connection_id].pathway_end;
@@ -720,56 +706,60 @@ void NavMap3D::add_region_sync_dirty_request(SelfList<NavRegion3D> *p_sync_reque
 	if (p_sync_request->in_list()) {
 	if (p_sync_request->in_list()) {
 		return;
 		return;
 	}
 	}
-	sync_dirty_requests.regions.add(p_sync_request);
+	RWLockWrite write_lock(sync_dirty_requests.regions.rwlock);
+	sync_dirty_requests.regions.list.add(p_sync_request);
 }
 }
 
 
 void NavMap3D::add_link_sync_dirty_request(SelfList<NavLink3D> *p_sync_request) {
 void NavMap3D::add_link_sync_dirty_request(SelfList<NavLink3D> *p_sync_request) {
 	if (p_sync_request->in_list()) {
 	if (p_sync_request->in_list()) {
 		return;
 		return;
 	}
 	}
-	sync_dirty_requests.links.add(p_sync_request);
+	RWLockWrite write_lock(sync_dirty_requests.links.rwlock);
+	sync_dirty_requests.links.list.add(p_sync_request);
 }
 }
 
 
 void NavMap3D::add_agent_sync_dirty_request(SelfList<NavAgent3D> *p_sync_request) {
 void NavMap3D::add_agent_sync_dirty_request(SelfList<NavAgent3D> *p_sync_request) {
 	if (p_sync_request->in_list()) {
 	if (p_sync_request->in_list()) {
 		return;
 		return;
 	}
 	}
-	sync_dirty_requests.agents.add(p_sync_request);
+	sync_dirty_requests.agents.list.add(p_sync_request);
 }
 }
 
 
 void NavMap3D::add_obstacle_sync_dirty_request(SelfList<NavObstacle3D> *p_sync_request) {
 void NavMap3D::add_obstacle_sync_dirty_request(SelfList<NavObstacle3D> *p_sync_request) {
 	if (p_sync_request->in_list()) {
 	if (p_sync_request->in_list()) {
 		return;
 		return;
 	}
 	}
-	sync_dirty_requests.obstacles.add(p_sync_request);
+	sync_dirty_requests.obstacles.list.add(p_sync_request);
 }
 }
 
 
 void NavMap3D::remove_region_sync_dirty_request(SelfList<NavRegion3D> *p_sync_request) {
 void NavMap3D::remove_region_sync_dirty_request(SelfList<NavRegion3D> *p_sync_request) {
 	if (!p_sync_request->in_list()) {
 	if (!p_sync_request->in_list()) {
 		return;
 		return;
 	}
 	}
-	sync_dirty_requests.regions.remove(p_sync_request);
+	RWLockWrite write_lock(sync_dirty_requests.regions.rwlock);
+	sync_dirty_requests.regions.list.remove(p_sync_request);
 }
 }
 
 
 void NavMap3D::remove_link_sync_dirty_request(SelfList<NavLink3D> *p_sync_request) {
 void NavMap3D::remove_link_sync_dirty_request(SelfList<NavLink3D> *p_sync_request) {
 	if (!p_sync_request->in_list()) {
 	if (!p_sync_request->in_list()) {
 		return;
 		return;
 	}
 	}
-	sync_dirty_requests.links.remove(p_sync_request);
+	RWLockWrite write_lock(sync_dirty_requests.links.rwlock);
+	sync_dirty_requests.links.list.remove(p_sync_request);
 }
 }
 
 
 void NavMap3D::remove_agent_sync_dirty_request(SelfList<NavAgent3D> *p_sync_request) {
 void NavMap3D::remove_agent_sync_dirty_request(SelfList<NavAgent3D> *p_sync_request) {
 	if (!p_sync_request->in_list()) {
 	if (!p_sync_request->in_list()) {
 		return;
 		return;
 	}
 	}
-	sync_dirty_requests.agents.remove(p_sync_request);
+	sync_dirty_requests.agents.list.remove(p_sync_request);
 }
 }
 
 
 void NavMap3D::remove_obstacle_sync_dirty_request(SelfList<NavObstacle3D> *p_sync_request) {
 void NavMap3D::remove_obstacle_sync_dirty_request(SelfList<NavObstacle3D> *p_sync_request) {
 	if (!p_sync_request->in_list()) {
 	if (!p_sync_request->in_list()) {
 		return;
 		return;
 	}
 	}
-	sync_dirty_requests.obstacles.remove(p_sync_request);
+	sync_dirty_requests.obstacles.list.remove(p_sync_request);
 }
 }
 
 
 void NavMap3D::_sync_dirty_map_update_requests() {
 void NavMap3D::_sync_dirty_map_update_requests() {
@@ -781,41 +771,69 @@ void NavMap3D::_sync_dirty_map_update_requests() {
 		iteration_dirty = true;
 		iteration_dirty = true;
 	}
 	}
 
 
-	if (!iteration_dirty) {
-		iteration_dirty = sync_dirty_requests.regions.first() || sync_dirty_requests.links.first();
-	}
-
 	// Sync NavRegions.
 	// Sync NavRegions.
-	for (SelfList<NavRegion3D> *element = sync_dirty_requests.regions.first(); element; element = element->next()) {
-		element->self()->sync();
+	RWLockWrite write_lock_regions(sync_dirty_requests.regions.rwlock);
+	for (SelfList<NavRegion3D> *element = sync_dirty_requests.regions.list.first(); element; element = element->next()) {
+		bool requires_map_update = element->self()->sync();
+		if (requires_map_update) {
+			iteration_dirty = true;
+		}
 	}
 	}
-	sync_dirty_requests.regions.clear();
+	sync_dirty_requests.regions.list.clear();
 
 
 	// Sync NavLinks.
 	// Sync NavLinks.
-	for (SelfList<NavLink3D> *element = sync_dirty_requests.links.first(); element; element = element->next()) {
-		element->self()->sync();
+	RWLockWrite write_lock_links(sync_dirty_requests.links.rwlock);
+	for (SelfList<NavLink3D> *element = sync_dirty_requests.links.list.first(); element; element = element->next()) {
+		bool requires_map_update = element->self()->sync();
+		if (requires_map_update) {
+			iteration_dirty = true;
+		}
 	}
 	}
-	sync_dirty_requests.links.clear();
+	sync_dirty_requests.links.list.clear();
 }
 }
 
 
 void NavMap3D::_sync_dirty_avoidance_update_requests() {
 void NavMap3D::_sync_dirty_avoidance_update_requests() {
 	// Sync NavAgents.
 	// Sync NavAgents.
 	if (!agents_dirty) {
 	if (!agents_dirty) {
-		agents_dirty = sync_dirty_requests.agents.first();
+		agents_dirty = sync_dirty_requests.agents.list.first();
 	}
 	}
-	for (SelfList<NavAgent3D> *element = sync_dirty_requests.agents.first(); element; element = element->next()) {
+	for (SelfList<NavAgent3D> *element = sync_dirty_requests.agents.list.first(); element; element = element->next()) {
 		element->self()->sync();
 		element->self()->sync();
 	}
 	}
-	sync_dirty_requests.agents.clear();
+	sync_dirty_requests.agents.list.clear();
 
 
 	// Sync NavObstacles.
 	// Sync NavObstacles.
 	if (!obstacles_dirty) {
 	if (!obstacles_dirty) {
-		obstacles_dirty = sync_dirty_requests.obstacles.first();
+		obstacles_dirty = sync_dirty_requests.obstacles.list.first();
 	}
 	}
-	for (SelfList<NavObstacle3D> *element = sync_dirty_requests.obstacles.first(); element; element = element->next()) {
+	for (SelfList<NavObstacle3D> *element = sync_dirty_requests.obstacles.list.first(); element; element = element->next()) {
 		element->self()->sync();
 		element->self()->sync();
 	}
 	}
-	sync_dirty_requests.obstacles.clear();
+	sync_dirty_requests.obstacles.list.clear();
+}
+
+void NavMap3D::add_region_async_thread_join_request(SelfList<NavRegion3D> *p_async_request) {
+	if (p_async_request->in_list()) {
+		return;
+	}
+	RWLockWrite write_lock(async_dirty_requests.regions.rwlock);
+	async_dirty_requests.regions.list.add(p_async_request);
+}
+
+void NavMap3D::remove_region_async_thread_join_request(SelfList<NavRegion3D> *p_async_request) {
+	if (!p_async_request->in_list()) {
+		return;
+	}
+	RWLockWrite write_lock(async_dirty_requests.regions.rwlock);
+	async_dirty_requests.regions.list.remove(p_async_request);
+}
+
+void NavMap3D::_sync_async_tasks() {
+	// Sync NavRegions that run async thread tasks.
+	RWLockWrite write_lock_regions(async_dirty_requests.regions.rwlock);
+	for (SelfList<NavRegion3D> *element = async_dirty_requests.regions.list.first(); element; element = element->next()) {
+		element->self()->sync_async_tasks();
+	}
 }
 }
 
 
 void NavMap3D::set_use_async_iterations(bool p_enabled) {
 void NavMap3D::set_use_async_iterations(bool p_enabled) {
@@ -870,4 +888,9 @@ NavMap3D::~NavMap3D() {
 		WorkerThreadPool::get_singleton()->wait_for_task_completion(iteration_build_thread_task_id);
 		WorkerThreadPool::get_singleton()->wait_for_task_completion(iteration_build_thread_task_id);
 		iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
 		iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
 	}
 	}
+
+	RWLockWrite write_lock(iteration_slot_rwlock);
+	for (NavMapIteration3D &iteration_slot : iteration_slots) {
+		iteration_slot.clear();
+	}
 }
 }

+ 27 - 5
modules/navigation_3d/nav_map_3d.h

@@ -110,12 +110,31 @@ class NavMap3D : public NavRid3D {
 	Nav3D::PerformanceData performance_data;
 	Nav3D::PerformanceData performance_data;
 
 
 	struct {
 	struct {
-		SelfList<NavRegion3D>::List regions;
-		SelfList<NavLink3D>::List links;
-		SelfList<NavAgent3D>::List agents;
-		SelfList<NavObstacle3D>::List obstacles;
+		struct {
+			RWLock rwlock;
+			SelfList<NavRegion3D>::List list;
+		} regions;
+		struct {
+			RWLock rwlock;
+			SelfList<NavLink3D>::List list;
+		} links;
+		struct {
+			RWLock rwlock;
+			SelfList<NavAgent3D>::List list;
+		} agents;
+		struct {
+			RWLock rwlock;
+			SelfList<NavObstacle3D>::List list;
+		} obstacles;
 	} sync_dirty_requests;
 	} sync_dirty_requests;
 
 
+	struct {
+		struct {
+			RWLock rwlock;
+			SelfList<NavRegion3D>::List list;
+		} regions;
+	} async_dirty_requests;
+
 	int path_query_slots_max = 4;
 	int path_query_slots_max = 4;
 
 
 	bool use_async_iterations = true;
 	bool use_async_iterations = true;
@@ -125,7 +144,6 @@ class NavMap3D : public NavRid3D {
 	mutable RWLock iteration_slot_rwlock;
 	mutable RWLock iteration_slot_rwlock;
 
 
 	NavMapIterationBuild3D iteration_build;
 	NavMapIterationBuild3D iteration_build;
-	bool iteration_build_use_threads = false;
 	WorkerThreadPool::TaskID iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
 	WorkerThreadPool::TaskID iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
 	static void _build_iteration_threaded(void *p_arg);
 	static void _build_iteration_threaded(void *p_arg);
 
 
@@ -236,6 +254,9 @@ public:
 	Vector3 get_region_connection_pathway_start(NavRegion3D *p_region, int p_connection_id) const;
 	Vector3 get_region_connection_pathway_start(NavRegion3D *p_region, int p_connection_id) const;
 	Vector3 get_region_connection_pathway_end(NavRegion3D *p_region, int p_connection_id) const;
 	Vector3 get_region_connection_pathway_end(NavRegion3D *p_region, int p_connection_id) const;
 
 
+	void add_region_async_thread_join_request(SelfList<NavRegion3D> *p_async_request);
+	void remove_region_async_thread_join_request(SelfList<NavRegion3D> *p_async_request);
+
 	void add_region_sync_dirty_request(SelfList<NavRegion3D> *p_sync_request);
 	void add_region_sync_dirty_request(SelfList<NavRegion3D> *p_sync_request);
 	void add_link_sync_dirty_request(SelfList<NavLink3D> *p_sync_request);
 	void add_link_sync_dirty_request(SelfList<NavLink3D> *p_sync_request);
 	void add_agent_sync_dirty_request(SelfList<NavAgent3D> *p_sync_request);
 	void add_agent_sync_dirty_request(SelfList<NavAgent3D> *p_sync_request);
@@ -252,6 +273,7 @@ public:
 private:
 private:
 	void _sync_dirty_map_update_requests();
 	void _sync_dirty_map_update_requests();
 	void _sync_dirty_avoidance_update_requests();
 	void _sync_dirty_avoidance_update_requests();
+	void _sync_async_tasks();
 
 
 	void compute_single_step(uint32_t index, NavAgent3D **agent);
 	void compute_single_step(uint32_t index, NavAgent3D **agent);
 
 

+ 154 - 116
modules/navigation_3d/nav_region_3d.cpp

@@ -32,9 +32,10 @@
 
 
 #include "nav_map_3d.h"
 #include "nav_map_3d.h"
 
 
-#include "3d/nav_map_builder_3d.h"
 #include "3d/nav_mesh_queries_3d.h"
 #include "3d/nav_mesh_queries_3d.h"
+#include "3d/nav_region_builder_3d.h"
 #include "3d/nav_region_iteration_3d.h"
 #include "3d/nav_region_iteration_3d.h"
+#include "core/config/project_settings.h"
 
 
 using namespace Nav3D;
 using namespace Nav3D;
 
 
@@ -43,6 +44,7 @@ void NavRegion3D::set_map(NavMap3D *p_map) {
 		return;
 		return;
 	}
 	}
 
 
+	cancel_async_thread_join();
 	cancel_sync_request();
 	cancel_sync_request();
 
 
 	if (map) {
 	if (map) {
@@ -50,11 +52,14 @@ void NavRegion3D::set_map(NavMap3D *p_map) {
 	}
 	}
 
 
 	map = p_map;
 	map = p_map;
-	polygons_dirty = true;
+	iteration_dirty = true;
 
 
 	if (map) {
 	if (map) {
 		map->add_region(this);
 		map->add_region(this);
 		request_sync();
 		request_sync();
+		if (iteration_build_thread_task_id != WorkerThreadPool::INVALID_TASK_ID) {
+			request_async_thread_join();
+		}
 	}
 	}
 }
 }
 
 
@@ -63,9 +68,7 @@ void NavRegion3D::set_enabled(bool p_enabled) {
 		return;
 		return;
 	}
 	}
 	enabled = p_enabled;
 	enabled = p_enabled;
-
-	// TODO: This should not require a full rebuild as the region has not really changed.
-	polygons_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 }
 }
@@ -73,7 +76,7 @@ void NavRegion3D::set_enabled(bool p_enabled) {
 void NavRegion3D::set_use_edge_connections(bool p_enabled) {
 void NavRegion3D::set_use_edge_connections(bool p_enabled) {
 	if (use_edge_connections != p_enabled) {
 	if (use_edge_connections != p_enabled) {
 		use_edge_connections = p_enabled;
 		use_edge_connections = p_enabled;
-		polygons_dirty = true;
+		iteration_dirty = true;
 	}
 	}
 
 
 	request_sync();
 	request_sync();
@@ -84,7 +87,7 @@ void NavRegion3D::set_transform(Transform3D p_transform) {
 		return;
 		return;
 	}
 	}
 	transform = p_transform;
 	transform = p_transform;
-	polygons_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 
 
@@ -106,16 +109,9 @@ void NavRegion3D::set_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) {
 	}
 	}
 #endif // DEBUG_ENABLED
 #endif // DEBUG_ENABLED
 
 
-	RWLockWrite write_lock(navmesh_rwlock);
-
-	pending_navmesh_vertices.clear();
-	pending_navmesh_polygons.clear();
+	navmesh = p_navigation_mesh;
 
 
-	if (p_navigation_mesh.is_valid()) {
-		p_navigation_mesh->get_data(pending_navmesh_vertices, pending_navmesh_polygons);
-	}
-
-	polygons_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 }
 }
@@ -148,7 +144,7 @@ void NavRegion3D::set_navigation_layers(uint32_t p_navigation_layers) {
 		return;
 		return;
 	}
 	}
 	navigation_layers = p_navigation_layers;
 	navigation_layers = p_navigation_layers;
-	region_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 }
 }
@@ -159,7 +155,7 @@ void NavRegion3D::set_enter_cost(real_t p_enter_cost) {
 		return;
 		return;
 	}
 	}
 	enter_cost = new_enter_cost;
 	enter_cost = new_enter_cost;
-	region_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 }
 }
@@ -170,7 +166,7 @@ void NavRegion3D::set_travel_cost(real_t p_travel_cost) {
 		return;
 		return;
 	}
 	}
 	travel_cost = new_travel_cost;
 	travel_cost = new_travel_cost;
-	region_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 }
 }
@@ -180,139 +176,150 @@ void NavRegion3D::set_owner_id(ObjectID p_owner_id) {
 		return;
 		return;
 	}
 	}
 	owner_id = p_owner_id;
 	owner_id = p_owner_id;
-	region_dirty = true;
+	iteration_dirty = true;
 
 
 	request_sync();
 	request_sync();
 }
 }
 
 
-bool NavRegion3D::sync() {
-	RWLockWrite write_lock(region_rwlock);
+void NavRegion3D::scratch_polygons() {
+	iteration_dirty = true;
 
 
-	bool something_changed = region_dirty || polygons_dirty;
+	request_sync();
+}
+
+real_t NavRegion3D::get_surface_area() const {
+	RWLockRead read_lock(iteration_rwlock);
+	return iteration->get_surface_area();
+}
 
 
-	region_dirty = false;
+AABB NavRegion3D::get_bounds() const {
+	RWLockRead read_lock(iteration_rwlock);
+	return iteration->get_bounds();
+}
 
 
-	update_polygons();
+LocalVector<Nav3D::Polygon> const &NavRegion3D::get_polygons() const {
+	RWLockRead read_lock(iteration_rwlock);
+	return iteration->get_navmesh_polygons();
+}
 
 
-	if (something_changed) {
-		iteration_id = iteration_id % UINT32_MAX + 1;
+bool NavRegion3D::sync() {
+	bool requires_map_update = false;
+	if (!map) {
+		return requires_map_update;
+	}
+
+	if (iteration_dirty && !iteration_building && !iteration_ready) {
+		_build_iteration();
 	}
 	}
 
 
-	return something_changed;
+	if (iteration_ready) {
+		_sync_iteration();
+		requires_map_update = true;
+	}
+
+	return requires_map_update;
 }
 }
 
 
-void NavRegion3D::update_polygons() {
-	if (!polygons_dirty) {
-		return;
+void NavRegion3D::sync_async_tasks() {
+	if (iteration_build_thread_task_id != WorkerThreadPool::INVALID_TASK_ID) {
+		if (WorkerThreadPool::get_singleton()->is_task_completed(iteration_build_thread_task_id)) {
+			WorkerThreadPool::get_singleton()->wait_for_task_completion(iteration_build_thread_task_id);
+
+			iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
+			iteration_building = false;
+			iteration_ready = true;
+			request_sync();
+		}
 	}
 	}
-	navmesh_polygons.clear();
-	surface_area = 0.0;
-	bounds = AABB();
-	polygons_dirty = false;
+}
 
 
-	if (map == nullptr) {
+void NavRegion3D::_build_iteration() {
+	if (!iteration_dirty || iteration_building || iteration_ready) {
 		return;
 		return;
 	}
 	}
 
 
-	RWLockRead read_lock(navmesh_rwlock);
+	iteration_dirty = false;
+	iteration_building = true;
+	iteration_ready = false;
 
 
-	if (pending_navmesh_vertices.is_empty() || pending_navmesh_polygons.is_empty()) {
-		return;
-	}
+	iteration_build.reset();
 
 
-	int len = pending_navmesh_vertices.size();
-	if (len == 0) {
-		return;
+	if (navmesh.is_valid()) {
+		navmesh->get_data(iteration_build.navmesh_data.vertices, iteration_build.navmesh_data.polygons);
 	}
 	}
 
 
-	const Vector3 *vertices_r = pending_navmesh_vertices.ptr();
+	iteration_build.map_cell_size = map->get_merge_rasterizer_cell_size();
 
 
-	navmesh_polygons.resize(pending_navmesh_polygons.size());
+	Ref<NavRegionIteration3D> new_iteration;
+	new_iteration.instantiate();
 
 
-	real_t _new_region_surface_area = 0.0;
-	AABB _new_bounds;
+	new_iteration->navigation_layers = get_navigation_layers();
+	new_iteration->enter_cost = get_enter_cost();
+	new_iteration->travel_cost = get_travel_cost();
+	new_iteration->owner_object_id = get_owner_id();
+	new_iteration->owner_type = get_type();
+	new_iteration->owner_rid = get_self();
+	new_iteration->enabled = get_enabled();
+	new_iteration->transform = get_transform();
+	new_iteration->owner_use_edge_connections = get_use_edge_connections();
 
 
-	bool first_vertex = true;
-	int navigation_mesh_polygon_index = 0;
+	iteration_build.region_iteration = new_iteration;
 
 
-	for (Polygon &polygon : navmesh_polygons) {
-		polygon.surface_area = 0.0;
+	if (use_async_iterations) {
+		iteration_build_thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavRegion3D::_build_iteration_threaded, &iteration_build, true, SNAME("NavRegionBuilder3D"));
+		request_async_thread_join();
+	} else {
+		NavRegionBuilder3D::build_iteration(iteration_build);
 
 
-		Vector<int> navigation_mesh_polygon = pending_navmesh_polygons[navigation_mesh_polygon_index];
-		navigation_mesh_polygon_index += 1;
+		iteration_building = false;
+		iteration_ready = true;
+	}
+}
 
 
-		int navigation_mesh_polygon_size = navigation_mesh_polygon.size();
-		if (navigation_mesh_polygon_size < 3) {
-			continue;
-		}
+void NavRegion3D::_build_iteration_threaded(void *p_arg) {
+	NavRegionIterationBuild3D *_iteration_build = static_cast<NavRegionIterationBuild3D *>(p_arg);
 
 
-		const int *indices = navigation_mesh_polygon.ptr();
-		bool valid(true);
+	NavRegionBuilder3D::build_iteration(*_iteration_build);
+}
 
 
-		polygon.vertices.resize(navigation_mesh_polygon_size);
-		polygon.edges.resize(navigation_mesh_polygon_size);
+void NavRegion3D::_sync_iteration() {
+	if (iteration_building || !iteration_ready) {
+		return;
+	}
 
 
-		real_t _new_polygon_surface_area = 0.0;
+	performance_data.pm_polygon_count = iteration_build.performance_data.pm_polygon_count;
+	performance_data.pm_edge_count = iteration_build.performance_data.pm_edge_count;
+	performance_data.pm_edge_merge_count = iteration_build.performance_data.pm_edge_merge_count;
 
 
-		for (int j(2); j < navigation_mesh_polygon_size; j++) {
-			const Face3 face = Face3(
-					transform.xform(vertices_r[indices[0]]),
-					transform.xform(vertices_r[indices[j - 1]]),
-					transform.xform(vertices_r[indices[j]]));
+	RWLockWrite write_lock(iteration_rwlock);
+	ERR_FAIL_COND(iteration.is_null());
+	iteration = Ref<NavRegionIteration3D>();
+	DEV_ASSERT(iteration.is_null());
+	iteration = iteration_build.region_iteration;
+	iteration_build.region_iteration = Ref<NavRegionIteration3D>();
+	DEV_ASSERT(iteration_build.region_iteration.is_null());
+	iteration_id = iteration_id % UINT32_MAX + 1;
 
 
-			_new_polygon_surface_area += face.get_area();
-		}
+	iteration_ready = false;
 
 
-		polygon.surface_area = _new_polygon_surface_area;
-		_new_region_surface_area += _new_polygon_surface_area;
-
-		for (int j(0); j < navigation_mesh_polygon_size; j++) {
-			int idx = indices[j];
-			if (idx < 0 || idx >= len) {
-				valid = false;
-				break;
-			}
-
-			Vector3 point_position = transform.xform(vertices_r[idx]);
-			polygon.vertices[j] = point_position;
-
-			if (first_vertex) {
-				first_vertex = false;
-				_new_bounds.position = point_position;
-			} else {
-				_new_bounds.expand_to(point_position);
-			}
-		}
+	cancel_async_thread_join();
+}
 
 
-		if (!valid) {
-			ERR_BREAK_MSG(!valid, "The navigation mesh set in this region is not valid!");
-		}
-	}
+Ref<NavRegionIteration3D> NavRegion3D::get_iteration() {
+	RWLockRead read_lock(iteration_rwlock);
+	return iteration;
+}
 
 
-	surface_area = _new_region_surface_area;
-	bounds = _new_bounds;
+void NavRegion3D::request_async_thread_join() {
+	DEV_ASSERT(map);
+	if (map && !async_list_element.in_list()) {
+		map->add_region_async_thread_join_request(&async_list_element);
+	}
 }
 }
 
 
-void NavRegion3D::get_iteration_update(NavRegionIteration3D &r_iteration) {
-	r_iteration.navigation_layers = get_navigation_layers();
-	r_iteration.enter_cost = get_enter_cost();
-	r_iteration.travel_cost = get_travel_cost();
-	r_iteration.owner_object_id = get_owner_id();
-	r_iteration.owner_type = get_type();
-	r_iteration.owner_rid = get_self();
-
-	r_iteration.enabled = get_enabled();
-	r_iteration.transform = get_transform();
-	r_iteration.owner_use_edge_connections = get_use_edge_connections();
-	r_iteration.bounds = get_bounds();
-	r_iteration.surface_area = get_surface_area();
-
-	r_iteration.navmesh_polygons.clear();
-	r_iteration.navmesh_polygons.resize(navmesh_polygons.size());
-	for (uint32_t i = 0; i < navmesh_polygons.size(); i++) {
-		Polygon &navmesh_polygon = navmesh_polygons[i];
-		navmesh_polygon.owner = &r_iteration;
-		r_iteration.navmesh_polygons[i] = navmesh_polygon;
+void NavRegion3D::cancel_async_thread_join() {
+	if (map && async_list_element.in_list()) {
+		map->remove_region_async_thread_join_request(&async_list_element);
 	}
 	}
 }
 }
 
 
@@ -328,11 +335,42 @@ void NavRegion3D::cancel_sync_request() {
 	}
 	}
 }
 }
 
 
+void NavRegion3D::set_use_async_iterations(bool p_enabled) {
+	if (use_async_iterations == p_enabled) {
+		return;
+	}
+#ifdef THREADS_ENABLED
+	use_async_iterations = p_enabled;
+#endif
+}
+
+bool NavRegion3D::get_use_async_iterations() const {
+	return use_async_iterations;
+}
+
 NavRegion3D::NavRegion3D() :
 NavRegion3D::NavRegion3D() :
-		sync_dirty_request_list_element(this) {
+		sync_dirty_request_list_element(this), async_list_element(this) {
 	type = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_REGION;
 	type = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_REGION;
+	iteration_build.region = this;
+	iteration.instantiate();
+
+#ifdef THREADS_ENABLED
+	use_async_iterations = GLOBAL_GET("navigation/world/region_use_async_iterations");
+#else
+	use_async_iterations = false;
+#endif
 }
 }
 
 
 NavRegion3D::~NavRegion3D() {
 NavRegion3D::~NavRegion3D() {
+	cancel_async_thread_join();
 	cancel_sync_request();
 	cancel_sync_request();
+
+	if (iteration_build_thread_task_id != WorkerThreadPool::INVALID_TASK_ID) {
+		WorkerThreadPool::get_singleton()->wait_for_task_completion(iteration_build_thread_task_id);
+		iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
+	}
+
+	iteration_build.region = nullptr;
+	iteration_build.region_iteration = Ref<NavRegionIteration3D>();
+	iteration = Ref<NavRegionIteration3D>();
 }
 }

+ 36 - 21
modules/navigation_3d/nav_region_3d.h

@@ -36,7 +36,7 @@
 #include "core/os/rw_lock.h"
 #include "core/os/rw_lock.h"
 #include "scene/resources/navigation_mesh.h"
 #include "scene/resources/navigation_mesh.h"
 
 
-struct NavRegionIteration3D;
+#include "3d/nav_region_iteration_3d.h"
 
 
 class NavRegion3D : public NavBase3D {
 class NavRegion3D : public NavBase3D {
 	RWLock region_rwlock;
 	RWLock region_rwlock;
@@ -47,21 +47,30 @@ class NavRegion3D : public NavBase3D {
 
 
 	bool use_edge_connections = true;
 	bool use_edge_connections = true;
 
 
-	bool region_dirty = true;
-	bool polygons_dirty = true;
-
-	LocalVector<Nav3D::Polygon> navmesh_polygons;
-
-	real_t surface_area = 0.0;
 	AABB bounds;
 	AABB bounds;
 
 
-	RWLock navmesh_rwlock;
-	Vector<Vector3> pending_navmesh_vertices;
-	Vector<Vector<int>> pending_navmesh_polygons;
+	Ref<NavigationMesh> navmesh;
+
+	Nav3D::PerformanceData performance_data;
 
 
 	uint32_t iteration_id = 0;
 	uint32_t iteration_id = 0;
 
 
 	SelfList<NavRegion3D> sync_dirty_request_list_element;
 	SelfList<NavRegion3D> sync_dirty_request_list_element;
+	mutable RWLock iteration_rwlock;
+	Ref<NavRegionIteration3D> iteration;
+
+	NavRegionIterationBuild3D iteration_build;
+	bool use_async_iterations = true;
+	SelfList<NavRegion3D> async_list_element;
+	WorkerThreadPool::TaskID iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
+	static void _build_iteration_threaded(void *p_arg);
+
+	bool iteration_dirty = true;
+	bool iteration_building = false;
+	bool iteration_ready = false;
+
+	void _build_iteration();
+	void _sync_iteration();
 
 
 public:
 public:
 	NavRegion3D();
 	NavRegion3D();
@@ -69,9 +78,7 @@ public:
 
 
 	uint32_t get_iteration_id() const { return iteration_id; }
 	uint32_t get_iteration_id() const { return iteration_id; }
 
 
-	void scratch_polygons() {
-		polygons_dirty = true;
-	}
+	void scratch_polygons();
 
 
 	void set_enabled(bool p_enabled);
 	void set_enabled(bool p_enabled);
 	bool get_enabled() const { return enabled; }
 	bool get_enabled() const { return enabled; }
@@ -90,17 +97,16 @@ public:
 	}
 	}
 
 
 	void set_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh);
 	void set_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh);
+	Ref<NavigationMesh> get_navigation_mesh() const { return navmesh; }
 
 
-	LocalVector<Nav3D::Polygon> const &get_polygons() const {
-		return navmesh_polygons;
-	}
+	LocalVector<Nav3D::Polygon> const &get_polygons() const;
 
 
 	Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const;
 	Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const;
 	Nav3D::ClosestPointQueryResult get_closest_point_info(const Vector3 &p_point) const;
 	Nav3D::ClosestPointQueryResult get_closest_point_info(const Vector3 &p_point) const;
 	Vector3 get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const;
 	Vector3 get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const;
 
 
-	real_t get_surface_area() const { return surface_area; }
-	AABB get_bounds() const { return bounds; }
+	real_t get_surface_area() const;
+	AABB get_bounds() const;
 
 
 	// NavBase properties.
 	// NavBase properties.
 	virtual void set_navigation_layers(uint32_t p_navigation_layers) override;
 	virtual void set_navigation_layers(uint32_t p_navigation_layers) override;
@@ -112,8 +118,17 @@ public:
 	void request_sync();
 	void request_sync();
 	void cancel_sync_request();
 	void cancel_sync_request();
 
 
-	void get_iteration_update(NavRegionIteration3D &r_iteration);
+	void sync_async_tasks();
+	void request_async_thread_join();
+	void cancel_async_thread_join();
+
+	Ref<NavRegionIteration3D> get_iteration();
+
+	// Performance Monitor
+	int get_pm_polygon_count() const { return performance_data.pm_polygon_count; }
+	int get_pm_edge_count() const { return performance_data.pm_edge_count; }
+	int get_pm_edge_merge_count() const { return performance_data.pm_edge_merge_count; }
 
 
-private:
-	void update_polygons();
+	void set_use_async_iterations(bool p_enabled);
+	bool get_use_async_iterations() const;
 };
 };

+ 18 - 20
modules/navigation_3d/nav_utils_3d.h

@@ -31,12 +31,13 @@
 #pragma once
 #pragma once
 
 
 #include "core/math/vector3.h"
 #include "core/math/vector3.h"
+#include "core/object/ref_counted.h"
 #include "core/templates/hash_map.h"
 #include "core/templates/hash_map.h"
 #include "core/templates/hashfuncs.h"
 #include "core/templates/hashfuncs.h"
 #include "servers/navigation/nav_heap.h"
 #include "servers/navigation/nav_heap.h"
 #include "servers/navigation/navigation_utilities.h"
 #include "servers/navigation/navigation_utilities.h"
 
 
-struct NavBaseIteration3D;
+class NavBaseIteration3D;
 
 
 namespace Nav3D {
 namespace Nav3D {
 struct Polygon;
 struct Polygon;
@@ -72,28 +73,28 @@ struct EdgeKey {
 	}
 	}
 };
 };
 
 
-struct Edge {
-	/// The gateway in the edge, as, in some case, the whole edge might not be navigable.
-	struct Connection {
-		/// Polygon that this connection leads to.
-		Polygon *polygon = nullptr;
+struct ConnectableEdge {
+	EdgeKey ek;
+	uint32_t polygon_index;
+	Vector3 pathway_start;
+	Vector3 pathway_end;
+};
 
 
-		/// Edge of the source polygon where this connection starts from.
-		int edge = -1;
+struct Connection {
+	/// Polygon that this connection leads to.
+	Polygon *polygon = nullptr;
 
 
-		/// Point on the edge where the gateway leading to the poly starts.
-		Vector3 pathway_start;
+	/// Edge of the source polygon where this connection starts from.
+	int edge = -1;
 
 
-		/// Point on the edge where the gateway leading to the poly ends.
-		Vector3 pathway_end;
-	};
+	/// Point on the edge where the gateway leading to the poly starts.
+	Vector3 pathway_start;
 
 
-	/// Connections from this edge to other polygons.
-	LocalVector<Connection> connections;
+	/// Point on the edge where the gateway leading to the poly ends.
+	Vector3 pathway_end;
 };
 };
 
 
 struct Polygon {
 struct Polygon {
-	/// Id of the polygon in the map.
 	uint32_t id = UINT32_MAX;
 	uint32_t id = UINT32_MAX;
 
 
 	/// Navigation region or link that contains this polygon.
 	/// Navigation region or link that contains this polygon.
@@ -101,9 +102,6 @@ struct Polygon {
 
 
 	LocalVector<Vector3> vertices;
 	LocalVector<Vector3> vertices;
 
 
-	/// The edges of this `Polygon`
-	LocalVector<Edge> edges;
-
 	real_t surface_area = 0.0;
 	real_t surface_area = 0.0;
 };
 };
 
 
@@ -179,7 +177,7 @@ struct ClosestPointQueryResult {
 };
 };
 
 
 struct EdgeConnectionPair {
 struct EdgeConnectionPair {
-	Edge::Connection connections[2];
+	Connection connections[2];
 	int size = 0;
 	int size = 0;
 };
 };
 
 

+ 2 - 0
servers/navigation_server_3d.cpp

@@ -84,6 +84,8 @@ void NavigationServer3D::_bind_methods() {
 
 
 	ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer3D::region_create);
 	ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer3D::region_create);
 	ClassDB::bind_method(D_METHOD("region_get_iteration_id", "region"), &NavigationServer3D::region_get_iteration_id);
 	ClassDB::bind_method(D_METHOD("region_get_iteration_id", "region"), &NavigationServer3D::region_get_iteration_id);
+	ClassDB::bind_method(D_METHOD("region_set_use_async_iterations", "region", "enabled"), &NavigationServer3D::region_set_use_async_iterations);
+	ClassDB::bind_method(D_METHOD("region_get_use_async_iterations", "region"), &NavigationServer3D::region_get_use_async_iterations);
 	ClassDB::bind_method(D_METHOD("region_set_enabled", "region", "enabled"), &NavigationServer3D::region_set_enabled);
 	ClassDB::bind_method(D_METHOD("region_set_enabled", "region", "enabled"), &NavigationServer3D::region_set_enabled);
 	ClassDB::bind_method(D_METHOD("region_get_enabled", "region"), &NavigationServer3D::region_get_enabled);
 	ClassDB::bind_method(D_METHOD("region_get_enabled", "region"), &NavigationServer3D::region_get_enabled);
 	ClassDB::bind_method(D_METHOD("region_set_use_edge_connections", "region", "enabled"), &NavigationServer3D::region_set_use_edge_connections);
 	ClassDB::bind_method(D_METHOD("region_set_use_edge_connections", "region", "enabled"), &NavigationServer3D::region_set_use_edge_connections);

+ 3 - 0
servers/navigation_server_3d.h

@@ -109,6 +109,9 @@ public:
 	virtual RID region_create() = 0;
 	virtual RID region_create() = 0;
 	virtual uint32_t region_get_iteration_id(RID p_region) const = 0;
 	virtual uint32_t region_get_iteration_id(RID p_region) const = 0;
 
 
+	virtual void region_set_use_async_iterations(RID p_region, bool p_enabled) = 0;
+	virtual bool region_get_use_async_iterations(RID p_region) const = 0;
+
 	virtual void region_set_enabled(RID p_region, bool p_enabled) = 0;
 	virtual void region_set_enabled(RID p_region, bool p_enabled) = 0;
 	virtual bool region_get_enabled(RID p_region) const = 0;
 	virtual bool region_get_enabled(RID p_region) const = 0;
 
 

+ 2 - 0
servers/navigation_server_3d_dummy.h

@@ -71,6 +71,8 @@ public:
 
 
 	RID region_create() override { return RID(); }
 	RID region_create() override { return RID(); }
 	uint32_t region_get_iteration_id(RID p_region) const override { return 0; }
 	uint32_t region_get_iteration_id(RID p_region) const override { return 0; }
+	void region_set_use_async_iterations(RID p_region, bool p_enabled) override {}
+	bool region_get_use_async_iterations(RID p_region) const override { return false; }
 	void region_set_enabled(RID p_region, bool p_enabled) override {}
 	void region_set_enabled(RID p_region, bool p_enabled) override {}
 	bool region_get_enabled(RID p_region) const override { return false; }
 	bool region_get_enabled(RID p_region) const override { return false; }
 	void region_set_use_edge_connections(RID p_region, bool p_enabled) override {}
 	void region_set_use_edge_connections(RID p_region, bool p_enabled) override {}

+ 3 - 0
tests/servers/test_navigation_server_3d.h

@@ -536,6 +536,7 @@ TEST_SUITE("[Navigation3D]") {
 		Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
 		Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
 		navigation_server->map_set_use_async_iterations(map, false);
 		navigation_server->map_set_use_async_iterations(map, false);
 		navigation_server->map_set_active(map, true);
 		navigation_server->map_set_active(map, true);
+		navigation_server->region_set_use_async_iterations(region, false);
 		navigation_server->region_set_map(region, map);
 		navigation_server->region_set_map(region, map);
 		navigation_server->region_set_navigation_mesh(region, navigation_mesh);
 		navigation_server->region_set_navigation_mesh(region, navigation_mesh);
 		navigation_server->physics_process(0.0); // Give server some cycles to commit.
 		navigation_server->physics_process(0.0); // Give server some cycles to commit.
@@ -632,6 +633,7 @@ TEST_SUITE("[Navigation3D]") {
 		Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
 		Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
 		navigation_server->map_set_use_async_iterations(map, false);
 		navigation_server->map_set_use_async_iterations(map, false);
 		navigation_server->map_set_active(map, true);
 		navigation_server->map_set_active(map, true);
+		navigation_server->region_set_use_async_iterations(region, false);
 		navigation_server->region_set_map(region, map);
 		navigation_server->region_set_map(region, map);
 		navigation_server->region_set_navigation_mesh(region, navigation_mesh);
 		navigation_server->region_set_navigation_mesh(region, navigation_mesh);
 		navigation_server->physics_process(0.0); // Give server some cycles to commit.
 		navigation_server->physics_process(0.0); // Give server some cycles to commit.
@@ -681,6 +683,7 @@ TEST_SUITE("[Navigation3D]") {
 		RID region = navigation_server->region_create();
 		RID region = navigation_server->region_create();
 		navigation_server->map_set_active(map, true);
 		navigation_server->map_set_active(map, true);
 		navigation_server->map_set_use_async_iterations(map, false);
 		navigation_server->map_set_use_async_iterations(map, false);
+		navigation_server->region_set_use_async_iterations(region, false);
 		navigation_server->region_set_map(region, map);
 		navigation_server->region_set_map(region, map);
 		navigation_server->region_set_navigation_mesh(region, navigation_mesh);
 		navigation_server->region_set_navigation_mesh(region, navigation_mesh);
 		navigation_server->physics_process(0.0); // Give server some cycles to commit.
 		navigation_server->physics_process(0.0); // Give server some cycles to commit.