Merge pull request #60774 from TokageItLab/root-seek-mode

Fixed broken root motion calculation in internal process of `AnimationBlendTree` such as `NodeOneShot`
This commit is contained in:
Rémi Verschelde 2022-05-18 15:24:21 +02:00 committed by GitHub
commit 5b3d596285
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 155 additions and 148 deletions

View File

@ -53,6 +53,7 @@
<return type="float" />
<argument index="0" name="time" type="float" />
<argument index="1" name="seek" type="bool" />
<argument index="2" name="seek_root" type="bool" />
<description>
User-defined callback called when a custom node is processed. The [code]time[/code] parameter is a relative delta, unless [code]seek[/code] 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.
@ -72,8 +73,9 @@
<argument index="1" name="time" type="float" />
<argument index="2" name="delta" type="float" />
<argument index="3" name="seeked" type="bool" />
<argument index="4" name="blend" type="float" />
<argument index="5" name="pingponged" type="int" default="0" />
<argument index="4" name="seek_root" type="bool" />
<argument index="5" name="blend" type="float" />
<argument index="6" name="pingponged" type="int" default="0" />
<description>
Blend an animation by [code]blend[/code] amount (name must be valid in the linked [AnimationPlayer]). A [code]time[/code] and [code]delta[/code] may be passed, as well as whether [code]seek[/code] happened.
</description>
@ -83,9 +85,10 @@
<argument index="0" name="input_index" type="int" />
<argument index="1" name="time" type="float" />
<argument index="2" name="seek" type="bool" />
<argument index="3" name="blend" type="float" />
<argument index="4" name="filter" type="int" enum="AnimationNode.FilterAction" default="0" />
<argument index="5" name="optimize" type="bool" default="true" />
<argument index="3" name="seek_root" type="bool" />
<argument index="4" name="blend" type="float" />
<argument index="5" name="filter" type="int" enum="AnimationNode.FilterAction" default="0" />
<argument index="6" name="optimize" type="bool" default="true" />
<description>
Blend an input. This is only useful for nodes created for an [AnimationNodeBlendTree]. The [code]time[/code] parameter is a relative delta, unless [code]seek[/code] is [code]true[/code], in which case it is absolute. A filter mode may be optionally passed (see [enum FilterAction] for options).
</description>
@ -96,9 +99,10 @@
<argument index="1" name="node" type="AnimationNode" />
<argument index="2" name="time" type="float" />
<argument index="3" name="seek" type="bool" />
<argument index="4" name="blend" type="float" />
<argument index="5" name="filter" type="int" enum="AnimationNode.FilterAction" default="0" />
<argument index="6" name="optimize" type="bool" default="true" />
<argument index="4" name="seek_root" type="bool" />
<argument index="5" name="blend" type="float" />
<argument index="6" name="filter" type="int" enum="AnimationNode.FilterAction" default="0" />
<argument index="7" name="optimize" type="bool" default="true" />
<description>
Blend another animation node (in case this node contains children animation nodes). This function is only useful if you inherit from [AnimationRootNode] instead, else editors will not display your node for addition.
</description>

View File

@ -20,9 +20,9 @@
<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="fadein_time" type="float" setter="set_fadein_time" getter="get_fadein_time" default="0.1">
<member name="fadein_time" type="float" setter="set_fadein_time" getter="get_fadein_time" default="0.0">
</member>
<member name="fadeout_time" type="float" setter="set_fadeout_time" getter="get_fadeout_time" default="0.1">
<member name="fadeout_time" type="float" setter="set_fadeout_time" getter="get_fadeout_time" default="0.0">
</member>
<member name="mix_mode" type="int" setter="set_mix_mode" getter="get_mix_mode" enum="AnimationNodeOneShot.MixMode" default="0">
</member>

View File

