2020-01-10 19:22:34 +08:00
/*************************************************************************/
2020-03-27 05:49:16 +08:00
/* navigation_obstacle_3d.cpp */
2020-01-10 19:22:34 +08:00
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2022-01-04 04:27:34 +08:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2020-01-10 19:22:34 +08:00
/* */
/* 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. */
/*************************************************************************/
2020-03-27 05:49:16 +08:00
# include "navigation_obstacle_3d.h"
2020-01-10 19:22:34 +08:00
2020-03-27 05:49:16 +08:00
# include "scene/3d/collision_shape_3d.h"
# include "scene/3d/physics_body_3d.h"
2020-03-28 02:21:27 +08:00
# include "servers/navigation_server_3d.h"
2020-01-10 19:22:34 +08:00
2020-03-27 05:49:16 +08:00
void NavigationObstacle3D : : _bind_methods ( ) {
2022-05-15 05:33:09 +08:00
ClassDB : : bind_method ( D_METHOD ( " get_rid " ) , & NavigationObstacle3D : : get_rid ) ;
2021-10-22 04:25:06 +08:00
ClassDB : : bind_method ( D_METHOD ( " set_estimate_radius " , " estimate_radius " ) , & NavigationObstacle3D : : set_estimate_radius ) ;
ClassDB : : bind_method ( D_METHOD ( " is_radius_estimated " ) , & NavigationObstacle3D : : is_radius_estimated ) ;
ClassDB : : bind_method ( D_METHOD ( " set_radius " , " radius " ) , & NavigationObstacle3D : : set_radius ) ;
ClassDB : : bind_method ( D_METHOD ( " get_radius " ) , & NavigationObstacle3D : : get_radius ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " estimate_radius " ) , " set_estimate_radius " , " is_radius_estimated " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " radius " , PROPERTY_HINT_RANGE , " 0.01,100,0.01 " ) , " set_radius " , " get_radius " ) ;
}
void NavigationObstacle3D : : _validate_property ( PropertyInfo & p_property ) const {
if ( p_property . name = = " radius " ) {
if ( estimate_radius ) {
2021-11-04 06:06:17 +08:00
p_property . usage = PROPERTY_USAGE_NO_EDITOR ;
2021-10-22 04:25:06 +08:00
}
}
2020-01-10 19:22:34 +08:00
}
2020-03-27 05:49:16 +08:00
void NavigationObstacle3D : : _notification ( int p_what ) {
2020-01-10 19:22:34 +08:00
switch ( p_what ) {
2022-01-08 02:55:22 +08:00
case NOTIFICATION_ENTER_TREE : {
2021-10-22 04:25:06 +08:00
parent_node3d = Object : : cast_to < Node3D > ( get_parent ( ) ) ;
2022-01-08 02:55:22 +08:00
reevaluate_agent_radius ( ) ;
2021-10-22 04:25:06 +08:00
if ( parent_node3d ! = nullptr ) {
// place agent on navigation map first or else the RVO agent callback creation fails silently later
NavigationServer3D : : get_singleton ( ) - > agent_set_map ( get_rid ( ) , parent_node3d - > get_world_3d ( ) - > get_navigation_map ( ) ) ;
}
2020-01-10 19:22:34 +08:00
set_physics_process_internal ( true ) ;
} break ;
2022-02-16 01:06:48 +08:00
2020-01-10 19:22:34 +08:00
case NOTIFICATION_EXIT_TREE : {
2021-10-22 04:25:06 +08:00
parent_node3d = nullptr ;
2020-01-10 19:22:34 +08:00
set_physics_process_internal ( false ) ;
} break ;
2022-02-16 01:06:48 +08:00
2021-03-08 16:47:18 +08:00
case NOTIFICATION_PARENTED : {
parent_node3d = Object : : cast_to < Node3D > ( get_parent ( ) ) ;
2021-10-22 04:25:06 +08:00
reevaluate_agent_radius ( ) ;
2021-03-08 16:47:18 +08:00
} break ;
2022-02-16 01:06:48 +08:00
2021-03-08 16:47:18 +08:00
case NOTIFICATION_UNPARENTED : {
parent_node3d = nullptr ;
} break ;
2022-02-16 01:06:48 +08:00
2020-01-10 19:22:34 +08:00
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS : {
2021-03-08 16:47:18 +08:00
if ( parent_node3d ) {
NavigationServer3D : : get_singleton ( ) - > agent_set_position ( agent , parent_node3d - > get_global_transform ( ) . origin ) ;
PhysicsBody3D * rigid = Object : : cast_to < PhysicsBody3D > ( get_parent ( ) ) ;
if ( rigid ) {
Vector3 v = rigid - > get_linear_velocity ( ) ;
NavigationServer3D : : get_singleton ( ) - > agent_set_velocity ( agent , v ) ;
NavigationServer3D : : get_singleton ( ) - > agent_set_target_velocity ( agent , v ) ;
}
2020-01-10 19:22:34 +08:00
}
} break ;
}
}
2020-05-12 23:01:17 +08:00
NavigationObstacle3D : : NavigationObstacle3D ( ) {
2020-03-28 02:21:27 +08:00
agent = NavigationServer3D : : get_singleton ( ) - > agent_create ( ) ;
2022-01-08 02:55:22 +08:00
initialize_agent ( ) ;
2020-01-10 19:22:34 +08:00
}
2020-03-27 05:49:16 +08:00
NavigationObstacle3D : : ~ NavigationObstacle3D ( ) {
2020-03-28 02:21:27 +08:00
NavigationServer3D : : get_singleton ( ) - > free ( agent ) ;
2020-01-10 19:22:34 +08:00
agent = RID ( ) ; // Pointless
}
2020-10-29 18:01:28 +08:00
TypedArray < String > NavigationObstacle3D : : get_configuration_warnings ( ) const {
TypedArray < String > warnings = Node : : get_configuration_warnings ( ) ;
2020-05-15 04:59:27 +08:00
2020-10-29 18:01:28 +08:00
if ( ! Object : : cast_to < Node3D > ( get_parent ( ) ) ) {
2022-05-15 05:33:09 +08:00
warnings . push_back ( RTR ( " The NavigationObstacle3D only serves to provide collision avoidance to a Node3D inheriting parent object. " ) ) ;
}
if ( Object : : cast_to < StaticBody3D > ( get_parent ( ) ) ) {
warnings . push_back ( RTR ( " The NavigationObstacle3D is intended for constantly moving bodies like CharacterBody3D or RigidDynamicBody3D as it creates only an RVO avoidance radius and does not follow scene geometry exactly. "
" \n Not constantly moving or complete static objects should be (re)baked to a NavigationMesh so agents can not only avoid them but also move along those objects outline at high detail " ) ) ;
2020-01-10 19:22:34 +08:00
}
2020-10-29 18:01:28 +08:00
return warnings ;
2020-01-10 19:22:34 +08:00
}
2021-10-22 04:25:06 +08:00
void NavigationObstacle3D : : initialize_agent ( ) {
NavigationServer3D : : get_singleton ( ) - > agent_set_neighbor_dist ( agent , 0.0 ) ;
NavigationServer3D : : get_singleton ( ) - > agent_set_max_neighbors ( agent , 0 ) ;
NavigationServer3D : : get_singleton ( ) - > agent_set_time_horizon ( agent , 0.0 ) ;
NavigationServer3D : : get_singleton ( ) - > agent_set_max_speed ( agent , 0.0 ) ;
}
void NavigationObstacle3D : : reevaluate_agent_radius ( ) {
if ( ! estimate_radius ) {
NavigationServer3D : : get_singleton ( ) - > agent_set_radius ( agent , radius ) ;
2022-01-08 02:55:22 +08:00
} else if ( parent_node3d & & parent_node3d - > is_inside_tree ( ) ) {
2021-10-22 04:25:06 +08:00
NavigationServer3D : : get_singleton ( ) - > agent_set_radius ( agent , estimate_agent_radius ( ) ) ;
}
}
real_t NavigationObstacle3D : : estimate_agent_radius ( ) const {
2021-03-08 16:47:18 +08:00
if ( parent_node3d ) {
// Estimate the radius of this physics body
real_t radius = 0.0 ;
for ( int i ( 0 ) ; i < parent_node3d - > get_child_count ( ) ; i + + ) {
// For each collision shape
CollisionShape3D * cs = Object : : cast_to < CollisionShape3D > ( parent_node3d - > get_child ( i ) ) ;
if ( cs ) {
// Take the distance between the Body center to the shape center
real_t r = cs - > get_transform ( ) . origin . length ( ) ;
if ( cs - > get_shape ( ) . is_valid ( ) ) {
// and add the enclosing shape radius
r + = cs - > get_shape ( ) - > get_enclosing_radius ( ) ;
}
Vector3 s = cs - > get_global_transform ( ) . basis . get_scale ( ) ;
r * = MAX ( s . x , MAX ( s . y , s . z ) ) ;
// Takes the biggest radius
radius = MAX ( radius , r ) ;
2020-01-10 19:22:34 +08:00
}
}
2021-03-08 16:47:18 +08:00
Vector3 s = parent_node3d - > get_global_transform ( ) . basis . get_scale ( ) ;
2020-01-10 19:22:34 +08:00
radius * = MAX ( s . x , MAX ( s . y , s . z ) ) ;
2021-10-22 04:25:06 +08:00
if ( radius > 0.0 ) {
return radius ;
2021-03-08 16:47:18 +08:00
}
}
2021-10-22 04:25:06 +08:00
return 1.0 ; // Never a 0 radius
}
void NavigationObstacle3D : : set_estimate_radius ( bool p_estimate_radius ) {
estimate_radius = p_estimate_radius ;
notify_property_list_changed ( ) ;
reevaluate_agent_radius ( ) ;
}
void NavigationObstacle3D : : set_radius ( real_t p_radius ) {
ERR_FAIL_COND_MSG ( p_radius < = 0.0 , " Radius must be greater than 0. " ) ;
radius = p_radius ;
reevaluate_agent_radius ( ) ;
2020-01-10 19:22:34 +08:00
}