Ported from godot4: Add more detailed Navigation Debug Visualization

- Adds more customization options to ProjectSettings.
- Displays navregion edge connections and navigation polygon edges in editor and at runtime.
- Majority of debug code moved from SceneTree to NavigationServer.
- Removes the irritating debug MeshInstance child node from NavigationRegion3D and replaces it with direct RenderingServer API.
- smix8
c394ea518e
This commit is contained in:
Relintai 2023-06-05 18:05:15 +02:00
parent 510ecfa8e4
commit 68b9f4c90e
12 changed files with 941 additions and 70 deletions

View File

@ -430,6 +430,11 @@
Emitted when a navigation map is updated, when a region moves or is modified. Emitted when a navigation map is updated, when a region moves or is modified.
</description> </description>
</signal> </signal>
<signal name="navigation_debug_changed">
<description>
Emitted when navigation debug settings are changed. Only available in debug builds.
</description>
</signal>
</signals> </signals>
<constants> <constants>
</constants> </constants>

View File

@ -532,9 +532,39 @@
<member name="debug/shapes/navigation/disabled_geometry_color" type="Color" setter="" getter="" default="Color( 1, 0.7, 0.1, 0.4 )"> <member name="debug/shapes/navigation/disabled_geometry_color" type="Color" setter="" getter="" default="Color( 1, 0.7, 0.1, 0.4 )">
Color of the disabled navigation geometry, visible when "Visible Navigation" is enabled in the Debug menu. Color of the disabled navigation geometry, visible when "Visible Navigation" is enabled in the Debug menu.
</member> </member>
<member name="debug/shapes/navigation/edge_connection_color" type="Color" setter="" getter="" default="Color(1, 0, 1, 1)">
Color to display edge connections between navigation regions, visible when "Visible Navigation" is enabled in the Debug menu.
</member>
<member name="debug/shapes/navigation/enable_edge_connections" type="bool" setter="" getter="" default="true">
If enabled, displays edge connections between navigation regions when "Visible Navigation" is enabled in the Debug menu.
</member>
<member name="debug/shapes/navigation/enable_edge_connections_xray" type="bool" setter="" getter="" default="true">
If enabled, displays edge connections between navigation regions through geometry when "Visible Navigation" is enabled in the Debug menu.
</member>
<member name="debug/shapes/navigation/enable_edge_lines" type="bool" setter="" getter="" default="true">
If enabled, displays navigation mesh polygon edges when "Visible Navigation" is enabled in the Debug menu.
</member>
<member name="debug/shapes/navigation/enable_edge_lines_xray" type="bool" setter="" getter="" default="true">
If enabled, displays navigation mesh polygon edges through geometry when "Visible Navigation" is enabled in the Debug menu.
</member>
<member name="debug/shapes/navigation/enable_geometry_face_random_color" type="bool" setter="" getter="" default="true">
If enabled, colorizes each navigation mesh polygon face with a random color when "Visible Navigation" is enabled in the Debug menu.
</member>
<member name="debug/shapes/navigation/geometry_color" type="Color" setter="" getter="" default="Color( 0.1, 1, 0.7, 0.4 )"> <member name="debug/shapes/navigation/geometry_color" type="Color" setter="" getter="" default="Color( 0.1, 1, 0.7, 0.4 )">
Color of the navigation geometry, visible when "Visible Navigation" is enabled in the Debug menu. Color of the navigation geometry, visible when "Visible Navigation" is enabled in the Debug menu.
</member> </member>
<member name="debug/shapes/navigation/geometry_edge_color" type="Color" setter="" getter="" default="Color(0.5, 1, 1, 1)">
Color to display enabled navigation mesh polygon edges, visible when "Visible Navigation" is enabled in the Debug menu.
</member>
<member name="debug/shapes/navigation/geometry_edge_disabled_color" type="Color" setter="" getter="" default="Color(0.5, 0.5, 0.5, 1)">
Color to display disabled navigation mesh polygon edges, visible when "Visible Navigation" is enabled in the Debug menu.
</member>
<member name="debug/shapes/navigation/geometry_face_color" type="Color" setter="" getter="" default="Color(0.5, 1, 1, 0.4)">
Color to display enabled navigation mesh polygon faces, visible when "Visible Navigation" is enabled in the Debug menu.
</member>
<member name="debug/shapes/navigation/geometry_face_disabled_color" type="Color" setter="" getter="" default="Color(0.5, 0.5, 0.5, 0.4)">
Color to display disabled navigation mesh polygon faces, visible when "Visible Navigation" is enabled in the Debug menu.
</member>
<member name="display/mouse_cursor/custom_image" type="String" setter="" getter="" default="&quot;&quot;"> <member name="display/mouse_cursor/custom_image" type="String" setter="" getter="" default="&quot;&quot;">
Custom image for the mouse cursor (limited to 256×256). Custom image for the mouse cursor (limited to 256×256).
</member> </member>

View File

@ -5668,6 +5668,19 @@ void EditorNode::_project_settings_changed() {
tree->set_debug_navigation_color(GLOBAL_GET("debug/shapes/navigation/geometry_color")); tree->set_debug_navigation_color(GLOBAL_GET("debug/shapes/navigation/geometry_color"));
tree->set_debug_navigation_disabled_color(GLOBAL_GET("debug/shapes/navigation/disabled_geometry_color")); tree->set_debug_navigation_disabled_color(GLOBAL_GET("debug/shapes/navigation/disabled_geometry_color"));
#ifdef DEBUG_ENABLED
NavigationServer::get_singleton_mut()->set_debug_navigation_edge_connection_color(GLOBAL_GET("debug/shapes/navigation/edge_connection_color"));
NavigationServer::get_singleton_mut()->set_debug_navigation_geometry_edge_color(GLOBAL_GET("debug/shapes/navigation/geometry_edge_color"));
NavigationServer::get_singleton_mut()->set_debug_navigation_geometry_face_color(GLOBAL_GET("debug/shapes/navigation/geometry_face_color"));
NavigationServer::get_singleton_mut()->set_debug_navigation_geometry_edge_disabled_color(GLOBAL_GET("debug/shapes/navigation/geometry_edge_disabled_color"));
NavigationServer::get_singleton_mut()->set_debug_navigation_geometry_face_disabled_color(GLOBAL_GET("debug/shapes/navigation/geometry_face_disabled_color"));
NavigationServer::get_singleton_mut()->set_debug_navigation_enable_edge_connections(GLOBAL_GET("debug/shapes/navigation/enable_edge_connections"));
NavigationServer::get_singleton_mut()->set_debug_navigation_enable_edge_connections_xray(GLOBAL_GET("debug/shapes/navigation/enable_edge_connections_xray"));
NavigationServer::get_singleton_mut()->set_debug_navigation_enable_edge_lines(GLOBAL_GET("debug/shapes/navigation/enable_edge_lines"));
NavigationServer::get_singleton_mut()->set_debug_navigation_enable_edge_lines_xray(GLOBAL_GET("debug/shapes/navigation/enable_edge_lines_xray"));
NavigationServer::get_singleton_mut()->set_debug_navigation_enable_geometry_face_random_color(GLOBAL_GET("debug/shapes/navigation/enable_geometry_face_random_color"));
#endif // DEBUG_ENABLED
_update_title(); _update_title();
} }
@ -5851,7 +5864,11 @@ EditorNode::EditorNode() {
RenderingServer::get_singleton()->textures_keep_original(true); RenderingServer::get_singleton()->textures_keep_original(true);
RenderingServer::get_singleton()->set_debug_generate_wireframes(true); RenderingServer::get_singleton()->set_debug_generate_wireframes(true);
NavigationServer::get_singleton()->set_active(false); // no nav by default if editor if (NavigationServer::get_singleton()->get_debug_enabled()) {
NavigationServer::get_singleton()->set_active(true);
} else {
NavigationServer::get_singleton()->set_active(false);
}
PhysicsServer::get_singleton()->set_active(false); // no physics by default if editor PhysicsServer::get_singleton()->set_active(false); // no physics by default if editor
Physics2DServer::get_singleton()->set_active(false); // no physics by default if editor Physics2DServer::get_singleton()->set_active(false); // no physics by default if editor

