diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index 7de626e4777c..a769cfeabf8c 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -127,6 +127,7 @@ Creates a Basis with a rotation such that the forward axis (-Z) points towards the [param target] position. The up axis (+Y) points as close to the [param up] vector as possible while staying perpendicular to the forward axis. The resulting Basis is orthonormalized. The [param target] and [param up] vectors cannot be zero, and cannot be parallel to each other. + If [param use_model_front] is [code]true[/code], the +Z axis (asset front) is treated as forward (implies +X is left) and points toward the [param target] position. By default, the -Z axis (camera forward) is treated as forward (implies +X is right). diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml index cad0e056e7b0..8247911a3435 100644 --- a/doc/classes/Node3D.xml +++ b/doc/classes/Node3D.xml @@ -115,10 +115,11 @@ - Rotates the node so that the local forward axis (-Z, [constant Vector3.FORWARD]) points toward the [param target] position. If the [param use_model_front] options is specified, then the model is oriented in reverse, towards the model front axis (+Z, [constant Vector3.MODEL_FRONT]), which is more useful for orienting 3D models. + Rotates the node so that the local forward axis (-Z, [constant Vector3.FORWARD]) points toward the [param target] position. The local up axis (+Y) points as close to the [param up] vector as possible while staying perpendicular to the local forward axis. The resulting transform is orthogonal, and the scale is preserved. Non-uniform scaling may not work correctly. The [param target] position cannot be the same as the node's position, the [param up] vector cannot be zero, and the direction from the node's position to the [param target] vector cannot be parallel to the [param up] vector. Operations take place in global space, which means that the node must be in the scene tree. + If [param use_model_front] is [code]true[/code], the +Z axis (asset front) is treated as forward (implies +X is left) and points toward the [param target] position. By default, the -Z axis (camera forward) is treated as forward (implies +X is right). diff --git a/doc/classes/PathFollow2D.xml b/doc/classes/PathFollow2D.xml index 60118675b47d..d958d4c14fc7 100644 --- a/doc/classes/PathFollow2D.xml +++ b/doc/classes/PathFollow2D.xml @@ -18,9 +18,6 @@ The node's offset along the curve. - - How far to look ahead of the curve to calculate the tangent if the node is rotating. E.g. shorter lookaheads will lead to faster rotations. - If [code]true[/code], any offset outside the path's length will wrap around, instead of stopping at the ends. Use it for cyclic paths. diff --git a/doc/classes/PathFollow3D.xml b/doc/classes/PathFollow3D.xml index 41727a7bd8e2..8d4101df0bd9 100644 --- a/doc/classes/PathFollow3D.xml +++ b/doc/classes/PathFollow3D.xml @@ -43,6 +43,9 @@ If [code]true[/code], the tilt property of [Curve3D] takes effect. + + If [code]true[/code], the node moves on the travel path with orienting the +Z axis as forward. See also [constant Vector3.FORWARD] and [constant Vector3.MODEL_FRONT]. + The node's offset perpendicular to the curve. diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml index d375838a687c..c01268779a25 100644 --- a/doc/classes/Transform3D.xml +++ b/doc/classes/Transform3D.xml @@ -97,6 +97,7 @@ Returns a copy of the transform rotated such that the forward axis (-Z) points towards the [param target] position. The up axis (+Y) points as close to the [param up] vector as possible while staying perpendicular to the forward axis. The resulting transform is orthonormalized. The existing rotation, scale, and skew information from the original transform is discarded. The [param target] and [param up] vectors cannot be zero, cannot be parallel to each other, and are defined in global/parent space. + If [param use_model_front] is [code]true[/code], the +Z axis (asset front) is treated as forward (implies +X is left) and points toward the [param target] position. By default, the -Z axis (camera forward) is treated as forward (implies +X is right). diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index 75cd04bee8af..d49c04445e85 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -274,10 +274,10 @@ void Path3DGizmo::redraw() { // Fish Bone. v3p.push_back(p1); - v3p.push_back(p1 + (side - forward + up * 0.3) * 0.06); + v3p.push_back(p1 + (side + forward + up * 0.3) * 0.06); v3p.push_back(p1); - v3p.push_back(p1 + (-side - forward + up * 0.3) * 0.06); + v3p.push_back(p1 + (-side + forward + up * 0.3) * 0.06); } add_lines(v3p, path_material); diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 5036dd30b169..3e6a484e7264 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -268,11 +268,11 @@ void PathFollow2D::_notification(int p_what) { } } -void PathFollow2D::set_cubic_interpolation(bool p_enable) { - cubic = p_enable; +void PathFollow2D::set_cubic_interpolation_enabled(bool p_enabled) { + cubic = p_enabled; } -bool PathFollow2D::get_cubic_interpolation() const { +bool PathFollow2D::is_cubic_interpolation_enabled() const { return cubic; } @@ -312,18 +312,15 @@ void PathFollow2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_progress_ratio", "ratio"), &PathFollow2D::set_progress_ratio); ClassDB::bind_method(D_METHOD("get_progress_ratio"), &PathFollow2D::get_progress_ratio); - ClassDB::bind_method(D_METHOD("set_rotates", "enable"), &PathFollow2D::set_rotates); - ClassDB::bind_method(D_METHOD("is_rotating"), &PathFollow2D::is_rotating); + ClassDB::bind_method(D_METHOD("set_rotates", "enabled"), &PathFollow2D::set_rotation_enabled); + ClassDB::bind_method(D_METHOD("is_rotating"), &PathFollow2D::is_rotation_enabled); - ClassDB::bind_method(D_METHOD("set_cubic_interpolation", "enable"), &PathFollow2D::set_cubic_interpolation); - ClassDB::bind_method(D_METHOD("get_cubic_interpolation"), &PathFollow2D::get_cubic_interpolation); + ClassDB::bind_method(D_METHOD("set_cubic_interpolation", "enabled"), &PathFollow2D::set_cubic_interpolation_enabled); + ClassDB::bind_method(D_METHOD("get_cubic_interpolation"), &PathFollow2D::is_cubic_interpolation_enabled); ClassDB::bind_method(D_METHOD("set_loop", "loop"), &PathFollow2D::set_loop); ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow2D::has_loop); - ClassDB::bind_method(D_METHOD("set_lookahead", "lookahead"), &PathFollow2D::set_lookahead); - ClassDB::bind_method(D_METHOD("get_lookahead"), &PathFollow2D::get_lookahead); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "progress", PROPERTY_HINT_RANGE, "0,10000,0.01,or_less,or_greater,suffix:px"), "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"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset"), "set_h_offset", "get_h_offset"); @@ -331,7 +328,6 @@ void PathFollow2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotates"), "set_rotates", "is_rotating"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lookahead", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001"), "set_lookahead", "get_lookahead"); } void PathFollow2D::set_progress(real_t p_progress) { @@ -395,20 +391,12 @@ real_t PathFollow2D::get_progress_ratio() const { } } -void PathFollow2D::set_lookahead(real_t p_lookahead) { - lookahead = p_lookahead; -} - -real_t PathFollow2D::get_lookahead() const { - return lookahead; -} - -void PathFollow2D::set_rotates(bool p_rotates) { - rotates = p_rotates; +void PathFollow2D::set_rotation_enabled(bool p_enabled) { + rotates = p_enabled; _update_transform(); } -bool PathFollow2D::is_rotating() const { +bool PathFollow2D::is_rotation_enabled() const { return rotates; } diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h index 89c77c49ebf3..bfd5cde5e9cb 100644 --- a/scene/2d/path_2d.h +++ b/scene/2d/path_2d.h @@ -70,7 +70,6 @@ class PathFollow2D : public Node2D { Timer *update_timer = nullptr; real_t h_offset = 0.0; real_t v_offset = 0.0; - real_t lookahead = 4.0; bool cubic = true; bool loop = true; bool rotates = true; @@ -98,17 +97,14 @@ class PathFollow2D : public Node2D { void set_progress_ratio(real_t p_ratio); real_t get_progress_ratio() const; - void set_lookahead(real_t p_lookahead); - real_t get_lookahead() const; - void set_loop(bool p_loop); bool has_loop() const; - void set_rotates(bool p_rotates); - bool is_rotating() const; + void set_rotation_enabled(bool p_enabled); + bool is_rotation_enabled() const; - void set_cubic_interpolation(bool p_enable); - bool get_cubic_interpolation() const; + void set_cubic_interpolation_enabled(bool p_enabled); + bool is_cubic_interpolation_enabled() const; PackedStringArray get_configuration_warnings() const override; diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index f02826b1c445..c71f80ea0e4d 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -192,6 +192,9 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { t.origin = pos; } else { t = c->sample_baked_with_rotation(progress, cubic, false); + 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); @@ -230,11 +233,11 @@ void PathFollow3D::_notification(int p_what) { } } -void PathFollow3D::set_cubic_interpolation(bool p_enable) { - cubic = p_enable; +void PathFollow3D::set_cubic_interpolation_enabled(bool p_enabled) { + cubic = p_enabled; } -bool PathFollow3D::get_cubic_interpolation() const { +bool PathFollow3D::is_cubic_interpolation_enabled() const { return cubic; } @@ -314,8 +317,11 @@ void PathFollow3D::_bind_methods() { 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); - ClassDB::bind_method(D_METHOD("set_cubic_interpolation", "enable"), &PathFollow3D::set_cubic_interpolation); - ClassDB::bind_method(D_METHOD("get_cubic_interpolation"), &PathFollow3D::get_cubic_interpolation); + 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); ClassDB::bind_method(D_METHOD("set_loop", "loop"), &PathFollow3D::set_loop); ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow3D::has_loop); @@ -330,6 +336,7 @@ void PathFollow3D::_bind_methods() { 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"); ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_mode", PROPERTY_HINT_ENUM, "None,Y,XY,XYZ,Oriented"), "set_rotation_mode", "get_rotation_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_model_front"), "set_use_model_front", "is_using_model_front"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tilt_enabled"), "set_tilt_enabled", "is_tilt_enabled"); @@ -412,6 +419,14 @@ PathFollow3D::RotationMode PathFollow3D::get_rotation_mode() const { return rotation_mode; } +void PathFollow3D::set_use_model_front(bool p_use_model_front) { + use_model_front = p_use_model_front; +} + +bool PathFollow3D::is_using_model_front() const { + return use_model_front; +} + void PathFollow3D::set_loop(bool p_loop) { loop = p_loop; } @@ -420,8 +435,8 @@ bool PathFollow3D::has_loop() const { return loop; } -void PathFollow3D::set_tilt_enabled(bool p_enable) { - tilt_enabled = p_enable; +void PathFollow3D::set_tilt_enabled(bool p_enabled) { + tilt_enabled = p_enabled; } bool PathFollow3D::is_tilt_enabled() const { diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index 9fdcc0f0ef32..6116e9805409 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -72,6 +72,8 @@ class PathFollow3D : public Node3D { ROTATION_ORIENTED }; + bool use_model_front = false; + static Transform3D correct_posture(Transform3D p_transform, PathFollow3D::RotationMode p_rotation_mode); private: @@ -108,14 +110,17 @@ class PathFollow3D : public Node3D { void set_loop(bool p_loop); bool has_loop() const; - void set_tilt_enabled(bool p_enable); + void set_tilt_enabled(bool p_enabled); bool is_tilt_enabled() const; void set_rotation_mode(RotationMode p_rotation_mode); RotationMode get_rotation_mode() const; - void set_cubic_interpolation(bool p_enable); - bool get_cubic_interpolation() const; + void set_use_model_front(bool p_use_model_front); + bool is_using_model_front() const; + + void set_cubic_interpolation_enabled(bool p_enabled); + bool is_cubic_interpolation_enabled() const; PackedStringArray get_configuration_warnings() const override; diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index a0bd22c79bfa..64e466cf7587 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -1648,9 +1648,9 @@ void Curve3D::_bake() const { Vector3 forward = forward_ptr[0]; if (abs(forward.dot(Vector3(0, 1, 0))) > 1.0 - UNIT_EPSILON) { - frame_prev = Basis::looking_at(-forward, Vector3(1, 0, 0)); + frame_prev = Basis::looking_at(forward, Vector3(1, 0, 0)); } else { - frame_prev = Basis::looking_at(-forward, Vector3(0, 1, 0)); + frame_prev = Basis::looking_at(forward, Vector3(0, 1, 0)); } up_write[0] = frame_prev.get_column(1); @@ -1809,8 +1809,8 @@ Basis Curve3D::_sample_posture(Interval p_interval, bool p_apply_tilt) const { } // Build frames at both ends of the interval, then interpolate. - const Basis frame_begin = Basis::looking_at(-forward_begin, up_begin); - const Basis frame_end = Basis::looking_at(-forward_end, up_end); + const Basis frame_begin = Basis::looking_at(forward_begin, up_begin); + const Basis frame_end = Basis::looking_at(forward_end, up_end); const Basis frame = frame_begin.slerp(frame_end, frac).orthonormalized(); if (!p_apply_tilt) { diff --git a/tests/scene/test_curve_3d.h b/tests/scene/test_curve_3d.h index 0f0d413354f8..4d7b718d7e0e 100644 --- a/tests/scene/test_curve_3d.h +++ b/tests/scene/test_curve_3d.h @@ -178,9 +178,9 @@ TEST_CASE("[Curve3D] Sampling") { } SUBCASE("sample_baked_with_rotation") { - CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0))) == Transform3D(Basis(Vector3(0, 0, 1), Vector3(1, 0, 0), Vector3(0, 1, 0)), Vector3(0, 0, 0))); - CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0))) == Transform3D(Basis(Vector3(0, 0, 1), Vector3(1, 0, 0), Vector3(0, 1, 0)), Vector3(0, 25, 0))); - CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0))) == Transform3D(Basis(Vector3(0, 0, 1), Vector3(1, 0, 0), Vector3(0, 1, 0)), Vector3(0, 50, 0))); + CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0))) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 0, 0))); + CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0))) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 25, 0))); + CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0))) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 50, 0))); } SUBCASE("sample_baked_tilt") {