Fix Pathfollow direction

Co-authored-by: aaronfranke <arnfranke@yahoo.com>
This commit is contained in:
Silc Renew 2023-02-08 02:48:33 +09:00 committed by Silc Lizard (Tokage) Renew
parent dc625bcbfc
commit 42aa5398d9
12 changed files with 60 additions and 53 deletions

View File

@ -127,6 +127,7 @@
<description> <description>
Creates a Basis with a rotation such that the forward axis (-Z) points towards the [param target] position. 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. 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).
</description> </description>
</method> </method>
<method name="orthonormalized" qualifiers="const"> <method name="orthonormalized" qualifiers="const">

View File

@ -115,10 +115,11 @@
<param index="1" name="up" type="Vector3" default="Vector3(0, 1, 0)" /> <param index="1" name="up" type="Vector3" default="Vector3(0, 1, 0)" />
<param index="2" name="use_model_front" type="bool" default="false" /> <param index="2" name="use_model_front" type="bool" default="false" />
<description> <description>
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 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. 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. 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).
</description> </description>
</method> </method>
<method name="look_at_from_position"> <method name="look_at_from_position">

View File

@ -18,9 +18,6 @@
<member name="h_offset" type="float" setter="set_h_offset" getter="get_h_offset" default="0.0"> <member name="h_offset" type="float" setter="set_h_offset" getter="get_h_offset" default="0.0">
The node's offset along the curve. The node's offset along the curve.
</member> </member>
<member name="lookahead" type="float" setter="set_lookahead" getter="get_lookahead" default="4.0">
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.
</member>
<member name="loop" type="bool" setter="set_loop" getter="has_loop" default="true"> <member name="loop" type="bool" setter="set_loop" getter="has_loop" default="true">
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. 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.
</member> </member>

View File

@ -43,6 +43,9 @@
<member name="tilt_enabled" type="bool" setter="set_tilt_enabled" getter="is_tilt_enabled" default="true"> <member name="tilt_enabled" type="bool" setter="set_tilt_enabled" getter="is_tilt_enabled" default="true">
If [code]true[/code], the tilt property of [Curve3D] takes effect. If [code]true[/code], the tilt property of [Curve3D] takes effect.
</member> </member>
<member name="use_model_front" type="bool" setter="set_use_model_front" getter="is_using_model_front" default="false">
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].
</member>
<member name="v_offset" type="float" setter="set_v_offset" getter="get_v_offset" default="0.0"> <member name="v_offset" type="float" setter="set_v_offset" getter="get_v_offset" default="0.0">
The node's offset perpendicular to the curve. The node's offset perpendicular to the curve.
</member> </member>

View File

@ -97,6 +97,7 @@
<description> <description>
Returns a copy of the transform rotated such that the forward axis (-Z) points towards the [param target] position. 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. 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).
</description> </description>
</method> </method>
<method name="orthonormalized" qualifiers="const"> <method name="orthonormalized" qualifiers="const">

View File

@ -274,10 +274,10 @@ void Path3DGizmo::redraw() {
// Fish Bone. // Fish Bone.
v3p.push_back(p1); 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);
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); add_lines(v3p, path_material);

View File

@ -268,11 +268,11 @@ void PathFollow2D::_notification(int p_what) {
} }
} }
void PathFollow2D::set_cubic_interpolation(bool p_enable) { void PathFollow2D::set_cubic_interpolation_enabled(bool p_enabled) {
cubic = p_enable; cubic = p_enabled;
} }
bool PathFollow2D::get_cubic_interpolation() const { bool PathFollow2D::is_cubic_interpolation_enabled() const {
return cubic; 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("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("get_progress_ratio"), &PathFollow2D::get_progress_ratio);
ClassDB::bind_method(D_METHOD("set_rotates", "enable"), &PathFollow2D::set_rotates); ClassDB::bind_method(D_METHOD("set_rotates", "enabled"), &PathFollow2D::set_rotation_enabled);
ClassDB::bind_method(D_METHOD("is_rotating"), &PathFollow2D::is_rotating); 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("set_cubic_interpolation", "enabled"), &PathFollow2D::set_cubic_interpolation_enabled);
ClassDB::bind_method(D_METHOD("get_cubic_interpolation"), &PathFollow2D::get_cubic_interpolation); 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("set_loop", "loop"), &PathFollow2D::set_loop);
ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow2D::has_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", 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, "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"); 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, "rotates"), "set_rotates", "is_rotating");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation"); 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, "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) { 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) { void PathFollow2D::set_rotation_enabled(bool p_enabled) {
lookahead = p_lookahead; rotates = p_enabled;
}
real_t PathFollow2D::get_lookahead() const {
return lookahead;
}
void PathFollow2D::set_rotates(bool p_rotates) {
rotates = p_rotates;
_update_transform(); _update_transform();
} }
bool PathFollow2D::is_rotating() const { bool PathFollow2D::is_rotation_enabled() const {
return rotates; return rotates;
} }

View File