View File

@ -102,6 +102,7 @@
#include "scene/resources/sphere_shape.h" #include "scene/resources/sphere_shape.h"
#include "scene/resources/surface_tool.h" #include "scene/resources/surface_tool.h"
#include "scene/resources/world_3d.h" #include "scene/resources/world_3d.h"
#include "servers/navigation_server.h"
#include "servers/rendering_server.h" #include "servers/rendering_server.h"
#ifdef MODULE_SKELETON_3D_ENABLED #ifdef MODULE_SKELETON_3D_ENABLED
@ -3541,11 +3542,6 @@ void CollisionPolygonSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
//// ////
NavigationMeshSpatialGizmoPlugin::NavigationMeshSpatialGizmoPlugin() { NavigationMeshSpatialGizmoPlugin::NavigationMeshSpatialGizmoPlugin() {
create_material("navigation_edge_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge", Color(0.5, 1, 1)));
create_material("navigation_edge_material_disabled", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge_disabled", Color(0.7, 0.7, 0.7)));
create_material("navigation_solid_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_solid", Color(0.5, 1, 1, 0.4)));
create_material("navigation_solid_material_disabled", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_solid_disabled", Color(0.7, 0.7, 0.7, 0.4)));
Color baking_aabb_material_color = Color(0.8, 0.5, 0.7); Color baking_aabb_material_color = Color(0.8, 0.5, 0.7);
baking_aabb_material_color.a = 0.1; baking_aabb_material_color.a = 0.1;
create_material("baking_aabb_material", baking_aabb_material_color); create_material("baking_aabb_material", baking_aabb_material_color);
@ -3564,33 +3560,28 @@ int NavigationMeshSpatialGizmoPlugin::get_priority() const {
} }
void NavigationMeshSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { void NavigationMeshSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
NavigationMeshInstance *navmesh = Object::cast_to<NavigationMeshInstance>(p_gizmo->get_spatial_node()); NavigationMeshInstance *navmesh_instance = Object::cast_to<NavigationMeshInstance>(p_gizmo->get_spatial_node());
Ref<Material> edge_material = get_material("navigation_edge_material", p_gizmo);
Ref<Material> edge_material_disabled = get_material("navigation_edge_material_disabled", p_gizmo);
Ref<Material> solid_material = get_material("navigation_solid_material", p_gizmo);
Ref<Material> solid_material_disabled = get_material("navigation_solid_material_disabled", p_gizmo);
p_gizmo->clear(); p_gizmo->clear();
Ref<NavigationMesh> navmeshie = navmesh->get_navigation_mesh(); Ref<NavigationMesh> navigationmesh = navmesh_instance->get_navigation_mesh();
if (navmeshie.is_null()) { if (navigationmesh.is_null()) {
return; return;
} }
AABB baking_aabb = navmeshie->get_filter_baking_aabb(); AABB baking_aabb = navigationmesh->get_filter_baking_aabb();
if (!baking_aabb.has_no_volume()) { if (!baking_aabb.has_no_volume()) {
Vector3 baking_aabb_offset = navmeshie->get_filter_baking_aabb_offset(); Vector3 baking_aabb_offset = navigationmesh->get_filter_baking_aabb_offset();
if (p_gizmo->is_selected()) { if (p_gizmo->is_selected()) {
Ref<Material> material = get_material("baking_aabb_material", p_gizmo); Ref<Material> material = get_material("baking_aabb_material", p_gizmo);
p_gizmo->add_solid_box(material, baking_aabb.get_size(), baking_aabb.get_center() + baking_aabb_offset); p_gizmo->add_solid_box(material, baking_aabb.get_size(), baking_aabb.get_center() + baking_aabb_offset);
} }
} }
PoolVector<Vector3> vertices = navmeshie->get_vertices(); PoolVector<Vector3> vertices = navigationmesh->get_vertices();
PoolVector<Vector3>::Read vr = vertices.read(); PoolVector<Vector3>::Read vr = vertices.read();
List<Face3> faces; List<Face3> faces;
for (int i = 0; i < navmeshie->get_polygon_count(); i++) { for (int i = 0; i < navigationmesh->get_polygon_count(); i++) {
Vector<int> p = navmeshie->get_polygon(i); Vector<int> p = navigationmesh->get_polygon(i);
for (int j = 2; j < p.size(); j++) { for (int j = 2; j < p.size(); j++) {
Face3 f; Face3 f;
@ -3649,17 +3640,90 @@ void NavigationMeshSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
Ref<TriangleMesh> tmesh = memnew(TriangleMesh); Ref<TriangleMesh> tmesh = memnew(TriangleMesh);
tmesh->create(tmeshfaces); tmesh->create(tmeshfaces);
if (lines.size()) {
p_gizmo->add_lines(lines, navmesh->is_enabled() ? edge_material : edge_material_disabled);
}
p_gizmo->add_collision_triangles(tmesh); p_gizmo->add_collision_triangles(tmesh);
Ref<ArrayMesh> m = memnew(ArrayMesh);
Array a; Ref<ArrayMesh> debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
a.resize(Mesh::ARRAY_MAX); int polygon_count = navigationmesh->get_polygon_count();
a[0] = tmeshfaces;
m->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); // build geometry face surface
m->surface_set_material(0, navmesh->is_enabled() ? solid_material : solid_material_disabled); Vector<Vector3> face_vertex_array;
p_gizmo->add_mesh(m); face_vertex_array.resize(polygon_count * 3);
for (int i = 0; i < polygon_count; i++) {
Vector<int> polygon = navigationmesh->get_polygon(i);
face_vertex_array.push_back(vertices[polygon[0]]);
face_vertex_array.push_back(vertices[polygon[1]]);
face_vertex_array.push_back(vertices[polygon[2]]);
}
Array face_mesh_array;
face_mesh_array.resize(Mesh::ARRAY_MAX);
face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array;
// if enabled add vertex colors to colorize each face individually
bool enabled_geometry_face_random_color = NavigationServer::get_singleton()->get_debug_navigation_enable_geometry_face_random_color();
if (enabled_geometry_face_random_color) {
Color debug_navigation_geometry_face_color = NavigationServer::get_singleton()->get_debug_navigation_geometry_face_color();
Color polygon_color = debug_navigation_geometry_face_color;
Vector<Color> face_color_array;
face_color_array.resize(polygon_count * 3);
for (int i = 0; i < polygon_count; i++) {
polygon_color = debug_navigation_geometry_face_color * (Color(Math::randf(), Math::randf(), Math::randf()));
Vector<int> polygon = navigationmesh->get_polygon(i);
face_color_array.push_back(polygon_color);
face_color_array.push_back(polygon_color);
face_color_array.push_back(polygon_color);
}
face_mesh_array[Mesh::ARRAY_COLOR] = face_color_array;
}
debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array);
Ref<SpatialMaterial> debug_geometry_face_material = NavigationServer::get_singleton_mut()->get_debug_navigation_geometry_face_material();
debug_mesh->surface_set_material(0, debug_geometry_face_material);
// if enabled build geometry edge line surface
bool enabled_edge_lines = NavigationServer::get_singleton()->get_debug_navigation_enable_edge_lines();
if (enabled_edge_lines) {
Vector<Vector3> line_vertex_array;
line_vertex_array.resize(polygon_count * 6);
for (int i = 0; i < polygon_count; i++) {
Vector<int> polygon = navigationmesh->get_polygon(i);
line_vertex_array.push_back(vertices[polygon[0]]);
line_vertex_array.push_back(vertices[polygon[1]]);
line_vertex_array.push_back(vertices[polygon[1]]);
line_vertex_array.push_back(vertices[polygon[2]]);
line_vertex_array.push_back(vertices[polygon[2]]);
line_vertex_array.push_back(vertices[polygon[0]]);
}
Array line_mesh_array;
line_mesh_array.resize(Mesh::ARRAY_MAX);
line_mesh_array[Mesh::ARRAY_VERTEX] = line_vertex_array;
debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, line_mesh_array);
Ref<SpatialMaterial> debug_geometry_edge_material = NavigationServer::get_singleton_mut()->get_debug_navigation_geometry_edge_material();
debug_mesh->surface_set_material(1, debug_geometry_edge_material);
}
if (!navmesh_instance->is_enabled()) {
if (debug_mesh.is_valid()) {
if (debug_mesh->get_surface_count() > 0) {
debug_mesh->surface_set_material(0, NavigationServer::get_singleton_mut()->get_debug_navigation_geometry_face_disabled_material());
}
if (debug_mesh->get_surface_count() > 1) {
debug_mesh->surface_set_material(1, NavigationServer::get_singleton_mut()->get_debug_navigation_geometry_edge_disabled_material());
}
}
}
p_gizmo->add_mesh(debug_mesh);
p_gizmo->add_collision_segments(lines); p_gizmo->add_collision_segments(lines);
} }

View File

@ -1926,6 +1926,8 @@ bool Main::start() {
} }
if (debug_navigation) { if (debug_navigation) {
sml->set_debug_navigation_hint(true); sml->set_debug_navigation_hint(true);
NavigationServer::get_singleton()->set_active(true);
NavigationServer::get_singleton_mut()->set_debug_enabled(true);
} }
#endif #endif

