Path: blob/master/modules/navigation_3d/3d/nav_map_builder_3d.cpp
11353 views
/**************************************************************************/1/* nav_map_builder_3d.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "nav_map_builder_3d.h"3132#include "../nav_link_3d.h"33#include "../nav_map_3d.h"34#include "../nav_region_3d.h"35#include "nav_map_iteration_3d.h"36#include "nav_region_iteration_3d.h"3738#include "core/config/project_settings.h"3940using namespace Nav3D;4142PointKey NavMapBuilder3D::get_point_key(const Vector3 &p_pos, const Vector3 &p_cell_size) {43const int x = static_cast<int>(Math::floor(p_pos.x / p_cell_size.x));44const int y = static_cast<int>(Math::floor(p_pos.y / p_cell_size.y));45const int z = static_cast<int>(Math::floor(p_pos.z / p_cell_size.z));4647PointKey p;48p.key = 0;49p.x = x;50p.y = y;51p.z = z;52return p;53}5455void NavMapBuilder3D::build_navmap_iteration(NavMapIterationBuild3D &r_build) {56PerformanceData &performance_data = r_build.performance_data;5758performance_data.pm_polygon_count = 0;59performance_data.pm_edge_count = 0;60performance_data.pm_edge_merge_count = 0;61performance_data.pm_edge_connection_count = 0;62performance_data.pm_edge_free_count = 0;6364_build_step_gather_region_polygons(r_build);6566_build_step_find_edge_connection_pairs(r_build);6768_build_step_merge_edge_connection_pairs(r_build);6970_build_step_edge_connection_margin_connections(r_build);7172_build_step_navlink_connections(r_build);7374_build_update_map_iteration(r_build);75}7677void NavMapBuilder3D::_build_step_gather_region_polygons(NavMapIterationBuild3D &r_build) {78PerformanceData &performance_data = r_build.performance_data;79NavMapIteration3D *map_iteration = r_build.map_iteration;8081const LocalVector<Ref<NavRegionIteration3D>> ®ions = map_iteration->region_iterations;82HashMap<const NavBaseIteration3D *, LocalVector<Connection>> ®ion_external_connections = map_iteration->external_region_connections;8384map_iteration->navbases_polygons_external_connections.clear();8586// Remove regions connections.87region_external_connections.clear();8889// Copy all region polygons in the map.90int polygon_count = 0;91for (const Ref<NavRegionIteration3D> ®ion : regions) {92const uint32_t polygons_size = region->navmesh_polygons.size();93polygon_count += polygons_size;9495region_external_connections[region.ptr()] = LocalVector<Connection>();96map_iteration->navbases_polygons_external_connections[region.ptr()] = LocalVector<LocalVector<Connection>>();97map_iteration->navbases_polygons_external_connections[region.ptr()].resize(polygons_size);98}99100performance_data.pm_polygon_count = polygon_count;101r_build.polygon_count = polygon_count;102}103104void NavMapBuilder3D::_build_step_find_edge_connection_pairs(NavMapIterationBuild3D &r_build) {105PerformanceData &performance_data = r_build.performance_data;106NavMapIteration3D *map_iteration = r_build.map_iteration;107int polygon_count = r_build.polygon_count;108109HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;110111// Group all edges per key.112connection_pairs_map.clear();113connection_pairs_map.reserve(polygon_count);114int free_edges_count = 0; // How many ConnectionPairs have only one Connection.115int edge_merge_error_count = 0;116117for (const Ref<NavRegionIteration3D> ®ion : map_iteration->region_iterations) {118for (const ConnectableEdge &connectable_edge : region->get_external_edges()) {119const EdgeKey &ek = connectable_edge.ek;120121HashMap<EdgeKey, EdgeConnectionPair, EdgeKey>::Iterator pair_it = connection_pairs_map.find(ek);122if (!pair_it) {123pair_it = connection_pairs_map.insert(ek, EdgeConnectionPair());124performance_data.pm_edge_count += 1;125++free_edges_count;126}127EdgeConnectionPair &pair = pair_it->value;128if (pair.size < 2) {129// Add the polygon/edge tuple to this key.130Connection new_connection;131new_connection.polygon = ®ion->navmesh_polygons[connectable_edge.polygon_index];132new_connection.edge = connectable_edge.edge;133new_connection.pathway_start = connectable_edge.pathway_start;134new_connection.pathway_end = connectable_edge.pathway_end;135136pair.connections[pair.size] = new_connection;137++pair.size;138if (pair.size == 2) {139--free_edges_count;140}141142} else {143// The edge is already connected with another edge, skip.144edge_merge_error_count++;145}146}147}148149if (edge_merge_error_count > 0 && GLOBAL_GET_CACHED(bool, "navigation/3d/warnings/navmesh_edge_merge_errors")) {150WARN_PRINT("Navigation map synchronization had " + itos(edge_merge_error_count) + " edge error(s).\nMore than 2 edges tried to occupy the same map rasterization space.\nThis causes a logical error in the navigation mesh geometry and is commonly caused by overlap or too densely placed edges.\nConsider baking with a higher 'cell_size', greater geometry margin, and less detailed bake objects to cause fewer edges.\nConsider lowering the 'navigation/3d/merge_rasterizer_cell_scale' in the project settings.\nThis warning can be toggled under 'navigation/3d/warnings/navmesh_edge_merge_errors' in the project settings.");151}152153r_build.free_edge_count = free_edges_count;154}155156void NavMapBuilder3D::_build_step_merge_edge_connection_pairs(NavMapIterationBuild3D &r_build) {157PerformanceData &performance_data = r_build.performance_data;158159HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;160LocalVector<Connection> &free_edges = r_build.iter_free_edges;161int free_edges_count = r_build.free_edge_count;162bool use_edge_connections = r_build.use_edge_connections;163164free_edges.clear();165free_edges.reserve(free_edges_count);166167NavMapIteration3D *map_iteration = r_build.map_iteration;168169HashMap<const NavBaseIteration3D *, LocalVector<LocalVector<Nav3D::Connection>>> &navbases_polygons_external_connections = map_iteration->navbases_polygons_external_connections;170171for (const KeyValue<EdgeKey, EdgeConnectionPair> &pair_it : connection_pairs_map) {172const EdgeConnectionPair &pair = pair_it.value;173if (pair.size == 2) {174// Connect edge that are shared in different polygons.175const Connection &c1 = pair.connections[0];176const Connection &c2 = pair.connections[1];177178navbases_polygons_external_connections[c1.polygon->owner][c1.polygon->id].push_back(c2);179navbases_polygons_external_connections[c2.polygon->owner][c2.polygon->id].push_back(c1);180performance_data.pm_edge_connection_count += 1;181182} else {183CRASH_COND_MSG(pair.size != 1, vformat("Number of connection != 1. Found: %d", pair.size));184if (use_edge_connections && pair.connections[0].polygon->owner->get_use_edge_connections()) {185free_edges.push_back(pair.connections[0]);186}187}188}189}190191void NavMapBuilder3D::_build_step_edge_connection_margin_connections(NavMapIterationBuild3D &r_build) {192PerformanceData &performance_data = r_build.performance_data;193NavMapIteration3D *map_iteration = r_build.map_iteration;194195real_t edge_connection_margin = r_build.edge_connection_margin;196197LocalVector<Connection> &free_edges = r_build.iter_free_edges;198HashMap<const NavBaseIteration3D *, LocalVector<Connection>> ®ion_external_connections = map_iteration->external_region_connections;199200HashMap<const NavBaseIteration3D *, LocalVector<LocalVector<Nav3D::Connection>>> &navbases_polygons_external_connections = map_iteration->navbases_polygons_external_connections;201202// Find the compatible near edges.203//204// Note:205// Considering that the edges must be compatible (for obvious reasons)206// to be connected, create new polygons to remove that small gap is207// not really useful and would result in wasteful computation during208// connection, integration and path finding.209performance_data.pm_edge_free_count = free_edges.size();210211const real_t edge_connection_margin_squared = edge_connection_margin * edge_connection_margin;212213for (uint32_t i = 0; i < free_edges.size(); i++) {214const Connection &free_edge = free_edges[i];215const Vector3 &edge_p1 = free_edge.pathway_start;216const Vector3 &edge_p2 = free_edge.pathway_end;217218for (uint32_t j = 0; j < free_edges.size(); j++) {219const Connection &other_edge = free_edges[j];220if (i == j || free_edge.polygon->owner == other_edge.polygon->owner) {221continue;222}223224const Vector3 &other_edge_p1 = other_edge.pathway_start;225const Vector3 &other_edge_p2 = other_edge.pathway_end;226227// Compute the projection of the opposite edge on the current one228Vector3 edge_vector = edge_p2 - edge_p1;229real_t projected_p1_ratio = edge_vector.dot(other_edge_p1 - edge_p1) / (edge_vector.length_squared());230real_t projected_p2_ratio = edge_vector.dot(other_edge_p2 - edge_p1) / (edge_vector.length_squared());231if ((projected_p1_ratio < 0.0 && projected_p2_ratio < 0.0) || (projected_p1_ratio > 1.0 && projected_p2_ratio > 1.0)) {232continue;233}234235// Check if the two edges are close to each other enough and compute a pathway between the two regions.236Vector3 self1 = edge_vector * CLAMP(projected_p1_ratio, 0.0, 1.0) + edge_p1;237Vector3 other1;238if (projected_p1_ratio >= 0.0 && projected_p1_ratio <= 1.0) {239other1 = other_edge_p1;240} else {241other1 = other_edge_p1.lerp(other_edge_p2, (1.0 - projected_p1_ratio) / (projected_p2_ratio - projected_p1_ratio));242}243if (other1.distance_squared_to(self1) > edge_connection_margin_squared) {244continue;245}246247Vector3 self2 = edge_vector * CLAMP(projected_p2_ratio, 0.0, 1.0) + edge_p1;248Vector3 other2;249if (projected_p2_ratio >= 0.0 && projected_p2_ratio <= 1.0) {250other2 = other_edge_p2;251} else {252other2 = other_edge_p1.lerp(other_edge_p2, (0.0 - projected_p1_ratio) / (projected_p2_ratio - projected_p1_ratio));253}254if (other2.distance_squared_to(self2) > edge_connection_margin_squared) {255continue;256}257258// The edges can now be connected.259Connection new_connection = other_edge;260new_connection.pathway_start = (self1 + other1) / 2.0;261new_connection.pathway_end = (self2 + other2) / 2.0;262//free_edge.polygon->connections.push_back(new_connection);263264// Add the connection to the region_connection map.265region_external_connections[free_edge.polygon->owner].push_back(new_connection);266navbases_polygons_external_connections[free_edge.polygon->owner][free_edge.polygon->id].push_back(new_connection);267performance_data.pm_edge_connection_count += 1;268}269}270}271272void NavMapBuilder3D::_build_step_navlink_connections(NavMapIterationBuild3D &r_build) {273NavMapIteration3D *map_iteration = r_build.map_iteration;274275real_t link_connection_radius = r_build.link_connection_radius;276277const LocalVector<Ref<NavLinkIteration3D>> &links = map_iteration->link_iterations;278279int polygon_count = r_build.polygon_count;280281real_t link_connection_radius_sqr = link_connection_radius * link_connection_radius;282283HashMap<const NavBaseIteration3D *, LocalVector<LocalVector<Nav3D::Connection>>> &navbases_polygons_external_connections = map_iteration->navbases_polygons_external_connections;284LocalVector<Nav3D::Polygon> &navlink_polygons = map_iteration->navlink_polygons;285navlink_polygons.clear();286navlink_polygons.resize(links.size());287uint32_t navlink_index = 0;288289// Search for polygons within range of a nav link.290for (const Ref<NavLinkIteration3D> &link : links) {291polygon_count++;292Polygon &new_polygon = navlink_polygons[navlink_index++];293294new_polygon.id = 0;295new_polygon.owner = link.ptr();296297const Vector3 link_start_pos = link->get_start_position();298const Vector3 link_end_pos = link->get_end_position();299300Polygon *closest_start_polygon = nullptr;301real_t closest_start_sqr_dist = link_connection_radius_sqr;302Vector3 closest_start_point;303304Polygon *closest_end_polygon = nullptr;305real_t closest_end_sqr_dist = link_connection_radius_sqr;306Vector3 closest_end_point;307308for (const Ref<NavRegionIteration3D> ®ion : map_iteration->region_iterations) {309AABB region_bounds = region->get_bounds().grow(link_connection_radius);310if (!region_bounds.has_point(link_start_pos) && !region_bounds.has_point(link_end_pos)) {311continue;312}313314for (Polygon &polyon : region->navmesh_polygons) {315for (uint32_t point_id = 2; point_id < polyon.vertices.size(); point_id += 1) {316const Face3 face(polyon.vertices[0], polyon.vertices[point_id - 1], polyon.vertices[point_id]);317318{319const Vector3 start_point = face.get_closest_point_to(link_start_pos);320const real_t sqr_dist = start_point.distance_squared_to(link_start_pos);321322// Pick the polygon that is within our radius and is closer than anything we've seen yet.323if (sqr_dist < closest_start_sqr_dist) {324closest_start_sqr_dist = sqr_dist;325closest_start_point = start_point;326closest_start_polygon = &polyon;327}328}329330{331const Vector3 end_point = face.get_closest_point_to(link_end_pos);332const real_t sqr_dist = end_point.distance_squared_to(link_end_pos);333334// Pick the polygon that is within our radius and is closer than anything we've seen yet.335if (sqr_dist < closest_end_sqr_dist) {336closest_end_sqr_dist = sqr_dist;337closest_end_point = end_point;338closest_end_polygon = &polyon;339}340}341}342}343}344345// If we have both a start and end point, then create a synthetic polygon to route through.346if (closest_start_polygon && closest_end_polygon) {347new_polygon.vertices.resize(4);348349// Build a set of vertices that create a thin polygon going from the start to the end point.350new_polygon.vertices[0] = closest_start_point;351new_polygon.vertices[1] = closest_start_point;352new_polygon.vertices[2] = closest_end_point;353new_polygon.vertices[3] = closest_end_point;354355// Setup connections to go forward in the link.356{357Connection entry_connection;358entry_connection.polygon = &new_polygon;359entry_connection.edge = -1;360entry_connection.pathway_start = new_polygon.vertices[0];361entry_connection.pathway_end = new_polygon.vertices[1];362navbases_polygons_external_connections[closest_start_polygon->owner][closest_start_polygon->id].push_back(entry_connection);363364Connection exit_connection;365exit_connection.polygon = closest_end_polygon;366exit_connection.edge = -1;367exit_connection.pathway_start = new_polygon.vertices[2];368exit_connection.pathway_end = new_polygon.vertices[3];369navbases_polygons_external_connections[link.ptr()].push_back(LocalVector<Nav3D::Connection>());370navbases_polygons_external_connections[link.ptr()][new_polygon.id].push_back(exit_connection);371}372373// If the link is bi-directional, create connections from the end to the start.374if (link->is_bidirectional()) {375Connection entry_connection;376entry_connection.polygon = &new_polygon;377entry_connection.edge = -1;378entry_connection.pathway_start = new_polygon.vertices[2];379entry_connection.pathway_end = new_polygon.vertices[3];380navbases_polygons_external_connections[closest_end_polygon->owner][closest_end_polygon->id].push_back(entry_connection);381382Connection exit_connection;383exit_connection.polygon = closest_start_polygon;384exit_connection.edge = -1;385exit_connection.pathway_start = new_polygon.vertices[0];386exit_connection.pathway_end = new_polygon.vertices[1];387navbases_polygons_external_connections[link.ptr()].push_back(LocalVector<Nav3D::Connection>());388navbases_polygons_external_connections[link.ptr()][new_polygon.id].push_back(exit_connection);389}390}391}392393r_build.polygon_count = polygon_count;394}395396void NavMapBuilder3D::_build_update_map_iteration(NavMapIterationBuild3D &r_build) {397NavMapIteration3D *map_iteration = r_build.map_iteration;398399map_iteration->navmesh_polygon_count = r_build.polygon_count;400401uint32_t navmesh_polygon_count = r_build.polygon_count;402uint32_t total_polygon_count = navmesh_polygon_count;403404map_iteration->path_query_slots_mutex.lock();405for (NavMeshQueries3D::PathQuerySlot &p_path_query_slot : map_iteration->path_query_slots) {406p_path_query_slot.traversable_polys.clear();407p_path_query_slot.traversable_polys.reserve(navmesh_polygon_count * 0.25);408p_path_query_slot.path_corridor.clear();409410p_path_query_slot.path_corridor.resize(total_polygon_count);411412p_path_query_slot.poly_to_id.clear();413p_path_query_slot.poly_to_id.reserve(total_polygon_count);414415int polygon_id = 0;416for (Ref<NavRegionIteration3D> ®ion : map_iteration->region_iterations) {417for (const Polygon &polygon : region->navmesh_polygons) {418p_path_query_slot.poly_to_id[&polygon] = polygon_id;419polygon_id++;420}421}422423for (const Polygon &polygon : map_iteration->navlink_polygons) {424p_path_query_slot.poly_to_id[&polygon] = polygon_id;425polygon_id++;426}427428DEV_ASSERT(p_path_query_slot.path_corridor.size() == p_path_query_slot.poly_to_id.size());429}430431map_iteration->path_query_slots_mutex.unlock();432}433434435