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

Rework AnimationNode process for retrieving the semantic time info #87171

Merged
1 commit merged into from
Mar 24, 2024
Merged
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
11 changes: 9 additions & 2 deletions doc/classes/AnimationNode.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
<description>
Base resource for [AnimationTree] nodes. In general, it's not used directly, but you can create custom ones with custom blending formulas.
Inherit this when creating animation nodes mainly for use in [AnimationNodeBlendTree], otherwise [AnimationRootNode] should be used instead.
You can access the time information as read-only parameter which is processed and stored in the previous frame for all nodes except [AnimationNodeOutput].
[b]Note:[/b] If more than two inputs exist in the [AnimationNode], which time information takes precedence depends on the type of [AnimationNode].
[codeblock]
var current_length = $AnimationTree[parameters/AnimationNodeName/current_length]
var current_position = $AnimationTree[parameters/AnimationNodeName/current_position]
var current_delta = $AnimationTree[parameters/AnimationNodeName/current_delta]
[/codeblock]
</description>
<tutorials>
<link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
Expand Down Expand Up @@ -56,7 +63,7 @@
When inheriting from [AnimationRootNode], implement this virtual method to return whether the [param parameter] is read-only. Parameters are custom local memory used for your animation nodes, given a resource can be reused in multiple trees.
</description>
</method>
<method name="_process" qualifiers="virtual const">
<method name="_process" qualifiers="virtual const" deprecated="Currently this is mostly useless as there is a lack of many APIs to extend AnimationNode by GDScript. It is planned that a more flexible API using structures will be provided in the future.">
<return type="float" />
<param index="0" name="time" type="float" />
<param index="1" name="seek" type="bool" />
Expand All @@ -65,7 +72,7 @@
<description>
When inheriting from [AnimationRootNode], implement this virtual method to run some code when this animation node is processed. The [param time] parameter is a relative delta, unless [param seek] is [code]true[/code], in which case it is absolute.
Here, call the [method blend_input], [method blend_node] or [method blend_animation] functions. You can also use [method get_parameter] and [method set_parameter] to modify local memory.
This function should return the time left for the current animation to finish (if unsure, pass the value from the main blend being called).
This function should return the delta.
</description>
</method>
<method name="add_input">
Expand Down
18 changes: 18 additions & 0 deletions doc/classes/AnimationNodeAnimation.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,27 @@
<member name="animation" type="StringName" setter="set_animation" getter="get_animation" default="&amp;&quot;&quot;">
Animation to use as an output. It is one of the animations provided by [member AnimationTree.anim_player].
</member>
<member name="loop_mode" type="int" setter="set_loop_mode" getter="get_loop_mode" enum="Animation.LoopMode">
If [member use_custom_timeline] is [code]true[/code], override the loop settings of the original [Animation] resource with the value.
</member>
<member name="play_mode" type="int" setter="set_play_mode" getter="get_play_mode" enum="AnimationNodeAnimation.PlayMode" default="0">
Determines the playback direction of the animation.
</member>
<member name="start_offset" type="float" setter="set_start_offset" getter="get_start_offset">
If [member use_custom_timeline] is [code]true[/code], offset the start position of the animation.
This is useful for adjusting which foot steps first in 3D walking animations.
</member>
<member name="stretch_time_scale" type="bool" setter="set_stretch_time_scale" getter="is_stretching_time_scale">
If [code]true[/code], scales the time so that the length specified in [member timeline_length] is one cycle.
This is useful for matching the periods of walking and running animations.
If [code]false[/code], the original animation length is respected. If you set the loop to [member loop_mode], the animation will loop in [member timeline_length].
</member>
<member name="timeline_length" type="float" setter="set_timeline_length" getter="get_timeline_length">
If [member use_custom_timeline] is [code]true[/code], offset the start position of the animation.
</member>
<member name="use_custom_timeline" type="bool" setter="set_use_custom_timeline" getter="is_using_custom_timeline" default="false">
If [code]true[/code], [AnimationNode] provides an animation based on the [Animation] resource with some parameters adjusted.
</member>
</members>
<constants>
<constant name="PLAY_MODE_FORWARD" value="0" enum="PlayMode">
Expand Down
5 changes: 5 additions & 0 deletions doc/classes/AnimationNodeOneShot.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,22 @@
<member name="autorestart_random_delay" type="float" setter="set_autorestart_random_delay" getter="get_autorestart_random_delay" default="0.0">
If [member autorestart] is [code]true[/code], a random additional delay (in seconds) between 0 and this value will be added to [member autorestart_delay].
</member>
<member name="break_loop_at_end" type="bool" setter="set_break_loop_at_end" getter="is_loop_broken_at_end" default="false">
If [code]true[/code], breaks the loop at the end of the loop cycle for transition, even if the animation is looping.
</member>
<member name="fadein_curve" type="Curve" setter="set_fadein_curve" getter="get_fadein_curve">
Determines how cross-fading between animations is eased. If empty, the transition will be linear.
</member>
<member name="fadein_time" type="float" setter="set_fadein_time" getter="get_fadein_time" default="0.0">
The fade-in duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a cross-fade that starts at 0 second and ends at 1 second during the animation.
[b]Note:[/b] [AnimationNodeOneShot] transitions the current state after the end of the fading. When [AnimationNodeOutput] is considered as the most upstream, so the [member fadein_time] is scaled depending on the downstream delta. For example, if this value is set to [code]1.0[/code] and a [AnimationNodeTimeScale] with a value of [code]2.0[/code] is chained downstream, the actual processing time will be 0.5 second.
</member>
<member name="fadeout_curve" type="Curve" setter="set_fadeout_curve" getter="get_fadeout_curve">
Determines how cross-fading between animations is eased. If empty, the transition will be linear.
</member>
<member name="fadeout_time" type="float" setter="set_fadeout_time" getter="get_fadeout_time" default="0.0">
The fade-out duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a cross-fade that starts at 4 second and ends at 5 second during the animation.
[b]Note:[/b] [AnimationNodeOneShot] transitions the current state after the end of the fading. When [AnimationNodeOutput] is considered as the most upstream, so the [member fadeout_time] is scaled depending on the downstream delta. For example, if this value is set to [code]1.0[/code] and an [AnimationNodeTimeScale] with a value of [code]2.0[/code] is chained downstream, the actual processing time will be 0.5 second.
</member>
<member name="mix_mode" type="int" setter="set_mix_mode" getter="get_mix_mode" enum="AnimationNodeOneShot.MixMode" default="0">
The blend type.
Expand Down
4 changes: 4 additions & 0 deletions doc/classes/AnimationNodeStateMachineTransition.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
<member name="advance_mode" type="int" setter="set_advance_mode" getter="get_advance_mode" enum="AnimationNodeStateMachineTransition.AdvanceMode" default="1">
Determines whether the transition should disabled, enabled when using [method AnimationNodeStateMachinePlayback.travel], or traversed automatically if the [member advance_condition] and [member advance_expression] checks are true (if assigned).
</member>
<member name="break_loop_at_end" type="bool" setter="set_break_loop_at_end" getter="is_loop_broken_at_end" default="false">
If [code]true[/code], breaks the loop at the end of the loop cycle for transition, even if the animation is looping.
</member>
<member name="priority" type="int" setter="set_priority" getter="get_priority" default="1">
Lower priority transitions are preferred when travelling through the tree via [method AnimationNodeStateMachinePlayback.travel] or [member advance_mode] is set to [constant ADVANCE_MODE_AUTO].
</member>
Expand All @@ -42,6 +45,7 @@
</member>
<member name="xfade_time" type="float" setter="set_xfade_time" getter="get_xfade_time" default="0.0">
The time to cross-fade between this state and the next.
[b]Note:[/b] [AnimationNodeStateMachine] transitions the current state immediately after the start of the fading. The precise remaining time can only be inferred from the main animation. When [AnimationNodeOutput] is considered as the most upstream, so the [member xfade_time] is not scaled depending on the downstream delta. See also [member AnimationNodeOneShot.fadeout_time].
</member>
</members>
<signals>
Expand Down
16 changes: 16 additions & 0 deletions doc/classes/AnimationNodeTransition.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
</tutorials>
<methods>
<method name="is_input_loop_broken_at_end" qualifiers="const">
<return type="bool" />
<param index="0" name="input" type="int" />
<description>
Returns whether the animation breaks the loop at the end of the loop cycle for transition.
</description>
</method>
<method name="is_input_reset" qualifiers="const">
<return type="bool" />
<param index="0" name="input" type="int" />
Expand All @@ -64,6 +71,14 @@
Enables or disables auto-advance for the given [param input] index. If enabled, state changes to the next input after playing the animation once. If enabled for the last input state, it loops to the first.
</description>
</method>
<method name="set_input_break_loop_at_end">
<return type="void" />
<param index="0" name="input" type="int" />
<param index="1" name="enable" type="bool" />
<description>
If [code]true[/code], breaks the loop at the end of the loop cycle for transition, even if the animation is looping.
</description>
</method>
<method name="set_input_reset">
<return type="void" />
<param index="0" name="input" type="int" />
Expand All @@ -85,6 +100,7 @@
</member>
<member name="xfade_time" type="float" setter="set_xfade_time" getter="get_xfade_time" default="0.0">
Cross-fading time (in seconds) between each animation connected to the inputs.
[b]Note:[/b] [AnimationNodeTransition] transitions the current state immediately after the start of the fading. The precise remaining time can only be inferred from the main animation. When [AnimationNodeOutput] is considered as the most upstream, so the [member xfade_time] is not scaled depending on the downstream delta. See also [member AnimationNodeOneShot.fadeout_time].
</member>
</members>
</class>
9 changes: 3 additions & 6 deletions editor/plugins/animation_blend_tree_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,6 @@ void AnimationNodeBlendTreeEditor::update_graph() {
options.push_back(F);
}