View File

@ -30,14 +30,15 @@
#include "navigation_region_2d.h" #include "navigation_region_2d.h"
#include "core/core_string_names.h"
#include "core/config/engine.h" #include "core/config/engine.h"
#include "core/core_string_names.h"
#include "core/os/mutex.h" #include "core/os/mutex.h"
#include "navigation_2d.h" #include "navigation_2d.h"
#include "scene/resources/navigation_mesh.h" #include "scene/resources/navigation_mesh.h"
#include "scene/resources/navigation_polygon.h"
#include "scene/resources/world_2d.h" #include "scene/resources/world_2d.h"
#include "servers/navigation_2d_server.h" #include "servers/navigation_2d_server.h"
#include "scene/resources/navigation_polygon.h" #include "servers/navigation_server.h"
#include "thirdparty/misc/triangulator.h" #include "thirdparty/misc/triangulator.h"
@ -63,9 +64,11 @@ void NavigationRegion2D::set_enabled(bool p_enabled) {
Navigation2DServer::get_singleton_mut()->connect("map_changed", this, "_map_changed"); Navigation2DServer::get_singleton_mut()->connect("map_changed", this, "_map_changed");
} }
if (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) { #ifdef DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint() || NavigationServer::get_singleton()->get_debug_enabled()) {
update(); update();
} }
#endif // DEBUG_ENABLED
} }
bool NavigationRegion2D::is_enabled() const { bool NavigationRegion2D::is_enabled() const {
@ -155,7 +158,8 @@ void NavigationRegion2D::_notification(int p_what) {
} }
} break; } break;
case NOTIFICATION_DRAW: { case NOTIFICATION_DRAW: {
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) && navpoly.is_valid()) { #ifdef DEBUG_ENABLED
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || NavigationServer::get_singleton()->get_debug_enabled()) && navpoly.is_valid()) {
PoolVector<Vector2> verts = navpoly->get_vertices(); PoolVector<Vector2> verts = navpoly->get_vertices();
if (verts.size() < 3) { if (verts.size() < 3) {
return; return;
@ -163,12 +167,12 @@ void NavigationRegion2D::_notification(int p_what) {
Color color; Color color;
if (enabled) { if (enabled) {
color = get_tree()->get_debug_navigation_color(); color = NavigationServer::get_singleton()->get_debug_navigation_geometry_face_color();
} else { } else {
color = get_tree()->get_debug_navigation_disabled_color(); color = NavigationServer::get_singleton()->get_debug_navigation_geometry_face_disabled_color();
} }
Color doors_color = color.lightened(0.2); Color doors_color = NavigationServer::get_singleton()->get_debug_navigation_edge_connection_color();
RandomPCG rand; RandomPCG rand;
@ -216,6 +220,7 @@ void NavigationRegion2D::_notification(int p_what) {
draw_arc(b, radius, angle - Math_PI / 2.0, angle + Math_PI / 2.0, 10, doors_color); draw_arc(b, radius, angle - Math_PI / 2.0, angle + Math_PI / 2.0, 10, doors_color);
} }
} }
#endif // DEBUG_ENABLED
} break; } break;
} }
} }
@ -255,11 +260,13 @@ void NavigationRegion2D::_navpoly_changed() {
} }
void NavigationRegion2D::_map_changed(RID p_map) { void NavigationRegion2D::_map_changed(RID p_map) {
#ifdef DEBUG_ENABLED
if (navigation != nullptr && enabled && (navigation->get_rid() == p_map)) { if (navigation != nullptr && enabled && (navigation->get_rid() == p_map)) {
update(); update();
} else if (is_inside_tree() && enabled && (get_world_2d()->get_navigation_map() == p_map)) { } else if (is_inside_tree() && enabled && (get_world_2d()->get_navigation_map() == p_map)) {
update(); update();
} }
#endif // DEBUG_ENABLED
} }
String NavigationRegion2D::get_configuration_warning() const { String NavigationRegion2D::get_configuration_warning() const {
@ -321,8 +328,18 @@ NavigationRegion2D::NavigationRegion2D() {
Navigation2DServer::get_singleton()->region_set_enter_cost(region, get_enter_cost()); Navigation2DServer::get_singleton()->region_set_enter_cost(region, get_enter_cost());
Navigation2DServer::get_singleton()->region_set_travel_cost(region, get_travel_cost()); Navigation2DServer::get_singleton()->region_set_travel_cost(region, get_travel_cost());
#ifdef DEBUG_ENABLED
Navigation2DServer::get_singleton_mut()->connect("map_changed", this, "_map_changed");
NavigationServer::get_singleton_mut()->connect("navigation_debug_changed", this, "_map_changed");
#endif // DEBUG_ENABLED
} }
NavigationRegion2D::~NavigationRegion2D() { NavigationRegion2D::~NavigationRegion2D() {
Navigation2DServer::get_singleton()->free(region); Navigation2DServer::get_singleton()->free(region);
#ifdef DEBUG_ENABLED
Navigation2DServer::get_singleton_mut()->disconnect("map_changed", this, "_map_changed");
NavigationServer::get_singleton_mut()->disconnect("navigation_debug_changed", this, "_map_changed");
#endif // DEBUG_ENABLED
} }

View File

@ -35,8 +35,8 @@
#include "navigation.h" #include "navigation.h"
#include "scene/resources/mesh.h" #include "scene/resources/mesh.h"
#include "scene/resources/navigation_mesh.h" #include "scene/resources/navigation_mesh.h"
#include "servers/navigation_server.h"
#include "scene/resources/world_3d.h" #include "scene/resources/world_3d.h"
#include "servers/navigation_server.h"
void NavigationMeshInstance::set_enabled(bool p_enabled) { void NavigationMeshInstance::set_enabled(bool p_enabled) {
if (enabled == p_enabled) { if (enabled == p_enabled) {
@ -58,14 +58,29 @@ void NavigationMeshInstance::set_enabled(bool p_enabled) {
} }
} }
if (debug_view) { #ifdef DEBUG_ENABLED
MeshInstance *dm = Object::cast_to<MeshInstance>(debug_view); if (debug_instance.is_valid()) {
if (is_enabled()) { if (!is_enabled()) {
dm->set_material_override(get_tree()->get_debug_navigation_material()); if (debug_mesh.is_valid()) {
if (debug_mesh->get_surface_count() > 0) {
RS::get_singleton()->instance_set_surface_material(debug_instance, 0, NavigationServer::get_singleton_mut()->get_debug_navigation_geometry_face_disabled_material()->get_rid());
}
if (debug_mesh->get_surface_count() > 1) {
RS::get_singleton()->instance_set_surface_material(debug_instance, 1, NavigationServer::get_singleton_mut()->get_debug_navigation_geometry_edge_disabled_material()->get_rid());
}
}
} else { } else {
dm->set_material_override(get_tree()->get_debug_navigation_disabled_material()); if (debug_mesh.is_valid()) {
if (debug_mesh->get_surface_count() > 0) {
RS::get_singleton()->instance_set_surface_material(debug_instance, 0, RID());
}
if (debug_mesh->get_surface_count() > 1) {
RS::get_singleton()->instance_set_surface_material(debug_instance, 1, RID());
}
}
} }
} }
#endif // DEBUG_ENABLED
update_gizmos(); update_gizmos();
} }
@ -130,40 +145,47 @@ void NavigationMeshInstance::_notification(int p_what) {
NavigationServer::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map()); NavigationServer::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
} }
if (navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) { #ifdef DEBUG_ENABLED
MeshInstance *dm = memnew(MeshInstance); if (NavigationServer::get_singleton()->get_debug_enabled()) {
dm->set_mesh(navmesh->get_debug_mesh()); _update_debug_mesh();
if (is_enabled()) {
dm->set_material_override(get_tree()->get_debug_navigation_material());
} else {
dm->set_material_override(get_tree()->get_debug_navigation_disabled_material());
}
add_child(dm);
debug_view = dm;
} }
#endif // DEBUG_ENABLED
} break; } break;
case NOTIFICATION_TRANSFORM_CHANGED: { case NOTIFICATION_TRANSFORM_CHANGED: {
NavigationServer::get_singleton()->region_set_transform(region, get_global_transform()); NavigationServer::get_singleton()->region_set_transform(region, get_global_transform());
#ifdef DEBUG_ENABLED
if (is_inside_tree() && debug_instance.is_valid()) {
RS::get_singleton()->instance_set_transform(debug_instance, get_global_transform());
}
#endif // DEBUG_ENABLED
} break; } break;
case NOTIFICATION_EXIT_TREE: { case NOTIFICATION_EXIT_TREE: {
if (navigation) { if (navigation) {
NavigationServer::get_singleton()->region_set_map(region, RID()); NavigationServer::get_singleton()->region_set_map(region, RID());
} }
if (debug_view) {
debug_view->queue_delete();
debug_view = nullptr;
}
navigation = nullptr; navigation = nullptr;
#ifdef DEBUG_ENABLED
if (debug_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_instance, false);
}
if (debug_edge_connections_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
}
#endif // DEBUG_ENABLED
} break; } break;
} }
} }
void NavigationMeshInstance::set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh) { void NavigationMeshInstance::set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh) {
if (p_navmesh == navmesh) if (p_navmesh == navmesh) {
return; return;
}
if (navmesh.is_valid()) { if (navmesh.is_valid()) {
navmesh->remove_change_receptor(this); navmesh->remove_change_receptor(this);
@ -177,20 +199,21 @@ void NavigationMeshInstance::set_navigation_mesh(const Ref<NavigationMesh> &p_na
NavigationServer::get_singleton()->region_set_navmesh(region, p_navmesh); NavigationServer::get_singleton()->region_set_navmesh(region, p_navmesh);
if (debug_view == nullptr && is_inside_tree() && navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) { #ifdef DEBUG_ENABLED
MeshInstance *dm = memnew(MeshInstance); if (is_inside_tree() && NavigationServer::get_singleton()->get_debug_enabled()) {
dm->set_mesh(navmesh->get_debug_mesh()); if (navmesh.is_valid()) {
if (is_enabled()) { _update_debug_mesh();
dm->set_material_override(get_tree()->get_debug_navigation_material()); _update_debug_edge_connections_mesh();
} else { } else {
dm->set_material_override(get_tree()->get_debug_navigation_disabled_material()); if (debug_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_instance, false);
}
if (debug_edge_connections_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
}
} }
add_child(dm);
debug_view = dm;
}
if (debug_view && navmesh.is_valid()) {
Object::cast_to<MeshInstance>(debug_view)->set_mesh(navmesh->get_debug_mesh());
} }
#endif // DEBUG_ENABLED
emit_signal("navigation_mesh_changed"); emit_signal("navigation_mesh_changed");
@ -250,6 +273,23 @@ void NavigationMeshInstance::_bake_finished(Ref<NavigationMesh> p_nav_mesh) {
emit_signal("bake_finished"); emit_signal("bake_finished");
} }
void NavigationMeshInstance::_navigation_changed() {
update_gizmos();
update_configuration_warning();
#ifdef DEBUG_ENABLED
_update_debug_edge_connections_mesh();
#endif // DEBUG_ENABLED
}
#ifdef DEBUG_ENABLED
void NavigationMeshInstance::_navigation_map_changed(RID p_map) {
if (is_inside_tree() && p_map == get_world_3d()->get_navigation_map()) {
_update_debug_edge_connections_mesh();
}
}
#endif // DEBUG_ENABLED
String NavigationMeshInstance::get_configuration_warning() const { String NavigationMeshInstance::get_configuration_warning() const {
if (!is_visible_in_tree() || !is_inside_tree()) { if (!is_visible_in_tree() || !is_inside_tree()) {
return String(); return String();
@ -282,6 +322,11 @@ void NavigationMeshInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("bake_navigation_mesh", "on_thread"), &NavigationMeshInstance::bake_navigation_mesh, DEFVAL(true)); ClassDB::bind_method(D_METHOD("bake_navigation_mesh", "on_thread"), &NavigationMeshInstance::bake_navigation_mesh, DEFVAL(true));
ClassDB::bind_method(D_METHOD("_bake_finished", "nav_mesh"), &NavigationMeshInstance::_bake_finished); ClassDB::bind_method(D_METHOD("_bake_finished", "nav_mesh"), &NavigationMeshInstance::_bake_finished);
ClassDB::bind_method(D_METHOD("_navigation_changed"), &NavigationMeshInstance::_navigation_changed);
ClassDB::bind_method(D_METHOD("_update_debug_mesh"), &NavigationMeshInstance::_update_debug_mesh);
ClassDB::bind_method(D_METHOD("_update_debug_edge_connections_mesh"), &NavigationMeshInstance::_update_debug_edge_connections_mesh);
ClassDB::bind_method(D_METHOD("_navigation_map_changed"), &NavigationMeshInstance::_navigation_map_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", 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, "enabled"), "set_enabled", "is_enabled");
@ -310,6 +355,12 @@ NavigationMeshInstance::NavigationMeshInstance() {
NavigationServer::get_singleton()->region_set_enter_cost(region, get_enter_cost()); NavigationServer::get_singleton()->region_set_enter_cost(region, get_enter_cost());
NavigationServer::get_singleton()->region_set_travel_cost(region, get_travel_cost()); NavigationServer::get_singleton()->region_set_travel_cost(region, get_travel_cost());
enabled = true; enabled = true;
#ifdef DEBUG_ENABLED
NavigationServer::get_singleton_mut()->connect("map_changed", this, "_navigation_map_changed");
NavigationServer::get_singleton_mut()->connect("navigation_debug_changed", this, "_update_debug_mesh");
NavigationServer::get_singleton_mut()->connect("navigation_debug_changed", this, "_update_debug_edge_connections_mesh");
#endif // DEBUG_ENABLED
} }
NavigationMeshInstance::~NavigationMeshInstance() { NavigationMeshInstance::~NavigationMeshInstance() {
@ -317,4 +368,246 @@ NavigationMeshInstance::~NavigationMeshInstance() {
navmesh->remove_change_receptor(this); navmesh->remove_change_receptor(this);
} }
NavigationServer::get_singleton()->free(region); NavigationServer::get_singleton()->free(region);
#ifdef DEBUG_ENABLED
NavigationServer::get_singleton_mut()->disconnect("map_changed", this, "_navigation_map_changed");
NavigationServer::get_singleton_mut()->disconnect("navigation_debug_changed", this, "_update_debug_mesh");
NavigationServer::get_singleton_mut()->disconnect("navigation_debug_changed", this, "_update_debug_edge_connections_mesh");
if (debug_instance.is_valid()) {
RenderingServer::get_singleton()->free(debug_instance);
}
if (debug_mesh.is_valid()) {
RenderingServer::get_singleton()->free(debug_mesh->get_rid());
}
if (debug_edge_connections_instance.is_valid()) {
RenderingServer::get_singleton()->free(debug_edge_connections_instance);
}
if (debug_edge_connections_mesh.is_valid()) {
RenderingServer::get_singleton()->free(debug_edge_connections_mesh->get_rid());
}
#endif // DEBUG_ENABLED
} }
#ifdef DEBUG_ENABLED
void NavigationMeshInstance::_update_debug_mesh() {
if (!NavigationServer::get_singleton()->get_debug_enabled()) {
if (debug_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_instance, false);
}
return;
}
if (!navmesh.is_valid()) {
if (debug_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_instance, false);
}
return;
}
if (!debug_instance.is_valid()) {
debug_instance = RenderingServer::get_singleton()->instance_create();
}
if (!debug_mesh.is_valid()) {
debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
}
debug_mesh->clear_surfaces();
bool enabled_geometry_face_random_color = NavigationServer::get_singleton()->get_debug_navigation_enable_geometry_face_random_color();
bool enabled_edge_lines = NavigationServer::get_singleton()->get_debug_navigation_enable_edge_lines();
PoolVector<Vector3> vertices = navmesh->get_vertices();
if (vertices.size() == 0) {
return;
}
int polygon_count = navmesh->get_polygon_count();
if (polygon_count == 0) {
return;
}
Vector<Vector3> face_vertex_array;
face_vertex_array.resize(polygon_count * 3);
Vector<Color> face_color_array;
if (enabled_geometry_face_random_color) {
face_color_array.resize(polygon_count * 3);
}
Vector<Vector3> line_vertex_array;
if (enabled_edge_lines) {
line_vertex_array.resize(polygon_count * 6);
}
Color debug_navigation_geometry_face_color = NavigationServer::get_singleton()->get_debug_navigation_geometry_face_color();
Ref<SpatialMaterial> face_material = NavigationServer::get_singleton_mut()->get_debug_navigation_geometry_face_material();
Ref<SpatialMaterial> line_material = NavigationServer::get_singleton_mut()->get_debug_navigation_geometry_edge_material();
Color polygon_color = debug_navigation_geometry_face_color;
for (int i = 0; i < polygon_count; i++) {
if (enabled_geometry_face_random_color) {
polygon_color = debug_navigation_geometry_face_color * (Color(Math::randf(), Math::randf(), Math::randf()));
}
Vector<int> polygon = navmesh->get_polygon(i);
face_vertex_array.push_back(vertices[polygon[0]]);
face_vertex_array.push_back(vertices[polygon[1]]);
face_vertex_array.push_back(vertices[polygon[2]]);
if (enabled_geometry_face_random_color) {
face_color_array.push_back(polygon_color);
face_color_array.push_back(polygon_color);
face_color_array.push_back(polygon_color);
}
if (enabled_edge_lines) {
line_vertex_array.push_back(vertices[polygon[0]]);
line_vertex_array.push_back(vertices[polygon[1]]);
line_vertex_array.push_back(vertices[polygon[1]]);
line_vertex_array.push_back(vertices[polygon[2]]);
line_vertex_array.push_back(vertices[polygon[2]]);
line_vertex_array.push_back(vertices[polygon[0]]);
}
}
Array face_mesh_array;
face_mesh_array.resize(Mesh::ARRAY_MAX);
face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array;
if (enabled_geometry_face_random_color) {
face_mesh_array[Mesh::ARRAY_COLOR] = face_color_array;
}
debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array);
debug_mesh->surface_set_material(0, face_material);
if (enabled_edge_lines) {
Array line_mesh_array;
line_mesh_array.resize(Mesh::ARRAY_MAX);
line_mesh_array[Mesh::ARRAY_VERTEX] = line_vertex_array;
debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, line_mesh_array);
debug_mesh->surface_set_material(1, line_material);
}
RS::get_singleton()->instance_set_base(debug_instance, debug_mesh->get_rid());
if (is_inside_tree()) {
RS::get_singleton()->instance_set_scenario(debug_instance, get_world_3d()->get_scenario());
RS::get_singleton()->instance_set_visible(debug_instance, is_visible_in_tree());
}
if (!is_enabled()) {
if (debug_mesh.is_valid()) {
if (debug_mesh->get_surface_count() > 0) {
RS::get_singleton()->instance_set_surface_material(debug_instance, 0, NavigationServer::get_singleton_mut()->get_debug_navigation_geometry_face_disabled_material()->get_rid());
}
if (debug_mesh->get_surface_count() > 1) {
RS::get_singleton()->instance_set_surface_material(debug_instance, 1, NavigationServer::get_singleton_mut()->get_debug_navigation_geometry_edge_disabled_material()->get_rid());
}
}
} else {
if (debug_mesh.is_valid()) {
if (debug_mesh->get_surface_count() > 0) {
RS::get_singleton()->instance_set_surface_material(debug_instance, 0, RID());
}
if (debug_mesh->get_surface_count() > 1) {
RS::get_singleton()->instance_set_surface_material(debug_instance, 1, RID());
}
}
}
}
#endif // DEBUG_ENABLED
#ifdef DEBUG_ENABLED
void NavigationMeshInstance::_update_debug_edge_connections_mesh() {
if (!NavigationServer::get_singleton()->get_debug_enabled()) {
if (debug_edge_connections_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
}
return;
}
if (!is_inside_tree()) {
return;
}
if (!navmesh.is_valid()) {
if (debug_edge_connections_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
}
return;
}
if (!debug_edge_connections_instance.is_valid()) {
debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create();
}
if (!debug_edge_connections_mesh.is_valid()) {
debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
}
debug_edge_connections_mesh->clear_surfaces();
float edge_connection_margin = NavigationServer::get_singleton()->map_get_edge_connection_margin(get_world_3d()->get_navigation_map());
float half_edge_connection_margin = edge_connection_margin * 0.5;
int connections_count = NavigationServer::get_singleton()->region_get_connections_count(region);
if (connections_count == 0) {
RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
return;
}
Vector<Vector3> vertex_array;
for (int i = 0; i < connections_count; i++) {
Vector3 connection_pathway_start = NavigationServer::get_singleton()->region_get_connection_pathway_start(region, i);
Vector3 connection_pathway_end = NavigationServer::get_singleton()->region_get_connection_pathway_end(region, i);
Vector3 direction_start_end = connection_pathway_start.direction_to(connection_pathway_end);
Vector3 direction_end_start = connection_pathway_end.direction_to(connection_pathway_start);
Vector3 start_right_dir = direction_start_end.cross(Vector3(0, 1, 0));
Vector3 start_left_dir = -start_right_dir;
Vector3 end_right_dir = direction_end_start.cross(Vector3(0, 1, 0));
Vector3 end_left_dir = -end_right_dir;
Vector3 left_start_pos = connection_pathway_start + (start_left_dir * half_edge_connection_margin);
Vector3 right_start_pos = connection_pathway_start + (start_right_dir * half_edge_connection_margin);
Vector3 left_end_pos = connection_pathway_end + (end_right_dir * half_edge_connection_margin);
Vector3 right_end_pos = connection_pathway_end + (end_left_dir * half_edge_connection_margin);
vertex_array.push_back(right_end_pos);
vertex_array.push_back(left_start_pos);
vertex_array.push_back(right_start_pos);
vertex_array.push_back(left_end_pos);
vertex_array.push_back(right_end_pos);
vertex_array.push_back(right_start_pos);
}
if (vertex_array.size() == 0) {
return;
}
Ref<SpatialMaterial> edge_connections_material = NavigationServer::get_singleton_mut()->get_debug_navigation_edge_connections_material();
Array mesh_array;
mesh_array.resize(Mesh::ARRAY_MAX);
mesh_array[Mesh::ARRAY_VERTEX] = vertex_array;
debug_edge_connections_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, mesh_array);
debug_edge_connections_mesh->surface_set_material(0, edge_connections_material);
RS::get_singleton()->instance_set_base(debug_edge_connections_instance, debug_edge_connections_mesh->get_rid());
RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, is_visible_in_tree());
if (is_inside_tree()) {
RS::get_singleton()->instance_set_scenario(debug_edge_connections_instance, get_world_3d()->get_scenario());
}
bool enable_edge_connections = NavigationServer::get_singleton()->get_debug_navigation_enable_edge_connections();
if (!enable_edge_connections) {
RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
}
}
#endif // DEBUG_ENABLED