@ -1417,14 +1417,14 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) {
void AnimationTimelineEdit::_anim_loop_pressed() {
undo_redo->create_action(TTR("Change Animation Loop"));
switch (animation->get_loop_mode()) {
case Animation::LoopMode::LOOP_NONE: {
undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LoopMode::LOOP_LINEAR);
case Animation::LOOP_NONE: {
undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LOOP_LINEAR);
} break;
case Animation::LoopMode::LOOP_LINEAR: {
undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LoopMode::LOOP_PINGPONG);
case Animation::LOOP_LINEAR: {
undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LOOP_PINGPONG);
} break;
case Animation::LoopMode::LOOP_PINGPONG: {
undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LoopMode::LOOP_NONE);
case Animation::LOOP_PINGPONG: {
undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LOOP_NONE);
} break;
default:
break;
@ -1724,15 +1724,15 @@ void AnimationTimelineEdit::update_values() {
}
switch (animation->get_loop_mode()) {
case Animation::LoopMode::LOOP_NONE: {
case Animation::LOOP_NONE: {
loop->set_icon(get_theme_icon(SNAME("Loop"), SNAME("EditorIcons")));
loop->set_pressed(false);
} break;
case Animation::LoopMode::LOOP_LINEAR: {
case Animation::LOOP_LINEAR: {
loop->set_icon(get_theme_icon(SNAME("Loop"), SNAME("EditorIcons")));
loop->set_pressed(true);
} break;
case Animation::LoopMode::LOOP_PINGPONG: {
case Animation::LOOP_PINGPONG: {
loop->set_icon(get_theme_icon(SNAME("PingPongLoop"), SNAME("EditorIcons")));
loop->set_pressed(true);
} break;

View File

@ -478,10 +478,10 @@ class AnimationTrackEditor : public VBoxContainer {
struct TrackClipboard {
NodePath full_path;
NodePath base_path;
Animation::TrackType track_type = Animation::TrackType::TYPE_ANIMATION;
Animation::InterpolationType interp_type = Animation::InterpolationType::INTERPOLATION_CUBIC;
Animation::UpdateMode update_mode = Animation::UpdateMode::UPDATE_CAPTURE;
Animation::LoopMode loop_mode = Animation::LoopMode::LOOP_LINEAR;
Animation::TrackType track_type = Animation::TYPE_ANIMATION;
Animation::InterpolationType interp_type = Animation::INTERPOLATION_CUBIC;
Animation::UpdateMode update_mode = Animation::UPDATE_CAPTURE;
Animation::LoopMode loop_mode = Animation::LOOP_LINEAR;
bool loop_wrap = false;
bool enabled = false;

View File

@ -466,7 +466,7 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R
static const char *loop_strings[loop_string_count] = { "loop_mode", "loop", "cycle" };
for (int i = 0; i < loop_string_count; i++) {
if (_teststr(animname, loop_strings[i])) {
anim->set_loop_mode(Animation::LoopMode::LOOP_LINEAR);
anim->set_loop_mode(Animation::LOOP_LINEAR);
animname = _fixstr(animname, loop_strings[i]);
Ref<AnimationLibrary> library = ap->get_animation_library(ap->find_animation_library(anim));

View File

@ -1338,7 +1338,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() {
float pos = cpos + step_off * anim->get_step();
bool valid = anim->get_loop_mode() != Animation::LoopMode::LOOP_NONE || (pos >= 0 && pos <= anim->get_length());
bool valid = anim->get_loop_mode() != Animation::LOOP_NONE || (pos >= 0 && pos <= anim->get_length());
onion.captures_valid.write[cidx] = valid;
if (valid) {
player->seek(pos, true);

View File

@ -219,14 +219,14 @@ void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref<Animatio
}
}
double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek) {
double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_seek_root) {
if (blend_points_used == 0) {
return 0.0;
}
if (blend_points_used == 1) {
// only one point available, just play that animation
return blend_node(blend_points[0].name, blend_points[0].node, p_time, p_seek, 1.0, FILTER_IGNORE, false);
return blend_node(blend_points[0].name, blend_points[0].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false);
}
double blend_pos = get_parameter(blend_position);
@ -295,7 +295,7 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek) {
double max_time_remaining = 0.0;
for (int i = 0; i < blend_points_used; i++) {
double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false);
double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, weights[i], FILTER_IGNORE, false);
max_time_remaining = MAX(max_time_remaining, remaining);
}

View File

@ -93,7 +93,7 @@ public:
void set_value_label(const String &p_label);
String get_value_label() const;
double process(double p_time, bool p_seek) override;
double process(double p_time, bool p_seek, bool p_seek_root) override;
String get_caption() const override;
Ref<AnimationNode> get_child_by_name(const StringName &p_name) override;

View File

@ -432,7 +432,7 @@ void AnimationNodeBlendSpace2D::_blend_triangle(const Vector2 &p_pos, const Vect
r_weights[2] = w;
}
double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_seek_root) {
_update_triangles();
Vector2 blend_pos = get_parameter(blend_position);
@ -502,7 +502,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
for (int j = 0; j < 3; j++) {
if (i == triangle_points[j]) {
//blend with the given weight
double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false);
double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, blend_weights[j], FILTER_IGNORE, false);
if (first || t < mind) {
mind = t;
first = false;
@ -514,7 +514,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
if (!found) {
//ignore
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false);
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, false);
}
}
} else {
@ -539,16 +539,16 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
na_n->set_backward(na_c->is_backward());
}
//see how much animation remains
from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, 0.0, FILTER_IGNORE, false);
from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, p_seek_root, 0.0, FILTER_IGNORE, false);
}
mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, 1.0, FILTER_IGNORE, false);
mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, p_seek_root, 1.0, FILTER_IGNORE, false);
length_internal = from + mind;
closest = new_closest;
} else {
mind = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, p_seek, 1.0, FILTER_IGNORE, false);
mind = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false);
}
}