if (tree->has_animation(anim->get_animation())) {
pb->set_max(tree->get_animation(anim->get_animation())->get_length());
}

pb->set_show_percentage(false);
pb->set_custom_minimum_size(Vector2(0, 14) * EDSCALE);
animations[E] = pb;
Expand Down Expand Up @@ -994,9 +990,10 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) {
if (tree->has_animation(an->get_animation())) {
Ref<Animation> anim = tree->get_animation(an->get_animation());
if (anim.is_valid()) {
E.value->set_max(anim->get_length());
//StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + E.input_node;
StringName time_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E.key) + "/time";
StringName length_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E.key) + "/current_length";
StringName time_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E.key) + "/current_position";
E.value->set_max(tree->get(length_path));
E.value->set_value(tree->get(time_path));
}
}
Expand Down
4 changes: 2 additions & 2 deletions editor/plugins/animation_state_machine_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1247,14 +1247,14 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw_all() {
{
float len = MAX(0.0001, current_length);
float pos = CLAMP(current_play_pos, 0, len);
float c = current_length == HUGE_LENGTH ? 1 : (pos / len);
float c = pos / len;
_state_machine_pos_draw_individual(playback->get_current_node(), c);
}

{
float len = MAX(0.0001, fade_from_length);
float pos = CLAMP(fade_from_current_play_pos, 0, len);
float c = fade_from_length == HUGE_LENGTH ? 1 : (pos / len);
float c = pos / len;
_state_machine_pos_draw_individual(playback->get_fading_from_node(), c);
}
}
Expand Down
39 changes: 23 additions & 16 deletions scene/animation/animation_blend_space_1d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,17 @@
#include "animation_blend_tree.h"