View File

@ -50,9 +50,20 @@ class NavigationMeshInstance : public Spatial {
uint32_t navigation_layers = 1; uint32_t navigation_layers = 1;
Navigation *navigation = nullptr; Navigation *navigation = nullptr;
Node *debug_view = nullptr;
Thread bake_thread; Thread bake_thread;
#ifdef DEBUG_ENABLED
RID debug_instance;
RID debug_edge_connections_instance;
Ref<ArrayMesh> debug_mesh;
Ref<ArrayMesh> debug_edge_connections_mesh;
private:
void _update_debug_mesh();
void _update_debug_edge_connections_mesh();
void _navigation_map_changed(RID p_map);
#endif // DEBUG_ENABLED
protected: protected:
void _notification(int p_what); void _notification(int p_what);
static void _bind_methods(); static void _bind_methods();
@ -80,6 +91,7 @@ public:
/// sets the new navigation mesh and emits a signal /// sets the new navigation mesh and emits a signal
void bake_navigation_mesh(bool p_on_thread); void bake_navigation_mesh(bool p_on_thread);
void _bake_finished(Ref<NavigationMesh> p_nav_mesh); void _bake_finished(Ref<NavigationMesh> p_nav_mesh);
void _navigation_changed();
String get_configuration_warning() const; String get_configuration_warning() const;

View File

@ -30,6 +30,8 @@
#include "navigation_mesh.h" #include "navigation_mesh.h"
#include "servers/navigation_server.h"
void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) { void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) {
ERR_FAIL_COND(p_mesh.is_null()); ERR_FAIL_COND(p_mesh.is_null());
@ -420,6 +422,101 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() {
return debug_mesh; return debug_mesh;
} }
#ifdef DEBUG_ENABLED
Ref<ArrayMesh> NavigationMesh::_get_debug_mesh() {
if (debug_mesh.is_valid()) {
// Blocks further updates for now, code below is intended for dynamic updates e.g. when settings change.
return debug_mesh;
}
if (!debug_mesh.is_valid()) {
debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
} else {
debug_mesh->clear_surfaces();
}
if (vertices.size() == 0) {
return debug_mesh;
}
int polygon_count = get_polygon_count();
if (polygon_count < 1) {
// no face, no play
return debug_mesh;
}
// build geometry face surface
Vector<Vector3> face_vertex_array;
face_vertex_array.resize(polygon_count * 3);
for (int i = 0; i < polygon_count; i++) {
Vector<int> polygon = get_polygon(i);
face_vertex_array.push_back(vertices[polygon[0]]);
face_vertex_array.push_back(vertices[polygon[1]]);
face_vertex_array.push_back(vertices[polygon[2]]);
}
Array face_mesh_array;
face_mesh_array.resize(Mesh::ARRAY_MAX);
face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array;
// if enabled add vertex colors to colorize each face individually
bool enabled_geometry_face_random_color = NavigationServer::get_singleton()->get_debug_navigation_enable_geometry_face_random_color();
if (enabled_geometry_face_random_color) {
Color debug_navigation_geometry_face_color = NavigationServer::get_singleton()->get_debug_navigation_geometry_face_color();
Color polygon_color = debug_navigation_geometry_face_color;
Vector<Color> face_color_array;
face_color_array.resize(polygon_count * 3);
for (int i = 0; i < polygon_count; i++) {
polygon_color = debug_navigation_geometry_face_color * (Color(Math::randf(), Math::randf(), Math::randf()));
Vector<int> polygon = get_polygon(i);
face_color_array.push_back(polygon_color);
face_color_array.push_back(polygon_color);
face_color_array.push_back(polygon_color);
}
face_mesh_array[Mesh::ARRAY_COLOR] = face_color_array;
}
debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array);
Ref<SpatialMaterial> debug_geometry_face_material = NavigationServer::get_singleton_mut()->get_debug_navigation_geometry_face_material();
debug_mesh->surface_set_material(debug_mesh->get_surface_count(), debug_geometry_face_material);
// if enabled build geometry edge line surface
bool enabled_edge_lines = NavigationServer::get_singleton()->get_debug_navigation_enable_edge_lines();
if (enabled_edge_lines) {
Vector<Vector3> line_vertex_array;
line_vertex_array.resize(polygon_count * 6);
for (int i = 0; i < polygon_count; i++) {
Vector<int> polygon = get_polygon(i);
line_vertex_array.push_back(vertices[polygon[0]]);
line_vertex_array.push_back(vertices[polygon[1]]);
line_vertex_array.push_back(vertices[polygon[1]]);
line_vertex_array.push_back(vertices[polygon[2]]);
line_vertex_array.push_back(vertices[polygon[2]]);
line_vertex_array.push_back(vertices[polygon[0]]);
}
Array line_mesh_array;
line_mesh_array.resize(Mesh::ARRAY_MAX);
line_mesh_array[Mesh::ARRAY_VERTEX] = line_vertex_array;
debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, line_mesh_array);
Ref<SpatialMaterial> debug_geometry_edge_material = NavigationServer::get_singleton_mut()->get_debug_navigation_geometry_edge_material();
debug_mesh->surface_set_material(debug_mesh->get_surface_count() - 1, debug_geometry_edge_material);
}
return debug_mesh;
}
#endif
void NavigationMesh::_bind_methods() { void NavigationMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_sample_partition_type", "sample_partition_type"), &NavigationMesh::set_sample_partition_type); ClassDB::bind_method(D_METHOD("set_sample_partition_type", "sample_partition_type"), &NavigationMesh::set_sample_partition_type);
ClassDB::bind_method(D_METHOD("get_sample_partition_type"), &NavigationMesh::get_sample_partition_type); ClassDB::bind_method(D_METHOD("get_sample_partition_type"), &NavigationMesh::get_sample_partition_type);

View File

@ -197,6 +197,10 @@ public:
Ref<Mesh> get_debug_mesh(); Ref<Mesh> get_debug_mesh();
#ifdef DEBUG_ENABLED
Ref<ArrayMesh> _get_debug_mesh();
#endif
NavigationMesh(); NavigationMesh();
}; };