View File

@ -126,7 +126,7 @@ public:
void set_y_label(const String &p_label);
String get_y_label() const;
virtual double process(double p_time, bool p_seek) override;
virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
virtual String get_caption() const override;
Vector2 get_closest_point(const Vector2 &p_point);

View File

@ -64,7 +64,7 @@ void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const {
}
}
double AnimationNodeAnimation::process(double p_time, bool p_seek) {
double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_seek_root) {
AnimationPlayer *ap = state->player;
ERR_FAIL_COND_V(!ap, 0);
@ -101,8 +101,8 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek) {
}
}
if (anim->get_loop_mode() == Animation::LoopMode::LOOP_PINGPONG) {
if (anim_size) {
if (anim->get_loop_mode() == Animation::LOOP_PINGPONG) {
if (!Math::is_zero_approx(anim_size)) {
if ((int)Math::floor(abs(time - prev_time) / anim_size) % 2 == 0) {
if (prev_time > 0 && time <= 0) {
backward = !backward;
@ -116,22 +116,24 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek) {
time = Math::pingpong(time, anim_size);
}
} else {
if (anim->get_loop_mode() == Animation::LoopMode::LOOP_LINEAR) {
if (anim_size) {
if (anim->get_loop_mode() == Animation::LOOP_LINEAR) {
if (!Math::is_zero_approx(anim_size)) {
time = Math::fposmod(time, anim_size);
}
} else if (time < 0) {
step += time;
time = 0;
} else if (time > anim_size) {
step += anim_size - time;
time = anim_size;
}
backward = false;
}
if (play_mode == PLAY_MODE_FORWARD) {
blend_animation(animation, time, step, p_seek, 1.0, pingponged);
blend_animation(animation, time, step, p_seek, p_seek_root, 1.0, pingponged);
} else {
blend_animation(animation, anim_size - time, -step, p_seek, 1.0, pingponged);
blend_animation(animation, anim_size - time, -step, p_seek, p_seek_root, 1.0, pingponged);
}
set_parameter(this->time, time);
@ -251,7 +253,7 @@ bool AnimationNodeOneShot::has_filter() const {
return true;
}
double AnimationNodeOneShot::process(double p_time, bool p_seek) {
double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_seek_root) {
bool active = get_parameter(this->active);
bool prev_active = get_parameter(this->prev_active);
double time = get_parameter(this->time);
@ -274,7 +276,7 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek) {
}
if (!active) {
return blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
return blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync);
}
}
@ -311,12 +313,12 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek) {
double main_rem;
if (mix == MIX_MODE_ADD) {
main_rem = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync);
} else {
main_rem = blend_input(0, p_time, p_seek, 1.0 - blend, FILTER_BLEND, !sync);
main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_BLEND, !sync);
}
double os_rem = blend_input(1, os_seek ? time : p_time, os_seek, blend, FILTER_PASS, false);
double os_rem = blend_input(1, os_seek ? time : p_time, os_seek, p_seek_root, blend, FILTER_PASS, false);
if (do_start) {
remaining = os_rem;
@ -420,10 +422,10 @@ bool AnimationNodeAdd2::has_filter() const {
return true;
}
double AnimationNodeAdd2::process(double p_time, bool p_seek) {
double AnimationNodeAdd2::process(double p_time, bool p_seek, bool p_seek_root) {
double amount = get_parameter(add_amount);
double rem0 = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync);
double rem0 = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync);
blend_input(1, p_time, p_seek, p_seek_root, amount, FILTER_PASS, !sync);
return rem0;
}
@ -466,11 +468,11 @@ bool AnimationNodeAdd3::has_filter() const {
return true;
}
double AnimationNodeAdd3::process(double p_time, bool p_seek) {
double AnimationNodeAdd3::process(double p_time, bool p_seek, bool p_seek_root) {
double amount = get_parameter(add_amount);
blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_PASS, !sync);
double rem0 = blend_input(1, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_PASS, !sync);
blend_input(0, p_time, p_seek, p_seek_root, MAX(0, -amount), FILTER_PASS, !sync);
double rem0 = blend_input(1, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync);
blend_input(2, p_time, p_seek, p_seek_root, MAX(0, amount), FILTER_PASS, !sync);
return rem0;
}
@ -502,11 +504,11 @@ String AnimationNodeBlend2::get_caption() const {
return "Blend2";
}
double AnimationNodeBlend2::process(double p_time, bool p_seek) {
double AnimationNodeBlend2::process(double p_time, bool p_seek, bool p_seek_root) {
double amount = get_parameter(blend_amount);
double rem0 = blend_input(0, p_time, p_seek, 1.0 - amount, FILTER_BLEND, !sync);
double rem1 = blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync);
double rem0 = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - amount, FILTER_BLEND, !sync);
double rem1 = blend_input(1, p_time, p_seek, p_seek_root, amount, FILTER_PASS, !sync);
return amount > 0.5 ? rem1 : rem0; //hacky but good enough
}
@ -557,11 +559,11 @@ bool AnimationNodeBlend3::is_using_sync() const {
return sync;
}
double AnimationNodeBlend3::process(double p_time, bool p_seek) {
double AnimationNodeBlend3::process(double p_time, bool p_seek, bool p_seek_root) {
double amount = get_parameter(blend_amount);
double rem0 = blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_IGNORE, !sync);
double rem1 = blend_input(1, p_time, p_seek, 1.0 - ABS(amount), FILTER_IGNORE, !sync);
double rem2 = blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_IGNORE, !sync);
double rem0 = blend_input(0, p_time, p_seek, p_seek_root, MAX(0, -amount), FILTER_IGNORE, !sync);
double rem1 = blend_input(1, p_time, p_seek, p_seek_root, 1.0 - ABS(amount), FILTER_IGNORE, !sync);
double rem2 = blend_input(2, p_time, p_seek, p_seek_root, MAX(0, amount), FILTER_IGNORE, !sync);
return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); //hacky but good enough
}
@ -595,12 +597,12 @@ String AnimationNodeTimeScale::get_caption() const {
return "TimeScale";
}
double AnimationNodeTimeScale::process(double p_time, bool p_seek) {
double AnimationNodeTimeScale::process(double p_time, bool p_seek, bool p_seek_root) {
double scale = get_parameter(this->scale);
if (p_seek) {
return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false);
return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, false);
} else {
return blend_input(0, p_time * scale, false, 1.0, FILTER_IGNORE, false);
return blend_input(0, p_time * scale, false, p_seek_root, 1.0, FILTER_IGNORE, false);
}
}
@ -625,16 +627,16 @@ String AnimationNodeTimeSeek::get_caption() const {
return "Seek";
}
double AnimationNodeTimeSeek::process(double p_time, bool p_seek) {
double AnimationNodeTimeSeek::process(double p_time, bool p_seek, bool p_seek_root) {
double seek_pos = get_parameter(this->seek_pos);
if (p_seek) {
return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false);
return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, false);
} else if (seek_pos >= 0) {
double ret = blend_input(0, seek_pos, true, 1.0, FILTER_IGNORE, false);
double ret = blend_input(0, seek_pos, true, true, 1.0, FILTER_IGNORE, false);
set_parameter(this->seek_pos, -1.0); //reset
return ret;
} else {
return blend_input(0, p_time, false, 1.0, FILTER_IGNORE, false);
return blend_input(0, p_time, false, p_seek_root, 1.0, FILTER_IGNORE, false);
}
}
@ -726,7 +728,7 @@ float AnimationNodeTransition::get_cross_fade_time() const {
return xfade;
}
double AnimationNodeTransition::process(double p_time, bool p_seek) {
double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_root) {
int current = get_parameter(this->current);
int prev = get_parameter(this->prev);
int prev_current = get_parameter(this->prev_current);
@ -754,7 +756,7 @@ double AnimationNodeTransition::process(double p_time, bool p_seek) {
if (prev < 0) { // process current animation, check for transition
rem = blend_input(current, p_time, p_seek, 1.0, FILTER_IGNORE, false);
rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false);
if (p_seek) {
time = p_time;
@ -772,16 +774,16 @@ double AnimationNodeTransition::process(double p_time, bool p_seek) {
if (!p_seek && switched) { //just switched, seek to start of current
rem = blend_input(current, 0, true, 1.0 - blend, FILTER_IGNORE, false);
rem = blend_input(current, 0, true, p_seek_root, 1.0 - blend, FILTER_IGNORE, false);
} else {
rem = blend_input(current, p_time, p_seek, 1.0 - blend, FILTER_IGNORE, false);
rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_IGNORE, false);
}
if (p_seek) { // don't seek prev animation
blend_input(prev, 0, false, blend, FILTER_IGNORE, false);
blend_input(prev, 0, false, p_seek_root, blend, FILTER_IGNORE, false);
time = p_time;
} else {
blend_input(prev, p_time, false, blend, FILTER_IGNORE, false);
blend_input(prev, p_time, false, p_seek_root, blend, FILTER_IGNORE, false);
time += p_time;
prev_xfading -= p_time;
if (prev_xfading < 0) {
@ -844,8 +846,8 @@ String AnimationNodeOutput::get_caption() const {
return "Output";
}
double AnimationNodeOutput::process(double p_time, bool p_seek) {
return blend_input(0, p_time, p_seek, 1.0);
double AnimationNodeOutput::process(double p_time, bool p_seek, bool p_seek_root) {
return blend_input(0, p_time, p_seek, p_seek_root, 1.0);
}
AnimationNodeOutput::AnimationNodeOutput() {
@ -1057,9 +1059,9 @@ String AnimationNodeBlendTree::get_caption() const {
return "BlendTree";
}
double AnimationNodeBlendTree::process(double p_time, bool p_seek) {
double AnimationNodeBlendTree::process(double p_time, bool p_seek, bool p_seek_root) {
Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output].node;
return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, 1.0);
return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, p_seek_root, 1.0);
}
void AnimationNodeBlendTree::get_node_list(List<StringName> *r_list) {

View File

@ -53,7 +53,7 @@ public:
static Vector<String> (*get_editable_animation_list)();
virtual String get_caption() const override;
virtual double process(double p_time, bool p_seek) override;
virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
void set_animation(const StringName &p_name);
StringName get_animation() const;
@ -87,8 +87,8 @@ public:
};
private:
float fade_in = 0.1;
float fade_out = 0.1;
float fade_in = 0.0;
float fade_out = 0.0;
bool autorestart = false;
float autorestart_delay = 1.0;
@ -138,7 +138,7 @@ public:
bool is_using_sync() const;
virtual bool has_filter() const override;
virtual double process(double p_time, bool p_seek) override;
virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeOneShot();
};
@ -164,7 +164,7 @@ public:
bool is_using_sync() const;
virtual bool has_filter() const override;
virtual double process(double p_time, bool p_seek) override;
virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeAdd2();
};
@ -188,7 +188,7 @@ public:
bool is_using_sync() const;
virtual bool has_filter() const override;
virtual double process(double p_time, bool p_seek) override;
virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeAdd3();
};
@ -207,7 +207,7 @@ public:
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
virtual String get_caption() const override;
virtual double process(double p_time, bool p_seek) override;
virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
void set_use_sync(bool p_sync);
bool is_using_sync() const;
@ -234,7 +234,7 @@ public:
void set_use_sync(bool p_sync);
bool is_using_sync() const;
double process(double p_time, bool p_seek) override;
double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeBlend3();
};
@ -252,7 +252,7 @@ public:
virtual String get_caption() const override;
double process(double p_time, bool p_seek) override;
double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeTimeScale();
};
@ -271,7 +271,7 @@ public:
virtual String get_caption() const override;
double process(double p_time, bool p_seek) override;
double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeTimeSeek();
};
@ -329,7 +329,7 @@ public:
void set_cross_fade_time(float p_fade);
float get_cross_fade_time() const;
double process(double p_time, bool p_seek) override;
double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeTransition();
};
@ -339,7 +339,7 @@ class AnimationNodeOutput : public AnimationNode {
public:
virtual String get_caption() const override;
virtual double process(double p_time, bool p_seek) override;
virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeOutput();
};
@ -408,7 +408,7 @@ public:
void get_node_connections(List<NodeConnection> *r_connections) const;
virtual String get_caption() const override;
virtual double process(double p_time, bool p_seek) override;
virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
void get_node_list(List<StringName> *r_list);

