2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* path_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. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2020-03-26 22:49:16 +01:00
# include "path_3d.h"
2017-08-19 01:02:56 +02:00
2022-06-15 23:24:06 +02:00
Path3D : : Path3D ( ) {
SceneTree * st = SceneTree : : get_singleton ( ) ;
if ( st & & st - > is_debugging_paths_hint ( ) ) {
debug_instance = RS : : get_singleton ( ) - > instance_create ( ) ;
set_notify_transform ( true ) ;
_update_debug_mesh ( ) ;
}
}
Path3D : : ~ Path3D ( ) {
if ( debug_instance . is_valid ( ) ) {
2023-05-09 15:50:48 +02:00
ERR_FAIL_NULL ( RenderingServer : : get_singleton ( ) ) ;
2022-06-15 23:24:06 +02:00
RS : : get_singleton ( ) - > free ( debug_instance ) ;
}
if ( debug_mesh . is_valid ( ) ) {
2023-05-09 15:50:48 +02:00
ERR_FAIL_NULL ( RenderingServer : : get_singleton ( ) ) ;
2022-06-15 23:24:06 +02:00
RS : : get_singleton ( ) - > free ( debug_mesh - > get_rid ( ) ) ;
}
}
2023-11-28 03:48:15 +01:00
void Path3D : : set_update_callback ( Callable p_callback ) {
update_callback = p_callback ;
}
2022-06-15 23:24:06 +02:00
void Path3D : : _notification ( int p_what ) {
switch ( p_what ) {
case NOTIFICATION_ENTER_TREE : {
SceneTree * st = SceneTree : : get_singleton ( ) ;
if ( st & & st - > is_debugging_paths_hint ( ) ) {
_update_debug_mesh ( ) ;
}
} break ;
case NOTIFICATION_EXIT_TREE : {
SceneTree * st = SceneTree : : get_singleton ( ) ;
if ( st & & st - > is_debugging_paths_hint ( ) ) {
RS : : get_singleton ( ) - > instance_set_visible ( debug_instance , false ) ;
}
} break ;
case NOTIFICATION_TRANSFORM_CHANGED : {
2023-11-28 03:48:15 +01:00
if ( is_inside_tree ( ) ) {
if ( debug_instance . is_valid ( ) ) {
RS : : get_singleton ( ) - > instance_set_transform ( debug_instance , get_global_transform ( ) ) ;
}
update_callback . call ( ) ;
2022-06-15 23:24:06 +02:00
}
} break ;
}
}
void Path3D : : _update_debug_mesh ( ) {
SceneTree * st = SceneTree : : get_singleton ( ) ;
if ( ! ( st & & st - > is_debugging_paths_hint ( ) ) ) {
return ;
}
if ( ! debug_mesh . is_valid ( ) ) {
debug_mesh = Ref < ArrayMesh > ( memnew ( ArrayMesh ) ) ;
}
if ( ! ( curve . is_valid ( ) ) ) {
RS : : get_singleton ( ) - > instance_set_visible ( debug_instance , false ) ;
return ;
}
if ( curve - > get_point_count ( ) < 2 ) {
RS : : get_singleton ( ) - > instance_set_visible ( debug_instance , false ) ;
return ;
}
2023-10-20 22:10:39 +02:00
real_t interval = 0.1 ;
const real_t length = curve - > get_baked_length ( ) ;
2022-06-15 23:24:06 +02:00
2023-10-20 22:10:39 +02:00
if ( length < = CMP_EPSILON ) {
RS : : get_singleton ( ) - > instance_set_visible ( debug_instance , false ) ;
return ;
2022-06-15 23:24:06 +02:00
}
2023-10-20 22:10:39 +02:00
const int sample_count = int ( length / interval ) + 2 ;
interval = length / ( sample_count - 1 ) ;
Vector < Vector3 > ribbon ;
ribbon . resize ( sample_count ) ;
Vector3 * ribbon_ptr = ribbon . ptrw ( ) ;
Vector < Vector3 > bones ;
bones . resize ( sample_count * 4 ) ;
Vector3 * bones_ptr = bones . ptrw ( ) ;
for ( int i = 0 ; i < sample_count ; i + + ) {
const Transform3D r = curve - > sample_baked_with_rotation ( i * interval , true , true ) ;
const Vector3 p1 = r . origin ;
const Vector3 side = r . basis . get_column ( 0 ) ;
const Vector3 up = r . basis . get_column ( 1 ) ;
const Vector3 forward = r . basis . get_column ( 2 ) ;
// Path3D as a ribbon.
ribbon_ptr [ i ] = p1 ;
// Fish Bone.
const Vector3 p_left = p1 + ( side + forward - up * 0.3 ) * 0.06 ;
const Vector3 p_right = p1 + ( - side + forward - up * 0.3 ) * 0.06 ;
const int bone_idx = i * 4 ;
bones_ptr [ bone_idx ] = p1 ;
bones_ptr [ bone_idx + 1 ] = p_left ;
bones_ptr [ bone_idx + 2 ] = p1 ;
bones_ptr [ bone_idx + 3 ] = p_right ;
}
Array ribbon_array ;
ribbon_array . resize ( Mesh : : ARRAY_MAX ) ;
ribbon_array [ Mesh : : ARRAY_VERTEX ] = ribbon ;
Array bone_array ;
bone_array . resize ( Mesh : : ARRAY_MAX ) ;
bone_array [ Mesh : : ARRAY_VERTEX ] = bones ;
2022-06-15 23:24:06 +02:00
debug_mesh - > clear_surfaces ( ) ;
2023-10-20 22:10:39 +02:00
debug_mesh - > add_surface_from_arrays ( Mesh : : PRIMITIVE_LINE_STRIP , ribbon_array ) ;
debug_mesh - > add_surface_from_arrays ( Mesh : : PRIMITIVE_LINES , bone_array ) ;
2022-06-15 23:24:06 +02:00
RS : : get_singleton ( ) - > instance_set_base ( debug_instance , debug_mesh - > get_rid ( ) ) ;
RS : : get_singleton ( ) - > mesh_surface_set_material ( debug_mesh - > get_rid ( ) , 0 , st - > get_debug_paths_material ( ) - > get_rid ( ) ) ;
2023-10-20 22:10:39 +02:00
RS : : get_singleton ( ) - > mesh_surface_set_material ( debug_mesh - > get_rid ( ) , 1 , st - > get_debug_paths_material ( ) - > get_rid ( ) ) ;
2022-06-15 23:24:06 +02:00
if ( is_inside_tree ( ) ) {
RS : : get_singleton ( ) - > instance_set_scenario ( debug_instance , get_world_3d ( ) - > get_scenario ( ) ) ;
RS : : get_singleton ( ) - > instance_set_transform ( debug_instance , get_global_transform ( ) ) ;
RS : : get_singleton ( ) - > instance_set_visible ( debug_instance , is_visible_in_tree ( ) ) ;
}
}
2020-03-26 22:49:16 +01:00
void Path3D : : _curve_changed ( ) {
2020-05-14 16:41:43 +02:00
if ( is_inside_tree ( ) & & Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2021-06-23 16:49:50 +02:00
update_gizmos ( ) ;
2020-05-14 16:41:43 +02:00
}
2018-04-28 02:52:15 +02:00
if ( is_inside_tree ( ) ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " curve_changed " ) ) ;
2018-04-28 02:52:15 +02:00
}
2018-10-17 14:45:51 +02:00
2023-08-04 03:05:25 +02:00
// Update the configuration warnings of all children of type PathFollow
// previously used for PathFollowOriented (now enforced orientation is done in PathFollow). Also trigger transform update on PathFollow3Ds in deferred mode.
2018-10-17 14:45:51 +02:00
if ( is_inside_tree ( ) ) {
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
2020-03-26 22:49:16 +01:00
PathFollow3D * child = Object : : cast_to < PathFollow3D > ( get_child ( i ) ) ;
2018-10-17 14:45:51 +02:00
if ( child ) {
2020-10-29 11:01:28 +01:00
child - > update_configuration_warnings ( ) ;
2023-08-04 03:05:25 +02:00
child - > update_transform ( ) ;
2018-10-17 14:45:51 +02:00
}
}
}
2022-06-15 23:24:06 +02:00
SceneTree * st = SceneTree : : get_singleton ( ) ;
if ( st & & st - > is_debugging_paths_hint ( ) ) {
_update_debug_mesh ( ) ;
}
2014-11-06 01:20:42 +01:00
}
2020-03-26 22:49:16 +01:00
void Path3D : : set_curve ( const Ref < Curve3D > & p_curve ) {
2014-11-06 01:20:42 +01:00
if ( curve . is_valid ( ) ) {
2023-07-03 21:29:37 +02:00
curve - > disconnect_changed ( callable_mp ( this , & Path3D : : _curve_changed ) ) ;
2014-11-06 01:20:42 +01:00
}
2017-03-05 16:44:50 +01:00
curve = p_curve ;
2014-11-06 01:20:42 +01:00
if ( curve . is_valid ( ) ) {
2023-07-03 21:29:37 +02:00
curve - > connect_changed ( callable_mp ( this , & Path3D : : _curve_changed ) ) ;
2014-11-06 01:20:42 +01:00
}
_curve_changed ( ) ;
}
2020-03-26 22:49:16 +01:00
Ref < Curve3D > Path3D : : get_curve ( ) const {
2014-11-06 01:20:42 +01:00
return curve ;
}
2020-03-26 22:49:16 +01:00
void Path3D : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " set_curve " , " curve " ) , & Path3D : : set_curve ) ;
ClassDB : : bind_method ( D_METHOD ( " get_curve " ) , & Path3D : : get_curve ) ;
2014-11-06 01:20:42 +01:00
2020-06-12 13:16:14 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " curve " , PROPERTY_HINT_RESOURCE_TYPE , " Curve3D " , PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT ) , " set_curve " , " get_curve " ) ;
2018-04-28 02:52:15 +02:00
ADD_SIGNAL ( MethodInfo ( " curve_changed " ) ) ;
2014-11-06 01:20:42 +01:00
}
2023-08-04 03:05:25 +02:00
// Update transform, in deferred mode by default to avoid superfluity.
void PathFollow3D : : update_transform ( bool p_immediate ) {
transform_dirty = true ;
if ( p_immediate ) {
_update_transform ( ) ;
} else {
callable_mp ( this , & PathFollow3D : : _update_transform ) . call_deferred ( ) ;
}
}
// Update transform immediately .
void PathFollow3D : : _update_transform ( ) {
if ( ! transform_dirty ) {
return ;
}
transform_dirty = false ;
2014-11-06 01:20:42 +01:00
2020-05-14 16:41:43 +02:00
if ( ! path ) {
2014-11-06 01:20:42 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-11-06 01:20:42 +01:00
2017-03-05 16:44:50 +01:00
Ref < Curve3D > c = path - > get_curve ( ) ;
2020-05-14 16:41:43 +02:00
if ( ! c . is_valid ( ) ) {
2014-11-06 01:20:42 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-11-06 01:20:42 +01:00
2021-01-30 01:55:54 +01:00
real_t bl = c - > get_baked_length ( ) ;
2019-02-24 00:20:54 +01:00
if ( bl = = 0.0 ) {
return ;
}
2019-01-11 20:03:12 +01:00
2022-08-07 12:29:12 +02:00
Transform3D t ;
2017-05-23 00:06:42 +02:00
2022-08-07 12:29:12 +02:00
if ( rotation_mode = = ROTATION_NONE ) {
Vector3 pos = c - > sample_baked ( progress , cubic ) ;
2019-01-11 20:03:12 +01:00
t . origin = pos ;
2022-08-07 12:29:12 +02:00
} else {
t = c - > sample_baked_with_rotation ( progress , cubic , false ) ;
2023-08-04 03:05:25 +02:00
Vector3 tangent = - t . basis . get_column ( 2 ) ; // Retain tangent for applying tilt.
t = PathFollow3D : : correct_posture ( t , rotation_mode ) ;
// Switch Z+ and Z- if necessary.
2023-02-07 18:48:33 +01:00
if ( use_model_front ) {
t . basis * = Basis : : from_scale ( Vector3 ( - 1.0 , 1.0 , - 1.0 ) ) ;
}
2014-11-06 01:20:42 +01:00
2023-08-04 03:05:25 +02:00
// Apply tilt *after* correct_posture().
2022-08-07 12:29:12 +02:00
if ( tilt_enabled ) {
const real_t tilt = c - > sample_baked_tilt ( progress ) ;
2014-11-06 01:20:42 +01:00
2023-08-04 03:05:25 +02:00
const Basis twist ( tangent , tilt ) ;
2022-08-07 12:29:12 +02:00
t . basis = twist * t . basis ;
2017-05-23 00:06:42 +02:00
}
2014-11-06 01:20:42 +01:00
}
2023-08-04 03:05:25 +02:00
// Apply offset and scale.
2022-08-07 12:29:12 +02:00
Vector3 scale = get_transform ( ) . basis . get_scale ( ) ;
t . translate_local ( Vector3 ( h_offset , v_offset , 0 ) ) ;
t . basis . scale_local ( scale ) ;
2014-11-06 01:20:42 +01:00
set_transform ( t ) ;
}
2020-03-26 22:49:16 +01:00
void PathFollow3D : : _notification ( int p_what ) {
2017-03-05 16:44:50 +01:00
switch ( p_what ) {
2014-11-06 01:20:42 +01:00
case NOTIFICATION_ENTER_TREE : {
2017-03-05 16:44:50 +01:00
Node * parent = get_parent ( ) ;
2014-11-06 01:20:42 +01:00
if ( parent ) {
2020-03-26 22:49:16 +01:00
path = Object : : cast_to < Path3D > ( parent ) ;
2017-09-06 23:50:18 +02:00
if ( path ) {
2023-08-04 03:05:25 +02:00
update_transform ( ) ;
2014-11-06 01:20:42 +01:00
}
}
} break ;
2022-02-15 18:06:48 +01:00
2014-11-06 01:20:42 +01:00
case NOTIFICATION_EXIT_TREE : {
2020-04-02 01:20:12 +02:00
path = nullptr ;
2014-11-06 01:20:42 +01:00
} break ;
}
}
2023-02-07 18:48:33 +01:00
void PathFollow3D : : set_cubic_interpolation_enabled ( bool p_enabled ) {
cubic = p_enabled ;
2014-11-06 01:20:42 +01:00
}
2023-02-07 18:48:33 +01:00
bool PathFollow3D : : is_cubic_interpolation_enabled ( ) const {
2014-11-06 01:20:42 +01:00
return cubic ;
}
2022-08-12 22:57:11 +02:00
void PathFollow3D : : _validate_property ( PropertyInfo & p_property ) const {
if ( p_property . name = = " offset " ) {
2021-01-30 01:55:54 +01:00
real_t max = 10000 ;
2020-05-14 16:41:43 +02:00
if ( path & & path - > get_curve ( ) . is_valid ( ) ) {
2018-01-11 23:35:12 +01:00
max = path - > get_curve ( ) - > get_baked_length ( ) ;
2020-05-14 16:41:43 +02:00
}
2018-01-11 23:35:12 +01:00
2022-03-27 19:16:03 +02:00
p_property . hint_string = " 0, " + rtos ( max ) + " ,0.01,or_less,or_greater " ;
2018-01-11 23:35:12 +01:00
}
2014-11-06 01:20:42 +01:00
}
2024-02-17 19:03:21 +01:00
PackedStringArray PathFollow3D : : get_configuration_warnings ( ) const {
2024-06-19 16:40:50 +02:00
PackedStringArray warnings = Node3D : : get_configuration_warnings ( ) ;
2020-05-14 22:59:27 +02:00
2020-10-29 11:01:28 +01:00
if ( is_visible_in_tree ( ) & & is_inside_tree ( ) ) {
if ( ! Object : : cast_to < Path3D > ( get_parent ( ) ) ) {
2022-03-28 15:24:14 +02:00
warnings . push_back ( RTR ( " PathFollow3D only works when set as a child of a Path3D node. " ) ) ;
2020-10-29 11:01:28 +01:00
} else {
2022-09-29 11:53:28 +02:00
Path3D * p = Object : : cast_to < Path3D > ( get_parent ( ) ) ;
if ( p - > get_curve ( ) . is_valid ( ) & & ! p - > get_curve ( ) - > is_up_vector_enabled ( ) & & rotation_mode = = ROTATION_ORIENTED ) {
2022-03-28 15:24:14 +02:00
warnings . push_back ( RTR ( " PathFollow3D's ROTATION_ORIENTED requires \" Up Vector \" to be enabled in its parent Path3D's Curve resource. " ) ) ;
2020-05-14 22:59:27 +02:00
}
2019-01-11 20:03:12 +01:00
}
2018-10-17 14:45:51 +02:00
}
2020-10-29 11:01:28 +01:00
return warnings ;
2018-10-17 14:45:51 +02:00
}
2022-08-07 12:29:12 +02:00
Transform3D PathFollow3D : : correct_posture ( Transform3D p_transform , PathFollow3D : : RotationMode p_rotation_mode ) {
Transform3D t = p_transform ;
// Modify frame according to rotation mode.
if ( p_rotation_mode = = PathFollow3D : : ROTATION_NONE ) {
// Clear rotation.
t . basis = Basis ( ) ;
} else if ( p_rotation_mode = = PathFollow3D : : ROTATION_ORIENTED ) {
2023-08-04 03:05:25 +02:00
Vector3 tangent = - t . basis . get_column ( 2 ) ;
2022-08-07 12:29:12 +02:00
2023-08-04 03:05:25 +02:00
// Y-axis points up by default.
t . basis = Basis : : looking_at ( tangent ) ;
2022-08-07 12:29:12 +02:00
} else {
// Lock some euler axes.
Vector3 euler = t . basis . get_euler_normalized ( EulerOrder : : YXZ ) ;
if ( p_rotation_mode = = PathFollow3D : : ROTATION_Y ) {
// Only Y-axis allowed.
euler [ 0 ] = 0 ;
euler [ 2 ] = 0 ;
} else if ( p_rotation_mode = = PathFollow3D : : ROTATION_XY ) {
// XY allowed.
euler [ 2 ] = 0 ;
}
Basis locked = Basis : : from_euler ( euler , EulerOrder : : YXZ ) ;
t . basis = locked ;
}
return t ;
}
2020-03-26 22:49:16 +01:00
void PathFollow3D : : _bind_methods ( ) {
2022-08-23 23:41:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_progress " , " progress " ) , & PathFollow3D : : set_progress ) ;
ClassDB : : bind_method ( D_METHOD ( " get_progress " ) , & PathFollow3D : : get_progress ) ;
2014-11-06 01:20:42 +01:00
2020-03-26 22:49:16 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_h_offset " , " h_offset " ) , & PathFollow3D : : set_h_offset ) ;
ClassDB : : bind_method ( D_METHOD ( " get_h_offset " ) , & PathFollow3D : : get_h_offset ) ;
2014-11-06 01:20:42 +01:00
2020-03-26 22:49:16 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_v_offset " , " v_offset " ) , & PathFollow3D : : set_v_offset ) ;
ClassDB : : bind_method ( D_METHOD ( " get_v_offset " ) , & PathFollow3D : : get_v_offset ) ;
2014-11-06 01:20:42 +01:00
2022-08-23 23:41:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_progress_ratio " , " ratio " ) , & PathFollow3D : : set_progress_ratio ) ;
ClassDB : : bind_method ( D_METHOD ( " get_progress_ratio " ) , & PathFollow3D : : get_progress_ratio ) ;
2014-11-06 01:20:42 +01:00
2020-03-26 22:49:16 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_rotation_mode " , " rotation_mode " ) , & PathFollow3D : : set_rotation_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_rotation_mode " ) , & PathFollow3D : : get_rotation_mode ) ;
2014-11-06 01:20:42 +01:00
2023-02-07 18:48:33 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_cubic_interpolation " , " enabled " ) , & PathFollow3D : : set_cubic_interpolation_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " get_cubic_interpolation " ) , & PathFollow3D : : is_cubic_interpolation_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " set_use_model_front " , " enabled " ) , & PathFollow3D : : set_use_model_front ) ;
ClassDB : : bind_method ( D_METHOD ( " is_using_model_front " ) , & PathFollow3D : : is_using_model_front ) ;
2014-11-06 01:20:42 +01:00
2020-03-26 22:49:16 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_loop " , " loop " ) , & PathFollow3D : : set_loop ) ;
ClassDB : : bind_method ( D_METHOD ( " has_loop " ) , & PathFollow3D : : has_loop ) ;
2014-11-06 01:20:42 +01:00
2022-08-07 12:29:12 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_tilt_enabled " , " enabled " ) , & PathFollow3D : : set_tilt_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_tilt_enabled " ) , & PathFollow3D : : is_tilt_enabled ) ;
ClassDB : : bind_static_method ( " PathFollow3D " , D_METHOD ( " correct_posture " , " transform " , " rotation_mode " ) , & PathFollow3D : : correct_posture ) ;
2022-03-27 19:16:03 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " progress " , PROPERTY_HINT_RANGE , " 0,10000,0.01,or_less,or_greater,suffix:m " ) , " set_progress " , " get_progress " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " progress_ratio " , PROPERTY_HINT_RANGE , " 0,1,0.0001,or_less,or_greater " , PROPERTY_USAGE_EDITOR ) , " set_progress_ratio " , " get_progress_ratio " ) ;
2021-12-03 01:09:19 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " h_offset " , PROPERTY_HINT_NONE , " suffix:m " ) , " set_h_offset " , " get_h_offset " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " v_offset " , PROPERTY_HINT_NONE , " suffix:m " ) , " set_v_offset " , " get_v_offset " ) ;
2019-01-11 20:03:12 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " rotation_mode " , PROPERTY_HINT_ENUM , " None,Y,XY,XYZ,Oriented " ) , " set_rotation_mode " , " get_rotation_mode " ) ;
2023-02-07 18:48:33 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " use_model_front " ) , " set_use_model_front " , " is_using_model_front " ) ;
2018-01-11 23:35:12 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " cubic_interp " ) , " set_cubic_interpolation " , " get_cubic_interpolation " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " loop " ) , " set_loop " , " has_loop " ) ;
2022-08-07 12:29:12 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " tilt_enabled " ) , " set_tilt_enabled " , " is_tilt_enabled " ) ;
2018-01-11 23:35:12 +01:00
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( ROTATION_NONE ) ;
BIND_ENUM_CONSTANT ( ROTATION_Y ) ;
BIND_ENUM_CONSTANT ( ROTATION_XY ) ;
BIND_ENUM_CONSTANT ( ROTATION_XYZ ) ;
2019-01-11 20:03:12 +01:00
BIND_ENUM_CONSTANT ( ROTATION_ORIENTED ) ;
2014-11-06 01:20:42 +01:00
}
2022-08-23 23:41:41 +02:00
void PathFollow3D : : set_progress ( real_t p_progress ) {
ERR_FAIL_COND ( ! isfinite ( p_progress ) ) ;
progress = p_progress ;
2017-10-21 17:26:55 +02:00
2019-11-30 12:43:34 +01:00
if ( path ) {
2020-04-13 07:42:47 +02:00
if ( path - > get_curve ( ) . is_valid ( ) ) {
2021-01-30 01:55:54 +01:00
real_t path_length = path - > get_curve ( ) - > get_baked_length ( ) ;
2019-11-30 12:43:34 +01:00
2022-04-17 17:26:05 +02:00
if ( loop & & path_length ) {
2022-08-23 23:41:41 +02:00
progress = Math : : fposmod ( progress , path_length ) ;
if ( ! Math : : is_zero_approx ( p_progress ) & & Math : : is_zero_approx ( progress ) ) {
progress = path_length ;
2020-04-13 07:42:47 +02:00
}
2023-06-15 13:49:52 +02:00
} else {
progress = CLAMP ( progress , 0 , path_length ) ;
2019-11-30 12:43:34 +01:00
}
}
2023-08-04 03:05:25 +02:00
update_transform ( ) ;
2019-11-30 12:43:34 +01:00
}
2014-11-06 01:20:42 +01:00
}
2021-01-30 01:55:54 +01:00
void PathFollow3D : : set_h_offset ( real_t p_h_offset ) {
2017-03-05 16:44:50 +01:00
h_offset = p_h_offset ;
2020-05-14 16:41:43 +02:00
if ( path ) {
2023-08-04 03:05:25 +02:00
update_transform ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-11-06 01:20:42 +01:00
}
2021-01-30 01:55:54 +01:00
real_t PathFollow3D : : get_h_offset ( ) const {
2014-11-06 01:20:42 +01:00
return h_offset ;
}
2021-01-30 01:55:54 +01:00
void PathFollow3D : : set_v_offset ( real_t p_v_offset ) {
2017-03-05 16:44:50 +01:00
v_offset = p_v_offset ;
2020-05-14 16:41:43 +02:00
if ( path ) {
2023-08-04 03:05:25 +02:00
update_transform ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-11-06 01:20:42 +01:00
}
2021-01-30 01:55:54 +01:00
real_t PathFollow3D : : get_v_offset ( ) const {
2014-11-06 01:20:42 +01:00
return v_offset ;
}
2022-08-23 23:41:41 +02:00
real_t PathFollow3D : : get_progress ( ) const {
return progress ;
2014-11-06 01:20:42 +01:00
}
2022-08-23 23:41:41 +02:00
void PathFollow3D : : set_progress_ratio ( real_t p_ratio ) {
2024-07-28 10:24:09 +02:00
ERR_FAIL_NULL_MSG ( path , " Can only set progress ratio on a PathFollow3D that is the child of a Path3D which is itself part of the scene tree. " ) ;
ERR_FAIL_COND_MSG ( path - > get_curve ( ) . is_null ( ) , " Can't set progress ratio on a PathFollow3D that does not have a Curve. " ) ;
ERR_FAIL_COND_MSG ( ! path - > get_curve ( ) - > get_baked_length ( ) , " Can't set progress ratio on a PathFollow3D that has a 0 length curve. " ) ;
set_progress ( p_ratio * path - > get_curve ( ) - > get_baked_length ( ) ) ;
2014-11-06 01:20:42 +01:00
}
2022-08-23 23:41:41 +02:00
real_t PathFollow3D : : get_progress_ratio ( ) const {
2020-05-14 16:41:43 +02:00
if ( path & & path - > get_curve ( ) . is_valid ( ) & & path - > get_curve ( ) - > get_baked_length ( ) ) {
2022-08-23 23:41:41 +02:00
return get_progress ( ) / path - > get_curve ( ) - > get_baked_length ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2014-11-06 01:20:42 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2014-11-06 01:20:42 +01:00
}
2020-03-26 22:49:16 +01:00
void PathFollow3D : : set_rotation_mode ( RotationMode p_rotation_mode ) {
2017-03-05 16:44:50 +01:00
rotation_mode = p_rotation_mode ;
2019-01-11 20:03:12 +01:00
2020-10-29 11:01:28 +01:00
update_configuration_warnings ( ) ;
2023-08-04 03:05:25 +02:00
update_transform ( ) ;
2014-11-06 01:20:42 +01:00
}
2020-03-26 22:49:16 +01:00
PathFollow3D : : RotationMode PathFollow3D : : get_rotation_mode ( ) const {
2014-11-06 01:20:42 +01:00
return rotation_mode ;
}
2023-02-07 18:48:33 +01:00
void PathFollow3D : : set_use_model_front ( bool p_use_model_front ) {
use_model_front = p_use_model_front ;
2023-08-04 03:05:25 +02:00
update_transform ( ) ;
2023-02-07 18:48:33 +01:00
}
bool PathFollow3D : : is_using_model_front ( ) const {
return use_model_front ;
}
2020-03-26 22:49:16 +01:00
void PathFollow3D : : set_loop ( bool p_loop ) {
2017-03-05 16:44:50 +01:00
loop = p_loop ;
2023-08-04 03:05:25 +02:00
update_transform ( ) ;
2014-11-06 01:20:42 +01:00
}
2020-03-26 22:49:16 +01:00
bool PathFollow3D : : has_loop ( ) const {
2014-11-06 01:20:42 +01:00
return loop ;
}
2022-08-07 12:29:12 +02:00
2023-02-07 18:48:33 +01:00
void PathFollow3D : : set_tilt_enabled ( bool p_enabled ) {
tilt_enabled = p_enabled ;
2023-08-04 03:05:25 +02:00
update_transform ( ) ;
2022-08-07 12:29:12 +02:00
}
bool PathFollow3D : : is_tilt_enabled ( ) const {
return tilt_enabled ;
}