View File

@ -34,6 +34,11 @@
#include "scene/3d/navigation_mesh_instance.h" #include "scene/3d/navigation_mesh_instance.h"
#include "scene/resources/navigation_mesh.h" #include "scene/resources/navigation_mesh.h"
#ifdef DEBUG_ENABLED
#include "core/config/project_settings.h"
#include "scene/resources/material.h"
#endif
NavigationServer *NavigationServer::singleton = nullptr; NavigationServer *NavigationServer::singleton = nullptr;
void NavigationServer::init() { void NavigationServer::init() {
@ -103,6 +108,10 @@ void NavigationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("process", "delta_time"), &NavigationServer::process); ClassDB::bind_method(D_METHOD("process", "delta_time"), &NavigationServer::process);
ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::RID, "map"))); ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::RID, "map")));
#ifdef DEBUG_ENABLED
ADD_SIGNAL(MethodInfo("navigation_debug_changed"));
#endif // DEBUG_ENABLED
} }
const NavigationServer *NavigationServer::get_singleton() { const NavigationServer *NavigationServer::get_singleton() {
@ -116,12 +125,266 @@ NavigationServer *NavigationServer::get_singleton_mut() {
NavigationServer::NavigationServer() { NavigationServer::NavigationServer() {
ERR_FAIL_COND(singleton != nullptr); ERR_FAIL_COND(singleton != nullptr);
singleton = this; singleton = this;
#ifdef DEBUG_ENABLED
_debug_enabled = true;
_debug_dirty = true;
_debug_navigation_edge_connection_color = GLOBAL_DEF("debug/shapes/navigation/edge_connection_color", Color(1.0, 0.0, 1.0, 1.0));
_debug_navigation_geometry_edge_color = GLOBAL_DEF("debug/shapes/navigation/geometry_edge_color", Color(0.5, 1.0, 1.0, 1.0));
_debug_navigation_geometry_face_color = GLOBAL_DEF("debug/shapes/navigation/geometry_face_color", Color(0.5, 1.0, 1.0, 0.4));
_debug_navigation_geometry_edge_disabled_color = GLOBAL_DEF("debug/shapes/navigation/geometry_edge_disabled_color", Color(0.5, 0.5, 0.5, 1.0));
_debug_navigation_geometry_face_disabled_color = GLOBAL_DEF("debug/shapes/navigation/geometry_face_disabled_color", Color(0.5, 0.5, 0.5, 0.4));
_debug_navigation_enable_edge_connections = GLOBAL_DEF("debug/shapes/navigation/enable_edge_connections", true);
_debug_navigation_enable_edge_connections_xray = GLOBAL_DEF("debug/shapes/navigation/enable_edge_connections_xray", true);
_debug_navigation_enable_edge_lines = GLOBAL_DEF("debug/shapes/navigation/enable_edge_lines", true);
_debug_navigation_enable_edge_lines_xray = GLOBAL_DEF("debug/shapes/navigation/enable_edge_lines_xray", true);
_debug_navigation_enable_geometry_face_random_color = GLOBAL_DEF("debug/shapes/navigation/enable_geometry_face_random_color", true);
#endif // DEBUG_ENABLED
} }
NavigationServer::~NavigationServer() { NavigationServer::~NavigationServer() {
singleton = nullptr; singleton = nullptr;
} }
#ifdef DEBUG_ENABLED
void NavigationServer::_emit_navigation_debug_changed_signal() {
if (_debug_dirty) {
_debug_dirty = false;
emit_signal("navigation_debug_changed");
}
}
#endif // DEBUG_ENABLED
#ifdef DEBUG_ENABLED
Ref<SpatialMaterial> NavigationServer::get_debug_navigation_geometry_face_material() {
if (_debug_navigation_geometry_face_material.is_valid()) {
return _debug_navigation_geometry_face_material;
}
bool enabled_geometry_face_random_color = get_debug_navigation_enable_geometry_face_random_color();
Color debug_navigation_geometry_face_color = get_debug_navigation_geometry_face_color();
Ref<SpatialMaterial> face_material = Ref<SpatialMaterial>(memnew(SpatialMaterial));
face_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
face_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
face_material->set_albedo(debug_navigation_geometry_face_color);
if (enabled_geometry_face_random_color) {
face_material->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
face_material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
}
_debug_navigation_geometry_face_material = face_material;
return _debug_navigation_geometry_face_material;
}
Ref<SpatialMaterial> NavigationServer::get_debug_navigation_geometry_edge_material() {
if (_debug_navigation_geometry_edge_material.is_valid()) {
return _debug_navigation_geometry_edge_material;
}
bool enabled_edge_lines_xray = get_debug_navigation_enable_edge_lines_xray();
Color debug_navigation_geometry_edge_color = get_debug_navigation_geometry_edge_color();
Ref<SpatialMaterial> line_material = Ref<SpatialMaterial>(memnew(SpatialMaterial));
line_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
line_material->set_albedo(debug_navigation_geometry_edge_color);
if (enabled_edge_lines_xray) {
line_material->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, true);
}
_debug_navigation_geometry_edge_material = line_material;
return _debug_navigation_geometry_edge_material;
}
Ref<SpatialMaterial> NavigationServer::get_debug_navigation_geometry_face_disabled_material() {
if (_debug_navigation_geometry_face_disabled_material.is_valid()) {
return _debug_navigation_geometry_face_disabled_material;
}
Color debug_navigation_geometry_face_disabled_color = get_debug_navigation_geometry_face_disabled_color();
Ref<SpatialMaterial> face_disabled_material = Ref<SpatialMaterial>(memnew(SpatialMaterial));
face_disabled_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
face_disabled_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
face_disabled_material->set_albedo(debug_navigation_geometry_face_disabled_color);
_debug_navigation_geometry_face_disabled_material = face_disabled_material;
return _debug_navigation_geometry_face_disabled_material;
}
Ref<SpatialMaterial> NavigationServer::get_debug_navigation_geometry_edge_disabled_material() {
if (_debug_navigation_geometry_edge_disabled_material.is_valid()) {
return _debug_navigation_geometry_edge_disabled_material;
}
bool enabled_edge_lines_xray = get_debug_navigation_enable_edge_lines_xray();
Color debug_navigation_geometry_edge_disabled_color = get_debug_navigation_geometry_edge_disabled_color();
Ref<SpatialMaterial> line_disabled_material = Ref<SpatialMaterial>(memnew(SpatialMaterial));
line_disabled_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
line_disabled_material->set_albedo(debug_navigation_geometry_edge_disabled_color);
if (enabled_edge_lines_xray) {
line_disabled_material->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, true);
}
_debug_navigation_geometry_edge_disabled_material = line_disabled_material;
return _debug_navigation_geometry_edge_disabled_material;
}
Ref<SpatialMaterial> NavigationServer::get_debug_navigation_edge_connections_material() {
if (_debug_navigation_edge_connections_material.is_valid()) {
return _debug_navigation_edge_connections_material;
}
bool enabled_edge_connections_xray = get_debug_navigation_enable_edge_connections_xray();
Color debug_navigation_edge_connection_color = get_debug_navigation_edge_connection_color();
Ref<SpatialMaterial> edge_connections_material = Ref<SpatialMaterial>(memnew(SpatialMaterial));
edge_connections_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
edge_connections_material->set_albedo(debug_navigation_edge_connection_color);
if (enabled_edge_connections_xray) {
edge_connections_material->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, true);
}
edge_connections_material->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MAX - 2);
_debug_navigation_edge_connections_material = edge_connections_material;
return _debug_navigation_edge_connections_material;
}
void NavigationServer::set_debug_navigation_edge_connection_color(const Color &p_color) {
_debug_navigation_edge_connection_color = p_color;
if (_debug_navigation_edge_connections_material.is_valid()) {
_debug_navigation_edge_connections_material->set_albedo(_debug_navigation_edge_connection_color);
}
}
Color NavigationServer::get_debug_navigation_edge_connection_color() const {
return _debug_navigation_edge_connection_color;
}
void NavigationServer::set_debug_navigation_geometry_edge_color(const Color &p_color) {
_debug_navigation_geometry_edge_color = p_color;
if (_debug_navigation_geometry_edge_material.is_valid()) {
_debug_navigation_geometry_edge_material->set_albedo(_debug_navigation_geometry_edge_color);
}
}
Color NavigationServer::get_debug_navigation_geometry_edge_color() const {
return _debug_navigation_geometry_edge_color;
}
void NavigationServer::set_debug_navigation_geometry_face_color(const Color &p_color) {
_debug_navigation_geometry_face_color = p_color;
if (_debug_navigation_geometry_face_material.is_valid()) {
_debug_navigation_geometry_face_material->set_albedo(_debug_navigation_geometry_face_color);
}
}
Color NavigationServer::get_debug_navigation_geometry_face_color() const {
return _debug_navigation_geometry_face_color;
}
void NavigationServer::set_debug_navigation_geometry_edge_disabled_color(const Color &p_color) {
_debug_navigation_geometry_edge_disabled_color = p_color;
if (_debug_navigation_geometry_edge_disabled_material.is_valid()) {
_debug_navigation_geometry_edge_disabled_material->set_albedo(_debug_navigation_geometry_edge_disabled_color);
}
}
Color NavigationServer::get_debug_navigation_geometry_edge_disabled_color() const {
return _debug_navigation_geometry_edge_disabled_color;
}
void NavigationServer::set_debug_navigation_geometry_face_disabled_color(const Color &p_color) {
_debug_navigation_geometry_face_disabled_color = p_color;
if (_debug_navigation_geometry_face_disabled_material.is_valid()) {
_debug_navigation_geometry_face_disabled_material->set_albedo(_debug_navigation_geometry_face_disabled_color);
}
}
Color NavigationServer::get_debug_navigation_geometry_face_disabled_color() const {
return _debug_navigation_geometry_face_disabled_color;
}
void NavigationServer::set_debug_navigation_enable_edge_connections(const bool p_value) {
_debug_navigation_enable_edge_connections = p_value;
_debug_dirty = true;
call_deferred("_emit_navigation_debug_changed_signal");
}
bool NavigationServer::get_debug_navigation_enable_edge_connections() const {
return _debug_navigation_enable_edge_connections;
}
void NavigationServer::set_debug_navigation_enable_edge_connections_xray(const bool p_value) {
_debug_navigation_enable_edge_connections_xray = p_value;
if (_debug_navigation_edge_connections_material.is_valid()) {
_debug_navigation_edge_connections_material->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, _debug_navigation_enable_edge_connections_xray);
}
}
bool NavigationServer::get_debug_navigation_enable_edge_connections_xray() const {
return _debug_navigation_enable_edge_connections_xray;
}
void NavigationServer::set_debug_navigation_enable_edge_lines(const bool p_value) {
_debug_navigation_enable_edge_lines = p_value;
_debug_dirty = true;
call_deferred("_emit_navigation_debug_changed_signal");
}
bool NavigationServer::get_debug_navigation_enable_edge_lines() const {
return _debug_navigation_enable_edge_lines;
}
void NavigationServer::set_debug_navigation_enable_edge_lines_xray(const bool p_value) {
_debug_navigation_enable_edge_lines_xray = p_value;
if (_debug_navigation_geometry_edge_material.is_valid()) {
_debug_navigation_geometry_edge_material->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, _debug_navigation_enable_edge_lines_xray);
}
}
bool NavigationServer::get_debug_navigation_enable_edge_lines_xray() const {
return _debug_navigation_enable_edge_lines_xray;
}
void NavigationServer::set_debug_navigation_enable_geometry_face_random_color(const bool p_value) {
_debug_navigation_enable_geometry_face_random_color = p_value;
_debug_dirty = true;
call_deferred("_emit_navigation_debug_changed_signal");
}
bool NavigationServer::get_debug_navigation_enable_geometry_face_random_color() const {
return _debug_navigation_enable_geometry_face_random_color;
}
void NavigationServer::set_debug_enabled(bool p_enabled) {
if (_debug_enabled != p_enabled) {
_debug_dirty = true;
}
_debug_enabled = p_enabled;
if (_debug_dirty) {
call_deferred("_emit_navigation_debug_changed_signal");
}
}
bool NavigationServer::get_debug_enabled() const {
return _debug_enabled;
}
#endif // DEBUG_ENABLED
Vector<NavigationServerManager::ClassInfo> NavigationServerManager::navigation_servers; Vector<NavigationServerManager::ClassInfo> NavigationServerManager::navigation_servers;
int NavigationServerManager::default_server_id = -1; int NavigationServerManager::default_server_id = -1;
int NavigationServerManager::default_server_priority = -1; int NavigationServerManager::default_server_priority = -1;
@ -181,4 +444,4 @@ NavigationServer *NavigationServerManager::new_server(const String &p_name) {
} else { } else {
return navigation_servers[id].create_callback(); return navigation_servers[id].create_callback();
} }
} }

