Path: blob/master/modules/navigation_3d/3d/nav_region_builder_3d.cpp
11353 views
/**************************************************************************/1/* nav_region_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_region_builder_3d.h"3132#include "../nav_map_3d.h"33#include "../nav_region_3d.h"34#include "nav_region_iteration_3d.h"3536#include "core/config/project_settings.h"3738using namespace Nav3D;3940void NavRegionBuilder3D::build_iteration(NavRegionIterationBuild3D &r_build) {41PerformanceData &performance_data = r_build.performance_data;4243performance_data.pm_polygon_count = 0;44performance_data.pm_edge_count = 0;45performance_data.pm_edge_merge_count = 0;46performance_data.pm_edge_connection_count = 0;47performance_data.pm_edge_free_count = 0;4849_build_step_process_navmesh_data(r_build);5051_build_step_find_edge_connection_pairs(r_build);5253_build_step_merge_edge_connection_pairs(r_build);5455_build_update_iteration(r_build);56}5758void NavRegionBuilder3D::_build_step_process_navmesh_data(NavRegionIterationBuild3D &r_build) {59Vector<Vector3> _navmesh_vertices = r_build.navmesh_data.vertices;60Vector<Vector<int>> _navmesh_polygons = r_build.navmesh_data.polygons;6162if (_navmesh_vertices.is_empty() || _navmesh_polygons.is_empty()) {63return;64}6566PerformanceData &performance_data = r_build.performance_data;67Ref<NavRegionIteration3D> region_iteration = r_build.region_iteration;6869const Transform3D ®ion_transform = region_iteration->transform;70LocalVector<Nav3D::Polygon> &navmesh_polygons = region_iteration->navmesh_polygons;7172const int vertex_count = _navmesh_vertices.size();7374const Vector3 *vertices_ptr = _navmesh_vertices.ptr();75const Vector<int> *polygons_ptr = _navmesh_polygons.ptr();7677navmesh_polygons.resize(_navmesh_polygons.size());7879real_t _new_region_surface_area = 0.0;80AABB _new_region_bounds;8182bool first_vertex = true;8384for (uint32_t i = 0; i < navmesh_polygons.size(); i++) {85Polygon &polygon = navmesh_polygons[i];86polygon.id = i;87polygon.owner = region_iteration.ptr();88polygon.surface_area = 0.0;8990Vector<int> polygon_indices = polygons_ptr[i];9192int polygon_size = polygon_indices.size();93if (polygon_size < 3) {94continue;95}9697const int *indices_ptr = polygon_indices.ptr();9899bool polygon_valid = true;100101polygon.vertices.resize(polygon_size);102103{104real_t _new_polygon_surface_area = 0.0;105106for (int j(2); j < polygon_size; j++) {107const Face3 face = Face3(108region_transform.xform(vertices_ptr[indices_ptr[0]]),109region_transform.xform(vertices_ptr[indices_ptr[j - 1]]),110region_transform.xform(vertices_ptr[indices_ptr[j]]));111112_new_polygon_surface_area += face.get_area();113}114115polygon.surface_area = _new_polygon_surface_area;116_new_region_surface_area += _new_polygon_surface_area;117}118119for (int j(0); j < polygon_size; j++) {120int vertex_index = indices_ptr[j];121if (vertex_index < 0 || vertex_index >= vertex_count) {122polygon_valid = false;123break;124}125126const Vector3 point_position = region_transform.xform(vertices_ptr[vertex_index]);127polygon.vertices[j] = point_position;128129if (first_vertex) {130first_vertex = false;131_new_region_bounds.position = point_position;132} else {133_new_region_bounds.expand_to(point_position);134}135}136137if (!polygon_valid) {138polygon.surface_area = 0.0;139polygon.vertices.clear();140ERR_FAIL_COND_MSG(!polygon_valid, "Corrupted navigation mesh set on region. The indices of a polygon are out of range.");141}142}143144region_iteration->surface_area = _new_region_surface_area;145region_iteration->bounds = _new_region_bounds;146147performance_data.pm_polygon_count = navmesh_polygons.size();148}149150Nav3D::PointKey NavRegionBuilder3D::get_point_key(const Vector3 &p_pos, const Vector3 &p_cell_size) {151const int x = static_cast<int>(Math::floor(p_pos.x / p_cell_size.x));152const int y = static_cast<int>(Math::floor(p_pos.y / p_cell_size.y));153const int z = static_cast<int>(Math::floor(p_pos.z / p_cell_size.z));154155PointKey p;156p.key = 0;157p.x = x;158p.y = y;159p.z = z;160return p;161}162163Nav3D::EdgeKey NavRegionBuilder3D::get_edge_key(const Vector3 &p_vertex1, const Vector3 &p_vertex2, const Vector3 &p_cell_size) {164EdgeKey ek(get_point_key(p_vertex1, p_cell_size), get_point_key(p_vertex2, p_cell_size));165return ek;166}167168void NavRegionBuilder3D::_build_step_find_edge_connection_pairs(NavRegionIterationBuild3D &r_build) {169PerformanceData &performance_data = r_build.performance_data;170171const Vector3 &map_cell_size = r_build.map_cell_size;172Ref<NavRegionIteration3D> region_iteration = r_build.region_iteration;173LocalVector<Nav3D::Polygon> &navmesh_polygons = region_iteration->navmesh_polygons;174175HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;176connection_pairs_map.clear();177178region_iteration->internal_connections.clear();179region_iteration->internal_connections.resize(navmesh_polygons.size());180181region_iteration->external_edges.clear();182183int free_edges_count = 0;184int edge_merge_error_count = 0;185186for (Polygon &poly : region_iteration->navmesh_polygons) {187for (uint32_t p = 0; p < poly.vertices.size(); p++) {188const int next_point = (p + 1) % poly.vertices.size();189const EdgeKey ek = get_edge_key(poly.vertices[p], poly.vertices[next_point], map_cell_size);190191HashMap<EdgeKey, EdgeConnectionPair, EdgeKey>::Iterator pair_it = connection_pairs_map.find(ek);192if (!pair_it) {193pair_it = connection_pairs_map.insert(ek, EdgeConnectionPair());194performance_data.pm_edge_count += 1;195++free_edges_count;196}197EdgeConnectionPair &pair = pair_it->value;198if (pair.size < 2) {199// Add the polygon/edge tuple to this key.200Connection new_connection;201new_connection.polygon = &poly;202new_connection.edge = p;203new_connection.pathway_start = poly.vertices[p];204new_connection.pathway_end = poly.vertices[next_point];205206pair.connections[pair.size] = new_connection;207++pair.size;208if (pair.size == 2) {209--free_edges_count;210}211212} else {213// The edge is already connected with another edge, skip.214edge_merge_error_count++;215}216}217}218219if (edge_merge_error_count > 0 && GLOBAL_GET_CACHED(bool, "navigation/3d/warnings/navmesh_edge_merge_errors")) {220WARN_PRINT("Navigation region synchronization had " + itos(edge_merge_error_count) + " edge error(s).\nMore than 2 edges tried to occupy the same map rasterization space.\nThis causes a logical error in the navigation mesh geometry and is commonly caused by overlap or too densely placed edges.\nConsider baking with a higher 'cell_size', greater geometry margin, and less detailed bake objects to cause fewer edges.\nConsider lowering the 'navigation/3d/merge_rasterizer_cell_scale' in the project settings.\nThis warning can be toggled under 'navigation/3d/warnings/navmesh_edge_merge_errors' in the project settings.");221}222223performance_data.pm_edge_free_count = free_edges_count;224}225226void NavRegionBuilder3D::_build_step_merge_edge_connection_pairs(NavRegionIterationBuild3D &r_build) {227PerformanceData &performance_data = r_build.performance_data;228229Ref<NavRegionIteration3D> region_iteration = r_build.region_iteration;230231HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;232233for (const KeyValue<EdgeKey, EdgeConnectionPair> &pair_it : connection_pairs_map) {234const EdgeConnectionPair &pair = pair_it.value;235if (pair.size == 2) {236// Connect edge that are shared in different polygons.237const Connection &c1 = pair.connections[0];238const Connection &c2 = pair.connections[1];239region_iteration->internal_connections[c1.polygon->id].push_back(c2);240region_iteration->internal_connections[c2.polygon->id].push_back(c1);241performance_data.pm_edge_merge_count += 1;242243} else {244ERR_FAIL_COND_MSG(pair.size != 1, vformat("Number of connection != 1. Found: %d", pair.size));245246const Connection &connection = pair.connections[0];247248ConnectableEdge ce;249ce.ek = pair_it.key;250ce.polygon_index = connection.polygon->id;251ce.edge = connection.edge;252ce.pathway_start = connection.pathway_start;253ce.pathway_end = connection.pathway_end;254255region_iteration->external_edges.push_back(ce);256}257}258}259260void NavRegionBuilder3D::_build_update_iteration(NavRegionIterationBuild3D &r_build) {261ERR_FAIL_NULL(r_build.region);262// Stub. End of the build.263}264265266