Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Z forward option to PathFollow3D #72843

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions doc/classes/PathFollow2D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,14 @@
<tutorials>
</tutorials>
<members>
<member name="cubic_interp" type="bool" setter="set_cubic_interpolation" getter="get_cubic_interpolation" default="true">
<member name="cubic_interpolation_enabled" type="bool" setter="set_cubic_interpolation_enabled" getter="is_cubic_interpolation_enabled" default="true">
If [code]true[/code], the position between two cached points is interpolated cubically, and linearly otherwise.
The points along the [Curve2D] of the [Path2D] are precomputed before use, for faster calculations. The point at the requested offset is then calculated interpolating between two adjacent cached points. This may present a problem if the curve makes sharp turns, as the cached points may not follow the curve closely enough.
There are two answers to this problem: either increase the number of cached points and increase memory consumption, or make a cubic interpolation between two points at the cost of (slightly) slower calculations.
</member>
<member name="h_offset" type="float" setter="set_h_offset" getter="get_h_offset" default="0.0">
The node's offset along the curve.
</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">
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>
Expand All @@ -30,7 +27,7 @@
<member name="progress_ratio" type="float" setter="set_progress_ratio" getter="get_progress_ratio" default="0.0">
The distance along the path as a number in the range 0.0 (for the first vertex) to 1.0 (for the last). This is just another way of expressing the progress within the path, as the offset supplied is multiplied internally by the path's length.
</member>
<member name="rotates" type="bool" setter="set_rotates" getter="is_rotating" default="true">
<member name="rotation_enabled" type="bool" setter="set_rotation_enabled" getter="is_rotation_enabled" default="true">
If [code]true[/code], this node rotates to follow the path, with the +X direction facing forward on the path.
</member>
<member name="v_offset" type="float" setter="set_v_offset" getter="get_v_offset" default="0.0">
Expand Down
5 changes: 4 additions & 1 deletion doc/classes/PathFollow3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
</method>
</methods>
<members>
<member name="cubic_interp" type="bool" setter="set_cubic_interpolation" getter="get_cubic_interpolation" default="true">
<member name="cubic_interpolation_enabled" type="bool" setter="set_cubic_interpolation_enabled" getter="is_cubic_interpolation_enabled" default="true">
If [code]true[/code], the position between two cached points is interpolated cubically, and linearly otherwise.
The points along the [Curve3D] of the [Path3D] are precomputed before use, for faster calculations. The point at the requested offset is then calculated interpolating between two adjacent cached points. This may present a problem if the curve makes sharp turns, as the cached points may not follow the curve closely enough.
There are two answers to this problem: either increase the number of cached points and increase memory consumption, or make a cubic interpolation between two points at the cost of (slightly) slower calculations.
Expand All @@ -46,6 +46,9 @@
<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.
</member>
<member name="z_forward" type="bool" setter="set_z_forward" getter="is_z_forward" default="false">
If [code]true[/code], the node moves with the glTF forward axis (+Z) in the front.
</member>
</members>
<constants>
<constant name="ROTATION_NONE" value="0" enum="RotationMode">
Expand Down
4 changes: 2 additions & 2 deletions editor/plugins/path_3d_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
60 changes: 36 additions & 24 deletions scene/2d/path_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -299,6 +299,30 @@ PackedStringArray PathFollow2D::get_configuration_warnings() const {
return warnings;
}

#ifndef DISABLE_DEPRECATED
bool PathFollow2D::_set(const StringName &p_name, const Variant &p_value) {
if ((p_name == SNAME("cubic_interp"))) {
set_cubic_interpolation_enabled(p_value);
return true;
}
if ((p_name == SNAME("rotates"))) {
set_rotation_enabled(p_value);
return true;
}
return false;
}
bool PathFollow2D::_get(const StringName &p_name, Variant &r_ret) const {
String name = p_name;
if ((p_name == SNAME("cubic_interp"))) {
r_ret = is_cubic_interpolation_enabled();
} else if ("rotates") {
r_ret = is_rotation_enabled();
} else {
return false;
}
return true;
}
#endif
void PathFollow2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_progress", "progress"), &PathFollow2D::set_progress);
ClassDB::bind_method(D_METHOD("get_progress"), &PathFollow2D::get_progress);
Expand All @@ -312,26 +336,22 @@ 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_rotation_enabled", "enabled"), &PathFollow2D::set_rotation_enabled);
ClassDB::bind_method(D_METHOD("is_rotation_enabled"), &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", "enabled"), &PathFollow2D::set_cubic_interpolation_enabled);
ClassDB::bind_method(D_METHOD("is_cubic_interpolation_enabled"), &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");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset"), "set_v_offset", "get_v_offset");
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, "rotation_enabled"), "set_rotation_enabled", "is_rotation_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interpolation_enabled"), "set_cubic_interpolation_enabled", "is_cubic_interpolation_enabled");
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) {
Expand Down Expand Up @@ -395,20 +415,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;
}

