Browse Source

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.
Yaohua Xiong 2 years ago
parent
commit
43b184b409
2 changed files with 46 additions and 24 deletions
  1. 38 18
      scene/3d/path_3d.cpp
  2. 8 6
      scene/3d/path_3d.h

+ 38 - 18
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<PathFollow3D>(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<Path3D>(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 {

+ 8 - 6
scene/3d/path_3d.h

@@ -72,10 +72,6 @@ public:
 		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 @@ private:
 	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 @@ public:
 
 	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() {}
 };