summaryrefslogtreecommitdiffstats
path: root/modules/navigation/3d
diff options
context:
space:
mode:
Diffstat (limited to 'modules/navigation/3d')
-rw-r--r--modules/navigation/3d/godot_navigation_server_3d.cpp151
-rw-r--r--modules/navigation/3d/godot_navigation_server_3d.h10
-rw-r--r--modules/navigation/3d/nav_mesh_generator_3d.cpp66
-rw-r--r--modules/navigation/3d/nav_mesh_generator_3d.h15
-rw-r--r--modules/navigation/3d/navigation_mesh_generator.cpp2
5 files changed, 240 insertions, 4 deletions
diff --git a/modules/navigation/3d/godot_navigation_server_3d.cpp b/modules/navigation/3d/godot_navigation_server_3d.cpp
index 15de33f58f..61a128e004 100644
--- a/modules/navigation/3d/godot_navigation_server_3d.cpp
+++ b/modules/navigation/3d/godot_navigation_server_3d.cpp
@@ -1202,6 +1202,11 @@ COMMAND_1(free, RID, p_object) {
} else if (obstacle_owner.owns(p_object)) {
internal_free_obstacle(p_object);
+#ifndef _3D_DISABLED
+ } else if (navmesh_generator_3d && navmesh_generator_3d->owns(p_object)) {
+ navmesh_generator_3d->free(p_object);
+#endif // _3D_DISABLED
+
} else {
ERR_PRINT("Attempted to free a NavigationServer RID that did not exist (or was already freed).");
}
@@ -1381,11 +1386,157 @@ PathQueryResult GodotNavigationServer3D::_query_path(const PathQueryParameters &
// add path postprocessing
+ if (r_query_result.path.size() > 2 && p_parameters.simplify_path) {
+ const LocalVector<uint32_t> &simplified_path_indices = get_simplified_path_indices(r_query_result.path, p_parameters.simplify_epsilon);
+
+ uint32_t indices_count = simplified_path_indices.size();
+
+ {
+ Vector3 *w = r_query_result.path.ptrw();
+ const Vector3 *r = r_query_result.path.ptr();
+ for (uint32_t i = 0; i < indices_count; i++) {
+ w[i] = r[simplified_path_indices[i]];
+ }
+ r_query_result.path.resize(indices_count);
+ }
+
+ if (p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES)) {
+ int32_t *w = r_query_result.path_types.ptrw();
+ const int32_t *r = r_query_result.path_types.ptr();
+ for (uint32_t i = 0; i < indices_count; i++) {
+ w[i] = r[simplified_path_indices[i]];
+ }
+ r_query_result.path_types.resize(indices_count);
+ }
+
+ if (p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS)) {
+ TypedArray<RID> simplified_path_rids;
+ simplified_path_rids.resize(indices_count);
+ for (uint32_t i = 0; i < indices_count; i++) {
+ simplified_path_rids[i] = r_query_result.path_rids[i];
+ }
+ r_query_result.path_rids = simplified_path_rids;
+ }
+
+ if (p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS)) {
+ int64_t *w = r_query_result.path_owner_ids.ptrw();
+ const int64_t *r = r_query_result.path_owner_ids.ptr();
+ for (uint32_t i = 0; i < indices_count; i++) {
+ w[i] = r[simplified_path_indices[i]];
+ }
+ r_query_result.path_owner_ids.resize(indices_count);
+ }
+ }
+
// add path stats
return r_query_result;
}
+RID GodotNavigationServer3D::source_geometry_parser_create() {
+#ifndef _3D_DISABLED
+ if (navmesh_generator_3d) {
+ return navmesh_generator_3d->source_geometry_parser_create();
+ }
+#endif // _3D_DISABLED
+ return RID();
+}
+
+void GodotNavigationServer3D::source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) {
+#ifndef _3D_DISABLED
+ if (navmesh_generator_3d) {
+ navmesh_generator_3d->source_geometry_parser_set_callback(p_parser, p_callback);
+ }
+#endif // _3D_DISABLED
+}
+
+Vector<Vector3> GodotNavigationServer3D::simplify_path(const Vector<Vector3> &p_path, real_t p_epsilon) {
+ if (p_path.size() <= 2) {
+ return p_path;
+ }
+
+ p_epsilon = MAX(0.0, p_epsilon);
+
+ LocalVector<uint32_t> simplified_path_indices = get_simplified_path_indices(p_path, p_epsilon);
+
+ uint32_t indices_count = simplified_path_indices.size();
+
+ Vector<Vector3> simplified_path;
+ simplified_path.resize(indices_count);
+
+ Vector3 *w = simplified_path.ptrw();
+ const Vector3 *r = p_path.ptr();
+ for (uint32_t i = 0; i < indices_count; i++) {
+ w[i] = r[simplified_path_indices[i]];
+ }
+
+ return simplified_path;
+}
+
+LocalVector<uint32_t> GodotNavigationServer3D::get_simplified_path_indices(const Vector<Vector3> &p_path, real_t p_epsilon) {
+ p_epsilon = MAX(0.0, p_epsilon);
+ real_t squared_epsilon = p_epsilon * p_epsilon;
+
+ LocalVector<bool> valid_points;
+ valid_points.resize(p_path.size());
+ for (uint32_t i = 0; i < valid_points.size(); i++) {
+ valid_points[i] = false;
+ }
+
+ simplify_path_segment(0, p_path.size() - 1, p_path, squared_epsilon, valid_points);
+
+ int valid_point_index = 0;
+
+ for (bool valid : valid_points) {
+ if (valid) {
+ valid_point_index += 1;
+ }
+ }
+
+ LocalVector<uint32_t> simplified_path_indices;
+ simplified_path_indices.resize(valid_point_index);
+ valid_point_index = 0;
+
+ for (uint32_t i = 0; i < valid_points.size(); i++) {
+ if (valid_points[i]) {
+ simplified_path_indices[valid_point_index] = i;
+ valid_point_index += 1;
+ }
+ }
+
+ return simplified_path_indices;
+}
+
+void GodotNavigationServer3D::simplify_path_segment(int p_start_inx, int p_end_inx, const Vector<Vector3> &p_points, real_t p_epsilon, LocalVector<bool> &r_valid_points) {
+ r_valid_points[p_start_inx] = true;
+ r_valid_points[p_end_inx] = true;
+
+ const Vector3 &start_point = p_points[p_start_inx];
+ const Vector3 &end_point = p_points[p_end_inx];
+
+ Vector3 path_segment[2] = { start_point, end_point };
+
+ real_t point_max_distance = 0.0;
+ int point_max_index = 0;
+
+ for (int i = p_start_inx; i < p_end_inx; i++) {
+ const Vector3 &checked_point = p_points[i];
+
+ const Vector3 closest_point = Geometry3D::get_closest_point_to_segment(checked_point, path_segment);
+ real_t distance_squared = closest_point.distance_squared_to(checked_point);
+
+ if (distance_squared > point_max_distance) {
+ point_max_index = i;
+ point_max_distance = distance_squared;
+ }
+ }
+
+ if (point_max_distance > p_epsilon) {
+ simplify_path_segment(p_start_inx, point_max_index, p_points, p_epsilon, r_valid_points);
+ simplify_path_segment(point_max_index, p_end_inx, p_points, p_epsilon, r_valid_points);
+ }
+}
+
int GodotNavigationServer3D::get_process_info(ProcessInfo p_info) const {
switch (p_info) {
case INFO_ACTIVE_MAPS: {
diff --git a/modules/navigation/3d/godot_navigation_server_3d.h b/modules/navigation/3d/godot_navigation_server_3d.h
index f7d991d47a..5ba7ed1088 100644
--- a/modules/navigation/3d/godot_navigation_server_3d.h
+++ b/modules/navigation/3d/godot_navigation_server_3d.h
@@ -264,6 +264,16 @@ public:
virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
virtual bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const override;
+ virtual RID source_geometry_parser_create() override;
+ virtual void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) override;
+
+ virtual Vector<Vector3> simplify_path(const Vector<Vector3> &p_path, real_t p_epsilon) override;
+
+private:
+ static void simplify_path_segment(int p_start_inx, int p_end_inx, const Vector<Vector3> &p_points, real_t p_epsilon, LocalVector<bool> &r_valid_points);
+ static LocalVector<uint32_t> get_simplified_path_indices(const Vector<Vector3> &p_path, real_t p_epsilon);
+
+public:
COMMAND_1(free, RID, p_object);
virtual void set_active(bool p_active) override;
diff --git a/modules/navigation/3d/nav_mesh_generator_3d.cpp b/modules/navigation/3d/nav_mesh_generator_3d.cpp
index 8b43eba080..cc3bbdbf01 100644
--- a/modules/navigation/3d/nav_mesh_generator_3d.cpp
+++ b/modules/navigation/3d/nav_mesh_generator_3d.cpp
@@ -45,12 +45,12 @@
#include "scene/resources/3d/convex_polygon_shape_3d.h"
#include "scene/resources/3d/cylinder_shape_3d.h"
#include "scene/resources/3d/height_map_shape_3d.h"
+#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "scene/resources/3d/shape_3d.h"
#include "scene/resources/3d/sphere_shape_3d.h"
#include "scene/resources/3d/world_boundary_shape_3d.h"
#include "scene/resources/navigation_mesh.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
@@ -66,11 +66,14 @@
NavMeshGenerator3D *NavMeshGenerator3D::singleton = nullptr;
Mutex NavMeshGenerator3D::baking_navmesh_mutex;
Mutex NavMeshGenerator3D::generator_task_mutex;
+RWLock NavMeshGenerator3D::generator_rid_rwlock;
bool NavMeshGenerator3D::use_threads = true;
bool NavMeshGenerator3D::baking_use_multiple_threads = true;
bool NavMeshGenerator3D::baking_use_high_priority_threads = true;
HashSet<Ref<NavigationMesh>> NavMeshGenerator3D::baking_navmeshes;
HashMap<WorkerThreadPool::TaskID, NavMeshGenerator3D::NavMeshGeneratorTask3D *> NavMeshGenerator3D::generator_tasks;
+RID_Owner<NavMeshGenerator3D::NavMeshGeometryParser3D> NavMeshGenerator3D::generator_parser_owner;
+LocalVector<NavMeshGenerator3D::NavMeshGeometryParser3D *> NavMeshGenerator3D::generator_parsers;
NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
return singleton;
@@ -85,7 +88,7 @@ NavMeshGenerator3D::NavMeshGenerator3D() {
// Using threads might cause problems on certain exports or with the Editor on certain devices.
// This is the main switch to turn threaded navmesh baking off should the need arise.
- use_threads = baking_use_multiple_threads && !Engine::get_singleton()->is_editor_hint();
+ use_threads = baking_use_multiple_threads;
}
NavMeshGenerator3D::~NavMeshGenerator3D() {
@@ -139,6 +142,13 @@ void NavMeshGenerator3D::cleanup() {
}
generator_tasks.clear();
+ generator_rid_rwlock.write_lock();
+ for (NavMeshGeometryParser3D *parser : generator_parsers) {
+ generator_parser_owner.free(parser->self);
+ }
+ generator_parsers.clear();
+ generator_rid_rwlock.write_unlock();
+
generator_task_mutex.unlock();
baking_navmesh_mutex.unlock();
}
@@ -254,6 +264,15 @@ void NavMeshGenerator3D::generator_parse_geometry_node(const Ref<NavigationMesh>
#endif
generator_parse_navigationobstacle_node(p_navigation_mesh, p_source_geometry_data, p_node);
+ generator_rid_rwlock.read_lock();
+ for (const NavMeshGeometryParser3D *parser : generator_parsers) {
+ if (!parser->callback.is_valid()) {
+ continue;
+ }
+ parser->callback.call(p_navigation_mesh, p_source_geometry_data, p_node);
+ }
+ generator_rid_rwlock.read_unlock();
+
if (p_recurse_children) {
for (int i = 0; i < p_node->get_child_count(); i++) {
generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, p_node->get_child(i), p_recurse_children);
@@ -700,7 +719,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
cfg.detailSampleDist = MAX(p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance(), 0.1f);
cfg.detailSampleMaxError = p_navigation_mesh->get_cell_height() * p_navigation_mesh->get_detail_sample_max_error();
- if (p_navigation_mesh->get_border_size() > 0.0 && !Math::is_equal_approx(p_navigation_mesh->get_cell_size(), p_navigation_mesh->get_border_size())) {
+ if (p_navigation_mesh->get_border_size() > 0.0 && Math::fmod(p_navigation_mesh->get_border_size(), p_navigation_mesh->get_cell_size()) != 0.0) {
WARN_PRINT("Property border_size is ceiled to cell_size voxel units and loses precision.");
}
if (!Math::is_equal_approx((float)cfg.walkableHeight * cfg.ch, p_navigation_mesh->get_agent_height())) {
@@ -920,4 +939,45 @@ bool NavMeshGenerator3D::generator_emit_callback(const Callable &p_callback) {
return ce.error == Callable::CallError::CALL_OK;
}
+RID NavMeshGenerator3D::source_geometry_parser_create() {
+ RWLockWrite write_lock(generator_rid_rwlock);
+
+ RID rid = generator_parser_owner.make_rid();
+
+ NavMeshGeometryParser3D *parser = generator_parser_owner.get_or_null(rid);
+ parser->self = rid;
+
+ generator_parsers.push_back(parser);
+
+ return rid;
+}
+
+void NavMeshGenerator3D::source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) {
+ RWLockWrite write_lock(generator_rid_rwlock);
+
+ NavMeshGeometryParser3D *parser = generator_parser_owner.get_or_null(p_parser);
+ ERR_FAIL_NULL(parser);
+
+ parser->callback = p_callback;
+}
+
+bool NavMeshGenerator3D::owns(RID p_object) {
+ RWLockRead read_lock(generator_rid_rwlock);
+ return generator_parser_owner.owns(p_object);
+}
+
+void NavMeshGenerator3D::free(RID p_object) {
+ RWLockWrite write_lock(generator_rid_rwlock);
+
+ if (generator_parser_owner.owns(p_object)) {
+ NavMeshGeometryParser3D *parser = generator_parser_owner.get_or_null(p_object);
+
+ generator_parsers.erase(parser);
+
+ generator_parser_owner.free(p_object);
+ } else {
+ ERR_PRINT("Attempted to free a NavMeshGenerator3D RID that did not exist (or was already freed).");
+ }
+}
+
#endif // _3D_DISABLED
diff --git a/modules/navigation/3d/nav_mesh_generator_3d.h b/modules/navigation/3d/nav_mesh_generator_3d.h
index 9c9b3bdefe..b46a1736e0 100644
--- a/modules/navigation/3d/nav_mesh_generator_3d.h
+++ b/modules/navigation/3d/nav_mesh_generator_3d.h
@@ -35,6 +35,7 @@
#include "core/object/class_db.h"
#include "core/object/worker_thread_pool.h"
+#include "core/templates/rid_owner.h"
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
class Node;
@@ -47,6 +48,14 @@ class NavMeshGenerator3D : public Object {
static Mutex baking_navmesh_mutex;
static Mutex generator_task_mutex;
+ static RWLock generator_rid_rwlock;
+ struct NavMeshGeometryParser3D {
+ RID self;
+ Callable callback;
+ };
+ static RID_Owner<NavMeshGeometryParser3D> generator_parser_owner;
+ static LocalVector<NavMeshGeometryParser3D *> generator_parsers;
+
static bool use_threads;
static bool baking_use_multiple_threads;
static bool baking_use_high_priority_threads;
@@ -102,6 +111,12 @@ public:
static void bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
static bool is_baking(Ref<NavigationMesh> p_navigation_mesh);
+ static RID source_geometry_parser_create();
+ static void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback);
+
+ static bool owns(RID p_object);
+ static void free(RID p_object);
+
NavMeshGenerator3D();
~NavMeshGenerator3D();
};
diff --git a/modules/navigation/3d/navigation_mesh_generator.cpp b/modules/navigation/3d/navigation_mesh_generator.cpp
index 8393896db1..54df42e266 100644
--- a/modules/navigation/3d/navigation_mesh_generator.cpp
+++ b/modules/navigation/3d/navigation_mesh_generator.cpp
@@ -32,7 +32,7 @@
#include "navigation_mesh_generator.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
+#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "servers/navigation_server_3d.h"
NavigationMeshGenerator *NavigationMeshGenerator::singleton = nullptr;