Expand Down
16 changes: 8 additions & 8 deletions scene/2d/path_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,17 @@ 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;

void _update_transform();

protected:
#ifndef DISABLE_DEPRECATED
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
#endif
void _validate_property(PropertyInfo &p_property) const;

void _notification(int p_what);
Expand All @@ -98,17 +101,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;

Expand Down
49 changes: 41 additions & 8 deletions scene/3d/path_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) {
t.origin = pos;
} else {
t = c->sample_baked_with_rotation(progress, cubic, false);
if (z_forward) {
t.basis = Basis(t.basis.get_column(1).normalized(), Math_PI) * t.basis;
}
Vector3 forward = t.basis.get_column(2); // Retain tangent for applying tilt
t = PathFollow3D::correct_posture(t, rotation_mode);

Expand Down Expand Up @@ -229,11 +232,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;
}

Expand Down Expand Up @@ -297,6 +300,24 @@ Transform3D PathFollow3D::correct_posture(Transform3D p_transform, PathFollow3D:
return t;
}

#ifndef DISABLE_DEPRECATED
bool PathFollow3D::_set(const StringName &p_name, const Variant &p_value) {
if ((p_name == SNAME("cubic_interp"))) {
set_cubic_interpolation_enabled(p_value);
return true;
}
return false;
}
bool PathFollow3D::_get(const StringName &p_name, Variant &r_ret) const {
String name = p_name;
if ((p_name == SNAME("cubic_interp"))) {
r_ret = is_cubic_interpolation_enabled();
} else {
return false;
}
return true;
}
#endif
void PathFollow3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_progress", "progress"), &PathFollow3D::set_progress);
ClassDB::bind_method(D_METHOD("get_progress"), &PathFollow3D::get_progress);
Expand All @@ -313,8 +334,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", "enabled"), &PathFollow3D::set_cubic_interpolation_enabled);
ClassDB::bind_method(D_METHOD("is_cubic_interpolation_enabled"), &PathFollow3D::is_cubic_interpolation_enabled);

ClassDB::bind_method(D_METHOD("set_z_forward", "enabled"), &PathFollow3D::set_z_forward);
ClassDB::bind_method(D_METHOD("is_z_forward"), &PathFollow3D::is_z_forward);

ClassDB::bind_method(D_METHOD("set_loop", "loop"), &PathFollow3D::set_loop);
ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow3D::has_loop);
Expand All @@ -329,7 +353,8 @@ 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, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "z_forward"), "set_z_forward", "is_z_forward");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interpolation_enabled"), "set_cubic_interpolation_enabled", "is_cubic_interpolation_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tilt_enabled"), "set_tilt_enabled", "is_tilt_enabled");

Expand Down Expand Up @@ -411,6 +436,14 @@ PathFollow3D::RotationMode PathFollow3D::get_rotation_mode() const {
return rotation_mode;
}

void PathFollow3D::set_z_forward(bool p_enabled) {
z_forward = p_enabled;
}

bool PathFollow3D::is_z_forward() const {
return z_forward;
}

void PathFollow3D::set_loop(bool p_loop) {
loop = p_loop;
}
Expand All @@ -419,8 +452,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 {
Expand Down
15 changes: 12 additions & 3 deletions scene/3d/path_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ class PathFollow3D : public Node3D {
ROTATION_ORIENTED
};

bool z_forward = false;

static Transform3D correct_posture(Transform3D p_transform, PathFollow3D::RotationMode p_rotation_mode);

private:
Expand All @@ -87,6 +89,10 @@ class PathFollow3D : public Node3D {
void _update_transform(bool p_update_xyz_rot = true);

protected:
#ifndef DISABLE_DEPRECATED
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
#endif
void _validate_property(PropertyInfo &p_property) const;

void _notification(int p_what);
Expand All @@ -108,14 +114,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_z_forward(bool p_enabled);
bool is_z_forward() const;

void set_cubic_interpolation_enabled(bool p_enabled);
bool is_cubic_interpolation_enabled() const;

PackedStringArray get_configuration_warnings() const override;

Expand Down
8 changes: 4 additions & 4 deletions scene/resources/curve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1620,9 +1620,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);
Expand Down Expand Up @@ -1781,8 +1781,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) {
Expand Down