Merge pull request #107381 from smix8/ref_iterations_2d
Change 2D navigation region and link updates to an async process
This commit is contained in:
commit
ce3ebacb3e
@ -905,6 +905,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" />
|
||||||
@ -986,6 +993,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" />
|
||||||
|
@ -457,6 +457,19 @@ uint32_t GodotNavigationServer2D::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) {
|
||||||
|
NavRegion2D *region = region_owner.get_or_null(p_region);
|
||||||
|
ERR_FAIL_NULL(region);
|
||||||
|
region->set_use_async_iterations(p_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GodotNavigationServer2D::region_get_use_async_iterations(RID p_region) const {
|
||||||
|
NavRegion2D *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) {
|
||||||
NavRegion2D *region = region_owner.get_or_null(p_region);
|
NavRegion2D *region = region_owner.get_or_null(p_region);
|
||||||
ERR_FAIL_NULL(region);
|
ERR_FAIL_NULL(region);
|
||||||
@ -581,7 +594,7 @@ void GodotNavigationServer2D::region_set_navigation_polygon(RID p_region, Ref<Na
|
|||||||
NavRegion2D *region = region_owner.get_or_null(p_region);
|
NavRegion2D *region = region_owner.get_or_null(p_region);
|
||||||
ERR_FAIL_NULL(region);
|
ERR_FAIL_NULL(region);
|
||||||
|
|
||||||
region->set_navigation_polygon(p_navigation_polygon);
|
region->set_navigation_mesh(p_navigation_polygon);
|
||||||
}
|
}
|
||||||
|
|
||||||
int GodotNavigationServer2D::region_get_connections_count(RID p_region) const {
|
int GodotNavigationServer2D::region_get_connections_count(RID p_region) const {
|
||||||
|
@ -146,6 +146,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;
|
||||||
|
|
||||||
|
@ -32,10 +32,13 @@
|
|||||||
|
|
||||||
#include "../nav_utils_2d.h"
|
#include "../nav_utils_2d.h"
|
||||||
|
|
||||||
|
#include "core/object/ref_counted.h"
|
||||||
#include "servers/navigation/navigation_utilities.h"
|
#include "servers/navigation/navigation_utilities.h"
|
||||||
|
|
||||||
struct NavBaseIteration2D {
|
class NavBaseIteration2D : public RefCounted {
|
||||||
uint32_t id = UINT32_MAX;
|
GDCLASS(NavBaseIteration2D, 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 NavBaseIteration2D {
|
|||||||
RID owner_rid;
|
RID owner_rid;
|
||||||
bool owner_use_edge_connections = false;
|
bool owner_use_edge_connections = false;
|
||||||
LocalVector<Nav2D::Polygon> navmesh_polygons;
|
LocalVector<Nav2D::Polygon> navmesh_polygons;
|
||||||
|
LocalVector<LocalVector<Nav2D::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 NavBaseIteration2D {
|
|||||||
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<Nav2D::Polygon> &get_navmesh_polygons() const { return navmesh_polygons; }
|
const LocalVector<Nav2D::Polygon> &get_navmesh_polygons() const { return navmesh_polygons; }
|
||||||
|
const LocalVector<LocalVector<Nav2D::Connection>> &get_internal_connections() const { return internal_connections; }
|
||||||
|
|
||||||
|
virtual ~NavBaseIteration2D() {
|
||||||
|
navmesh_polygons.clear();
|
||||||
|
internal_connections.clear();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -76,26 +76,23 @@ void NavMapBuilder2D::_build_step_gather_region_polygons(NavMapIterationBuild2D
|
|||||||
PerformanceData &performance_data = r_build.performance_data;
|
PerformanceData &performance_data = r_build.performance_data;
|
||||||
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
||||||
|
|
||||||
LocalVector<NavRegionIteration2D> ®ions = map_iteration->region_iterations;
|
const LocalVector<Ref<NavRegionIteration2D>> ®ions = map_iteration->region_iterations;
|
||||||
HashMap<uint32_t, LocalVector<Edge::Connection>> ®ion_external_connections = map_iteration->external_region_connections;
|
HashMap<const NavBaseIteration2D *, LocalVector<Connection>> ®ion_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 NavRegionIteration2D ®ion : 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 (NavRegionIteration2D ®ion : regions) {
|
for (const Ref<NavRegionIteration2D> ®ion : regions) {
|
||||||
if (!region.get_enabled()) {
|
const uint32_t polygons_size = region->navmesh_polygons.size();
|
||||||
continue;
|
polygon_count += polygons_size;
|
||||||
}
|
|
||||||
LocalVector<Polygon> &polygons_source = region.navmesh_polygons;
|
region_external_connections[region.ptr()] = LocalVector<Connection>();
|
||||||
for (uint32_t n = 0; n < polygons_source.size(); n++) {
|
map_iteration->navbases_polygons_external_connections[region.ptr()] = LocalVector<LocalVector<Connection>>();
|
||||||
polygons_source[n].id = polygon_count;
|
map_iteration->navbases_polygons_external_connections[region.ptr()].resize(polygons_size);
|
||||||
polygon_count++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
performance_data.pm_polygon_count = polygon_count;
|
performance_data.pm_polygon_count = polygon_count;
|
||||||
@ -106,7 +103,7 @@ void NavMapBuilder2D::_build_step_find_edge_connection_pairs(NavMapIterationBuil
|
|||||||
PerformanceData &performance_data = r_build.performance_data;
|
PerformanceData &performance_data = r_build.performance_data;
|
||||||
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
||||||
int polygon_count = r_build.polygon_count;
|
int polygon_count = r_build.polygon_count;
|
||||||
const Vector2 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.
|
||||||
@ -114,15 +111,9 @@ void NavMapBuilder2D::_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 (NavRegionIteration2D ®ion : map_iteration->region_iterations) {
|
for (const Ref<NavRegionIteration2D> ®ion : map_iteration->region_iterations) {
|
||||||
if (!region.get_enabled()) {
|
for (const ConnectableEdge &connectable_edge : region->get_external_edges()) {
|
||||||
continue;
|
const EdgeKey &ek = connectable_edge.ek;
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey>::Iterator pair_it = connection_pairs_map.find(ek);
|
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey>::Iterator pair_it = connection_pairs_map.find(ek);
|
||||||
if (!pair_it) {
|
if (!pair_it) {
|
||||||
@ -133,11 +124,10 @@ void NavMapBuilder2D::_build_step_find_edge_connection_pairs(NavMapIterationBuil
|
|||||||
EdgeConnectionPair &pair = pair_it->value;
|
EdgeConnectionPair &pair = pair_it->value;
|
||||||
if (pair.size < 2) {
|
if (pair.size < 2) {
|
||||||
// Add the polygon/edge tuple to this key.
|
// Add the polygon/edge tuple to this key.
|
||||||
Edge::Connection new_connection;
|
Connection new_connection;
|
||||||
new_connection.polygon = &poly;
|
new_connection.polygon = ®ion->navmesh_polygons[connectable_edge.polygon_index];
|
||||||
new_connection.edge = p;
|
new_connection.pathway_start = connectable_edge.pathway_start;
|
||||||
new_connection.pathway_start = poly.vertices[p];
|
new_connection.pathway_end = connectable_edge.pathway_end;
|
||||||
new_connection.pathway_end = poly.vertices[next_point];
|
|
||||||
|
|
||||||
pair.connections[pair.size] = new_connection;
|
pair.connections[pair.size] = new_connection;
|
||||||
++pair.size;
|
++pair.size;
|
||||||
@ -151,7 +141,6 @@ void NavMapBuilder2D::_build_step_find_edge_connection_pairs(NavMapIterationBuil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
r_build.free_edge_count = free_edges_count;
|
r_build.free_edge_count = free_edges_count;
|
||||||
}
|
}
|
||||||
@ -160,23 +149,28 @@ void NavMapBuilder2D::_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);
|
||||||
|
|
||||||
|
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
||||||
|
|
||||||
|
HashMap<const NavBaseIteration2D *, LocalVector<LocalVector<Nav2D::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 Connection &c1 = pair.connections[0];
|
||||||
const Edge::Connection &c2 = pair.connections[1];
|
const Connection &c2 = pair.connections[1];
|
||||||
c1.polygon->edges[c1.edge].connections.push_back(c2);
|
|
||||||
c2.polygon->edges[c2.edge].connections.push_back(c1);
|
navbases_polygons_external_connections[c1.polygon->owner][c1.polygon->id].push_back(c2);
|
||||||
// Note: The pathway_start/end are full for those connection and do not need to be modified.
|
navbases_polygons_external_connections[c2.polygon->owner][c2.polygon->id].push_back(c1);
|
||||||
performance_data.pm_edge_merge_count += 1;
|
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()) {
|
||||||
@ -191,8 +185,11 @@ void NavMapBuilder2D::_build_step_edge_connection_margin_connections(NavMapItera
|
|||||||
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
NavMapIteration2D *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>> ®ion_external_connections = map_iteration->external_region_connections;
|
LocalVector<Connection> &free_edges = r_build.iter_free_edges;
|
||||||
|
HashMap<const NavBaseIteration2D *, LocalVector<Connection>> ®ion_external_connections = map_iteration->external_region_connections;
|
||||||
|
|
||||||
|
HashMap<const NavBaseIteration2D *, LocalVector<LocalVector<Nav2D::Connection>>> &navbases_polygons_external_connections = map_iteration->navbases_polygons_external_connections;
|
||||||
|
|
||||||
// Find the compatible near edges.
|
// Find the compatible near edges.
|
||||||
//
|
//
|
||||||
@ -206,23 +203,23 @@ void NavMapBuilder2D::_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];
|
const Connection &free_edge = free_edges[i];
|
||||||
Vector2 edge_p1 = free_edge.polygon->vertices[free_edge.edge];
|
const Vector2 &edge_p1 = free_edge.pathway_start;
|
||||||
Vector2 edge_p2 = free_edge.polygon->vertices[(free_edge.edge + 1) % free_edge.polygon->vertices.size()];
|
const Vector2 &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;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 other_edge_p1 = other_edge.polygon->vertices[other_edge.edge];
|
const Vector2 &other_edge_p1 = other_edge.pathway_start;
|
||||||
Vector2 other_edge_p2 = other_edge.polygon->vertices[(other_edge.edge + 1) % other_edge.polygon->vertices.size()];
|
const Vector2 &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
|
||||||
Vector2 edge_vector = edge_p2 - edge_p1;
|
Vector2 edge_vector = edge_p2 - edge_p1;
|
||||||
real_t projected_p1_ratio = edge_vector.dot(other_edge_p1 - edge_p1) / edge_vector.length_squared();
|
real_t projected_p1_ratio = edge_vector.dot(other_edge_p1 - edge_p1) / (edge_vector.length_squared());
|
||||||
real_t projected_p2_ratio = edge_vector.dot(other_edge_p2 - edge_p1) / edge_vector.length_squared();
|
real_t projected_p2_ratio = edge_vector.dot(other_edge_p2 - edge_p1) / (edge_vector.length_squared());
|
||||||
if ((projected_p1_ratio < 0.0 && projected_p2_ratio < 0.0) || (projected_p1_ratio > 1.0 && projected_p2_ratio > 1.0)) {
|
if ((projected_p1_ratio < 0.0 && projected_p2_ratio < 0.0) || (projected_p1_ratio > 1.0 && projected_p2_ratio > 1.0)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -251,13 +248,14 @@ void NavMapBuilder2D::_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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,18 +266,28 @@ void NavMapBuilder2D::_build_step_navlink_connections(NavMapIterationBuild2D &r_
|
|||||||
|
|
||||||
real_t link_connection_radius = r_build.link_connection_radius;
|
real_t link_connection_radius = r_build.link_connection_radius;
|
||||||
|
|
||||||
LocalVector<NavLinkIteration2D> &links = map_iteration->link_iterations;
|
const LocalVector<Ref<NavLinkIteration2D>> &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 NavBaseIteration2D *, LocalVector<LocalVector<Nav2D::Connection>>> &navbases_polygons_external_connections = map_iteration->navbases_polygons_external_connections;
|
||||||
|
LocalVector<Nav2D::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 (NavLinkIteration2D &link : links) {
|
for (const Ref<NavLinkIteration2D> &link : links) {
|
||||||
if (!link.get_enabled()) {
|
polygon_count++;
|
||||||
continue;
|
Polygon &new_polygon = navlink_polygons[navlink_index++];
|
||||||
}
|
|
||||||
const Vector2 link_start_pos = link.get_start_position();
|
new_polygon.id = 0;
|
||||||
const Vector2 link_end_pos = link.get_end_position();
|
new_polygon.owner = link.ptr();
|
||||||
|
|
||||||
|
const Vector2 link_start_pos = link->get_start_position();
|
||||||
|
const Vector2 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;
|
||||||
@ -289,16 +297,13 @@ void NavMapBuilder2D::_build_step_navlink_connections(NavMapIterationBuild2D &r_
|
|||||||
real_t closest_end_sqr_dist = link_connection_radius_sqr;
|
real_t closest_end_sqr_dist = link_connection_radius_sqr;
|
||||||
Vector2 closest_end_point;
|
Vector2 closest_end_point;
|
||||||
|
|
||||||
for (NavRegionIteration2D ®ion : map_iteration->region_iterations) {
|
for (const Ref<NavRegionIteration2D> ®ion : map_iteration->region_iterations) {
|
||||||
if (!region.get_enabled()) {
|
Rect2 region_bounds = region->get_bounds().grow(link_connection_radius);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Rect2 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 Triangle2 triangle(polyon.vertices[0], polyon.vertices[point_id - 1], polyon.vertices[point_id]);
|
const Triangle2 triangle(polyon.vertices[0], polyon.vertices[point_id - 1], polyon.vertices[point_id]);
|
||||||
|
|
||||||
@ -331,14 +336,6 @@ void NavMapBuilder2D::_build_step_navlink_connections(NavMapIterationBuild2D &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.
|
||||||
@ -349,36 +346,38 @@ void NavMapBuilder2D::_build_step_navlink_connections(NavMapIterationBuild2D &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<Nav2D::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()) {
|
if (link->is_bidirectional()) {
|
||||||
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[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<Nav2D::Connection>());
|
||||||
|
navbases_polygons_external_connections[link.ptr()][new_polygon.id].push_back(exit_connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -391,12 +390,35 @@ void NavMapBuilder2D::_build_update_map_iteration(NavMapIterationBuild2D &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 (NavMeshQueries2D::PathQuerySlot &p_path_query_slot : map_iteration->path_query_slots) {
|
for (NavMeshQueries2D::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<NavRegionIteration2D> ®ion : 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();
|
||||||
}
|
}
|
||||||
|
@ -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 NavLinkIteration2D;
|
class NavLinkIteration2D;
|
||||||
class NavRegion2D;
|
class NavRegion2D;
|
||||||
struct NavRegionIteration2D;
|
class NavRegionIteration2D;
|
||||||
struct NavMapIteration2D;
|
struct NavMapIteration2D;
|
||||||
|
|
||||||
struct NavMapIterationBuild2D {
|
struct NavMapIterationBuild2D {
|
||||||
@ -52,7 +52,7 @@ struct NavMapIterationBuild2D {
|
|||||||
int free_edge_count = 0;
|
int free_edge_count = 0;
|
||||||
|
|
||||||
HashMap<Nav2D::EdgeKey, Nav2D::EdgeConnectionPair, Nav2D::EdgeKey> iter_connection_pairs_map;
|
HashMap<Nav2D::EdgeKey, Nav2D::EdgeConnectionPair, Nav2D::EdgeKey> iter_connection_pairs_map;
|
||||||
LocalVector<Nav2D::Edge::Connection> iter_free_edges;
|
LocalVector<Nav2D::Connection> iter_free_edges;
|
||||||
|
|
||||||
NavMapIteration2D *map_iteration = nullptr;
|
NavMapIteration2D *map_iteration = nullptr;
|
||||||
|
|
||||||
@ -74,19 +74,33 @@ struct NavMapIteration2D {
|
|||||||
mutable SafeNumeric<uint32_t> users;
|
mutable SafeNumeric<uint32_t> users;
|
||||||
RWLock rwlock;
|
RWLock rwlock;
|
||||||
|
|
||||||
LocalVector<NavRegionIteration2D> region_iterations;
|
LocalVector<Ref<NavRegionIteration2D>> region_iterations;
|
||||||
LocalVector<NavLinkIteration2D> link_iterations;
|
LocalVector<Ref<NavLinkIteration2D>> 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<Nav2D::Edge::Connection>> external_region_connections;
|
HashMap<const NavBaseIteration2D *, LocalVector<Nav2D::Connection>> external_region_connections;
|
||||||
|
HashMap<const NavBaseIteration2D *, LocalVector<LocalVector<Nav2D::Connection>>> navbases_polygons_external_connections;
|
||||||
|
|
||||||
HashMap<NavRegion2D *, uint32_t> region_ptr_to_region_id;
|
LocalVector<Nav2D::Polygon> navlink_polygons;
|
||||||
|
|
||||||
|
HashMap<NavRegion2D *, Ref<NavRegionIteration2D>> region_ptr_to_region_iteration;
|
||||||
|
|
||||||
LocalVector<NavMeshQueries2D::PathQuerySlot> path_query_slots;
|
LocalVector<NavMeshQueries2D::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() {
|
||||||
|
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 NavMapIterationRead2D {
|
class NavMapIterationRead2D {
|
||||||
|
@ -234,22 +234,15 @@ void NavMeshQueries2D::_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<NavRegionIteration2D> ®ions = p_map_iteration.region_iterations;
|
const LocalVector<Ref<NavRegionIteration2D>> ®ions = p_map_iteration.region_iterations;
|
||||||
|
|
||||||
for (const NavRegionIteration2D ®ion : regions) {
|
for (const Ref<NavRegionIteration2D> ®ion : regions) {
|
||||||
if (!region.get_enabled()) {
|
if (!_query_task_is_connection_owner_usable(p_query_task, region.ptr())) {
|
||||||
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())) {
|
|
||||||
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;
|
||||||
@ -279,7 +272,47 @@ void NavMeshQueries2D::_query_task_find_start_end_positions(NavMeshPathQueryTask
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &p_query_task) {
|
void NavMeshQueries2D::_query_task_search_polygon_connections(NavMeshPathQueryTask2D &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 Vector2 &p_end_point) {
|
||||||
|
const NavBaseIteration2D *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();
|
||||||
|
|
||||||
|
Vector2 new_entry = Geometry2D::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 NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &p_query_task, const NavMapIteration2D &p_map_iteration) {
|
||||||
const Vector2 p_target_position = p_query_task.target_position;
|
const Vector2 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;
|
||||||
@ -297,15 +330,15 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &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;
|
||||||
begin_navigation_poly.back_navigation_edge_pathway_end = begin_point;
|
begin_navigation_poly.back_navigation_edge_pathway_end = begin_point;
|
||||||
begin_navigation_poly.traveled_distance = 0.0;
|
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;
|
||||||
@ -313,47 +346,27 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &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 NavBaseIteration2D *, LocalVector<LocalVector<Nav2D::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.
|
const NavBaseIteration2D *least_cost_navbase = least_cost_poly.poly->owner;
|
||||||
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 NavBaseIteration2D *connection_owner = connection.polygon->owner;
|
const uint32_t navbase_local_polygon_id = least_cost_poly.poly->id;
|
||||||
const bool owner_is_usable = _query_task_is_connection_owner_usable(p_query_task, connection_owner);
|
const LocalVector<LocalVector<Connection>> &navbase_polygons_to_connections = least_cost_poly.poly->owner->get_internal_connections();
|
||||||
if (!owner_is_usable) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Vector2 new_entry = Geometry2D::get_closest_point_to_segment(least_cost_poly.entry, connection.pathway_start, connection.pathway_end);
|
if (navbase_polygons_to_connections.size() > 0) {
|
||||||
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;
|
const LocalVector<Connection> &polygon_connections = navbase_polygons_to_connections[navbase_local_polygon_id];
|
||||||
|
|
||||||
// Check if the neighbor polygon has already been processed.
|
for (const Connection &connection : polygon_connections) {
|
||||||
NavigationPoly &neighbor_poly = navigation_polys[connection.polygon->id];
|
_query_task_search_polygon_connections(p_query_task, connection, least_cost_id, least_cost_poly, poly_enter_cost, end_point);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
@ -371,6 +384,7 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &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++) {
|
||||||
Triangle2 t(end_poly->vertices[0], end_poly->vertices[point_id - 1], end_poly->vertices[point_id]);
|
Triangle2 t(end_poly->vertices[0], end_poly->vertices[point_id - 1], end_poly->vertices[point_id]);
|
||||||
Vector2 spoint = t.get_closest_point_to(p_target_position);
|
Vector2 spoint = t.get_closest_point_to(p_target_position);
|
||||||
@ -383,6 +397,7 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &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++) {
|
||||||
Triangle2 t(begin_poly->vertices[0], begin_poly->vertices[point_id - 1], begin_poly->vertices[point_id]);
|
Triangle2 t(begin_poly->vertices[0], begin_poly->vertices[point_id - 1], begin_poly->vertices[point_id]);
|
||||||
Vector2 spoint = t.get_closest_point_to(p_target_position);
|
Vector2 spoint = t.get_closest_point_to(p_target_position);
|
||||||
@ -408,13 +423,14 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &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;
|
uint32_t _bp_id = p_query_task.path_query_slot->poly_to_id[begin_poly];
|
||||||
navigation_polys[begin_poly->id].traveled_distance = 0;
|
navigation_polys[_bp_id].poly = begin_poly;
|
||||||
least_cost_id = begin_poly->id;
|
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) {
|
||||||
@ -432,6 +448,7 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -442,6 +459,7 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &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++) {
|
||||||
Triangle2 t(begin_poly->vertices[0], begin_poly->vertices[point_id - 1], begin_poly->vertices[point_id]);
|
Triangle2 t(begin_poly->vertices[0], begin_poly->vertices[point_id - 1], begin_poly->vertices[point_id]);
|
||||||
Vector2 spoint = t.get_closest_point_to(p_target_position);
|
Vector2 spoint = t.get_closest_point_to(p_target_position);
|
||||||
@ -484,7 +502,7 @@ void NavMeshQueries2D::query_task_map_iteration_get_path(NavMeshPathQueryTask2D
|
|||||||
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 == NavMeshPathQueryTask2D::TaskStatus::QUERY_FINISHED || p_query_task.status == NavMeshPathQueryTask2D::TaskStatus::QUERY_FAILED) {
|
if (p_query_task.status == NavMeshPathQueryTask2D::TaskStatus::QUERY_FINISHED || p_query_task.status == NavMeshPathQueryTask2D::TaskStatus::QUERY_FAILED) {
|
||||||
return;
|
return;
|
||||||
@ -733,9 +751,9 @@ ClosestPointQueryResult NavMeshQueries2D::map_iteration_get_closest_point_info(c
|
|||||||
|
|
||||||
// TODO: Check for further 2D improvements.
|
// TODO: Check for further 2D improvements.
|
||||||
|
|
||||||
const LocalVector<NavRegionIteration2D> ®ions = p_map_iteration.region_iterations;
|
const LocalVector<Ref<NavRegionIteration2D>> ®ions = p_map_iteration.region_iterations;
|
||||||
for (const NavRegionIteration2D ®ion : regions) {
|
for (const Ref<NavRegionIteration2D> ®ion : regions) {
|
||||||
for (const Polygon &polygon : region.get_navmesh_polygons()) {
|
for (const Polygon &polygon : region->get_navmesh_polygons()) {
|
||||||
real_t cross = (polygon.vertices[1] - polygon.vertices[0]).cross(polygon.vertices[2] - polygon.vertices[0]);
|
real_t cross = (polygon.vertices[1] - polygon.vertices[0]).cross(polygon.vertices[2] - polygon.vertices[0]);
|
||||||
Vector2 closest_on_polygon;
|
Vector2 closest_on_polygon;
|
||||||
real_t closest = FLT_MAX;
|
real_t closest = FLT_MAX;
|
||||||
@ -803,8 +821,8 @@ Vector2 NavMeshQueries2D::map_iteration_get_random_point(const NavMapIteration2D
|
|||||||
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 NavRegionIteration2D ®ion = p_map_iteration.region_iterations[i];
|
const Ref<NavRegionIteration2D> ®ion = p_map_iteration.region_iterations[i];
|
||||||
if (!region.enabled || (p_navigation_layers & region.navigation_layers) == 0) {
|
if (!region->get_enabled() || (p_navigation_layers & region->get_navigation_layers()) == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
accessible_regions.push_back(i);
|
accessible_regions.push_back(i);
|
||||||
@ -820,9 +838,9 @@ Vector2 NavMeshQueries2D::map_iteration_get_random_point(const NavMapIteration2D
|
|||||||
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 NavRegionIteration2D ®ion = p_map_iteration.region_iterations[accessible_regions[accessible_region_index]];
|
const Ref<NavRegionIteration2D> ®ion = 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;
|
||||||
@ -843,16 +861,16 @@ Vector2 NavMeshQueries2D::map_iteration_get_random_point(const NavMapIteration2D
|
|||||||
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(), Vector2());
|
ERR_FAIL_UNSIGNED_INDEX_V(random_region_index, accessible_regions.size(), Vector2());
|
||||||
|
|
||||||
const NavRegionIteration2D &random_region = p_map_iteration.region_iterations[accessible_regions[random_region_index]];
|
const Ref<NavRegionIteration2D> &random_region = p_map_iteration.region_iterations[accessible_regions[random_region_index]];
|
||||||
|
|
||||||
return NavMeshQueries2D::polygons_get_random_point(random_region.navmesh_polygons, p_navigation_layers, p_uniformly);
|
return NavMeshQueries2D::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 NavRegionIteration2D &random_region = p_map_iteration.region_iterations[accessible_regions[random_region_index]];
|
const Ref<NavRegionIteration2D> &random_region = p_map_iteration.region_iterations[accessible_regions[random_region_index]];
|
||||||
|
|
||||||
return NavMeshQueries2D::polygons_get_random_point(random_region.navmesh_polygons, p_navigation_layers, p_uniformly);
|
return NavMeshQueries2D::polygons_get_random_point(random_region->navmesh_polygons, p_navigation_layers, p_uniformly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -978,8 +996,15 @@ void NavMeshQueries2D::_query_task_clip_path(NavMeshPathQueryTask2D &p_query_tas
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool NavMeshQueries2D::_query_task_is_connection_owner_usable(const NavMeshPathQueryTask2D &p_query_task, const NavBaseIteration2D *p_owner) {
|
bool NavMeshQueries2D::_query_task_is_connection_owner_usable(const NavMeshPathQueryTask2D &p_query_task, const NavBaseIteration2D *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;
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
|
|
||||||
#include "../nav_utils_2d.h"
|
#include "../nav_utils_2d.h"
|
||||||
|
|
||||||
|
#include "core/templates/a_hash_map.h"
|
||||||
|
|
||||||
#include "servers/navigation/navigation_path_query_parameters_2d.h"
|
#include "servers/navigation/navigation_path_query_parameters_2d.h"
|
||||||
#include "servers/navigation/navigation_path_query_result_2d.h"
|
#include "servers/navigation/navigation_path_query_result_2d.h"
|
||||||
#include "servers/navigation/navigation_utilities.h"
|
#include "servers/navigation/navigation_utilities.h"
|
||||||
@ -48,6 +50,7 @@ public:
|
|||||||
Heap<Nav2D::NavigationPoly *, Nav2D::NavPolyTravelCostGreaterThan, Nav2D::NavPolyHeapIndexer> traversable_polys;
|
Heap<Nav2D::NavigationPoly *, Nav2D::NavPolyTravelCostGreaterThan, Nav2D::NavPolyHeapIndexer> traversable_polys;
|
||||||
bool in_use = false;
|
bool in_use = false;
|
||||||
uint32_t slot_index = 0;
|
uint32_t slot_index = 0;
|
||||||
|
AHashMap<const Nav2D::Polygon *, uint32_t> poly_to_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NavMeshPathQueryTask2D {
|
struct NavMeshPathQueryTask2D {
|
||||||
@ -128,7 +131,7 @@ public:
|
|||||||
static void query_task_map_iteration_get_path(NavMeshPathQueryTask2D &p_query_task, const NavMapIteration2D &p_map_iteration);
|
static void query_task_map_iteration_get_path(NavMeshPathQueryTask2D &p_query_task, const NavMapIteration2D &p_map_iteration);
|
||||||
static void _query_task_push_back_point_with_metadata(NavMeshPathQueryTask2D &p_query_task, const Vector2 &p_point, const Nav2D::Polygon *p_point_polygon);
|
static void _query_task_push_back_point_with_metadata(NavMeshPathQueryTask2D &p_query_task, const Vector2 &p_point, const Nav2D::Polygon *p_point_polygon);
|
||||||
static void _query_task_find_start_end_positions(NavMeshPathQueryTask2D &p_query_task, const NavMapIteration2D &p_map_iteration);
|
static void _query_task_find_start_end_positions(NavMeshPathQueryTask2D &p_query_task, const NavMapIteration2D &p_map_iteration);
|
||||||
static void _query_task_build_path_corridor(NavMeshPathQueryTask2D &p_query_task);
|
static void _query_task_build_path_corridor(NavMeshPathQueryTask2D &p_query_task, const NavMapIteration2D &p_map_iteration);
|
||||||
static void _query_task_post_process_corridorfunnel(NavMeshPathQueryTask2D &p_query_task);
|
static void _query_task_post_process_corridorfunnel(NavMeshPathQueryTask2D &p_query_task);
|
||||||
static void _query_task_post_process_edgecentered(NavMeshPathQueryTask2D &p_query_task);
|
static void _query_task_post_process_edgecentered(NavMeshPathQueryTask2D &p_query_task);
|
||||||
static void _query_task_post_process_nopostprocessing(NavMeshPathQueryTask2D &p_query_task);
|
static void _query_task_post_process_nopostprocessing(NavMeshPathQueryTask2D &p_query_task);
|
||||||
@ -136,6 +139,8 @@ public:
|
|||||||
static void _query_task_simplified_path_points(NavMeshPathQueryTask2D &p_query_task);
|
static void _query_task_simplified_path_points(NavMeshPathQueryTask2D &p_query_task);
|
||||||
static bool _query_task_is_connection_owner_usable(const NavMeshPathQueryTask2D &p_query_task, const NavBaseIteration2D *p_owner);
|
static bool _query_task_is_connection_owner_usable(const NavMeshPathQueryTask2D &p_query_task, const NavBaseIteration2D *p_owner);
|
||||||
|
|
||||||
|
static void _query_task_search_polygon_connections(NavMeshPathQueryTask2D &p_query_task, const Nav2D::Connection &p_connection, uint32_t p_least_cost_id, const Nav2D::NavigationPoly &p_least_cost_poly, real_t p_poly_enter_cost, const Vector2 &p_end_point);
|
||||||
|
|
||||||
static void simplify_path_segment(int p_start_inx, int p_end_inx, const LocalVector<Vector2> &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<Vector2> &p_points, real_t p_epsilon, LocalVector<uint32_t> &r_simplified_path_indices);
|
||||||
static LocalVector<uint32_t> get_simplified_path_indices(const LocalVector<Vector2> &p_path, real_t p_epsilon);
|
static LocalVector<uint32_t> get_simplified_path_indices(const LocalVector<Vector2> &p_path, real_t p_epsilon);
|
||||||
};
|
};
|
||||||
|
255
modules/navigation_2d/2d/nav_region_builder_2d.cpp
Normal file
255
modules/navigation_2d/2d/nav_region_builder_2d.cpp
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* nav_region_builder_2d.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_2d.h"
|
||||||
|
|
||||||
|
#include "../nav_map_2d.h"
|
||||||
|
#include "../nav_region_2d.h"
|
||||||
|
#include "../triangle2.h"
|
||||||
|
#include "nav_region_iteration_2d.h"
|
||||||
|
|
||||||
|
using namespace Nav2D;
|
||||||
|
|
||||||
|
void NavRegionBuilder2D::build_iteration(NavRegionIterationBuild2D &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 NavRegionBuilder2D::_build_step_process_navmesh_data(NavRegionIterationBuild2D &r_build) {
|
||||||
|
Vector<Vector2> _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<NavRegionIteration2D> region_iteration = r_build.region_iteration;
|
||||||
|
|
||||||
|
const Transform2D ®ion_transform = region_iteration->transform;
|
||||||
|
LocalVector<Nav2D::Polygon> &navmesh_polygons = region_iteration->navmesh_polygons;
|
||||||
|
|
||||||
|
const int vertex_count = _navmesh_vertices.size();
|
||||||
|
|
||||||
|
const Vector2 *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;
|
||||||
|
Rect2 _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 Triangle2 triangle = Triangle2(
|
||||||
|
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 += triangle.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 Vector2 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
Nav2D::PointKey NavRegionBuilder2D::get_point_key(const Vector2 &p_pos, const Vector2 &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));
|
||||||
|
|
||||||
|
PointKey p;
|
||||||
|
p.key = 0;
|
||||||
|
p.x = x;
|
||||||
|
p.y = y;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nav2D::EdgeKey NavRegionBuilder2D::get_edge_key(const Vector2 &p_vertex1, const Vector2 &p_vertex2, const Vector2 &p_cell_size) {
|
||||||
|
EdgeKey ek(get_point_key(p_vertex1, p_cell_size), get_point_key(p_vertex2, p_cell_size));
|
||||||
|
return ek;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegionBuilder2D::_build_step_find_edge_connection_pairs(NavRegionIterationBuild2D &r_build) {
|
||||||
|
PerformanceData &performance_data = r_build.performance_data;
|
||||||
|
|
||||||
|
const Vector2 &map_cell_size = r_build.map_cell_size;
|
||||||
|
Ref<NavRegionIteration2D> region_iteration = r_build.region_iteration;
|
||||||
|
LocalVector<Nav2D::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 NavRegionBuilder2D::_build_step_merge_edge_connection_pairs(NavRegionIterationBuild2D &r_build) {
|
||||||
|
PerformanceData &performance_data = r_build.performance_data;
|
||||||
|
|
||||||
|
Ref<NavRegionIteration2D> 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 NavRegionBuilder2D::_build_update_iteration(NavRegionIterationBuild2D &r_build) {
|
||||||
|
ERR_FAIL_NULL(r_build.region);
|
||||||
|
// Stub. End of the build.
|
||||||
|
}
|
48
modules/navigation_2d/2d/nav_region_builder_2d.h
Normal file
48
modules/navigation_2d/2d/nav_region_builder_2d.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* nav_region_builder_2d.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_2d.h"
|
||||||
|
|
||||||
|
struct NavRegionIterationBuild2D;
|
||||||
|
|
||||||
|
class NavRegionBuilder2D {
|
||||||
|
static void _build_step_process_navmesh_data(NavRegionIterationBuild2D &r_build);
|
||||||
|
static void _build_step_find_edge_connection_pairs(NavRegionIterationBuild2D &r_build);
|
||||||
|
static void _build_step_merge_edge_connection_pairs(NavRegionIterationBuild2D &r_build);
|
||||||
|
static void _build_update_iteration(NavRegionIterationBuild2D &r_build);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static Nav2D::PointKey get_point_key(const Vector2 &p_pos, const Vector2 &p_cell_size);
|
||||||
|
static Nav2D::EdgeKey get_edge_key(const Vector2 &p_vertex1, const Vector2 &p_vertex2, const Vector2 &p_cell_size);
|
||||||
|
|
||||||
|
static void build_iteration(NavRegionIterationBuild2D &r_build);
|
||||||
|
};
|
@ -32,15 +32,61 @@
|
|||||||
|
|
||||||
#include "../nav_utils_2d.h"
|
#include "../nav_utils_2d.h"
|
||||||
#include "nav_base_iteration_2d.h"
|
#include "nav_base_iteration_2d.h"
|
||||||
|
#include "scene/resources/2d/navigation_polygon.h"
|
||||||
|
|
||||||
#include "core/math/rect2.h"
|
#include "core/math/rect2.h"
|
||||||
|
|
||||||
struct NavRegionIteration2D : NavBaseIteration2D {
|
class NavRegion2D;
|
||||||
|
class NavRegionIteration2D;
|
||||||
|
|
||||||
|
struct NavRegionIterationBuild2D {
|
||||||
|
Nav2D::PerformanceData performance_data;
|
||||||
|
|
||||||
|
NavRegion2D *region = nullptr;
|
||||||
|
|
||||||
|
Vector2 map_cell_size;
|
||||||
|
Transform2D region_transform;
|
||||||
|
|
||||||
|
struct NavMeshData {
|
||||||
|
Vector<Vector2> vertices;
|
||||||
|
Vector<Vector<int>> polygons;
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
vertices.clear();
|
||||||
|
polygons.clear();
|
||||||
|
}
|
||||||
|
} navmesh_data;
|
||||||
|
|
||||||
|
Ref<NavRegionIteration2D> region_iteration;
|
||||||
|
|
||||||
|
HashMap<Nav2D::EdgeKey, Nav2D::EdgeConnectionPair, Nav2D::EdgeKey> iter_connection_pairs_map;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
performance_data.reset();
|
||||||
|
|
||||||
|
navmesh_data.clear();
|
||||||
|
region_iteration = Ref<NavRegionIteration2D>();
|
||||||
|
iter_connection_pairs_map.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NavRegionIteration2D : public NavBaseIteration2D {
|
||||||
|
GDCLASS(NavRegionIteration2D, NavBaseIteration2D);
|
||||||
|
|
||||||
|
public:
|
||||||
Transform2D transform;
|
Transform2D transform;
|
||||||
real_t surface_area = 0.0;
|
real_t surface_area = 0.0;
|
||||||
Rect2 bounds;
|
Rect2 bounds;
|
||||||
|
LocalVector<Nav2D::ConnectableEdge> external_edges;
|
||||||
|
|
||||||
const Transform2D &get_transform() const { return transform; }
|
const Transform2D &get_transform() const { return transform; }
|
||||||
real_t get_surface_area() const { return surface_area; }
|
real_t get_surface_area() const { return surface_area; }
|
||||||
Rect2 get_bounds() const { return bounds; }
|
Rect2 get_bounds() const { return bounds; }
|
||||||
|
const LocalVector<Nav2D::ConnectableEdge> &get_external_edges() const { return external_edges; }
|
||||||
|
|
||||||
|
virtual ~NavRegionIteration2D() override {
|
||||||
|
external_edges.clear();
|
||||||
|
navmesh_polygons.clear();
|
||||||
|
internal_connections.clear();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -44,7 +44,7 @@ void NavLink2D::set_map(NavMap2D *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 NavLink2D::set_enabled(bool p_enabled) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
enabled = p_enabled;
|
enabled = p_enabled;
|
||||||
|
iteration_dirty = true;
|
||||||
// TODO: This should not require a full rebuild as the link has not really changed.
|
|
||||||
link_dirty = true;
|
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
@ -69,27 +67,27 @@ void NavLink2D::set_bidirectional(bool p_bidirectional) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bidirectional = p_bidirectional;
|
bidirectional = p_bidirectional;
|
||||||
link_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavLink2D::set_start_position(const Vector2 &p_position) {
|
void NavLink2D::set_start_position(const Vector2 p_position) {
|
||||||
if (start_position == p_position) {
|
if (start_position == p_position) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
start_position = p_position;
|
start_position = p_position;
|
||||||
link_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavLink2D::set_end_position(const Vector2 &p_position) {
|
void NavLink2D::set_end_position(const Vector2 p_position) {
|
||||||
if (end_position == p_position) {
|
if (end_position == 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 NavLink2D::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 NavLink2D::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 NavLink2D::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 NavLink2D::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 NavLink2D::is_dirty() const {
|
bool NavLink2D::sync() {
|
||||||
return link_dirty;
|
bool requires_map_update = false;
|
||||||
|
if (!map) {
|
||||||
|
return requires_map_update;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavLink2D::sync() {
|
if (iteration_dirty && !iteration_building && !iteration_ready) {
|
||||||
if (link_dirty) {
|
_build_iteration();
|
||||||
|
iteration_ready = false;
|
||||||
|
requires_map_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return requires_map_update;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavLink2D::_build_iteration() {
|
||||||
|
if (!iteration_dirty || iteration_building || iteration_ready) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iteration_dirty = false;
|
||||||
|
iteration_building = true;
|
||||||
|
iteration_ready = false;
|
||||||
|
|
||||||
|
Ref<NavLinkIteration2D> 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<NavLinkIteration2D>();
|
||||||
|
DEV_ASSERT(iteration.is_null());
|
||||||
|
iteration = new_iteration;
|
||||||
iteration_id = iteration_id % UINT32_MAX + 1;
|
iteration_id = iteration_id % UINT32_MAX + 1;
|
||||||
}
|
|
||||||
|
|
||||||
link_dirty = false;
|
iteration_building = false;
|
||||||
|
iteration_ready = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavLink2D::request_sync() {
|
void NavLink2D::request_sync() {
|
||||||
@ -160,27 +196,19 @@ void NavLink2D::cancel_sync_request() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ref<NavLinkIteration2D> NavLink2D::get_iteration() {
|
||||||
|
RWLockRead read_lock(iteration_rwlock);
|
||||||
|
return iteration;
|
||||||
|
}
|
||||||
|
|
||||||
NavLink2D::NavLink2D() :
|
NavLink2D::NavLink2D() :
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
NavLink2D::~NavLink2D() {
|
NavLink2D::~NavLink2D() {
|
||||||
cancel_sync_request();
|
cancel_sync_request();
|
||||||
}
|
|
||||||
|
iteration = Ref<NavLinkIteration2D>();
|
||||||
void NavLink2D::get_iteration_update(NavLinkIteration2D &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();
|
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,10 @@
|
|||||||
#include "nav_base_2d.h"
|
#include "nav_base_2d.h"
|
||||||
#include "nav_utils_2d.h"
|
#include "nav_utils_2d.h"
|
||||||
|
|
||||||
struct NavLinkIteration2D : NavBaseIteration2D {
|
class NavLinkIteration2D : public NavBaseIteration2D {
|
||||||
|
GDCLASS(NavLinkIteration2D, NavBaseIteration2D);
|
||||||
|
|
||||||
|
public:
|
||||||
bool bidirectional = true;
|
bool bidirectional = true;
|
||||||
Vector2 start_position;
|
Vector2 start_position;
|
||||||
Vector2 end_position;
|
Vector2 end_position;
|
||||||
@ -42,6 +45,11 @@ struct NavLinkIteration2D : NavBaseIteration2D {
|
|||||||
Vector2 get_start_position() const { return start_position; }
|
Vector2 get_start_position() const { return start_position; }
|
||||||
Vector2 get_end_position() const { return end_position; }
|
Vector2 get_end_position() const { return end_position; }
|
||||||
bool is_bidirectional() const { return bidirectional; }
|
bool is_bidirectional() const { return bidirectional; }
|
||||||
|
|
||||||
|
virtual ~NavLinkIteration2D() override {
|
||||||
|
navmesh_polygons.clear();
|
||||||
|
internal_connections.clear();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "core/templates/self_list.h"
|
#include "core/templates/self_list.h"
|
||||||
@ -53,12 +61,20 @@ class NavLink2D : public NavBase2D {
|
|||||||
Vector2 end_position;
|
Vector2 end_position;
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
|
|
||||||
bool link_dirty = true;
|
|
||||||
|
|
||||||
SelfList<NavLink2D> sync_dirty_request_list_element;
|
SelfList<NavLink2D> sync_dirty_request_list_element;
|
||||||
|
|
||||||
uint32_t iteration_id = 0;
|
uint32_t iteration_id = 0;
|
||||||
|
|
||||||
|
mutable RWLock iteration_rwlock;
|
||||||
|
Ref<NavLinkIteration2D> iteration;
|
||||||
|
|
||||||
|
bool iteration_dirty = true;
|
||||||
|
bool iteration_building = false;
|
||||||
|
bool iteration_ready = false;
|
||||||
|
|
||||||
|
void _build_iteration();
|
||||||
|
void _sync_iteration();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NavLink2D();
|
NavLink2D();
|
||||||
~NavLink2D();
|
~NavLink2D();
|
||||||
@ -78,12 +94,12 @@ public:
|
|||||||
return bidirectional;
|
return bidirectional;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_start_position(const Vector2 &p_position);
|
void set_start_position(Vector2 p_position);
|
||||||
Vector2 get_start_position() const {
|
Vector2 get_start_position() const {
|
||||||
return start_position;
|
return start_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_end_position(const Vector2 &p_position);
|
void set_end_position(Vector2 p_position);
|
||||||
Vector2 get_end_position() const {
|
Vector2 get_end_position() const {
|
||||||
return end_position;
|
return end_position;
|
||||||
}
|
}
|
||||||
@ -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;
|
bool sync();
|
||||||
void sync();
|
|
||||||
void request_sync();
|
void request_sync();
|
||||||
void cancel_sync_request();
|
void cancel_sync_request();
|
||||||
|
|
||||||
void get_iteration_update(NavLinkIteration2D &r_iteration);
|
Ref<NavLinkIteration2D> get_iteration();
|
||||||
};
|
};
|
||||||
|
@ -109,7 +109,7 @@ void NavMap2D::set_link_connection_radius(real_t p_link_connection_radius) {
|
|||||||
iteration_dirty = true;
|
iteration_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 NavMap2D::get_merge_rasterizer_cell_size() const {
|
const Vector2 &NavMap2D::get_merge_rasterizer_cell_size() const {
|
||||||
return merge_rasterizer_cell_size;
|
return merge_rasterizer_cell_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,6 +188,8 @@ ClosestPointQueryResult NavMap2D::get_closest_point_info(const Vector2 &p_point)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::add_region(NavRegion2D *p_region) {
|
void NavMap2D::add_region(NavRegion2D *p_region) {
|
||||||
|
DEV_ASSERT(!regions.has(p_region));
|
||||||
|
|
||||||
regions.push_back(p_region);
|
regions.push_back(p_region);
|
||||||
iteration_dirty = true;
|
iteration_dirty = true;
|
||||||
}
|
}
|
||||||
@ -199,6 +201,8 @@ void NavMap2D::remove_region(NavRegion2D *p_region) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::add_link(NavLink2D *p_link) {
|
void NavMap2D::add_link(NavLink2D *p_link) {
|
||||||
|
DEV_ASSERT(!links.has(p_link));
|
||||||
|
|
||||||
links.push_back(p_link);
|
links.push_back(p_link);
|
||||||
iteration_dirty = true;
|
iteration_dirty = true;
|
||||||
}
|
}
|
||||||
@ -311,49 +315,22 @@ void NavMap2D::_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;
|
next_map_iteration.clear();
|
||||||
uint32_t enabled_link_count = 0;
|
|
||||||
|
|
||||||
for (NavRegion2D *region : regions) {
|
next_map_iteration.region_iterations.resize(regions.size());
|
||||||
if (!region->get_enabled()) {
|
next_map_iteration.link_iterations.resize(links.size());
|
||||||
continue;
|
|
||||||
}
|
|
||||||
enabled_region_count++;
|
|
||||||
}
|
|
||||||
for (NavLink2D *link : links) {
|
|
||||||
if (!link->get_enabled()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
enabled_link_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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 (NavRegion2D *region : regions) {
|
for (NavRegion2D *region : regions) {
|
||||||
if (!region->get_enabled()) {
|
const Ref<NavRegionIteration2D> region_iteration = region->get_iteration();
|
||||||
continue;
|
next_map_iteration.region_iterations[region_id_count++] = region_iteration;
|
||||||
}
|
next_map_iteration.region_ptr_to_region_iteration[region] = region_iteration;
|
||||||
NavRegionIteration2D ®ion_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;
|
|
||||||
}
|
}
|
||||||
for (NavLink2D *link : links) {
|
for (NavLink2D *link : links) {
|
||||||
if (!link->get_enabled()) {
|
const Ref<NavLinkIteration2D> link_iteration = link->get_iteration();
|
||||||
continue;
|
next_map_iteration.link_iterations[link_id_count++] = link_iteration;
|
||||||
}
|
|
||||||
NavLinkIteration2D &link_iteration = next_map_iteration.link_iterations[link_id_count];
|
|
||||||
link_iteration.id = link_id_count++;
|
|
||||||
link->get_iteration_update(link_iteration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iteration_build.map_iteration = &next_map_iteration;
|
iteration_build.map_iteration = &next_map_iteration;
|
||||||
@ -379,9 +356,6 @@ void NavMap2D::_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;
|
||||||
|
|
||||||
@ -403,6 +377,8 @@ void NavMap2D::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) {
|
||||||
@ -426,6 +402,16 @@ void NavMap2D::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 (NavRegion2D *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 NavMap2D::_sync_avoidance() {
|
void NavMap2D::_sync_avoidance() {
|
||||||
@ -569,9 +555,9 @@ int NavMap2D::get_region_connections_count(NavRegion2D *p_region) const {
|
|||||||
|
|
||||||
GET_MAP_ITERATION_CONST();
|
GET_MAP_ITERATION_CONST();
|
||||||
|
|
||||||
HashMap<NavRegion2D *, uint32_t>::ConstIterator found_id = map_iteration.region_ptr_to_region_id.find(p_region);
|
HashMap<NavRegion2D *, Ref<NavRegionIteration2D>>::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 NavBaseIteration2D *, 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();
|
||||||
}
|
}
|
||||||
@ -585,9 +571,9 @@ Vector2 NavMap2D::get_region_connection_pathway_start(NavRegion2D *p_region, int
|
|||||||
|
|
||||||
GET_MAP_ITERATION_CONST();
|
GET_MAP_ITERATION_CONST();
|
||||||
|
|
||||||
HashMap<NavRegion2D *, uint32_t>::ConstIterator found_id = map_iteration.region_ptr_to_region_id.find(p_region);
|
HashMap<NavRegion2D *, Ref<NavRegionIteration2D>>::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 NavBaseIteration2D *, 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()), Vector2());
|
ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector2());
|
||||||
return found_connections->value[p_connection_id].pathway_start;
|
return found_connections->value[p_connection_id].pathway_start;
|
||||||
@ -602,9 +588,9 @@ Vector2 NavMap2D::get_region_connection_pathway_end(NavRegion2D *p_region, int p
|
|||||||
|
|
||||||
GET_MAP_ITERATION_CONST();
|
GET_MAP_ITERATION_CONST();
|
||||||
|
|
||||||
HashMap<NavRegion2D *, uint32_t>::ConstIterator found_id = map_iteration.region_ptr_to_region_id.find(p_region);
|
HashMap<NavRegion2D *, Ref<NavRegionIteration2D>>::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 NavBaseIteration2D *, 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()), Vector2());
|
ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector2());
|
||||||
return found_connections->value[p_connection_id].pathway_end;
|
return found_connections->value[p_connection_id].pathway_end;
|
||||||
@ -618,56 +604,60 @@ void NavMap2D::add_region_sync_dirty_request(SelfList<NavRegion2D> *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 NavMap2D::add_link_sync_dirty_request(SelfList<NavLink2D> *p_sync_request) {
|
void NavMap2D::add_link_sync_dirty_request(SelfList<NavLink2D> *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 NavMap2D::add_agent_sync_dirty_request(SelfList<NavAgent2D> *p_sync_request) {
|
void NavMap2D::add_agent_sync_dirty_request(SelfList<NavAgent2D> *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 NavMap2D::add_obstacle_sync_dirty_request(SelfList<NavObstacle2D> *p_sync_request) {
|
void NavMap2D::add_obstacle_sync_dirty_request(SelfList<NavObstacle2D> *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 NavMap2D::remove_region_sync_dirty_request(SelfList<NavRegion2D> *p_sync_request) {
|
void NavMap2D::remove_region_sync_dirty_request(SelfList<NavRegion2D> *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 NavMap2D::remove_link_sync_dirty_request(SelfList<NavLink2D> *p_sync_request) {
|
void NavMap2D::remove_link_sync_dirty_request(SelfList<NavLink2D> *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 NavMap2D::remove_agent_sync_dirty_request(SelfList<NavAgent2D> *p_sync_request) {
|
void NavMap2D::remove_agent_sync_dirty_request(SelfList<NavAgent2D> *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 NavMap2D::remove_obstacle_sync_dirty_request(SelfList<NavObstacle2D> *p_sync_request) {
|
void NavMap2D::remove_obstacle_sync_dirty_request(SelfList<NavObstacle2D> *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 NavMap2D::_sync_dirty_map_update_requests() {
|
void NavMap2D::_sync_dirty_map_update_requests() {
|
||||||
@ -679,41 +669,69 @@ void NavMap2D::_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<NavRegion2D> *element = sync_dirty_requests.regions.first(); element; element = element->next()) {
|
RWLockWrite write_lock_regions(sync_dirty_requests.regions.rwlock);
|
||||||
element->self()->sync();
|
for (SelfList<NavRegion2D> *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<NavLink2D> *element = sync_dirty_requests.links.first(); element; element = element->next()) {
|
RWLockWrite write_lock_links(sync_dirty_requests.links.rwlock);
|
||||||
element->self()->sync();
|
for (SelfList<NavLink2D> *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 NavMap2D::_sync_dirty_avoidance_update_requests() {
|
void NavMap2D::_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<NavAgent2D> *element = sync_dirty_requests.agents.first(); element; element = element->next()) {
|
for (SelfList<NavAgent2D> *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<NavObstacle2D> *element = sync_dirty_requests.obstacles.first(); element; element = element->next()) {
|
for (SelfList<NavObstacle2D> *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 NavMap2D::add_region_async_thread_join_request(SelfList<NavRegion2D> *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 NavMap2D::remove_region_async_thread_join_request(SelfList<NavRegion2D> *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 NavMap2D::_sync_async_tasks() {
|
||||||
|
// Sync NavRegions that run async thread tasks.
|
||||||
|
RWLockWrite write_lock_regions(async_dirty_requests.regions.rwlock);
|
||||||
|
for (SelfList<NavRegion2D> *element = async_dirty_requests.regions.list.first(); element; element = element->next()) {
|
||||||
|
element->self()->sync_async_tasks();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::set_use_async_iterations(bool p_enabled) {
|
void NavMap2D::set_use_async_iterations(bool p_enabled) {
|
||||||
@ -768,4 +786,9 @@ NavMap2D::~NavMap2D() {
|
|||||||
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 (NavMapIteration2D &iteration_slot : iteration_slots) {
|
||||||
|
iteration_slot.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,12 +102,31 @@ class NavMap2D : public NavRid2D {
|
|||||||
Nav2D::PerformanceData performance_data;
|
Nav2D::PerformanceData performance_data;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
SelfList<NavRegion2D>::List regions;
|
struct {
|
||||||
SelfList<NavLink2D>::List links;
|
RWLock rwlock;
|
||||||
SelfList<NavAgent2D>::List agents;
|
SelfList<NavRegion2D>::List list;
|
||||||
SelfList<NavObstacle2D>::List obstacles;
|
} regions;
|
||||||
|
struct {
|
||||||
|
RWLock rwlock;
|
||||||
|
SelfList<NavLink2D>::List list;
|
||||||
|
} links;
|
||||||
|
struct {
|
||||||
|
RWLock rwlock;
|
||||||
|
SelfList<NavAgent2D>::List list;
|
||||||
|
} agents;
|
||||||
|
struct {
|
||||||
|
RWLock rwlock;
|
||||||
|
SelfList<NavObstacle2D>::List list;
|
||||||
|
} obstacles;
|
||||||
} sync_dirty_requests;
|
} sync_dirty_requests;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct {
|
||||||
|
RWLock rwlock;
|
||||||
|
SelfList<NavRegion2D>::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;
|
||||||
@ -117,7 +136,6 @@ class NavMap2D : public NavRid2D {
|
|||||||
mutable RWLock iteration_slot_rwlock;
|
mutable RWLock iteration_slot_rwlock;
|
||||||
|
|
||||||
NavMapIterationBuild2D iteration_build;
|
NavMapIterationBuild2D 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);
|
||||||
|
|
||||||
@ -160,7 +178,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Nav2D::PointKey get_point_key(const Vector2 &p_pos) const;
|
Nav2D::PointKey get_point_key(const Vector2 &p_pos) const;
|
||||||
Vector2 get_merge_rasterizer_cell_size() const;
|
const Vector2 &get_merge_rasterizer_cell_size() const;
|
||||||
|
|
||||||
void query_path(NavMeshQueries2D::NavMeshPathQueryTask2D &p_query_task);
|
void query_path(NavMeshQueries2D::NavMeshPathQueryTask2D &p_query_task);
|
||||||
|
|
||||||
@ -218,6 +236,9 @@ public:
|
|||||||
Vector2 get_region_connection_pathway_start(NavRegion2D *p_region, int p_connection_id) const;
|
Vector2 get_region_connection_pathway_start(NavRegion2D *p_region, int p_connection_id) const;
|
||||||
Vector2 get_region_connection_pathway_end(NavRegion2D *p_region, int p_connection_id) const;
|
Vector2 get_region_connection_pathway_end(NavRegion2D *p_region, int p_connection_id) const;
|
||||||
|
|
||||||
|
void add_region_async_thread_join_request(SelfList<NavRegion2D> *p_async_request);
|
||||||
|
void remove_region_async_thread_join_request(SelfList<NavRegion2D> *p_async_request);
|
||||||
|
|
||||||
void add_region_sync_dirty_request(SelfList<NavRegion2D> *p_sync_request);
|
void add_region_sync_dirty_request(SelfList<NavRegion2D> *p_sync_request);
|
||||||
void add_link_sync_dirty_request(SelfList<NavLink2D> *p_sync_request);
|
void add_link_sync_dirty_request(SelfList<NavLink2D> *p_sync_request);
|
||||||
void add_agent_sync_dirty_request(SelfList<NavAgent2D> *p_sync_request);
|
void add_agent_sync_dirty_request(SelfList<NavAgent2D> *p_sync_request);
|
||||||
@ -234,6 +255,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 p_index, NavAgent2D **p_agent);
|
void compute_single_step(uint32_t p_index, NavAgent2D **p_agent);
|
||||||
|
|
||||||
|
@ -31,11 +31,11 @@
|
|||||||
#include "nav_region_2d.h"
|
#include "nav_region_2d.h"
|
||||||
|
|
||||||
#include "nav_map_2d.h"
|
#include "nav_map_2d.h"
|
||||||
#include "triangle2.h"
|
|
||||||
|
|
||||||
#include "2d/nav_map_builder_2d.h"
|
|
||||||
#include "2d/nav_mesh_queries_2d.h"
|
#include "2d/nav_mesh_queries_2d.h"
|
||||||
|
#include "2d/nav_region_builder_2d.h"
|
||||||
#include "2d/nav_region_iteration_2d.h"
|
#include "2d/nav_region_iteration_2d.h"
|
||||||
|
#include "core/config/project_settings.h"
|
||||||
|
|
||||||
using namespace Nav2D;
|
using namespace Nav2D;
|
||||||
|
|
||||||
@ -44,6 +44,7 @@ void NavRegion2D::set_map(NavMap2D *p_map) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancel_async_thread_join();
|
||||||
cancel_sync_request();
|
cancel_sync_request();
|
||||||
|
|
||||||
if (map) {
|
if (map) {
|
||||||
@ -51,11 +52,14 @@ void NavRegion2D::set_map(NavMap2D *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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,9 +68,7 @@ void NavRegion2D::set_enabled(bool p_enabled) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
enabled = p_enabled;
|
enabled = p_enabled;
|
||||||
|
iteration_dirty = true;
|
||||||
// TODO: This should not require a full rebuild as the region has not really changed.
|
|
||||||
polygons_dirty = true;
|
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
@ -74,39 +76,32 @@ void NavRegion2D::set_enabled(bool p_enabled) {
|
|||||||
void NavRegion2D::set_use_edge_connections(bool p_enabled) {
|
void NavRegion2D::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();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavRegion2D::set_transform(const Transform2D &p_transform) {
|
void NavRegion2D::set_transform(Transform2D p_transform) {
|
||||||
if (transform == p_transform) {
|
if (transform == p_transform) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
transform = p_transform;
|
transform = p_transform;
|
||||||
polygons_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavRegion2D::set_navigation_polygon(Ref<NavigationPolygon> p_navigation_polygon) {
|
void NavRegion2D::set_navigation_mesh(Ref<NavigationPolygon> p_navigation_mesh) {
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
if (map && p_navigation_polygon.is_valid() && !Math::is_equal_approx(double(map->get_cell_size()), double(p_navigation_polygon->get_cell_size()))) {
|
if (map && p_navigation_mesh.is_valid() && !Math::is_equal_approx(double(map->get_cell_size()), double(p_navigation_mesh->get_cell_size()))) {
|
||||||
ERR_PRINT_ONCE(vformat("Attempted to update a navigation region with a navigation mesh that uses a `cell_size` of %s while assigned to a navigation map set to a `cell_size` of %s. The cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function. The cell size for default navigation maps can also be changed in the ProjectSettings.", double(p_navigation_polygon->get_cell_size()), double(map->get_cell_size())));
|
ERR_PRINT_ONCE(vformat("Attempted to update a navigation region with a navigation mesh that uses a `cell_size` of %s while assigned to a navigation map set to a `cell_size` of %s. The cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function. The cell size for default navigation maps can also be changed in the ProjectSettings.", double(p_navigation_mesh->get_cell_size()), double(map->get_cell_size())));
|
||||||
}
|
}
|
||||||
#endif // DEBUG_ENABLED
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
RWLockWrite write_lock(navmesh_rwlock);
|
navmesh = p_navigation_mesh;
|
||||||
|
|
||||||
pending_navmesh_vertices.clear();
|
iteration_dirty = true;
|
||||||
pending_navmesh_polygons.clear();
|
|
||||||
|
|
||||||
if (p_navigation_polygon.is_valid()) {
|
|
||||||
p_navigation_polygon->get_data(pending_navmesh_vertices, pending_navmesh_polygons);
|
|
||||||
}
|
|
||||||
|
|
||||||
polygons_dirty = true;
|
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
@ -132,7 +127,7 @@ void NavRegion2D::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();
|
||||||
}
|
}
|
||||||
@ -143,7 +138,7 @@ void NavRegion2D::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();
|
||||||
}
|
}
|
||||||
@ -154,7 +149,7 @@ void NavRegion2D::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();
|
||||||
}
|
}
|
||||||
@ -164,139 +159,150 @@ void NavRegion2D::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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavRegion2D::scratch_polygons() {
|
||||||
|
iteration_dirty = true;
|
||||||
|
|
||||||
|
request_sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
real_t NavRegion2D::get_surface_area() const {
|
||||||
|
RWLockRead read_lock(iteration_rwlock);
|
||||||
|
return iteration->get_surface_area();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect2 NavRegion2D::get_bounds() const {
|
||||||
|
RWLockRead read_lock(iteration_rwlock);
|
||||||
|
return iteration->get_bounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalVector<Nav2D::Polygon> const &NavRegion2D::get_polygons() const {
|
||||||
|
RWLockRead read_lock(iteration_rwlock);
|
||||||
|
return iteration->get_navmesh_polygons();
|
||||||
|
}
|
||||||
|
|
||||||
bool NavRegion2D::sync() {
|
bool NavRegion2D::sync() {
|
||||||
RWLockWrite write_lock(region_rwlock);
|
bool requires_map_update = false;
|
||||||
|
if (!map) {
|
||||||
bool something_changed = region_dirty || polygons_dirty;
|
return requires_map_update;
|
||||||
|
|
||||||
region_dirty = false;
|
|
||||||
|
|
||||||
update_polygons();
|
|
||||||
|
|
||||||
if (something_changed) {
|
|
||||||
iteration_id = iteration_id % UINT32_MAX + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return something_changed;
|
if (iteration_dirty && !iteration_building && !iteration_ready) {
|
||||||
|
_build_iteration();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavRegion2D::update_polygons() {
|
if (iteration_ready) {
|
||||||
if (!polygons_dirty) {
|
_sync_iteration();
|
||||||
return;
|
requires_map_update = true;
|
||||||
}
|
}
|
||||||
navmesh_polygons.clear();
|
|
||||||
surface_area = 0.0;
|
|
||||||
bounds = Rect2();
|
|
||||||
polygons_dirty = false;
|
|
||||||
|
|
||||||
if (map == nullptr) {
|
return requires_map_update;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegion2D::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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegion2D::_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()) {
|
iteration_build.reset();
|
||||||
return;
|
|
||||||
|
if (navmesh.is_valid()) {
|
||||||
|
navmesh->get_data(iteration_build.navmesh_data.vertices, iteration_build.navmesh_data.polygons);
|
||||||
}
|
}
|
||||||
|
|
||||||
int len = pending_navmesh_vertices.size();
|
iteration_build.map_cell_size = map->get_merge_rasterizer_cell_size();
|
||||||
if (len == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Vector2 *vertices_r = pending_navmesh_vertices.ptr();
|
Ref<NavRegionIteration2D> new_iteration;
|
||||||
|
new_iteration.instantiate();
|
||||||
|
|
||||||
navmesh_polygons.resize(pending_navmesh_polygons.size());
|
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();
|
||||||
|
|
||||||
real_t _new_region_surface_area = 0.0;
|
iteration_build.region_iteration = new_iteration;
|
||||||
Rect2 _new_bounds;
|
|
||||||
|
|
||||||
bool first_vertex = true;
|
if (use_async_iterations) {
|
||||||
int navigation_mesh_polygon_index = 0;
|
iteration_build_thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavRegion2D::_build_iteration_threaded, &iteration_build, true, SNAME("NavRegionBuilder2D"));
|
||||||
|
request_async_thread_join();
|
||||||
for (Polygon &polygon : navmesh_polygons) {
|
|
||||||
polygon.surface_area = 0.0;
|
|
||||||
|
|
||||||
Vector<int> navigation_mesh_polygon = pending_navmesh_polygons[navigation_mesh_polygon_index];
|
|
||||||
navigation_mesh_polygon_index += 1;
|
|
||||||
|
|
||||||
int navigation_mesh_polygon_size = navigation_mesh_polygon.size();
|
|
||||||
if (navigation_mesh_polygon_size < 3) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int *indices = navigation_mesh_polygon.ptr();
|
|
||||||
bool valid(true);
|
|
||||||
|
|
||||||
polygon.vertices.resize(navigation_mesh_polygon_size);
|
|
||||||
polygon.edges.resize(navigation_mesh_polygon_size);
|
|
||||||
|
|
||||||
real_t _new_polygon_surface_area = 0.0;
|
|
||||||
|
|
||||||
for (int j(2); j < navigation_mesh_polygon_size; j++) {
|
|
||||||
const Triangle2 triangle = Triangle2(
|
|
||||||
transform.xform(vertices_r[indices[0]]),
|
|
||||||
transform.xform(vertices_r[indices[j - 1]]),
|
|
||||||
transform.xform(vertices_r[indices[j]]));
|
|
||||||
|
|
||||||
_new_polygon_surface_area += triangle.get_area();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 point_position = transform.xform(vertices_r[idx]);
|
|
||||||
polygon.vertices[j] = point_position;
|
|
||||||
|
|
||||||
if (first_vertex) {
|
|
||||||
first_vertex = false;
|
|
||||||
_new_bounds.position = point_position;
|
|
||||||
} else {
|
} else {
|
||||||
_new_bounds.expand_to(point_position);
|
NavRegionBuilder2D::build_iteration(iteration_build);
|
||||||
|
|
||||||
|
iteration_building = false;
|
||||||
|
iteration_ready = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid) {
|
void NavRegion2D::_build_iteration_threaded(void *p_arg) {
|
||||||
ERR_BREAK_MSG(!valid, "The navigation polygon set in this region is not valid!");
|
NavRegionIterationBuild2D *_iteration_build = static_cast<NavRegionIterationBuild2D *>(p_arg);
|
||||||
|
|
||||||
|
NavRegionBuilder2D::build_iteration(*_iteration_build);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegion2D::_sync_iteration() {
|
||||||
|
if (iteration_building || !iteration_ready) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
RWLockWrite write_lock(iteration_rwlock);
|
||||||
|
ERR_FAIL_COND(iteration.is_null());
|
||||||
|
iteration = Ref<NavRegionIteration2D>();
|
||||||
|
DEV_ASSERT(iteration.is_null());
|
||||||
|
iteration = iteration_build.region_iteration;
|
||||||
|
iteration_build.region_iteration = Ref<NavRegionIteration2D>();
|
||||||
|
DEV_ASSERT(iteration_build.region_iteration.is_null());
|
||||||
|
iteration_id = iteration_id % UINT32_MAX + 1;
|
||||||
|
|
||||||
|
iteration_ready = false;
|
||||||
|
|
||||||
|
cancel_async_thread_join();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<NavRegionIteration2D> NavRegion2D::get_iteration() {
|
||||||
|
RWLockRead read_lock(iteration_rwlock);
|
||||||
|
return iteration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegion2D::request_async_thread_join() {
|
||||||
|
DEV_ASSERT(map);
|
||||||
|
if (map && !async_list_element.in_list()) {
|
||||||
|
map->add_region_async_thread_join_request(&async_list_element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
surface_area = _new_region_surface_area;
|
void NavRegion2D::cancel_async_thread_join() {
|
||||||
bounds = _new_bounds;
|
if (map && async_list_element.in_list()) {
|
||||||
}
|
map->remove_region_async_thread_join_request(&async_list_element);
|
||||||
|
|
||||||
void NavRegion2D::get_iteration_update(NavRegionIteration2D &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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,11 +318,42 @@ void NavRegion2D::cancel_sync_request() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavRegion2D::set_use_async_iterations(bool p_enabled) {
|
||||||
|
if (use_async_iterations == p_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
|
use_async_iterations = p_enabled;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NavRegion2D::get_use_async_iterations() const {
|
||||||
|
return use_async_iterations;
|
||||||
|
}
|
||||||
|
|
||||||
NavRegion2D::NavRegion2D() :
|
NavRegion2D::NavRegion2D() :
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
NavRegion2D::~NavRegion2D() {
|
NavRegion2D::~NavRegion2D() {
|
||||||
|
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<NavRegionIteration2D>();
|
||||||
|
iteration = Ref<NavRegionIteration2D>();
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
#include "core/os/rw_lock.h"
|
#include "core/os/rw_lock.h"
|
||||||
#include "scene/resources/2d/navigation_polygon.h"
|
#include "scene/resources/2d/navigation_polygon.h"
|
||||||
|
|
||||||
struct NavRegionIteration2D;
|
#include "2d/nav_region_iteration_2d.h"
|
||||||
|
|
||||||
class NavRegion2D : public NavBase2D {
|
class NavRegion2D : public NavBase2D {
|
||||||
RWLock region_rwlock;
|
RWLock region_rwlock;
|
||||||
@ -47,21 +47,30 @@ class NavRegion2D : public NavBase2D {
|
|||||||
|
|
||||||
bool use_edge_connections = true;
|
bool use_edge_connections = true;
|
||||||
|
|
||||||
bool region_dirty = true;
|
|
||||||
bool polygons_dirty = true;
|
|
||||||
|
|
||||||
LocalVector<Nav2D::Polygon> navmesh_polygons;
|
|
||||||
|
|
||||||
real_t surface_area = 0.0;
|
|
||||||
Rect2 bounds;
|
Rect2 bounds;
|
||||||
|
|
||||||
RWLock navmesh_rwlock;
|
Ref<NavigationPolygon> navmesh;
|
||||||
Vector<Vector2> pending_navmesh_vertices;
|
|
||||||
Vector<Vector<int>> pending_navmesh_polygons;
|
Nav2D::PerformanceData performance_data;
|
||||||
|
|
||||||
uint32_t iteration_id = 0;
|
uint32_t iteration_id = 0;
|
||||||
|
|
||||||
SelfList<NavRegion2D> sync_dirty_request_list_element;
|
SelfList<NavRegion2D> sync_dirty_request_list_element;
|
||||||
|
mutable RWLock iteration_rwlock;
|
||||||
|
Ref<NavRegionIteration2D> iteration;
|
||||||
|
|
||||||
|
NavRegionIterationBuild2D iteration_build;
|
||||||
|
bool use_async_iterations = true;
|
||||||
|
SelfList<NavRegion2D> 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:
|
||||||
NavRegion2D();
|
NavRegion2D();
|
||||||
@ -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() {
|
void scratch_polygons();
|
||||||
polygons_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_enabled(bool p_enabled);
|
void set_enabled(bool p_enabled);
|
||||||
bool get_enabled() const { return enabled; }
|
bool get_enabled() const { return enabled; }
|
||||||
@ -84,22 +91,21 @@ public:
|
|||||||
virtual void set_use_edge_connections(bool p_enabled) override;
|
virtual void set_use_edge_connections(bool p_enabled) override;
|
||||||
virtual bool get_use_edge_connections() const override { return use_edge_connections; }
|
virtual bool get_use_edge_connections() const override { return use_edge_connections; }
|
||||||
|
|
||||||
void set_transform(const Transform2D &p_transform);
|
void set_transform(Transform2D transform);
|
||||||
const Transform2D &get_transform() const {
|
const Transform2D &get_transform() const {
|
||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_navigation_polygon(Ref<NavigationPolygon> p_navigation_polygon);
|
void set_navigation_mesh(Ref<NavigationPolygon> p_navigation_mesh);
|
||||||
|
Ref<NavigationPolygon> get_navigation_mesh() const { return navmesh; }
|
||||||
|
|
||||||
LocalVector<Nav2D::Polygon> const &get_polygons() const {
|
LocalVector<Nav2D::Polygon> const &get_polygons() const;
|
||||||
return navmesh_polygons;
|
|
||||||
}
|
|
||||||
|
|
||||||
Nav2D::ClosestPointQueryResult get_closest_point_info(const Vector2 &p_point) const;
|
Nav2D::ClosestPointQueryResult get_closest_point_info(const Vector2 &p_point) const;
|
||||||
Vector2 get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const;
|
Vector2 get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const;
|
||||||
|
|
||||||
real_t get_surface_area() const { return surface_area; }
|
real_t get_surface_area() const;
|
||||||
Rect2 get_bounds() const { return bounds; }
|
Rect2 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;
|
||||||
@ -111,8 +117,17 @@ public:
|
|||||||
void request_sync();
|
void request_sync();
|
||||||
void cancel_sync_request();
|
void cancel_sync_request();
|
||||||
|
|
||||||
void get_iteration_update(NavRegionIteration2D &r_iteration);
|
void sync_async_tasks();
|
||||||
|
void request_async_thread_join();
|
||||||
|
void cancel_async_thread_join();
|
||||||
|
|
||||||
private:
|
Ref<NavRegionIteration2D> get_iteration();
|
||||||
void update_polygons();
|
|
||||||
|
// 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; }
|
||||||
|
|
||||||
|
void set_use_async_iterations(bool p_enabled);
|
||||||
|
bool get_use_async_iterations() const;
|
||||||
};
|
};
|
||||||
|
@ -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 NavBaseIteration2D;
|
class NavBaseIteration2D;
|
||||||
|
|
||||||
namespace Nav2D {
|
namespace Nav2D {
|
||||||
struct Polygon;
|
struct Polygon;
|
||||||
@ -71,8 +72,13 @@ struct EdgeKey {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Edge {
|
struct ConnectableEdge {
|
||||||
/// The gateway in the edge, as, in some case, the whole edge might not be navigable.
|
EdgeKey ek;
|
||||||
|
uint32_t polygon_index;
|
||||||
|
Vector2 pathway_start;
|
||||||
|
Vector2 pathway_end;
|
||||||
|
};
|
||||||
|
|
||||||
struct Connection {
|
struct Connection {
|
||||||
/// Polygon that this connection leads to.
|
/// Polygon that this connection leads to.
|
||||||
Polygon *polygon = nullptr;
|
Polygon *polygon = nullptr;
|
||||||
@ -87,12 +93,7 @@ struct Edge {
|
|||||||
Vector2 pathway_end;
|
Vector2 pathway_end;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Connections from this edge to other polygons.
|
|
||||||
LocalVector<Connection> connections;
|
|
||||||
};
|
|
||||||
|
|
||||||
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.
|
||||||
@ -100,9 +101,6 @@ struct Polygon {
|
|||||||
|
|
||||||
LocalVector<Vector2> vertices;
|
LocalVector<Vector2> vertices;
|
||||||
|
|
||||||
/// The edges of this `Polygon`
|
|
||||||
LocalVector<Edge> edges;
|
|
||||||
|
|
||||||
real_t surface_area = 0.0;
|
real_t surface_area = 0.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -177,7 +175,7 @@ struct ClosestPointQueryResult {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct EdgeConnectionPair {
|
struct EdgeConnectionPair {
|
||||||
Edge::Connection connections[2];
|
Connection connections[2];
|
||||||
int size = 0;
|
int size = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -76,6 +76,8 @@ void NavigationServer2D::_bind_methods() {
|
|||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer2D::region_create);
|
ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer2D::region_create);
|
||||||
ClassDB::bind_method(D_METHOD("region_get_iteration_id", "region"), &NavigationServer2D::region_get_iteration_id);
|
ClassDB::bind_method(D_METHOD("region_get_iteration_id", "region"), &NavigationServer2D::region_get_iteration_id);
|
||||||
|
ClassDB::bind_method(D_METHOD("region_set_use_async_iterations", "region", "enabled"), &NavigationServer2D::region_set_use_async_iterations);
|
||||||
|
ClassDB::bind_method(D_METHOD("region_get_use_async_iterations", "region"), &NavigationServer2D::region_get_use_async_iterations);
|
||||||
ClassDB::bind_method(D_METHOD("region_set_enabled", "region", "enabled"), &NavigationServer2D::region_set_enabled);
|
ClassDB::bind_method(D_METHOD("region_set_enabled", "region", "enabled"), &NavigationServer2D::region_set_enabled);
|
||||||
ClassDB::bind_method(D_METHOD("region_get_enabled", "region"), &NavigationServer2D::region_get_enabled);
|
ClassDB::bind_method(D_METHOD("region_get_enabled", "region"), &NavigationServer2D::region_get_enabled);
|
||||||
ClassDB::bind_method(D_METHOD("region_set_use_edge_connections", "region", "enabled"), &NavigationServer2D::region_set_use_edge_connections);
|
ClassDB::bind_method(D_METHOD("region_set_use_edge_connections", "region", "enabled"), &NavigationServer2D::region_set_use_edge_connections);
|
||||||
|
@ -102,6 +102,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;
|
||||||
|
|
||||||
|
@ -64,6 +64,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 {}
|
||||||
|
@ -602,6 +602,7 @@ TEST_SUITE("[Navigation2D]") {
|
|||||||
navigation_polygon->add_outline(PackedVector2Array({ Vector2(-1000.0, -1000.0), Vector2(1000.0, -1000.0), Vector2(1000.0, 1000.0), Vector2(-1000.0, 1000.0) }));
|
navigation_polygon->add_outline(PackedVector2Array({ Vector2(-1000.0, -1000.0), Vector2(1000.0, -1000.0), Vector2(1000.0, 1000.0), Vector2(-1000.0, 1000.0) }));
|
||||||
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_polygon(region, navigation_polygon);
|
navigation_server->region_set_navigation_polygon(region, navigation_polygon);
|
||||||
navigation_server->physics_process(0.0); // Give server some cycles to commit.
|
navigation_server->physics_process(0.0); // Give server some cycles to commit.
|
||||||
@ -659,6 +660,7 @@ TEST_SUITE("[Navigation2D]") {
|
|||||||
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_polygon(region, navigation_polygon);
|
navigation_server->region_set_navigation_polygon(region, navigation_polygon);
|
||||||
navigation_server->physics_process(0.0); // Give server some cycles to commit.
|
navigation_server->physics_process(0.0); // Give server some cycles to commit.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user