diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index a6f6abccbd75..a0d03d7a012e 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -956,6 +956,23 @@ Path simplification can be helpful to mitigate various path following issues that can arise with certain agent types and script behaviors. E.g. "steering" agents or avoidance in "open fields". + + + + Creates a new source geometry parser. If a [Callable] is set for the parser with [method source_geometry_parser_set_callback] the callback will be called for every single node that gets parsed whenever [method parse_source_geometry_data] is used. + + + + + + + + Sets the [param callback] [Callable] for the specific source geometry [param parser]. The [Callable] will receive a call with the following parameters: + - [code]navigation_mesh[/code] - The [NavigationPolygon] reference used to define the parse settings. Do NOT edit or add directly to the navigation mesh. + - [code]source_geometry_data[/code] - The [NavigationMeshSourceGeometryData2D] reference. Add custom source geometry for navigation mesh baking to this object. + - [code]node[/code] - The [Node] that is parsed. + + diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index 6fcf033544fb..ff3e6fd8a64f 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -1103,6 +1103,23 @@ Path simplification can be helpful to mitigate various path following issues that can arise with certain agent types and script behaviors. E.g. "steering" agents or avoidance in "open fields". + + + + Creates a new source geometry parser. If a [Callable] is set for the parser with [method source_geometry_parser_set_callback] the callback will be called for every single node that gets parsed whenever [method parse_source_geometry_data] is used. + + + + + + + + Sets the [param callback] [Callable] for the specific source geometry [param parser]. The [Callable] will receive a call with the following parameters: + - [code]navigation_mesh[/code] - The [NavigationMesh] reference used to define the parse settings. Do NOT edit or add directly to the navigation mesh. + - [code]source_geometry_data[/code] - The [NavigationMeshSourceGeometryData3D] reference. Add custom source geometry for navigation mesh baking to this object. + - [code]node[/code] - The [Node] that is parsed. + + diff --git a/modules/navigation/2d/godot_navigation_server_2d.cpp b/modules/navigation/2d/godot_navigation_server_2d.cpp index 5eefbe422807..bf69adc14c96 100644 --- a/modules/navigation/2d/godot_navigation_server_2d.cpp +++ b/modules/navigation/2d/godot_navigation_server_2d.cpp @@ -389,7 +389,16 @@ bool FORWARD_1_C(agent_is_map_changed, RID, p_agent, rid_to_rid); void FORWARD_2(agent_set_paused, RID, p_agent, bool, p_paused, rid_to_rid, bool_to_bool); bool FORWARD_1_C(agent_get_paused, RID, p_agent, rid_to_rid); -void FORWARD_1(free, RID, p_object, rid_to_rid); +void GodotNavigationServer2D::free(RID p_object) { +#ifdef CLIPPER2_ENABLED + if (navmesh_generator_2d && navmesh_generator_2d->owns(p_object)) { + navmesh_generator_2d->free(p_object); + return; + } +#endif // CLIPPER2_ENABLED + NavigationServer3D::get_singleton()->free(p_object); +} + void FORWARD_2(agent_set_avoidance_callback, RID, p_agent, Callable, p_callback, rid_to_rid, callable_to_callable); bool GodotNavigationServer2D::agent_has_avoidance_callback(RID p_agent) const { return NavigationServer3D::get_singleton()->agent_has_avoidance_callback(p_agent); @@ -453,3 +462,20 @@ void GodotNavigationServer2D::query_path(const Refset_path_rids(_query_result.path_rids); p_query_result->set_path_owner_ids(_query_result.path_owner_ids); } + +RID GodotNavigationServer2D::source_geometry_parser_create() { +#ifdef CLIPPER2_ENABLED + if (navmesh_generator_2d) { + return navmesh_generator_2d->source_geometry_parser_create(); + } +#endif // CLIPPER2_ENABLED + return RID(); +} + +void GodotNavigationServer2D::source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) { +#ifdef CLIPPER2_ENABLED + if (navmesh_generator_2d) { + navmesh_generator_2d->source_geometry_parser_set_callback(p_parser, p_callback); + } +#endif // CLIPPER2_ENABLED +} diff --git a/modules/navigation/2d/godot_navigation_server_2d.h b/modules/navigation/2d/godot_navigation_server_2d.h index ba375afd33dd..ea77fa5e6ecf 100644 --- a/modules/navigation/2d/godot_navigation_server_2d.h +++ b/modules/navigation/2d/godot_navigation_server_2d.h @@ -253,6 +253,9 @@ class GodotNavigationServer2D : public NavigationServer2D { virtual void bake_from_source_geometry_data_async(const Ref &p_navigation_mesh, const Ref &p_source_geometry_data, const Callable &p_callback = Callable()) override; virtual bool is_baking_navigation_polygon(Ref p_navigation_polygon) 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 simplify_path(const Vector &p_path, real_t p_epsilon) override; }; diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp index 900025952461..13399b858ed4 100644 --- a/modules/navigation/2d/nav_mesh_generator_2d.cpp +++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp @@ -53,11 +53,14 @@ NavMeshGenerator2D *NavMeshGenerator2D::singleton = nullptr; Mutex NavMeshGenerator2D::baking_navmesh_mutex; Mutex NavMeshGenerator2D::generator_task_mutex; +RWLock NavMeshGenerator2D::generator_rid_rwlock; bool NavMeshGenerator2D::use_threads = true; bool NavMeshGenerator2D::baking_use_multiple_threads = true; bool NavMeshGenerator2D::baking_use_high_priority_threads = true; HashSet> NavMeshGenerator2D::baking_navmeshes; HashMap NavMeshGenerator2D::generator_tasks; +RID_Owner NavMeshGenerator2D::generator_parser_owner; +LocalVector NavMeshGenerator2D::generator_parsers; NavMeshGenerator2D *NavMeshGenerator2D::get_singleton() { return singleton; @@ -126,6 +129,13 @@ void NavMeshGenerator2D::cleanup() { } generator_tasks.clear(); + generator_rid_rwlock.write_lock(); + for (NavMeshGeometryParser2D *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(); } @@ -236,6 +246,15 @@ void NavMeshGenerator2D::generator_parse_geometry_node(Ref p_ generator_parse_tilemap_node(p_navigation_mesh, p_source_geometry_data, p_node); generator_parse_navigationobstacle_node(p_navigation_mesh, p_source_geometry_data, p_node); + generator_rid_rwlock.read_lock(); + for (const NavMeshGeometryParser2D *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); @@ -813,6 +832,47 @@ bool NavMeshGenerator2D::generator_emit_callback(const Callable &p_callback) { return ce.error == Callable::CallError::CALL_OK; } +RID NavMeshGenerator2D::source_geometry_parser_create() { + RWLockWrite write_lock(generator_rid_rwlock); + + RID rid = generator_parser_owner.make_rid(); + + NavMeshGeometryParser2D *parser = generator_parser_owner.get_or_null(rid); + parser->self = rid; + + generator_parsers.push_back(parser); + + return rid; +} + +void NavMeshGenerator2D::source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) { + RWLockWrite write_lock(generator_rid_rwlock); + + NavMeshGeometryParser2D *parser = generator_parser_owner.get_or_null(p_parser); + ERR_FAIL_NULL(parser); + + parser->callback = p_callback; +} + +bool NavMeshGenerator2D::owns(RID p_object) { + RWLockRead read_lock(generator_rid_rwlock); + return generator_parser_owner.owns(p_object); +} + +void NavMeshGenerator2D::free(RID p_object) { + RWLockWrite write_lock(generator_rid_rwlock); + + if (generator_parser_owner.owns(p_object)) { + NavMeshGeometryParser2D *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 NavMeshGenerator2D RID that did not exist (or was already freed)."); + } +} + void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref p_navigation_mesh, Ref p_source_geometry_data) { if (p_navigation_mesh.is_null() || p_source_geometry_data.is_null()) { return; diff --git a/modules/navigation/2d/nav_mesh_generator_2d.h b/modules/navigation/2d/nav_mesh_generator_2d.h index 2567a170ef65..235a84d54879 100644 --- a/modules/navigation/2d/nav_mesh_generator_2d.h +++ b/modules/navigation/2d/nav_mesh_generator_2d.h @@ -35,6 +35,7 @@ #include "core/object/class_db.h" #include "core/object/worker_thread_pool.h" +#include "core/templates/rid_owner.h" class Node; class NavigationPolygon; @@ -46,6 +47,14 @@ class NavMeshGenerator2D : public Object { static Mutex baking_navmesh_mutex; static Mutex generator_task_mutex; + static RWLock generator_rid_rwlock; + struct NavMeshGeometryParser2D { + RID self; + Callable callback; + }; + static RID_Owner generator_parser_owner; + static LocalVector generator_parsers; + static bool use_threads; static bool baking_use_multiple_threads; static bool baking_use_high_priority_threads; @@ -97,6 +106,12 @@ class NavMeshGenerator2D : public Object { static void bake_from_source_geometry_data_async(Ref p_navigation_mesh, Ref p_source_geometry_data, const Callable &p_callback = Callable()); static bool is_baking(Ref p_navigation_polygon); + 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); + NavMeshGenerator2D(); ~NavMeshGenerator2D(); }; diff --git a/modules/navigation/3d/godot_navigation_server_3d.cpp b/modules/navigation/3d/godot_navigation_server_3d.cpp index 301b4aa8f38d..61a128e00479 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)."); } @@ -1428,6 +1433,23 @@ PathQueryResult GodotNavigationServer3D::_query_path(const PathQueryParameters & 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 GodotNavigationServer3D::simplify_path(const Vector &p_path, real_t p_epsilon) { if (p_path.size() <= 2) { return p_path; diff --git a/modules/navigation/3d/godot_navigation_server_3d.h b/modules/navigation/3d/godot_navigation_server_3d.h index 89839ff459b7..5ba7ed1088e2 100644 --- a/modules/navigation/3d/godot_navigation_server_3d.h +++ b/modules/navigation/3d/godot_navigation_server_3d.h @@ -264,6 +264,9 @@ class GodotNavigationServer3D : public NavigationServer3D { virtual void bake_from_source_geometry_data_async(const Ref &p_navigation_mesh, const Ref &p_source_geometry_data, const Callable &p_callback = Callable()) override; virtual bool is_baking_navigation_mesh(Ref 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 simplify_path(const Vector &p_path, real_t p_epsilon) override; private: diff --git a/modules/navigation/3d/nav_mesh_generator_3d.cpp b/modules/navigation/3d/nav_mesh_generator_3d.cpp index b1b3cbed5de3..cc3bbdbf01ed 100644 --- a/modules/navigation/3d/nav_mesh_generator_3d.cpp +++ b/modules/navigation/3d/nav_mesh_generator_3d.cpp @@ -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> NavMeshGenerator3D::baking_navmeshes; HashMap NavMeshGenerator3D::generator_tasks; +RID_Owner NavMeshGenerator3D::generator_parser_owner; +LocalVector NavMeshGenerator3D::generator_parsers; NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() { return singleton; @@ -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 #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); @@ -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 9c9b3bdefef2..b46a1736e01b 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 generator_parser_owner; + static LocalVector generator_parsers; + static bool use_threads; static bool baking_use_multiple_threads; static bool baking_use_high_priority_threads; @@ -102,6 +111,12 @@ class NavMeshGenerator3D : public Object { static void bake_from_source_geometry_data_async(Ref p_navigation_mesh, Ref p_source_geometry_data, const Callable &p_callback = Callable()); static bool is_baking(Ref 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/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp index 625ae8abde15..c5ce82265b7b 100644 --- a/servers/navigation_server_2d.cpp +++ b/servers/navigation_server_2d.cpp @@ -165,6 +165,9 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data_async", "navigation_polygon", "source_geometry_data", "callback"), &NavigationServer2D::bake_from_source_geometry_data_async, DEFVAL(Callable())); ClassDB::bind_method(D_METHOD("is_baking_navigation_polygon", "navigation_polygon"), &NavigationServer2D::is_baking_navigation_polygon); + ClassDB::bind_method(D_METHOD("source_geometry_parser_create"), &NavigationServer2D::source_geometry_parser_create); + ClassDB::bind_method(D_METHOD("source_geometry_parser_set_callback", "parser", "callback"), &NavigationServer2D::source_geometry_parser_set_callback); + ClassDB::bind_method(D_METHOD("simplify_path", "path", "epsilon"), &NavigationServer2D::simplify_path); ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer2D::free); diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h index 39d4c19064eb..a8d9678a6f01 100644 --- a/servers/navigation_server_2d.h +++ b/servers/navigation_server_2d.h @@ -306,6 +306,9 @@ class NavigationServer2D : public Object { virtual void bake_from_source_geometry_data_async(const Ref &p_navigation_mesh, const Ref &p_source_geometry_data, const Callable &p_callback = Callable()) = 0; virtual bool is_baking_navigation_polygon(Ref p_navigation_polygon) const = 0; + virtual RID source_geometry_parser_create() = 0; + virtual void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) = 0; + virtual Vector simplify_path(const Vector &p_path, real_t p_epsilon) = 0; NavigationServer2D(); diff --git a/servers/navigation_server_2d_dummy.h b/servers/navigation_server_2d_dummy.h index 5d4cfbf91b1e..465cfcca9885 100644 --- a/servers/navigation_server_2d_dummy.h +++ b/servers/navigation_server_2d_dummy.h @@ -170,6 +170,9 @@ class NavigationServer2DDummy : public NavigationServer2D { void bake_from_source_geometry_data_async(const Ref &p_navigation_mesh, const Ref &p_source_geometry_data, const Callable &p_callback = Callable()) override {} bool is_baking_navigation_polygon(Ref p_navigation_polygon) const override { return false; } + RID source_geometry_parser_create() override { return RID(); } + void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) override {} + Vector simplify_path(const Vector &p_path, real_t p_epsilon) override { return Vector(); } void set_debug_enabled(bool p_enabled) {} diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index fda26aacc1ad..b21c6b60f0c0 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -188,6 +188,9 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_baking_navigation_mesh", "navigation_mesh"), &NavigationServer3D::is_baking_navigation_mesh); #endif // _3D_DISABLED + ClassDB::bind_method(D_METHOD("source_geometry_parser_create"), &NavigationServer3D::source_geometry_parser_create); + ClassDB::bind_method(D_METHOD("source_geometry_parser_set_callback", "parser", "callback"), &NavigationServer3D::source_geometry_parser_set_callback); + ClassDB::bind_method(D_METHOD("simplify_path", "path", "epsilon"), &NavigationServer3D::simplify_path); ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer3D::free); diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index 5a93c662b2d9..17c0771732fc 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -351,6 +351,9 @@ class NavigationServer3D : public Object { virtual bool is_baking_navigation_mesh(Ref p_navigation_mesh) const = 0; #endif // _3D_DISABLED + virtual RID source_geometry_parser_create() = 0; + virtual void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) = 0; + virtual Vector simplify_path(const Vector &p_path, real_t p_epsilon) = 0; NavigationServer3D(); diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h index 7079aa66beb3..5c9e97d22655 100644 --- a/servers/navigation_server_3d_dummy.h +++ b/servers/navigation_server_3d_dummy.h @@ -182,6 +182,9 @@ class NavigationServer3DDummy : public NavigationServer3D { bool is_baking_navigation_mesh(Ref p_navigation_mesh) const override { return false; } #endif // _3D_DISABLED + RID source_geometry_parser_create() override { return RID(); } + void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) override {} + Vector simplify_path(const Vector &p_path, real_t p_epsilon) override { return Vector(); } void free(RID p_object) override {}