View File

@ -292,7 +292,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
return true;
}
double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek) {
double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_seek_root) {
//if not playing and it can restart, then restart
if (!playing && start_request == StringName()) {
if (!stop_request && p_state_machine->start_node) {
@ -356,7 +356,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
current = p_state_machine->start_node;
}
len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 1.0, AnimationNode::FILTER_IGNORE, false);
len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 1.0, AnimationNode::FILTER_IGNORE, false);
pos_current = 0;
}
@ -381,10 +381,10 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
}
float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, fade_blend, AnimationNode::FILTER_IGNORE, false);
float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_seek_root, fade_blend, AnimationNode::FILTER_IGNORE, false);
if (fading_from != StringName()) {
p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false);
p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_seek_root, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false);
}
//guess playback position
@ -538,12 +538,12 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
current = next;
if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) {
len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false);
len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false);
pos_current = MIN(pos_current, len_current);
p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, 0, AnimationNode::FILTER_IGNORE, false);
p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false);
} else {
len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false);
len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false);
pos_current = 0;
}
@ -1071,11 +1071,11 @@ Vector2 AnimationNodeStateMachine::get_graph_offset() const {
return graph_offset;
}
double AnimationNodeStateMachine::process(double p_time, bool p_seek) {
double AnimationNodeStateMachine::process(double p_time, bool p_seek, bool p_seek_root) {
Ref<AnimationNodeStateMachinePlayback> playback = get_parameter(this->playback);
ERR_FAIL_COND_V(playback.is_null(), 0.0);
return playback->process(this, p_time, p_seek);
return playback->process(this, p_time, p_seek, p_seek_root);
}
String AnimationNodeStateMachine::get_caption() const {

View File

@ -120,7 +120,7 @@ class AnimationNodeStateMachinePlayback : public Resource {
bool _travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel);
double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek);
double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_seek_root);
bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
@ -226,7 +226,7 @@ public:
void set_graph_offset(const Vector2 &p_offset);
Vector2 get_graph_offset() const;
virtual double process(double p_time, bool p_seek) override;
virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
virtual String get_caption() const override;
virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name) override;