View File

@ -36,6 +36,7 @@
#include "core/object/reference.h" #include "core/object/reference.h"
class NavigationMesh; class NavigationMesh;
class SpatialMaterial;
/// This server uses the concept of internal mutability. /// This server uses the concept of internal mutability.
/// All the constant functions can be called in multithread because internally /// All the constant functions can be called in multithread because internally
@ -217,6 +218,72 @@ public:
NavigationServer(); NavigationServer();
virtual ~NavigationServer(); virtual ~NavigationServer();
#ifdef DEBUG_ENABLED
void set_debug_enabled(bool p_enabled);
bool get_debug_enabled() const;
void set_debug_navigation_edge_connection_color(const Color &p_color);
Color get_debug_navigation_edge_connection_color() const;
void set_debug_navigation_geometry_edge_color(const Color &p_color);
Color get_debug_navigation_geometry_edge_color() const;
void set_debug_navigation_geometry_face_color(const Color &p_color);
Color get_debug_navigation_geometry_face_color() const;
void set_debug_navigation_geometry_edge_disabled_color(const Color &p_color);
Color get_debug_navigation_geometry_edge_disabled_color() const;
void set_debug_navigation_geometry_face_disabled_color(const Color &p_color);
Color get_debug_navigation_geometry_face_disabled_color() const;
void set_debug_navigation_enable_edge_connections(const bool p_value);
bool get_debug_navigation_enable_edge_connections() const;
void set_debug_navigation_enable_edge_connections_xray(const bool p_value);
bool get_debug_navigation_enable_edge_connections_xray() const;
void set_debug_navigation_enable_edge_lines(const bool p_value);
bool get_debug_navigation_enable_edge_lines() const;
void set_debug_navigation_enable_edge_lines_xray(const bool p_value);
bool get_debug_navigation_enable_edge_lines_xray() const;
void set_debug_navigation_enable_geometry_face_random_color(const bool p_value);
bool get_debug_navigation_enable_geometry_face_random_color() const;
Ref<SpatialMaterial> get_debug_navigation_geometry_face_material();
Ref<SpatialMaterial> get_debug_navigation_geometry_edge_material();
Ref<SpatialMaterial> get_debug_navigation_geometry_face_disabled_material();
Ref<SpatialMaterial> get_debug_navigation_geometry_edge_disabled_material();
Ref<SpatialMaterial> get_debug_navigation_edge_connections_material();
void _emit_navigation_debug_changed_signal();
protected:
bool _debug_enabled;
bool _debug_dirty;
Color _debug_navigation_edge_connection_color;
Color _debug_navigation_geometry_edge_color;
Color _debug_navigation_geometry_face_color;
Color _debug_navigation_geometry_edge_disabled_color;
Color _debug_navigation_geometry_face_disabled_color;
bool _debug_navigation_enable_edge_connections;
bool _debug_navigation_enable_edge_connections_xray;
bool _debug_navigation_enable_edge_lines;
bool _debug_navigation_enable_edge_lines_xray;
bool _debug_navigation_enable_geometry_face_random_color;
Ref<SpatialMaterial> _debug_navigation_geometry_edge_material;
Ref<SpatialMaterial> _debug_navigation_geometry_face_material;
Ref<SpatialMaterial> _debug_navigation_geometry_edge_disabled_material;
Ref<SpatialMaterial> _debug_navigation_geometry_face_disabled_material;
Ref<SpatialMaterial> _debug_navigation_edge_connections_material;
#endif // DEBUG_ENABLED
}; };
typedef NavigationServer *(*CreateNavigationServerCallback)(); typedef NavigationServer *(*CreateNavigationServerCallback)();