summaryrefslogtreecommitdiffstats
path: root/modules/navigation/nav_map.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/navigation/nav_map.cpp')
-rw-r--r--modules/navigation/nav_map.cpp145
1 files changed, 70 insertions, 75 deletions
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 0c91e8dea3..f89f5b5812 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -221,27 +221,27 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
// List of all reachable navigation polys.
LocalVector<gd::NavigationPoly> navigation_polys;
- navigation_polys.reserve(polygons.size() * 0.75);
+ navigation_polys.resize(polygons.size() + link_polygons.size());
- // Add the start polygon to the reachable navigation polygons.
- gd::NavigationPoly begin_navigation_poly = gd::NavigationPoly(begin_poly);
- begin_navigation_poly.self_id = 0;
+ // Initialize the matching navigation polygon.
+ gd::NavigationPoly &begin_navigation_poly = navigation_polys[begin_poly->id];
+ begin_navigation_poly.poly = begin_poly;
begin_navigation_poly.entry = begin_point;
begin_navigation_poly.back_navigation_edge_pathway_start = begin_point;
begin_navigation_poly.back_navigation_edge_pathway_end = begin_point;
- navigation_polys.push_back(begin_navigation_poly);
- // List of polygon IDs to visit.
- List<uint32_t> to_visit;
- to_visit.push_back(0);
+ // Heap of polygons to travel next.
+ gd::Heap<gd::NavigationPoly *, gd::NavPolyTravelCostGreaterThan, gd::NavPolyHeapIndexer>
+ traversable_polys;
+ traversable_polys.reserve(polygons.size() * 0.25);
// This is an implementation of the A* algorithm.
- int least_cost_id = 0;
+ int least_cost_id = begin_poly->id;
int prev_least_cost_id = -1;
bool found_route = false;
const gd::Polygon *reachable_end = nullptr;
- real_t reachable_d = FLT_MAX;
+ real_t distance_to_reachable_end = FLT_MAX;
bool is_reachable = true;
while (true) {
@@ -260,51 +260,57 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
real_t poly_enter_cost = 0.0;
real_t poly_travel_cost = least_cost_poly.poly->owner->get_travel_cost();
- if (prev_least_cost_id != -1 && (navigation_polys[prev_least_cost_id].poly->owner->get_self() != least_cost_poly.poly->owner->get_self())) {
+ if (prev_least_cost_id != -1 && navigation_polys[prev_least_cost_id].poly->owner->get_self() != least_cost_poly.poly->owner->get_self()) {
poly_enter_cost = least_cost_poly.poly->owner->get_enter_cost();
}
prev_least_cost_id = least_cost_id;
Vector3 pathway[2] = { connection.pathway_start, connection.pathway_end };
const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly.entry, pathway);
- const real_t new_distance = (least_cost_poly.entry.distance_to(new_entry) * poly_travel_cost) + poly_enter_cost + least_cost_poly.traveled_distance;
-
- int64_t already_visited_polygon_index = navigation_polys.find(gd::NavigationPoly(connection.polygon));
-
- if (already_visited_polygon_index != -1) {
- // Polygon already visited, check if we can reduce the travel cost.
- gd::NavigationPoly &avp = navigation_polys[already_visited_polygon_index];
- if (new_distance < avp.traveled_distance) {
- avp.back_navigation_poly_id = least_cost_id;
- avp.back_navigation_edge = connection.edge;
- avp.back_navigation_edge_pathway_start = connection.pathway_start;
- avp.back_navigation_edge_pathway_end = connection.pathway_end;
- avp.traveled_distance = new_distance;
- avp.entry = new_entry;
+ const real_t new_traveled_distance = least_cost_poly.entry.distance_to(new_entry) * poly_travel_cost + poly_enter_cost + least_cost_poly.traveled_distance;
+
+ // Check if the neighbor polygon has already been processed.
+ gd::NavigationPoly &neighbor_poly = navigation_polys[connection.polygon->id];
+ if (neighbor_poly.poly != nullptr) {
+ // If the neighbor polygon hasn't been traversed yet and the new path leading to
+ // it is shorter, update the polygon.
+ if (neighbor_poly.traversable_poly_index < traversable_polys.size() &&
+ new_traveled_distance < neighbor_poly.traveled_distance) {
+ 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) *
+ neighbor_poly.poly->owner->get_travel_cost();
+ neighbor_poly.entry = new_entry;
+
+ // Update the priority of the polygon in the heap.
+ traversable_polys.shift(neighbor_poly.traversable_poly_index);
}
} else {
- // Add the neighbor polygon to the reachable ones.
- gd::NavigationPoly new_navigation_poly = gd::NavigationPoly(connection.polygon);
- new_navigation_poly.self_id = navigation_polys.size();
- new_navigation_poly.back_navigation_poly_id = least_cost_id;
- new_navigation_poly.back_navigation_edge = connection.edge;
- new_navigation_poly.back_navigation_edge_pathway_start = connection.pathway_start;
- new_navigation_poly.back_navigation_edge_pathway_end = connection.pathway_end;
- new_navigation_poly.traveled_distance = new_distance;
- new_navigation_poly.entry = new_entry;
- navigation_polys.push_back(new_navigation_poly);
-
- // Add the neighbor polygon to the polygons to visit.
- to_visit.push_back(navigation_polys.size() - 1);
+ // Initialize the matching navigation polygon.
+ neighbor_poly.poly = connection.polygon;
+ 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) *
+ neighbor_poly.poly->owner->get_travel_cost();
+ neighbor_poly.entry = new_entry;
+
+ // Add the polygon to the heap of polygons to traverse next.
+ traversable_polys.push(&neighbor_poly);
}
}
}
- // Removes the least cost polygon from the list of polygons to visit so we can advance.
- to_visit.erase(least_cost_id);
-
- // When the list of polygons to visit is empty at this point it means the End Polygon is not reachable
- if (to_visit.size() == 0) {
+ // When the heap of traversable polygons is empty at this point it means the end polygon is
+ // unreachable.
+ if (traversable_polys.is_empty()) {
// Thus use the further reachable polygon
ERR_BREAK_MSG(is_reachable == false, "It's not expect to not find the most reachable polygons");
is_reachable = false;
@@ -366,13 +372,12 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
return path;
}
- // Reset open and navigation_polys
- gd::NavigationPoly np = navigation_polys[0];
- navigation_polys.clear();
- navigation_polys.push_back(np);
- to_visit.clear();
- to_visit.push_back(0);
- least_cost_id = 0;
+ for (gd::NavigationPoly &nav_poly : navigation_polys) {
+ nav_poly.poly = nullptr;
+ }
+ navigation_polys[begin_poly->id].poly = begin_poly;
+
+ least_cost_id = begin_poly->id;
prev_least_cost_id = -1;
reachable_end = nullptr;
@@ -380,26 +385,14 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
continue;
}
- // Find the polygon with the minimum cost from the list of polygons to visit.
- least_cost_id = -1;
- real_t least_cost = FLT_MAX;
- for (List<uint32_t>::Element *element = to_visit.front(); element != nullptr; element = element->next()) {
- gd::NavigationPoly *np = &navigation_polys[element->get()];
- real_t cost = np->traveled_distance;
- cost += (np->entry.distance_to(end_point) * np->poly->owner->get_travel_cost());
- if (cost < least_cost) {
- least_cost_id = np->self_id;
- least_cost = cost;
- }
- }
-
- ERR_BREAK(least_cost_id == -1);
+ // Pop the polygon with the lowest travel cost from the heap of traversable polygons.
+ least_cost_id = traversable_polys.pop()->poly->id;
- // Stores the further 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) {
- real_t d = navigation_polys[least_cost_id].entry.distance_to(p_destination);
- if (reachable_d > d) {
- reachable_d = d;
+ real_t distance = navigation_polys[least_cost_id].entry.distance_to(p_destination);
+ if (distance_to_reachable_end > distance) {
+ distance_to_reachable_end = distance;
reachable_end = navigation_polys[least_cost_id].poly;
}
}
@@ -943,29 +936,30 @@ void NavMap::sync() {
}
// Resize the polygon count.
- int count = 0;
+ int polygon_count = 0;
for (const NavRegion *region : regions) {
if (!region->get_enabled()) {
continue;
}
- count += region->get_polygons().size();
+ polygon_count += region->get_polygons().size();
}
- polygons.resize(count);
+ polygons.resize(polygon_count);
// Copy all region polygons in the map.
- count = 0;
+ polygon_count = 0;
for (const NavRegion *region : regions) {
if (!region->get_enabled()) {
continue;
}
const LocalVector<gd::Polygon> &polygons_source = region->get_polygons();
for (uint32_t n = 0; n < polygons_source.size(); n++) {
- polygons[count + n] = polygons_source[n];
+ polygons[polygon_count] = polygons_source[n];
+ polygons[polygon_count].id = polygon_count;
+ polygon_count++;
}
- count += region->get_polygons().size();
}
- _new_pm_polygon_count = polygons.size();
+ _new_pm_polygon_count = polygon_count;
// Group all edges per key.
HashMap<gd::EdgeKey, Vector<gd::Edge::Connection>, gd::EdgeKey> connections;
@@ -1136,6 +1130,7 @@ void NavMap::sync() {
// If we have both a start and end point, then create a synthetic polygon to route through.
if (closest_start_polygon && closest_end_polygon) {
gd::Polygon &new_polygon = link_polygons[link_poly_idx++];
+ new_polygon.id = polygon_count++;
new_polygon.owner = link;
new_polygon.edges.clear();