@ -70,7 +70,6 @@ private:
Timer *update_timer = nullptr; Timer *update_timer = nullptr;
real_t h_offset = 0.0; real_t h_offset = 0.0;
real_t v_offset = 0.0; real_t v_offset = 0.0;
real_t lookahead = 4.0;
bool cubic = true; bool cubic = true;
bool loop = true; bool loop = true;
bool rotates = true; bool rotates = true;
@ -98,17 +97,14 @@ public:
void set_progress_ratio(real_t p_ratio); void set_progress_ratio(real_t p_ratio);
real_t get_progress_ratio() const; real_t get_progress_ratio() const;
void set_lookahead(real_t p_lookahead);
real_t get_lookahead() const;
void set_loop(bool p_loop); void set_loop(bool p_loop);
bool has_loop() const; bool has_loop() const;
void set_rotates(bool p_rotates); void set_rotation_enabled(bool p_enabled);
bool is_rotating() const; bool is_rotation_enabled() const;
void set_cubic_interpolation(bool p_enable); void set_cubic_interpolation_enabled(bool p_enabled);
bool get_cubic_interpolation() const; bool is_cubic_interpolation_enabled() const;
PackedStringArray get_configuration_warnings() const override; PackedStringArray get_configuration_warnings() const override;

View File

@ -192,6 +192,9 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) {
t.origin = pos; t.origin = pos;
} else { } else {
t = c->sample_baked_with_rotation(progress, cubic, false); 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 Vector3 forward = t.basis.get_column(2); // Retain tangent for applying tilt
t = PathFollow3D::correct_posture(t, rotation_mode); 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) { void PathFollow3D::set_cubic_interpolation_enabled(bool p_enabled) {
cubic = p_enable; cubic = p_enabled;
} }
bool PathFollow3D::get_cubic_interpolation() const { bool PathFollow3D::is_cubic_interpolation_enabled() const {
return cubic; 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("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("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("set_cubic_interpolation", "enabled"), &PathFollow3D::set_cubic_interpolation_enabled);
ClassDB::bind_method(D_METHOD("get_cubic_interpolation"), &PathFollow3D::get_cubic_interpolation); 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("set_loop", "loop"), &PathFollow3D::set_loop);
ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow3D::has_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, "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::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::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, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tilt_enabled"), "set_tilt_enabled", "is_tilt_enabled"); 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; 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) { void PathFollow3D::set_loop(bool p_loop) {
loop = p_loop; loop = p_loop;
} }
@ -420,8 +435,8 @@ bool PathFollow3D::has_loop() const {
return loop; return loop;
} }
void PathFollow3D::set_tilt_enabled(bool p_enable) { void PathFollow3D::set_tilt_enabled(bool p_enabled) {
tilt_enabled = p_enable; tilt_enabled = p_enabled;
} }
bool PathFollow3D::is_tilt_enabled() const { bool PathFollow3D::is_tilt_enabled() const {

View File

@ -72,6 +72,8 @@ public:
ROTATION_ORIENTED ROTATION_ORIENTED
}; };
bool use_model_front = false;
static Transform3D correct_posture(Transform3D p_transform, PathFollow3D::RotationMode p_rotation_mode); static Transform3D correct_posture(Transform3D p_transform, PathFollow3D::RotationMode p_rotation_mode);
private: private:
@ -108,14 +110,17 @@ public:
void set_loop(bool p_loop); void set_loop(bool p_loop);
bool has_loop() const; bool has_loop() const;
void set_tilt_enabled(bool p_enable); void set_tilt_enabled(bool p_enabled);
bool is_tilt_enabled() const; bool is_tilt_enabled() const;
void set_rotation_mode(RotationMode p_rotation_mode); void set_rotation_mode(RotationMode p_rotation_mode);
RotationMode get_rotation_mode() const; RotationMode get_rotation_mode() const;
void set_cubic_interpolation(bool p_enable); void set_use_model_front(bool p_use_model_front);
bool get_cubic_interpolation() const; 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; PackedStringArray get_configuration_warnings() const override;

View File

@ -1648,9 +1648,9 @@ void Curve3D::_bake() const {
Vector3 forward = forward_ptr[0]; Vector3 forward = forward_ptr[0];
if (abs(forward.dot(Vector3(0, 1, 0))) > 1.0 - UNIT_EPSILON) { 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 { } 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); 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. // Build frames at both ends of the interval, then interpolate.
const Basis frame_begin = Basis::looking_at(-forward_begin, up_begin); 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_end = Basis::looking_at(forward_end, up_end);
const Basis frame = frame_begin.slerp(frame_end, frac).orthonormalized(); const Basis frame = frame_begin.slerp(frame_end, frac).orthonormalized();
if (!p_apply_tilt) { if (!p_apply_tilt) {

View File

@ -178,9 +178,9 @@ TEST_CASE("[Curve3D] Sampling") {
} }
SUBCASE("sample_baked_with_rotation") { 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, 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, 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, 50, 0))) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 50, 0)));
} }
SUBCASE("sample_baked_tilt") { SUBCASE("sample_baked_tilt") {