From 43b184b4093072e600f06c6c92b300280d58495c Mon Sep 17 00:00:00 2001 From: Yaohua Xiong Date: Fri, 4 Aug 2023 09:05:25 +0800 Subject: [PATCH] Fix PathFollow3D update issues - transform is not updated after setting new flags such as `use_model_front` - transform is not updated when the parent Path3D changes - correct_posture() behaves differently when assuming model front - _update_transform() was in immediate mode, could leads to chained calls on scene instantiation. --- scene/3d/path_3d.cpp | 56 ++++++++++++++++++++++++++++++-------------- scene/3d/path_3d.h | 14 ++++++----- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index e38375d339c4..be31a9e4df82 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -165,13 +165,14 @@ void Path3D::_curve_changed() { emit_signal(SNAME("curve_changed")); } - // update the configuration warnings of all children of type PathFollow - // previously used for PathFollowOriented (now enforced orientation is done in PathFollow) + // 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. if (is_inside_tree()) { for (int i = 0; i < get_child_count(); i++) { PathFollow3D *child = Object::cast_to(get_child(i)); if (child) { child->update_configuration_warnings(); + child->update_transform(); } } } @@ -207,9 +208,24 @@ void Path3D::_bind_methods() { ADD_SIGNAL(MethodInfo("curve_changed")); } -////////////// +// 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; -void PathFollow3D::_update_transform(bool p_update_xyz_rot) { if (!path) { return; } @@ -231,23 +247,25 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { t.origin = pos; } else { t = c->sample_baked_with_rotation(progress, cubic, false); + 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. if (use_model_front) { t.basis *= Basis::from_scale(Vector3(-1.0, 1.0, -1.0)); } - Vector3 forward = t.basis.get_column(2); // Retain tangent for applying tilt - t = PathFollow3D::correct_posture(t, rotation_mode); - // Apply tilt *after* correct_posture + // Apply tilt *after* correct_posture(). if (tilt_enabled) { const real_t tilt = c->sample_baked_tilt(progress); - const Basis twist(forward, tilt); + const Basis twist(tangent, tilt); t.basis = twist * t.basis; } } + // Apply offset and scale. Vector3 scale = get_transform().basis.get_scale(); - t.translate_local(Vector3(h_offset, v_offset, 0)); t.basis.scale_local(scale); @@ -261,7 +279,7 @@ void PathFollow3D::_notification(int p_what) { if (parent) { path = Object::cast_to(parent); if (path) { - _update_transform(false); + update_transform(); } } } break; @@ -316,11 +334,10 @@ Transform3D PathFollow3D::correct_posture(Transform3D p_transform, PathFollow3D: // Clear rotation. t.basis = Basis(); } else if (p_rotation_mode == PathFollow3D::ROTATION_ORIENTED) { - // Y-axis always straight up. - Vector3 up(0.0, 1.0, 0.0); - Vector3 forward = t.basis.get_column(2); + Vector3 tangent = -t.basis.get_column(2); - t.basis = Basis::looking_at(-forward, up); + // Y-axis points up by default. + t.basis = Basis::looking_at(tangent); } else { // Lock some euler axes. Vector3 euler = t.basis.get_euler_normalized(EulerOrder::YXZ); @@ -405,14 +422,14 @@ void PathFollow3D::set_progress(real_t p_progress) { } } - _update_transform(); + update_transform(); } } void PathFollow3D::set_h_offset(real_t p_h_offset) { h_offset = p_h_offset; if (path) { - _update_transform(); + update_transform(); } } @@ -423,7 +440,7 @@ real_t PathFollow3D::get_h_offset() const { void PathFollow3D::set_v_offset(real_t p_v_offset) { v_offset = p_v_offset; if (path) { - _update_transform(); + update_transform(); } } @@ -453,7 +470,7 @@ void PathFollow3D::set_rotation_mode(RotationMode p_rotation_mode) { rotation_mode = p_rotation_mode; update_configuration_warnings(); - _update_transform(); + update_transform(); } PathFollow3D::RotationMode PathFollow3D::get_rotation_mode() const { @@ -462,6 +479,7 @@ PathFollow3D::RotationMode PathFollow3D::get_rotation_mode() const { void PathFollow3D::set_use_model_front(bool p_use_model_front) { use_model_front = p_use_model_front; + update_transform(); } bool PathFollow3D::is_using_model_front() const { @@ -470,6 +488,7 @@ bool PathFollow3D::is_using_model_front() const { void PathFollow3D::set_loop(bool p_loop) { loop = p_loop; + update_transform(); } bool PathFollow3D::has_loop() const { @@ -478,6 +497,7 @@ bool PathFollow3D::has_loop() const { void PathFollow3D::set_tilt_enabled(bool p_enabled) { tilt_enabled = p_enabled; + update_transform(); } bool PathFollow3D::is_tilt_enabled() const { diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index 6116e9805409..6664bcbaa494 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -72,10 +72,6 @@ class PathFollow3D : public Node3D { ROTATION_ORIENTED }; - bool use_model_front = false; - - static Transform3D correct_posture(Transform3D p_transform, PathFollow3D::RotationMode p_rotation_mode); - private: Path3D *path = nullptr; real_t progress = 0.0; @@ -84,14 +80,16 @@ class PathFollow3D : public Node3D { bool cubic = true; bool loop = true; bool tilt_enabled = true; + bool transform_dirty = true; + bool use_model_front = false; RotationMode rotation_mode = ROTATION_XYZ; - void _update_transform(bool p_update_xyz_rot = true); - protected: void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); + void _update_transform(); + static void _bind_methods(); public: @@ -124,6 +122,10 @@ class PathFollow3D : public Node3D { PackedStringArray get_configuration_warnings() const override; + void update_transform(bool p_immediate = false); + + static Transform3D correct_posture(Transform3D p_transform, PathFollow3D::RotationMode p_rotation_mode); + PathFollow3D() {} };