void AnimationNodeBlendSpace1D::get_parameter_list(List<PropertyInfo> *r_list) const {
AnimationNode::get_parameter_list(r_list);
r_list->push_back(PropertyInfo(Variant::FLOAT, blend_position));
r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, length_internal, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
}

Variant AnimationNodeBlendSpace1D::get_parameter_default_value(const StringName &p_parameter) const {
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
if (ret != Variant()) {
return ret;
}

if (p_parameter == closest) {
return -1;
} else {
Expand Down Expand Up @@ -272,9 +277,9 @@ void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref<Animatio
}
}

double AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
if (blend_points_used == 0) {
return 0.0;
return NodeTimeInfo();
}

AnimationMixer::PlaybackInfo pi = p_playback_info;
Expand All @@ -287,8 +292,7 @@ double AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_

double blend_pos = get_parameter(blend_position);
int cur_closest = get_parameter(closest);
double cur_length_internal = get_parameter(length_internal);
double max_time_remaining = 0.0;
NodeTimeInfo mind;

if (blend_mode == BLEND_MODE_INTERPOLATED) {
int point_lower = -1;
Expand Down Expand Up @@ -341,12 +345,17 @@ double AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_
}

// actually blend the animations now

bool first = true;
double max_weight = 0.0;
for (int i = 0; i < blend_points_used; i++) {
if (i == point_lower || i == point_higher) {
pi.weight = weights[i];
double remaining = blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
max_time_remaining = MAX(max_time_remaining, remaining);
NodeTimeInfo t = blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
if (first || pi.weight > max_weight) {
max_weight = pi.weight;
mind = t;
first = false;
}
} else if (sync) {
pi.weight = 0;
blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
Expand All @@ -365,7 +374,7 @@ double AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_
}

if (new_closest != cur_closest && new_closest != -1) {
double from = 0.0;
NodeTimeInfo from;
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) {
//for ping-pong loop
Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node);
Expand All @@ -376,18 +385,17 @@ double AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_
//see how much animation remains
pi.seeked = false;
pi.weight = 0;
from = cur_length_internal - blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
}

pi.time = from;
pi.time = from.position;
pi.seeked = true;
pi.weight = 1.0;
max_time_remaining = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only);
cur_length_internal = from + max_time_remaining;
mind = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only);
cur_closest = new_closest;
} else {
pi.weight = 1.0;
max_time_remaining = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
mind = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
}

if (sync) {
Expand All @@ -402,8 +410,7 @@ double AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_
}

set_parameter(closest, cur_closest);
set_parameter(length_internal, cur_length_internal);
return max_time_remaining;
return mind;
}

String AnimationNodeBlendSpace1D::get_caption() const {
Expand Down
3 changes: 1 addition & 2 deletions scene/animation/animation_blend_space_1d.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode {

StringName blend_position = "blend_position";
StringName closest = "closest";
StringName length_internal = "length_internal";

BlendMode blend_mode = BLEND_MODE_INTERPOLATED;

Expand Down Expand Up @@ -114,7 +113,7 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode {
void set_use_sync(bool p_sync);
bool is_using_sync() const;

virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
String get_caption() const override;

Ref<AnimationNode> get_child_by_name(const StringName &p_name) const override;
Expand Down
Loading
Loading