From a7520fca6a1af5ae455563909e7ce34b1cb3ab71 Mon Sep 17 00:00:00 2001
From: smix8 <52464204+smix8@users.noreply.github.com>
Date: Sat, 11 Jan 2025 02:16:53 +0100
Subject: [PATCH] Add functions to get axis-aligned bounds of navigation
regions
Adds functions to get the navigation mesh Rect2 of a NavigationRegion2D or AABB of a NavigationRegion3D.
---
doc/classes/NavigationRegion2D.xml | 6 ++++
doc/classes/NavigationRegion3D.xml | 6 ++++
doc/classes/NavigationServer2D.xml | 7 +++++
doc/classes/NavigationServer3D.xml | 7 +++++
.../2d/godot_navigation_server_2d.cpp | 12 ++++++++
.../2d/godot_navigation_server_2d.h | 1 +
.../3d/godot_navigation_server_3d.cpp | 7 +++++
.../3d/godot_navigation_server_3d.h | 1 +
scene/2d/navigation_region_2d.cpp | 28 +++++++++++++++++++
scene/2d/navigation_region_2d.h | 5 ++++
scene/3d/navigation_region_3d.cpp | 27 ++++++++++++++++++
scene/3d/navigation_region_3d.h | 5 ++++
servers/navigation_server_2d.cpp | 1 +
servers/navigation_server_2d.h | 1 +
servers/navigation_server_2d_dummy.h | 1 +
servers/navigation_server_3d.cpp | 1 +
servers/navigation_server_3d.h | 2 ++
servers/navigation_server_3d_dummy.h | 1 +
18 files changed, 119 insertions(+)
diff --git a/doc/classes/NavigationRegion2D.xml b/doc/classes/NavigationRegion2D.xml
index 31ce1dba4a..731245fb48 100644
--- a/doc/classes/NavigationRegion2D.xml
+++ b/doc/classes/NavigationRegion2D.xml
@@ -23,6 +23,12 @@
Bakes the [NavigationPolygon]. If [param on_thread] is set to [code]true[/code] (default), the baking is done on a separate thread.
+
+
+
+ Returns the axis-aligned rectangle for the region's transformed navigation mesh.
+
+
diff --git a/doc/classes/NavigationRegion3D.xml b/doc/classes/NavigationRegion3D.xml
index ad31fd0632..019afce30b 100644
--- a/doc/classes/NavigationRegion3D.xml
+++ b/doc/classes/NavigationRegion3D.xml
@@ -23,6 +23,12 @@
Bakes the [NavigationMesh]. If [param on_thread] is set to [code]true[/code] (default), the baking is done on a separate thread. Baking on separate thread is useful because navigation baking is not a cheap operation. When it is completed, it automatically sets the new [NavigationMesh]. Please note that baking on separate thread may be very slow if geometry is parsed from meshes as async access to each mesh involves heavy synchronization. Also, please note that baking on a separate thread is automatically disabled on operating systems that cannot use threads (such as Web with threads disabled).
+
+
+
+ Returns the axis-aligned bounding box for the region's transformed navigation mesh.
+
+
diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml
index 41ef298bc3..95db5591d0 100644
--- a/doc/classes/NavigationServer2D.xml
+++ b/doc/classes/NavigationServer2D.xml
@@ -784,6 +784,13 @@
Creates a new region.
+
+
+
+
+ Returns the axis-aligned rectangle for the [param region]'s transformed navigation mesh.
+
+
diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml
index 6f0c68740a..64da1cdade 100644
--- a/doc/classes/NavigationServer3D.xml
+++ b/doc/classes/NavigationServer3D.xml
@@ -925,6 +925,13 @@
Creates a new region.
+
+
+
+
+ Returns the axis-aligned bounding box for the [param region]'s transformed navigation mesh.
+
+
diff --git a/modules/navigation/2d/godot_navigation_server_2d.cpp b/modules/navigation/2d/godot_navigation_server_2d.cpp
index f6876b31e2..89e4de3b02 100644
--- a/modules/navigation/2d/godot_navigation_server_2d.cpp
+++ b/modules/navigation/2d/godot_navigation_server_2d.cpp
@@ -158,6 +158,13 @@ static Ref poly_to_mesh(Ref d) {
}
}
+static Rect2 aabb_to_rect2(AABB aabb) {
+ Rect2 rect2;
+ rect2.position = Vector2(aabb.position.x, aabb.position.z);
+ rect2.size = Vector2(aabb.size.x, aabb.size.z);
+ return rect2;
+}
+
void GodotNavigationServer2D::init() {
#ifdef CLIPPER2_ENABLED
navmesh_generator_2d = memnew(NavMeshGenerator2D);
@@ -332,6 +339,11 @@ Vector2 GodotNavigationServer2D::region_get_random_point(RID p_region, uint32_t
return v3_to_v2(result);
}
+Rect2 GodotNavigationServer2D::region_get_bounds(RID p_region) const {
+ AABB bounds = NavigationServer3D::get_singleton()->region_get_bounds(p_region);
+ return aabb_to_rect2(bounds);
+}
+
RID FORWARD_0(link_create);
void FORWARD_2(link_set_map, RID, p_link, RID, p_map, rid_to_rid, rid_to_rid);
diff --git a/modules/navigation/2d/godot_navigation_server_2d.h b/modules/navigation/2d/godot_navigation_server_2d.h
index f4a5023a0d..361b812bdf 100644
--- a/modules/navigation/2d/godot_navigation_server_2d.h
+++ b/modules/navigation/2d/godot_navigation_server_2d.h
@@ -105,6 +105,7 @@ public:
virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override;
virtual Vector2 region_get_closest_point(RID p_region, const Vector2 &p_point) const override;
virtual Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override;
+ virtual Rect2 region_get_bounds(RID p_region) const override;
virtual RID link_create() override;
diff --git a/modules/navigation/3d/godot_navigation_server_3d.cpp b/modules/navigation/3d/godot_navigation_server_3d.cpp
index e2b8d0509b..ef15bcd6a3 100644
--- a/modules/navigation/3d/godot_navigation_server_3d.cpp
+++ b/modules/navigation/3d/godot_navigation_server_3d.cpp
@@ -595,6 +595,13 @@ Vector3 GodotNavigationServer3D::region_get_random_point(RID p_region, uint32_t
return region->get_random_point(p_navigation_layers, p_uniformly);
}
+AABB GodotNavigationServer3D::region_get_bounds(RID p_region) const {
+ const NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_NULL_V(region, AABB());
+
+ return region->get_bounds();
+}
+
RID GodotNavigationServer3D::link_create() {
MutexLock lock(operations_mutex);
diff --git a/modules/navigation/3d/godot_navigation_server_3d.h b/modules/navigation/3d/godot_navigation_server_3d.h
index 268dbee8e7..c595fbc02e 100644
--- a/modules/navigation/3d/godot_navigation_server_3d.h
+++ b/modules/navigation/3d/godot_navigation_server_3d.h
@@ -187,6 +187,7 @@ public:
virtual Vector3 region_get_closest_point(RID p_region, const Vector3 &p_point) const override;
virtual Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const override;
virtual Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override;
+ virtual AABB region_get_bounds(RID p_region) const override;
virtual RID link_create() override;
COMMAND_2(link_set_map, RID, p_link, RID, p_map);
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index e33c7c08f9..365d5ba4f6 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -200,6 +200,9 @@ void NavigationRegion2D::set_navigation_polygon(const Ref &p_
#ifdef DEBUG_ENABLED
debug_mesh_dirty = true;
#endif // DEBUG_ENABLED
+
+ _update_bounds();
+
NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, p_navigation_polygon);
if (navigation_polygon.is_valid()) {
@@ -341,6 +344,8 @@ void NavigationRegion2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_navigation_polygon_changed"), &NavigationRegion2D::_navigation_polygon_changed);
+ ClassDB::bind_method(D_METHOD("get_bounds"), &NavigationRegion2D::get_bounds);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navigation_polygon", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_edge_connections"), "set_use_edge_connections", "get_use_edge_connections");
@@ -648,3 +653,26 @@ void NavigationRegion2D::_set_debug_visible(bool p_visible) {
}
}
#endif // DEBUG_ENABLED
+
+void NavigationRegion2D::_update_bounds() {
+ if (navigation_polygon.is_null()) {
+ bounds = Rect2();
+ return;
+ }
+
+ const Vector &vertices = navigation_polygon->get_vertices();
+ if (vertices.is_empty()) {
+ bounds = Rect2();
+ return;
+ }
+
+ const Transform2D gt = is_inside_tree() ? get_global_transform() : get_transform();
+
+ Rect2 new_bounds;
+ new_bounds.position = gt.xform(vertices[0]);
+
+ for (const Vector2 &vertex : vertices) {
+ new_bounds.expand_to(gt.xform(vertex));
+ }
+ bounds = new_bounds;
+}
diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h
index 761eaef5ce..9ea87b1d90 100644
--- a/scene/2d/navigation_region_2d.h
+++ b/scene/2d/navigation_region_2d.h
@@ -50,6 +50,8 @@ class NavigationRegion2D : public Node2D {
void _navigation_polygon_changed();
+ Rect2 bounds;
+
#ifdef DEBUG_ENABLED
private:
RID debug_mesh_rid;
@@ -113,10 +115,13 @@ public:
void _bake_finished(Ref p_navigation_polygon);
bool is_baking() const;
+ Rect2 get_bounds() const { return bounds; }
+
NavigationRegion2D();
~NavigationRegion2D();
private:
+ void _update_bounds();
void _region_enter_navigation_map();
void _region_exit_navigation_map();
void _region_update_transform();
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index ce8cef5699..34be901eef 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -193,6 +193,8 @@ void NavigationRegion3D::set_navigation_mesh(const Ref &p_naviga
navigation_mesh->connect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
}
+ _update_bounds();
+
NavigationServer3D::get_singleton()->region_set_navigation_mesh(region, p_navigation_mesh);
#ifdef DEBUG_ENABLED
@@ -314,6 +316,8 @@ void NavigationRegion3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("bake_navigation_mesh", "on_thread"), &NavigationRegion3D::bake_navigation_mesh, DEFVAL(true));
ClassDB::bind_method(D_METHOD("is_baking"), &NavigationRegion3D::is_baking);
+ ClassDB::bind_method(D_METHOD("get_bounds"), &NavigationRegion3D::get_bounds);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navigation_mesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_edge_connections"), "set_use_edge_connections", "get_use_edge_connections");
@@ -740,3 +744,26 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() {
}
}
#endif // DEBUG_ENABLED
+
+void NavigationRegion3D::_update_bounds() {
+ if (navigation_mesh.is_null()) {
+ bounds = AABB();
+ return;
+ }
+
+ const Vector &vertices = navigation_mesh->get_vertices();
+ if (vertices.is_empty()) {
+ bounds = AABB();
+ return;
+ }
+
+ const Transform3D gt = is_inside_tree() ? get_global_transform() : get_transform();
+
+ AABB new_bounds;
+ new_bounds.position = gt.xform(vertices[0]);
+
+ for (const Vector3 &vertex : vertices) {
+ new_bounds.expand_to(gt.xform(vertex));
+ }
+ bounds = new_bounds;
+}
diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h
index 82468627de..97abe3979c 100644
--- a/scene/3d/navigation_region_3d.h
+++ b/scene/3d/navigation_region_3d.h
@@ -51,6 +51,8 @@ class NavigationRegion3D : public Node3D {
void _navigation_mesh_changed();
+ AABB bounds;
+
#ifdef DEBUG_ENABLED
RID debug_instance;
RID debug_edge_connections_instance;
@@ -110,10 +112,13 @@ public:
PackedStringArray get_configuration_warnings() const override;
+ AABB get_bounds() const { return bounds; }
+
NavigationRegion3D();
~NavigationRegion3D();
private:
+ void _update_bounds();
void _region_enter_navigation_map();
void _region_exit_navigation_map();
void _region_update_transform();
diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp
index a24a331268..7994cd1758 100644
--- a/servers/navigation_server_2d.cpp
+++ b/servers/navigation_server_2d.cpp
@@ -91,6 +91,7 @@ void NavigationServer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_end);
ClassDB::bind_method(D_METHOD("region_get_closest_point", "region", "to_point"), &NavigationServer2D::region_get_closest_point);
ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer2D::region_get_random_point);
+ ClassDB::bind_method(D_METHOD("region_get_bounds", "region"), &NavigationServer2D::region_get_bounds);
ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer2D::link_create);
ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer2D::link_set_map);
diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h
index 510cf39652..e53d84aef1 100644
--- a/servers/navigation_server_2d.h
+++ b/servers/navigation_server_2d.h
@@ -154,6 +154,7 @@ public:
virtual Vector2 region_get_closest_point(RID p_region, const Vector2 &p_point) const = 0;
virtual Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0;
+ virtual Rect2 region_get_bounds(RID p_region) const = 0;
/// Creates a new link between positions in the nav map.
virtual RID link_create() = 0;
diff --git a/servers/navigation_server_2d_dummy.h b/servers/navigation_server_2d_dummy.h
index dd6e618e0b..4e16c00e4b 100644
--- a/servers/navigation_server_2d_dummy.h
+++ b/servers/navigation_server_2d_dummy.h
@@ -87,6 +87,7 @@ public:
Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override { return Vector2(); }
Vector2 region_get_closest_point(RID p_region, const Vector2 &p_point) const override { return Vector2(); }
Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector2(); }
+ Rect2 region_get_bounds(RID p_region) const override { return Rect2(); }
RID link_create() override { return RID(); }
void link_set_map(RID p_link, RID p_map) override {}
diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp
index 702439e4af..a34eeb36f9 100644
--- a/servers/navigation_server_3d.cpp
+++ b/servers/navigation_server_3d.cpp
@@ -106,6 +106,7 @@ void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("region_get_closest_point", "region", "to_point"), &NavigationServer3D::region_get_closest_point);
ClassDB::bind_method(D_METHOD("region_get_closest_point_normal", "region", "to_point"), &NavigationServer3D::region_get_closest_point_normal);
ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer3D::region_get_random_point);
+ ClassDB::bind_method(D_METHOD("region_get_bounds", "region"), &NavigationServer3D::region_get_bounds);
ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer3D::link_create);
ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer3D::link_set_map);
diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h
index 5a9896a86a..e94ca5a415 100644
--- a/servers/navigation_server_3d.h
+++ b/servers/navigation_server_3d.h
@@ -176,6 +176,8 @@ public:
virtual Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const = 0;
virtual Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0;
+ virtual AABB region_get_bounds(RID p_region) const = 0;
+
/// Creates a new link between positions in the nav map.
virtual RID link_create() = 0;
diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h
index 2ab4c6e6e2..c3f1420bfa 100644
--- a/servers/navigation_server_3d_dummy.h
+++ b/servers/navigation_server_3d_dummy.h
@@ -99,6 +99,7 @@ public:
Vector3 region_get_closest_point(RID p_region, const Vector3 &p_point) const override { return Vector3(); }
Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const override { return Vector3(); }
Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector3(); }
+ AABB region_get_bounds(RID p_region) const override { return AABB(); }
RID link_create() override { return RID(); }
void link_set_map(RID p_link, RID p_map) override {}