summaryrefslogtreecommitdiff
path: root/scene/3d/navigation_agent_3d.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/3d/navigation_agent_3d.cpp')
-rw-r--r--scene/3d/navigation_agent_3d.cpp515
1 files changed, 428 insertions, 87 deletions
diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp
index 39068fe83c..16f357194e 100644
--- a/scene/3d/navigation_agent_3d.cpp
+++ b/scene/3d/navigation_agent_3d.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* navigation_agent_3d.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
+/**************************************************************************/
+/* navigation_agent_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "navigation_agent_3d.h"
@@ -74,29 +74,36 @@ void NavigationAgent3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationAgent3D::set_navigation_layer_value);
ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationAgent3D::get_navigation_layer_value);
+ ClassDB::bind_method(D_METHOD("set_path_metadata_flags", "flags"), &NavigationAgent3D::set_path_metadata_flags);
+ ClassDB::bind_method(D_METHOD("get_path_metadata_flags"), &NavigationAgent3D::get_path_metadata_flags);
+
ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationAgent3D::set_navigation_map);
ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationAgent3D::get_navigation_map);
- ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent3D::set_target_location);
- ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent3D::get_target_location);
- ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent3D::get_next_location);
+ ClassDB::bind_method(D_METHOD("set_target_position", "position"), &NavigationAgent3D::set_target_position);
+ ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationAgent3D::get_target_position);
+
+ ClassDB::bind_method(D_METHOD("get_next_path_position"), &NavigationAgent3D::get_next_path_position);
ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent3D::distance_to_target);
ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent3D::set_velocity);
- ClassDB::bind_method(D_METHOD("get_nav_path"), &NavigationAgent3D::get_nav_path);
- ClassDB::bind_method(D_METHOD("get_nav_path_index"), &NavigationAgent3D::get_nav_path_index);
+ ClassDB::bind_method(D_METHOD("get_current_navigation_result"), &NavigationAgent3D::get_current_navigation_result);
+ ClassDB::bind_method(D_METHOD("get_current_navigation_path"), &NavigationAgent3D::get_current_navigation_path);
+ ClassDB::bind_method(D_METHOD("get_current_navigation_path_index"), &NavigationAgent3D::get_current_navigation_path_index);
ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent3D::is_target_reached);
ClassDB::bind_method(D_METHOD("is_target_reachable"), &NavigationAgent3D::is_target_reachable);
ClassDB::bind_method(D_METHOD("is_navigation_finished"), &NavigationAgent3D::is_navigation_finished);
- ClassDB::bind_method(D_METHOD("get_final_location"), &NavigationAgent3D::get_final_location);
+ ClassDB::bind_method(D_METHOD("get_final_position"), &NavigationAgent3D::get_final_position);
ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent3D::_avoidance_done);
ADD_GROUP("Pathfinding", "");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_target_position", "get_target_position");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:m"), "set_path_desired_distance", "get_path_desired_distance");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:m"), "set_target_desired_distance", "get_target_desired_distance");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_height_offset", PROPERTY_HINT_RANGE, "-100.0,100,0.01,suffix:m"), "set_agent_height_offset", "get_agent_height_offset");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1,suffix:m"), "set_path_max_distance", "get_path_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "path_metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_path_metadata_flags", "get_path_metadata_flags");
ADD_GROUP("Avoidance", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
@@ -109,17 +116,40 @@ void NavigationAgent3D::_bind_methods() {
ADD_SIGNAL(MethodInfo("path_changed"));
ADD_SIGNAL(MethodInfo("target_reached"));
+ ADD_SIGNAL(MethodInfo("waypoint_reached", PropertyInfo(Variant::DICTIONARY, "details")));
+ ADD_SIGNAL(MethodInfo("link_reached", PropertyInfo(Variant::DICTIONARY, "details")));
ADD_SIGNAL(MethodInfo("navigation_finished"));
ADD_SIGNAL(MethodInfo("velocity_computed", PropertyInfo(Variant::VECTOR3, "safe_velocity")));
+
+ ClassDB::bind_method(D_METHOD("set_debug_enabled", "enabled"), &NavigationAgent3D::set_debug_enabled);
+ ClassDB::bind_method(D_METHOD("get_debug_enabled"), &NavigationAgent3D::get_debug_enabled);
+ ClassDB::bind_method(D_METHOD("set_debug_use_custom", "enabled"), &NavigationAgent3D::set_debug_use_custom);
+ ClassDB::bind_method(D_METHOD("get_debug_use_custom"), &NavigationAgent3D::get_debug_use_custom);
+ ClassDB::bind_method(D_METHOD("set_debug_path_custom_color", "color"), &NavigationAgent3D::set_debug_path_custom_color);
+ ClassDB::bind_method(D_METHOD("get_debug_path_custom_color"), &NavigationAgent3D::get_debug_path_custom_color);
+ ClassDB::bind_method(D_METHOD("set_debug_path_custom_point_size", "point_size"), &NavigationAgent3D::set_debug_path_custom_point_size);
+ ClassDB::bind_method(D_METHOD("get_debug_path_custom_point_size"), &NavigationAgent3D::get_debug_path_custom_point_size);
+
+ ADD_GROUP("Debug", "debug_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_enabled"), "set_debug_enabled", "get_debug_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_use_custom"), "set_debug_use_custom", "get_debug_use_custom");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_path_custom_color"), "set_debug_path_custom_color", "get_debug_path_custom_color");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "debug_path_custom_point_size", PROPERTY_HINT_RANGE, "1,50,1,suffix:px"), "set_debug_path_custom_point_size", "get_debug_path_custom_point_size");
}
void NavigationAgent3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_POST_ENTER_TREE: {
// need to use POST_ENTER_TREE cause with normal ENTER_TREE not all required Nodes are ready.
- // cannot use READY as ready does not get called if Node is readded to SceneTree
+ // cannot use READY as ready does not get called if Node is re-added to SceneTree
set_agent_parent(get_parent());
set_physics_process_internal(true);
+
+#ifdef DEBUG_ENABLED
+ if (NavigationServer3D::get_singleton()->get_debug_enabled()) {
+ debug_path_dirty = true;
+ }
+#endif // DEBUG_ENABLED
} break;
case NOTIFICATION_PARENTED: {
@@ -142,6 +172,12 @@ void NavigationAgent3D::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
set_agent_parent(nullptr);
set_physics_process_internal(false);
+
+#ifdef DEBUG_ENABLED
+ if (debug_path_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(debug_path_instance, false);
+ }
+#endif // DEBUG_ENABLED
} break;
case NOTIFICATION_PAUSED: {
@@ -165,26 +201,31 @@ void NavigationAgent3D::_notification(int p_what) {
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- if (agent_parent) {
+ if (agent_parent && target_position_submitted) {
if (avoidance_enabled) {
// agent_position on NavigationServer is avoidance only and has nothing to do with pathfinding
// no point in flooding NavigationServer queue with agent position updates that get send to the void if avoidance is not used
- NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin);
+ NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_position());
}
_check_distance_to_target();
}
+#ifdef DEBUG_ENABLED
+ if (debug_path_dirty) {
+ _update_debug_path();
+ }
+#endif // DEBUG_ENABLED
} break;
}
}
NavigationAgent3D::NavigationAgent3D() {
agent = NavigationServer3D::get_singleton()->agent_create();
- set_neighbor_distance(50.0);
- set_max_neighbors(10);
- set_time_horizon(5.0);
- set_radius(1.0);
- set_max_speed(10.0);
- set_ignore_y(true);
+ NavigationServer3D::get_singleton()->agent_set_neighbor_distance(agent, neighbor_distance);
+ NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
+ NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, time_horizon);
+ NavigationServer3D::get_singleton()->agent_set_radius(agent, radius);
+ NavigationServer3D::get_singleton()->agent_set_max_speed(agent, max_speed);
+ NavigationServer3D::get_singleton()->agent_set_ignore_y(agent, ignore_y);
// Preallocate query and result objects to improve performance.
navigation_query = Ref<NavigationPathQueryParameters3D>();
@@ -192,19 +233,41 @@ NavigationAgent3D::NavigationAgent3D() {
navigation_result = Ref<NavigationPathQueryResult3D>();
navigation_result.instantiate();
+
+#ifdef DEBUG_ENABLED
+ NavigationServer3D::get_singleton()->connect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationAgent3D::_navigation_debug_changed));
+#endif // DEBUG_ENABLED
}
NavigationAgent3D::~NavigationAgent3D() {
+ ERR_FAIL_NULL(NavigationServer3D::get_singleton());
NavigationServer3D::get_singleton()->free(agent);
agent = RID(); // Pointless
+
+#ifdef DEBUG_ENABLED
+ NavigationServer3D::get_singleton()->disconnect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationAgent3D::_navigation_debug_changed));
+
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ if (debug_path_instance.is_valid()) {
+ RenderingServer::get_singleton()->free(debug_path_instance);
+ }
+ if (debug_path_mesh.is_valid()) {
+ RenderingServer::get_singleton()->free(debug_path_mesh->get_rid());
+ }
+#endif // DEBUG_ENABLED
}
void NavigationAgent3D::set_avoidance_enabled(bool p_enabled) {
+ if (avoidance_enabled == p_enabled) {
+ return;
+ }
+
avoidance_enabled = p_enabled;
+
if (avoidance_enabled) {
- NavigationServer3D::get_singleton()->agent_set_callback(agent, this, "_avoidance_done");
+ NavigationServer3D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent3D::_avoidance_done));
} else {
- NavigationServer3D::get_singleton()->agent_set_callback(agent, nullptr, "_avoidance_done");
+ NavigationServer3D::get_singleton()->agent_set_callback(agent, Callable());
}
}
@@ -213,8 +276,13 @@ bool NavigationAgent3D::get_avoidance_enabled() const {
}
void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) {
+ if (agent_parent == p_agent_parent) {
+ return;
+ }
+
// remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map
- NavigationServer3D::get_singleton()->agent_set_callback(agent, nullptr, "_avoidance_done");
+ NavigationServer3D::get_singleton()->agent_set_callback(agent, Callable());
+
if (Object::cast_to<Node3D>(p_agent_parent) != nullptr) {
// place agent on navigation map first or else the RVO agent callback creation fails silently later
agent_parent = Object::cast_to<Node3D>(p_agent_parent);
@@ -223,8 +291,11 @@ void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) {
} else {
NavigationServer3D::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_3d()->get_navigation_map());
}
+
// create new avoidance callback if enabled
- set_avoidance_enabled(avoidance_enabled);
+ if (avoidance_enabled) {
+ NavigationServer3D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent3D::_avoidance_done));
+ }
} else {
agent_parent = nullptr;
NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID());
@@ -232,11 +303,13 @@ void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) {
}
void NavigationAgent3D::set_navigation_layers(uint32_t p_navigation_layers) {
- bool navigation_layers_changed = navigation_layers != p_navigation_layers;
- navigation_layers = p_navigation_layers;
- if (navigation_layers_changed) {
- _request_repath();
+ if (navigation_layers == p_navigation_layers) {
+ return;
}
+
+ navigation_layers = p_navigation_layers;
+
+ _request_repath();
}
uint32_t NavigationAgent3D::get_navigation_layers() const {
@@ -261,8 +334,21 @@ bool NavigationAgent3D::get_navigation_layer_value(int p_layer_number) const {
return get_navigation_layers() & (1 << (p_layer_number - 1));
}
+void NavigationAgent3D::set_path_metadata_flags(BitField<NavigationPathQueryParameters3D::PathMetadataFlags> p_path_metadata_flags) {
+ if (path_metadata_flags == p_path_metadata_flags) {
+ return;
+ }
+
+ path_metadata_flags = p_path_metadata_flags;
+}
+
void NavigationAgent3D::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
+ }
+
map_override = p_navigation_map;
+
NavigationServer3D::get_singleton()->agent_set_map(agent, map_override);
_request_repath();
}
@@ -276,84 +362,131 @@ RID NavigationAgent3D::get_navigation_map() const {
return RID();
}
-void NavigationAgent3D::set_path_desired_distance(real_t p_dd) {
- path_desired_distance = p_dd;
+void NavigationAgent3D::set_path_desired_distance(real_t p_path_desired_distance) {
+ if (Math::is_equal_approx(path_desired_distance, p_path_desired_distance)) {
+ return;
+ }
+
+ path_desired_distance = p_path_desired_distance;
}
-void NavigationAgent3D::set_target_desired_distance(real_t p_dd) {
- target_desired_distance = p_dd;
+void NavigationAgent3D::set_target_desired_distance(real_t p_target_desired_distance) {
+ if (Math::is_equal_approx(target_desired_distance, p_target_desired_distance)) {
+ return;
+ }
+
+ target_desired_distance = p_target_desired_distance;
}
void NavigationAgent3D::set_radius(real_t p_radius) {
+ if (Math::is_equal_approx(radius, p_radius)) {
+ return;
+ }
+
radius = p_radius;
+
NavigationServer3D::get_singleton()->agent_set_radius(agent, radius);
}
-void NavigationAgent3D::set_agent_height_offset(real_t p_hh) {
- navigation_height_offset = p_hh;
+void NavigationAgent3D::set_agent_height_offset(real_t p_agent_height_offset) {
+ if (Math::is_equal_approx(navigation_height_offset, p_agent_height_offset)) {
+ return;
+ }
+
+ navigation_height_offset = p_agent_height_offset;
}
void NavigationAgent3D::set_ignore_y(bool p_ignore_y) {
+ if (ignore_y == p_ignore_y) {
+ return;
+ }
+
ignore_y = p_ignore_y;
+
NavigationServer3D::get_singleton()->agent_set_ignore_y(agent, ignore_y);
}
void NavigationAgent3D::set_neighbor_distance(real_t p_distance) {
+ if (Math::is_equal_approx(neighbor_distance, p_distance)) {
+ return;
+ }
+
neighbor_distance = p_distance;
+
NavigationServer3D::get_singleton()->agent_set_neighbor_distance(agent, neighbor_distance);
}
void NavigationAgent3D::set_max_neighbors(int p_count) {
+ if (max_neighbors == p_count) {
+ return;
+ }
+
max_neighbors = p_count;
+
NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
}
void NavigationAgent3D::set_time_horizon(real_t p_time) {
+ if (Math::is_equal_approx(time_horizon, p_time)) {
+ return;
+ }
+
time_horizon = p_time;
+
NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, time_horizon);
}
void NavigationAgent3D::set_max_speed(real_t p_max_speed) {
+ if (Math::is_equal_approx(max_speed, p_max_speed)) {
+ return;
+ }
+
max_speed = p_max_speed;
+
NavigationServer3D::get_singleton()->agent_set_max_speed(agent, max_speed);
}
-void NavigationAgent3D::set_path_max_distance(real_t p_pmd) {
- path_max_distance = p_pmd;
+void NavigationAgent3D::set_path_max_distance(real_t p_path_max_distance) {
+ if (Math::is_equal_approx(path_max_distance, p_path_max_distance)) {
+ return;
+ }
+
+ path_max_distance = p_path_max_distance;
}
real_t NavigationAgent3D::get_path_max_distance() {
return path_max_distance;
}
-void NavigationAgent3D::set_target_location(Vector3 p_location) {
- target_location = p_location;
+void NavigationAgent3D::set_target_position(Vector3 p_position) {
+ // Intentionally not checking for equality of the parameter, as we want to update the path even if the target position is the same in case the world changed.
+ // Revisit later when the navigation server can update the path without requesting a new path.
+
+ target_position = p_position;
+ target_position_submitted = true;
+
_request_repath();
}
-Vector3 NavigationAgent3D::get_target_location() const {
- return target_location;
+Vector3 NavigationAgent3D::get_target_position() const {
+ return target_position;
}
-Vector3 NavigationAgent3D::get_next_location() {
+Vector3 NavigationAgent3D::get_next_path_position() {
update_navigation();
const Vector<Vector3> &navigation_path = navigation_result->get_path();
if (navigation_path.size() == 0) {
ERR_FAIL_COND_V_MSG(agent_parent == nullptr, Vector3(), "The agent has no parent.");
- return agent_parent->get_global_transform().origin;
+ return agent_parent->get_global_position();
} else {
- return navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0);
+ return navigation_path[navigation_path_index] - Vector3(0, navigation_height_offset, 0);
}
}
-const Vector<Vector3> &NavigationAgent3D::get_nav_path() const {
- return navigation_result->get_path();
-}
-
real_t NavigationAgent3D::distance_to_target() const {
ERR_FAIL_COND_V_MSG(agent_parent == nullptr, 0.0, "The agent has no parent.");
- return agent_parent->get_global_transform().origin.distance_to(target_location);
+ return agent_parent->get_global_position().distance_to(target_position);
}
bool NavigationAgent3D::is_target_reached() const {
@@ -361,7 +494,7 @@ bool NavigationAgent3D::is_target_reached() const {
}
bool NavigationAgent3D::is_target_reachable() {
- return target_desired_distance >= get_final_location().distance_to(target_location);
+ return target_desired_distance >= get_final_position().distance_to(target_position);
}
bool NavigationAgent3D::is_navigation_finished() {
@@ -369,7 +502,7 @@ bool NavigationAgent3D::is_navigation_finished() {
return navigation_finished;
}
-Vector3 NavigationAgent3D::get_final_location() {
+Vector3 NavigationAgent3D::get_final_position() {
update_navigation();
const Vector<Vector3> &navigation_path = navigation_result->get_path();
@@ -380,10 +513,15 @@ Vector3 NavigationAgent3D::get_final_location() {
}
void NavigationAgent3D::set_velocity(Vector3 p_velocity) {
+ // Intentionally not checking for equality of the parameter.
+ // We need to always submit the velocity to the navigation server, even when it is the same, in order to run avoidance every frame.
+ // Revisit later when the navigation server can update avoidance without users resubmitting the velocity.
+
target_velocity = p_velocity;
+ velocity_submitted = true;
+
NavigationServer3D::get_singleton()->agent_set_target_velocity(agent, target_velocity);
NavigationServer3D::get_singleton()->agent_set_velocity(agent, prev_safe_velocity);
- velocity_submitted = true;
}
void NavigationAgent3D::_avoidance_done(Vector3 p_new_velocity) {
@@ -415,13 +553,16 @@ void NavigationAgent3D::update_navigation() {
if (!agent_parent->is_inside_tree()) {
return;
}
+ if (!target_position_submitted) {
+ return;
+ }
if (update_frame_id == Engine::get_singleton()->get_physics_frames()) {
return;
}
update_frame_id = Engine::get_singleton()->get_physics_frames();
- Vector3 origin = agent_parent->get_global_transform().origin;
+ Vector3 origin = agent_parent->get_global_position();
bool reload_path = false;
@@ -431,12 +572,12 @@ void NavigationAgent3D::update_navigation() {
reload_path = true;
} else {
// Check if too far from the navigation path
- if (nav_path_index > 0) {
+ if (navigation_path_index > 0) {
const Vector<Vector3> &navigation_path = navigation_result->get_path();
Vector3 segment[2];
- segment[0] = navigation_path[nav_path_index - 1];
- segment[1] = navigation_path[nav_path_index];
+ segment[0] = navigation_path[navigation_path_index - 1];
+ segment[1] = navigation_path[navigation_path_index];
segment[0].y -= navigation_height_offset;
segment[1].y -= navigation_height_offset;
Vector3 p = Geometry3D::get_closest_point_to_segment(origin, segment);
@@ -449,8 +590,9 @@ void NavigationAgent3D::update_navigation() {
if (reload_path) {
navigation_query->set_start_position(origin);
- navigation_query->set_target_position(target_location);
+ navigation_query->set_target_position(target_position);
navigation_query->set_navigation_layers(navigation_layers);
+ navigation_query->set_metadata_flags(path_metadata_flags);
if (map_override.is_valid()) {
navigation_query->set_map(map_override);
@@ -459,8 +601,11 @@ void NavigationAgent3D::update_navigation() {
}
NavigationServer3D::get_singleton()->query_path(navigation_query, navigation_result);
+#ifdef DEBUG_ENABLED
+ debug_path_dirty = true;
+#endif // DEBUG_ENABLED
navigation_finished = false;
- nav_path_index = 0;
+ navigation_path_index = 0;
emit_signal(SNAME("path_changed"));
}
@@ -470,14 +615,59 @@ void NavigationAgent3D::update_navigation() {
// Check if we can advance the navigation path
if (navigation_finished == false) {
- // Advances to the next far away location.
+ // Advances to the next far away position.
const Vector<Vector3> &navigation_path = navigation_result->get_path();
- while (origin.distance_to(navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0)) < path_desired_distance) {
- nav_path_index += 1;
- if (nav_path_index == navigation_path.size()) {
+ const Vector<int32_t> &navigation_path_types = navigation_result->get_path_types();
+ const TypedArray<RID> &navigation_path_rids = navigation_result->get_path_rids();
+ const Vector<int64_t> &navigation_path_owners = navigation_result->get_path_owner_ids();
+
+ while (origin.distance_to(navigation_path[navigation_path_index] - Vector3(0, navigation_height_offset, 0)) < path_desired_distance) {
+ Dictionary details;
+
+ const Vector3 waypoint = navigation_path[navigation_path_index];
+ details[SNAME("position")] = waypoint;
+
+ int waypoint_type = -1;
+ if (path_metadata_flags.has_flag(NavigationPathQueryParameters3D::PathMetadataFlags::PATH_METADATA_INCLUDE_TYPES)) {
+ const NavigationPathQueryResult3D::PathSegmentType type = NavigationPathQueryResult3D::PathSegmentType(navigation_path_types[navigation_path_index]);
+
+ details[SNAME("type")] = type;
+ waypoint_type = type;
+ }
+
+ if (path_metadata_flags.has_flag(NavigationPathQueryParameters3D::PathMetadataFlags::PATH_METADATA_INCLUDE_RIDS)) {
+ details[SNAME("rid")] = navigation_path_rids[navigation_path_index];
+ }
+
+ if (path_metadata_flags.has_flag(NavigationPathQueryParameters3D::PathMetadataFlags::PATH_METADATA_INCLUDE_OWNERS)) {
+ const ObjectID waypoint_owner_id = ObjectID(navigation_path_owners[navigation_path_index]);
+
+ // Get a reference to the owning object.
+ Object *owner = nullptr;
+ if (waypoint_owner_id.is_valid()) {
+ owner = ObjectDB::get_instance(waypoint_owner_id);
+ }
+
+ details[SNAME("owner")] = owner;
+ }
+
+ // Emit a signal for the waypoint
+ emit_signal(SNAME("waypoint_reached"), details);
+
+ // Emit a signal if we've reached a navigation link
+ if (waypoint_type == NavigationPathQueryResult3D::PATH_SEGMENT_TYPE_LINK) {
+ emit_signal(SNAME("link_reached"), details);
+ }
+
+ // Move to the next waypoint on the list
+ navigation_path_index += 1;
+
+ // Check to see if we've finished our route
+ if (navigation_path_index == navigation_path.size()) {
_check_distance_to_target();
- nav_path_index -= 1;
+ navigation_path_index -= 1;
navigation_finished = true;
+ target_position_submitted = false;
emit_signal(SNAME("navigation_finished"));
break;
}
@@ -495,8 +685,159 @@ void NavigationAgent3D::_request_repath() {
void NavigationAgent3D::_check_distance_to_target() {
if (!target_reached) {
if (distance_to_target() < target_desired_distance) {
- emit_signal(SNAME("target_reached"));
target_reached = true;
+ emit_signal(SNAME("target_reached"));
+ }
+ }
+}
+
+////////DEBUG////////////////////////////////////////////////////////////
+
+void NavigationAgent3D::set_debug_enabled(bool p_enabled) {
+#ifdef DEBUG_ENABLED
+ if (debug_enabled == p_enabled) {
+ return;
+ }
+
+ debug_enabled = p_enabled;
+ debug_path_dirty = true;
+#endif // DEBUG_ENABLED
+}
+
+bool NavigationAgent3D::get_debug_enabled() const {
+ return debug_enabled;
+}
+
+void NavigationAgent3D::set_debug_use_custom(bool p_enabled) {
+#ifdef DEBUG_ENABLED
+ if (debug_use_custom == p_enabled) {
+ return;
+ }
+
+ debug_use_custom = p_enabled;
+ debug_path_dirty = true;
+#endif // DEBUG_ENABLED
+}
+
+bool NavigationAgent3D::get_debug_use_custom() const {
+ return debug_use_custom;
+}
+
+void NavigationAgent3D::set_debug_path_custom_color(Color p_color) {
+#ifdef DEBUG_ENABLED
+ if (debug_path_custom_color == p_color) {
+ return;
+ }
+
+ debug_path_custom_color = p_color;
+ debug_path_dirty = true;
+#endif // DEBUG_ENABLED
+}
+
+Color NavigationAgent3D::get_debug_path_custom_color() const {
+ return debug_path_custom_color;
+}
+
+void NavigationAgent3D::set_debug_path_custom_point_size(float p_point_size) {
+#ifdef DEBUG_ENABLED
+ if (Math::is_equal_approx(debug_path_custom_point_size, p_point_size)) {
+ return;
+ }
+
+ debug_path_custom_point_size = p_point_size;
+ debug_path_dirty = true;
+#endif // DEBUG_ENABLED
+}
+
+float NavigationAgent3D::get_debug_path_custom_point_size() const {
+ return debug_path_custom_point_size;
+}
+
+#ifdef DEBUG_ENABLED
+void NavigationAgent3D::_navigation_debug_changed() {
+ debug_path_dirty = true;
+}
+
+void NavigationAgent3D::_update_debug_path() {
+ if (!debug_path_dirty) {
+ return;
+ }
+ debug_path_dirty = false;
+
+ if (!debug_path_instance.is_valid()) {
+ debug_path_instance = RenderingServer::get_singleton()->instance_create();
+ }
+
+ if (!debug_path_mesh.is_valid()) {
+ debug_path_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ }
+
+ debug_path_mesh->clear_surfaces();
+
+ if (!(debug_enabled && NavigationServer3D::get_singleton()->get_debug_navigation_enable_agent_paths())) {
+ return;
+ }
+
+ if (!(agent_parent && agent_parent->is_inside_tree())) {
+ return;
+ }
+
+ const Vector<Vector3> &navigation_path = navigation_result->get_path();
+
+ if (navigation_path.size() <= 1) {
+ return;
+ }
+
+ Vector<Vector3> debug_path_lines_vertex_array;
+
+ for (int i = 0; i < navigation_path.size() - 1; i++) {
+ debug_path_lines_vertex_array.push_back(navigation_path[i]);
+ debug_path_lines_vertex_array.push_back(navigation_path[i + 1]);
+ }
+
+ Array debug_path_lines_mesh_array;
+ debug_path_lines_mesh_array.resize(Mesh::ARRAY_MAX);
+ debug_path_lines_mesh_array[Mesh::ARRAY_VERTEX] = debug_path_lines_vertex_array;
+
+ debug_path_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, debug_path_lines_mesh_array);
+
+ Ref<StandardMaterial3D> debug_agent_path_line_material = NavigationServer3D::get_singleton()->get_debug_navigation_agent_path_line_material();
+ if (debug_use_custom) {
+ if (!debug_agent_path_line_custom_material.is_valid()) {
+ debug_agent_path_line_custom_material = debug_agent_path_line_material->duplicate();
}
+ debug_agent_path_line_custom_material->set_albedo(debug_path_custom_color);
+ debug_path_mesh->surface_set_material(0, debug_agent_path_line_custom_material);
+ } else {
+ debug_path_mesh->surface_set_material(0, debug_agent_path_line_material);
+ }
+
+ Vector<Vector3> debug_path_points_vertex_array;
+
+ for (int i = 0; i < navigation_path.size(); i++) {
+ debug_path_points_vertex_array.push_back(navigation_path[i]);
}
+
+ Array debug_path_points_mesh_array;
+ debug_path_points_mesh_array.resize(Mesh::ARRAY_MAX);
+ debug_path_points_mesh_array[Mesh::ARRAY_VERTEX] = debug_path_lines_vertex_array;
+
+ debug_path_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, debug_path_points_mesh_array);
+
+ Ref<StandardMaterial3D> debug_agent_path_point_material = NavigationServer3D::get_singleton()->get_debug_navigation_agent_path_point_material();
+ if (debug_use_custom) {
+ if (!debug_agent_path_point_custom_material.is_valid()) {
+ debug_agent_path_point_custom_material = debug_agent_path_point_material->duplicate();
+ }
+ debug_agent_path_point_custom_material->set_albedo(debug_path_custom_color);
+ debug_agent_path_point_custom_material->set_point_size(debug_path_custom_point_size);
+ debug_path_mesh->surface_set_material(1, debug_agent_path_point_custom_material);
+ } else {
+ debug_path_mesh->surface_set_material(1, debug_agent_path_point_material);
+ }
+
+ RS::get_singleton()->instance_set_base(debug_path_instance, debug_path_mesh->get_rid());
+ RS::get_singleton()->instance_set_scenario(debug_path_instance, agent_parent->get_world_3d()->get_scenario());
+ RS::get_singleton()->instance_set_visible(debug_path_instance, agent_parent->is_visible_in_tree());
}
+#endif // DEBUG_ENABLED