View File

@ -832,7 +832,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
nc->audio_start = p_time;
}
} else if (nc->audio_playing) {
bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE;
bool loop = a->get_loop_mode() != Animation::LOOP_NONE;
bool stop = false;
@ -883,15 +883,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
double at_anim_pos = 0.0;
switch (anim->get_loop_mode()) {
case Animation::LoopMode::LOOP_NONE: {
case Animation::LOOP_NONE: {
at_anim_pos = MIN((double)anim->get_length(), p_time - pos); //seek to end
} break;
case Animation::LoopMode::LOOP_LINEAR: {
case Animation::LOOP_LINEAR: {
at_anim_pos = Math::fposmod(p_time - pos, (double)anim->get_length()); //seek to loop
} break;
case Animation::LoopMode::LOOP_PINGPONG: {
case Animation::LOOP_PINGPONG: {
at_anim_pos = Math::pingpong(p_time - pos, (double)anim->get_length());
} break;
@ -944,7 +944,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
int pingponged = 0;
switch (cd.from->animation->get_loop_mode()) {
case Animation::LoopMode::LOOP_NONE: {
case Animation::LOOP_NONE: {
if (next_pos < 0) {
next_pos = 0;
} else if (next_pos > len) {
@ -969,7 +969,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
}
} break;
case Animation::LoopMode::LOOP_LINEAR: {
case Animation::LOOP_LINEAR: {
double looped_next_pos = Math::fposmod(next_pos, (double)len);
if (looped_next_pos == 0 && next_pos != 0) {
// Loop multiples of the length to it, rather than 0
@ -980,7 +980,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
}
} break;
case Animation::LoopMode::LOOP_PINGPONG: {
case Animation::LOOP_PINGPONG: {
if ((int)Math::floor(abs(next_pos - cd.pos) / len) % 2 == 0) {
if (next_pos < 0 && cd.pos >= 0) {
cd.speed_scale *= -1.0;

View File

@ -88,7 +88,7 @@ void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) {
}
}
void AnimationNode::blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged) {
void AnimationNode::blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_seek_root, real_t p_blend, int p_pingponged) {
ERR_FAIL_COND(!state);
ERR_FAIL_COND(!state->player->has_animation(p_animation));
@ -115,17 +115,18 @@ void AnimationNode::blend_animation(const StringName &p_animation, double p_time
anim_state.animation = animation;
anim_state.seeked = p_seeked;
anim_state.pingponged = p_pingponged;
anim_state.seek_root = p_seek_root;
state->animation_states.push_back(anim_state);
}
double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections) {
double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, bool p_seek_root, const Vector<StringName> &p_connections) {
base_path = p_base_path;
parent = p_parent;
connections = p_connections;
state = p_state;
double t = process(p_time, p_seek);
double t = process(p_time, p_seek, p_seek_root);
state = nullptr;
parent = nullptr;
@ -144,7 +145,7 @@ void AnimationNode::make_invalid(const String &p_reason) {
state->invalid_reasons += String::utf8("") + p_reason;
}
double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize) {
ERR_FAIL_INDEX_V(p_input, inputs.size(), 0);
ERR_FAIL_COND_V(!state, 0);
@ -163,7 +164,7 @@ double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_
//inputs.write[p_input].last_pass = state->last_pass;
real_t activity = 0.0;
double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_blend, p_filter, p_optimize, &activity);
double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_optimize, &activity);
Vector<AnimationTree::Activity> *activity_ptr = state->tree->input_activity_map.getptr(base_path);
@ -174,11 +175,11 @@ double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_
return ret;
}
double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_blend, p_filter, p_optimize);
double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize) {
return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_optimize);
}
double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) {
double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) {
ERR_FAIL_COND_V(!p_node.is_valid(), 0);
ERR_FAIL_COND_V(!state, 0);
@ -286,9 +287,9 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
}
if (!p_seek && p_optimize && !any_valid) {
return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_connections);
return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_seek_root, p_connections);
}
return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_connections);
return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_seek_root, p_connections);
}
int AnimationNode::get_input_count() const {
@ -332,9 +333,9 @@ void AnimationNode::remove_input(int p_index) {
emit_changed();
}
double AnimationNode::process(double p_time, bool p_seek) {
double AnimationNode::process(double p_time, bool p_seek, bool p_seek_root) {
double ret;
if (GDVIRTUAL_CALL(_process, p_time, p_seek, ret)) {
if (GDVIRTUAL_CALL(_process, p_time, p_seek, p_seek_root, ret)) {
return ret;
}
@ -418,9 +419,9 @@ void AnimationNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters);
ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters);
ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0));
ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true));
ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true));
ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "seek_root", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0));
ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "seek_root", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true));
ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "seek_root", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true));
ClassDB::bind_method(D_METHOD("set_parameter", "name", "value"), &AnimationNode::set_parameter);
ClassDB::bind_method(D_METHOD("get_parameter", "name"), &AnimationNode::get_parameter);
@ -432,7 +433,7 @@ void AnimationNode::_bind_methods() {
GDVIRTUAL_BIND(_get_parameter_list);
GDVIRTUAL_BIND(_get_child_by_name, "name");
GDVIRTUAL_BIND(_get_parameter_default_value, "parameter");
GDVIRTUAL_BIND(_process, "time", "seek");
GDVIRTUAL_BIND(_process, "time", "seek", "seek_root");
GDVIRTUAL_BIND(_get_caption);
GDVIRTUAL_BIND(_has_filter);
@ -859,7 +860,6 @@ void AnimationTree::_process_graph(double p_delta) {
_update_properties(); //if properties need updating, update them
//check all tracks, see if they need modification
root_motion_transform = Transform3D();
if (!root.is_valid()) {
@ -938,11 +938,11 @@ void AnimationTree::_process_graph(double p_delta) {
{
if (started) {
//if started, seek
root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, 0, true, Vector<StringName>());
root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, 0, true, false, Vector<StringName>());
started = false;
}
root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, p_delta, false, Vector<StringName>());
root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, p_delta, false, false, Vector<StringName>());
}
if (!state.valid) {
@ -962,6 +962,7 @@ void AnimationTree::_process_graph(double p_delta) {
int pingponged = as.pingponged;
#ifndef _3D_DISABLED
bool backward = signbit(delta);
bool calc_root = !seeked || as.seek_root;
#endif // _3D_DISABLED
for (int i = 0; i < a->get_track_count(); i++) {
@ -990,7 +991,7 @@ void AnimationTree::_process_graph(double p_delta) {
case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
if (track->root_motion) {
if (track->root_motion && calc_root) {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->loc = Vector3(0, 0, 0);
@ -1052,7 +1053,7 @@ void AnimationTree::_process_graph(double p_delta) {
}
a->position_track_interpolate(i, 0, &loc[1]);
t->loc += (loc[1] - loc[0]) * blend;
prev_time = 0;
prev_time = (double)a->get_length();
}
}
@ -1086,7 +1087,7 @@ void AnimationTree::_process_graph(double p_delta) {
case Animation::TYPE_ROTATION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
if (track->root_motion) {
if (track->root_motion && calc_root) {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->loc = Vector3(0, 0, 0);
@ -1148,7 +1149,7 @@ void AnimationTree::_process_graph(double p_delta) {
}
a->rotation_track_interpolate(i, 0, &rot[1]);
t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
prev_time = 0;
prev_time = (double)a->get_length();
}
}
@ -1182,7 +1183,7 @@ void AnimationTree::_process_graph(double p_delta) {
case Animation::TYPE_SCALE_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
if (track->root_motion) {
if (track->root_motion && calc_root) {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->loc = Vector3(0, 0, 0);
@ -1244,7 +1245,7 @@ void AnimationTree::_process_graph(double p_delta) {
}
a->scale_track_interpolate(i, 0, &scale[1]);
t->scale += (scale[1] - scale[0]) * blend;
prev_time = 0;
prev_time = (double)a->get_length();
}
}
@ -1301,8 +1302,7 @@ void AnimationTree::_process_graph(double p_delta) {
Animation::UpdateMode update_mode = a->value_track_get_update_mode(i);
if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) { //delta == 0 means seek
if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) {
Variant value = a->value_track_interpolate(i, time);
if (value == Variant()) {
@ -1443,7 +1443,7 @@ void AnimationTree::_process_graph(double p_delta) {
t->start = time;
}
} else if (t->playing) {
bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE;
bool loop = a->get_loop_mode() != Animation::LOOP_NONE;
bool stop = false;
@ -1512,13 +1512,13 @@ void AnimationTree::_process_graph(double p_delta) {
double at_anim_pos = 0.0;
switch (anim->get_loop_mode()) {
case Animation::LoopMode::LOOP_NONE: {
case Animation::LOOP_NONE: {
at_anim_pos = MAX((double)anim->get_length(), time - pos); //seek to end
} break;
case Animation::LoopMode::LOOP_LINEAR: {
case Animation::LOOP_LINEAR: {
at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); //seek to loop
} break;
case Animation::LoopMode::LOOP_PINGPONG: {
case Animation::LOOP_PINGPONG: {
at_anim_pos = Math::pingpong(time - pos, (double)a->get_length());
} break;
default:

View File

@ -68,6 +68,7 @@ public:
const Vector<real_t> *track_blends = nullptr;
real_t blend = 0.0;
bool seeked = false;
bool seek_root = false;
int pingponged = 0;
};
@ -85,7 +86,7 @@ public:
Vector<real_t> blends;
State *state = nullptr;
double _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections);
double _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, bool p_seek_root, const Vector<StringName> &p_connections);
//all this is temporary
StringName base_path;
@ -98,12 +99,12 @@ public:
Array _get_filters() const;
void _set_filters(const Array &p_filters);
friend class AnimationNodeBlendTree;
double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr);
double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr);
protected:
void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged = 0);
double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
double blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_seek_root, real_t p_blend, int p_pingponged = 0);
double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
double blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
void make_invalid(const String &p_reason);
@ -115,7 +116,7 @@ protected:
GDVIRTUAL0RC(Array, _get_parameter_list)
GDVIRTUAL1RC(Ref<AnimationNode>, _get_child_by_name, StringName)
GDVIRTUAL1RC(Variant, _get_parameter_default_value, StringName)
GDVIRTUAL2RC(double, _process, double, bool)
GDVIRTUAL3RC(double, _process, double, bool, bool)
GDVIRTUAL0RC(String, _get_caption)
GDVIRTUAL0RC(bool, _has_filter)
@ -133,7 +134,7 @@ public:
virtual void get_child_nodes(List<ChildNode> *r_child_nodes);
virtual double process(double p_time, bool p_seek);
virtual double process(double p_time, bool p_seek, bool p_seek_root);
virtual String get_caption() const;
int get_input_count() const;