1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-04 12:00:25 +00:00

Rework AnimationNode process for retrieving the semantic time info

This commit is contained in:
Silc Lizard (Tokage) Renew
2024-01-08 06:08:10 +09:00
parent fe01776f05
commit 6dd410854c
18 changed files with 704 additions and 319 deletions

View File

@@ -44,7 +44,26 @@ StringName AnimationNodeAnimation::get_animation() const {
Vector<String> (*AnimationNodeAnimation::get_editable_animation_list)() = nullptr;
void AnimationNodeAnimation::get_parameter_list(List<PropertyInfo> *r_list) const {
r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
AnimationNode::get_parameter_list(r_list);
}
AnimationNode::NodeTimeInfo AnimationNodeAnimation::get_node_time_info() const {
NodeTimeInfo nti;
if (!process_state->tree->has_animation(animation)) {
return nti;
}
if (use_custom_timeline) {
nti.length = timeline_length;
nti.loop_mode = loop_mode;
} else {
Ref<Animation> anim = process_state->tree->get_animation(animation);
nti.length = (double)anim->get_length();
nti.loop_mode = anim->get_loop_mode();
}
nti.position = get_parameter(current_position);
return nti;
}
void AnimationNodeAnimation::_validate_property(PropertyInfo &p_property) const {
@@ -62,11 +81,34 @@ void AnimationNodeAnimation::_validate_property(PropertyInfo &p_property) const
p_property.hint_string = anims;
}
}
if (!use_custom_timeline) {
if (p_property.name == "timeline_length" || p_property.name == "start_offset" || p_property.name == "loop_mode" || p_property.name == "stretch_time_scale") {
p_property.usage = PROPERTY_USAGE_NONE;
}
}
}
double AnimationNodeAnimation::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
double cur_time = get_parameter(time);
AnimationNode::NodeTimeInfo AnimationNodeAnimation::process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
process_state->is_testing = p_test_only;
AnimationMixer::PlaybackInfo pi = p_playback_info;
if (p_playback_info.seeked) {
pi.delta = get_node_time_info().position - p_playback_info.time;
} else {
pi.time = get_node_time_info().position + (backward ? -p_playback_info.delta : p_playback_info.delta);
}
NodeTimeInfo nti = _process(pi, p_test_only);
if (!p_test_only) {
set_node_time_info(nti);
}
return nti;
}
AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
if (!process_state->tree->has_animation(animation)) {
AnimationNodeBlendTree *tree = Object::cast_to<AnimationNodeBlendTree>(node_state.parent);
if (tree) {
@@ -77,87 +119,129 @@ double AnimationNodeAnimation::_process(const AnimationMixer::PlaybackInfo p_pla
make_invalid(vformat(RTR("Animation not found: '%s'"), animation));
}
return 0;
return NodeTimeInfo();
}
Ref<Animation> anim = process_state->tree->get_animation(animation);
double anim_size = (double)anim->get_length();
double step = 0.0;
double prev_time = cur_time;
NodeTimeInfo cur_nti = get_node_time_info();
double cur_len = cur_nti.length;
double cur_time = p_playback_info.time;
double cur_delta = p_playback_info.delta;
Animation::LoopMode cur_loop_mode = cur_nti.loop_mode;
double prev_time = cur_nti.position;
Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
bool node_backward = play_mode == PLAY_MODE_BACKWARD;
double p_time = p_playback_info.time;
bool p_seek = p_playback_info.seeked;
bool p_is_external_seeking = p_playback_info.is_external_seeking;
if (p_playback_info.seeked) {
step = p_time - cur_time;
cur_time = p_time;
} else {
p_time *= backward ? -1.0 : 1.0;
cur_time = cur_time + p_time;
step = p_time;
}
bool is_just_looped = false;
bool is_looping = false;
if (anim->get_loop_mode() == Animation::LOOP_PINGPONG) {
if (!Math::is_zero_approx(anim_size)) {
if (prev_time >= 0 && cur_time < 0) {
backward = !backward;
looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
// 1. Progress for AnimationNode.
if (cur_loop_mode != Animation::LOOP_NONE) {
if (cur_loop_mode == Animation::LOOP_LINEAR) {
if (!Math::is_zero_approx(cur_len)) {
if (prev_time <= cur_len && cur_time > cur_len) {
is_just_looped = true; // Don't break with negative timescale since remain will not be 0.
}
cur_time = Math::fposmod(cur_time, cur_len);
}
if (prev_time <= anim_size && cur_time > anim_size) {
backward = !backward;
looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
backward = false;
} else {
if (!Math::is_zero_approx(cur_len)) {
if (prev_time >= 0 && cur_time < 0) {
backward = !backward;
} else if (prev_time <= cur_len && cur_time > cur_len) {
backward = !backward;
is_just_looped = true; // Don't break with negative timescale since remain will not be 0.
}
cur_time = Math::pingpong(cur_time, cur_len);
}
cur_time = Math::pingpong(cur_time, anim_size);
}
is_looping = true;
} else if (anim->get_loop_mode() == Animation::LOOP_LINEAR) {
if (!Math::is_zero_approx(anim_size)) {
if (prev_time >= 0 && cur_time < 0) {
looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
}
if (prev_time <= anim_size && cur_time > anim_size) {
looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
}
cur_time = Math::fposmod(cur_time, anim_size);
}
backward = false;
is_looping = true;
} else {
if (cur_time < 0) {
step += cur_time;
cur_delta += cur_time;
cur_time = 0;
} else if (cur_time > anim_size) {
step += anim_size - cur_time;
cur_time = anim_size;
} else if (cur_time > cur_len) {
cur_delta += cur_time - cur_len;
cur_time = cur_len;
}
backward = false;
// If ended, don't progress animation. So set delta to 0.
if (p_time > 0) {
// If ended, don't progress AnimationNode. So set delta to 0.
if (!Math::is_zero_approx(cur_delta)) {
if (play_mode == PLAY_MODE_FORWARD) {
if (prev_time >= anim_size) {
step = 0;
if (prev_time >= cur_len) {
cur_delta = 0;
}
} else {
if (prev_time <= 0) {
step = 0;
cur_delta = 0;
}
}
}
}
// 2. For return, store "AnimationNode" time info here, not "Animation" time info as below.
NodeTimeInfo nti;
nti.length = cur_len;
nti.position = cur_time;
nti.delta = cur_delta;
nti.loop_mode = cur_loop_mode;
nti.is_just_looped = is_just_looped;
// 3. Progress for Animation.
double prev_playback_time = prev_time - start_offset;
double cur_playback_time = cur_time - start_offset;
if (stretch_time_scale) {
double mlt = anim_size / cur_len;
cur_playback_time *= mlt;
cur_delta *= mlt;
}
if (cur_loop_mode == Animation::LOOP_LINEAR) {
if (!Math::is_zero_approx(anim_size)) {
prev_playback_time = Math::fposmod(prev_playback_time, anim_size);
cur_playback_time = Math::fposmod(cur_playback_time, anim_size);
if (prev_playback_time >= 0 && cur_playback_time < 0) {
looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
}
if (prev_playback_time <= anim_size && cur_playback_time > anim_size) {
looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
}
}
} else if (cur_loop_mode == Animation::LOOP_PINGPONG) {
if (!Math::is_zero_approx(anim_size)) {
if (Math::fposmod(cur_playback_time, anim_size * 2.0) >= anim_size) {
cur_delta = -cur_delta; // Needed for retrieveing discrete keys correctly.
}
prev_playback_time = Math::pingpong(prev_playback_time, anim_size);
cur_playback_time = Math::pingpong(cur_playback_time, anim_size);
if (prev_playback_time >= 0 && cur_playback_time < 0) {
looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
}
if (prev_playback_time <= anim_size && cur_playback_time > anim_size) {
looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
}
}
} else {
if (cur_playback_time < 0) {
cur_playback_time = 0;
} else if (cur_playback_time > anim_size) {
cur_playback_time = anim_size;
}
// Emit start & finish signal. Internally, the detections are the same for backward.
// We should use call_deferred since the track keys are still being processed.
if (process_state->tree && !p_test_only) {
// AnimationTree uses seek to 0 "internally" to process the first key of the animation, which is used as the start detection.
if (p_seek && !p_is_external_seeking && cur_time == 0) {
if (p_seek && !p_is_external_seeking && cur_playback_time == 0) {
process_state->tree->call_deferred(SNAME("emit_signal"), "animation_started", animation);
}
// Finished.
if (prev_time < anim_size && cur_time >= anim_size) {
if (prev_time - start_offset < anim_size && cur_playback_time >= anim_size) {
process_state->tree->call_deferred(SNAME("emit_signal"), "animation_finished", animation);
}
}
@@ -166,19 +250,18 @@ double AnimationNodeAnimation::_process(const AnimationMixer::PlaybackInfo p_pla
if (!p_test_only) {
AnimationMixer::PlaybackInfo pi = p_playback_info;
if (play_mode == PLAY_MODE_FORWARD) {
pi.time = cur_time;
pi.delta = step;
pi.time = cur_playback_time;
pi.delta = cur_delta;
} else {
pi.time = anim_size - cur_time;
pi.delta = -step;
pi.time = anim_size - cur_playback_time;
pi.delta = -cur_delta;
}
pi.weight = 1.0;
pi.looped_flag = looped_flag;
blend_animation(animation, pi);
}
set_parameter(time, cur_time);
return is_looping ? HUGE_LENGTH : anim_size - cur_time;
return nti;
}
String AnimationNodeAnimation::get_caption() const {
@@ -201,6 +284,48 @@ bool AnimationNodeAnimation::is_backward() const {
return backward;
}
void AnimationNodeAnimation::set_use_custom_timeline(bool p_use_custom_timeline) {
use_custom_timeline = p_use_custom_timeline;
notify_property_list_changed();
}
bool AnimationNodeAnimation::is_using_custom_timeline() const {
return use_custom_timeline;
}
void AnimationNodeAnimation::set_timeline_length(double p_length) {
timeline_length = p_length;
}
double AnimationNodeAnimation::get_timeline_length() const {
return timeline_length;
}
void AnimationNodeAnimation::set_stretch_time_scale(bool p_strech_time_scale) {
stretch_time_scale = p_strech_time_scale;
notify_property_list_changed();
}
bool AnimationNodeAnimation::is_stretching_time_scale() const {
return stretch_time_scale;
}
void AnimationNodeAnimation::set_start_offset(double p_offset) {
start_offset = p_offset;
}
double AnimationNodeAnimation::get_start_offset() const {
return start_offset;
}
void AnimationNodeAnimation::set_loop_mode(Animation::LoopMode p_loop_mode) {
loop_mode = p_loop_mode;
}
Animation::LoopMode AnimationNodeAnimation::get_loop_mode() const {
return loop_mode;
}
void AnimationNodeAnimation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_animation", "name"), &AnimationNodeAnimation::set_animation);
ClassDB::bind_method(D_METHOD("get_animation"), &AnimationNodeAnimation::get_animation);
@@ -208,8 +333,28 @@ void AnimationNodeAnimation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_play_mode", "mode"), &AnimationNodeAnimation::set_play_mode);
ClassDB::bind_method(D_METHOD("get_play_mode"), &AnimationNodeAnimation::get_play_mode);
ClassDB::bind_method(D_METHOD("set_use_custom_timeline", "use_custom_timeline"), &AnimationNodeAnimation::set_use_custom_timeline);
ClassDB::bind_method(D_METHOD("is_using_custom_timeline"), &AnimationNodeAnimation::is_using_custom_timeline);
ClassDB::bind_method(D_METHOD("set_timeline_length", "timeline_length"), &AnimationNodeAnimation::set_timeline_length);
ClassDB::bind_method(D_METHOD("get_timeline_length"), &AnimationNodeAnimation::get_timeline_length);
ClassDB::bind_method(D_METHOD("set_stretch_time_scale", "stretch_time_scale"), &AnimationNodeAnimation::set_stretch_time_scale);
ClassDB::bind_method(D_METHOD("is_stretching_time_scale"), &AnimationNodeAnimation::is_stretching_time_scale);
ClassDB::bind_method(D_METHOD("set_start_offset", "start_offset"), &AnimationNodeAnimation::set_start_offset);
ClassDB::bind_method(D_METHOD("get_start_offset"), &AnimationNodeAnimation::get_start_offset);
ClassDB::bind_method(D_METHOD("set_loop_mode", "loop_mode"), &AnimationNodeAnimation::set_loop_mode);
ClassDB::bind_method(D_METHOD("get_loop_mode"), &AnimationNodeAnimation::get_loop_mode);
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation");
ADD_PROPERTY(PropertyInfo(Variant::INT, "play_mode", PROPERTY_HINT_ENUM, "Forward,Backward"), "set_play_mode", "get_play_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_custom_timeline"), "set_use_custom_timeline", "is_using_custom_timeline");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "timeline_length", PROPERTY_HINT_RANGE, "0.001,60,0.001,or_greater,or_less,hide_slider,suffix:s"), "set_timeline_length", "get_timeline_length");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stretch_time_scale"), "set_stretch_time_scale", "is_stretching_time_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "start_offset", PROPERTY_HINT_RANGE, "-60,60,0.001,or_greater,or_less,hide_slider,suffix:s"), "set_start_offset", "get_start_offset");
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Ping-Pong"), "set_loop_mode", "get_loop_mode");
BIND_ENUM_CONSTANT(PLAY_MODE_FORWARD);
BIND_ENUM_CONSTANT(PLAY_MODE_BACKWARD);
@@ -240,16 +385,21 @@ AnimationNodeSync::AnimationNodeSync() {
////////////////////////////////////////////////////////
void AnimationNodeOneShot::get_parameter_list(List<PropertyInfo> *r_list) const {
AnimationNode::get_parameter_list(r_list);
r_list->push_back(PropertyInfo(Variant::BOOL, active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY));
r_list->push_back(PropertyInfo(Variant::BOOL, internal_active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY));
r_list->push_back(PropertyInfo(Variant::INT, request, PROPERTY_HINT_ENUM, ",Fire,Abort,Fade Out"));
r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, fade_in_remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, fade_out_remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, time_to_restart, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
}
Variant AnimationNodeOneShot::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 == request) {
return ONE_SHOT_REQUEST_NONE;
} else if (p_parameter == active || p_parameter == internal_active) {
@@ -262,6 +412,10 @@ Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_pa
}
bool AnimationNodeOneShot::is_parameter_read_only(const StringName &p_parameter) const {
if (AnimationNode::is_parameter_read_only(p_parameter)) {
return true;
}
if (p_parameter == active || p_parameter == internal_active) {
return true;
}
@@ -332,6 +486,14 @@ AnimationNodeOneShot::MixMode AnimationNodeOneShot::get_mix_mode() const {
return mix;
}
void AnimationNodeOneShot::set_break_loop_at_end(bool p_enable) {
break_loop_at_end = p_enable;
}
bool AnimationNodeOneShot::is_loop_broken_at_end() const {
return break_loop_at_end;
}
String AnimationNodeOneShot::get_caption() const {
return "OneShot";
}
@@ -340,14 +502,14 @@ bool AnimationNodeOneShot::has_filter() const {
return true;
}
double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
OneShotRequest cur_request = static_cast<OneShotRequest>((int)get_parameter(request));
bool cur_active = get_parameter(active);
bool cur_internal_active = get_parameter(internal_active);
double cur_time = get_parameter(time);
double cur_remaining = get_parameter(remaining);
double cur_fade_out_remaining = get_parameter(fade_out_remaining);
NodeTimeInfo cur_nti = get_node_time_info();
double cur_time_to_restart = get_parameter(time_to_restart);
double cur_fade_in_remaining = get_parameter(fade_in_remaining);
double cur_fade_out_remaining = get_parameter(fade_out_remaining);
set_parameter(request, ONE_SHOT_REQUEST_NONE);
@@ -356,6 +518,8 @@ double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playb
bool is_fading_out = cur_active == true && cur_internal_active == false;
double p_time = p_playback_info.time;
double p_delta = p_playback_info.delta;
double abs_delta = Math::abs(p_delta);
bool p_seek = p_playback_info.seeked;
bool p_is_external_seeking = p_playback_info.is_external_seeking;
@@ -374,6 +538,7 @@ double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playb
// Request fading.
is_fading_out = true;
cur_fade_out_remaining = fade_out;
cur_fade_in_remaining = 0;
} else {
// Shot is ended, do nothing.
is_shooting = false;
@@ -382,7 +547,7 @@ double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playb
set_parameter(time_to_restart, -1);
} else if (!do_start && !cur_active) {
if (cur_time_to_restart >= 0.0 && !p_seek) {
cur_time_to_restart -= p_time;
cur_time_to_restart -= abs_delta;
if (cur_time_to_restart < 0) {
do_start = true; // Restart.
}
@@ -413,8 +578,11 @@ double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playb
}
if (do_start) {
cur_time = 0;
os_seek = true;
if (!cur_internal_active) {
cur_fade_in_remaining = fade_in; // If already active, don't fade-in again.
}
cur_internal_active = true;
set_parameter(request, ONE_SHOT_REQUEST_NONE);
set_parameter(internal_active, true);
set_parameter(active, true);
@@ -422,20 +590,17 @@ double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playb
real_t blend = 1.0;
bool use_blend = sync;
if (cur_time < fade_in) {
if (cur_fade_in_remaining > 0) {
if (fade_in > 0) {
use_blend = true;
blend = cur_time / fade_in;
blend = (fade_in - cur_fade_in_remaining) / fade_in;
if (fade_in_curve.is_valid()) {
blend = fade_in_curve->sample(blend);
}
} else {
blend = 0; // Should not happen.
}
} else if (!do_start && !is_fading_out && cur_remaining <= fade_out) {
is_fading_out = true;
cur_fade_out_remaining = cur_remaining;
set_parameter(internal_active, false);
}
if (is_fading_out) {
@@ -451,34 +616,36 @@ double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playb
}
AnimationMixer::PlaybackInfo pi = p_playback_info;
double main_rem = 0.0;
NodeTimeInfo main_nti;
if (mix == MIX_MODE_ADD) {
pi.weight = 1.0;
main_rem = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
main_nti = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
} else {
pi.seeked &= use_blend;
pi.weight = 1.0 - blend;
main_rem = blend_input(0, pi, FILTER_BLEND, sync, p_test_only); // Unlike below, processing this edge is a corner case.
main_nti = blend_input(0, pi, FILTER_BLEND, sync, p_test_only); // Unlike below, processing this edge is a corner case.
}
pi = p_playback_info;
if (os_seek) {
pi.time = cur_time;
if (do_start) {
pi.time = 0;
} else if (os_seek) {
pi.time = cur_nti.position;
}
pi.seeked = os_seek;
pi.weight = Math::is_zero_approx(blend) ? CMP_EPSILON : blend;
double os_rem = blend_input(1, pi, FILTER_PASS, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
if (do_start) {
cur_remaining = os_rem;
NodeTimeInfo os_nti = blend_input(1, pi, FILTER_PASS, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
if (cur_fade_in_remaining <= 0 && !do_start && !is_fading_out && os_nti.get_remain(break_loop_at_end) <= fade_out) {
is_fading_out = true;
cur_fade_out_remaining = os_nti.get_remain(break_loop_at_end);
cur_fade_in_remaining = 0;
set_parameter(internal_active, false);
}
if (p_seek) {
cur_time = p_time;
} else {
cur_time += p_time;
cur_remaining = os_rem;
cur_fade_out_remaining -= p_time;
if (cur_remaining <= 0 || (is_fading_out && cur_fade_out_remaining <= 0)) {
if (!p_seek) {
if (os_nti.get_remain(break_loop_at_end) <= 0 || (is_fading_out && cur_fade_out_remaining <= 0)) {
set_parameter(internal_active, false);
set_parameter(active, false);
if (auto_restart) {
@@ -486,13 +653,17 @@ double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playb
set_parameter(time_to_restart, restart_sec);
}
}
double d = Math::abs(os_nti.delta);
if (!do_start) {
cur_fade_in_remaining = MAX(0, cur_fade_in_remaining - d); // Don't consider seeked delta by restart.
}
cur_fade_out_remaining = MAX(0, cur_fade_out_remaining - d);
}
set_parameter(time, cur_time);
set_parameter(remaining, cur_remaining);
set_parameter(fade_in_remaining, cur_fade_in_remaining);
set_parameter(fade_out_remaining, cur_fade_out_remaining);
return MAX(main_rem, cur_remaining);
return cur_internal_active ? os_nti : main_nti;
}
void AnimationNodeOneShot::_bind_methods() {
@@ -508,6 +679,9 @@ void AnimationNodeOneShot::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fadeout_curve", "curve"), &AnimationNodeOneShot::set_fade_out_curve);
ClassDB::bind_method(D_METHOD("get_fadeout_curve"), &AnimationNodeOneShot::get_fade_out_curve);
ClassDB::bind_method(D_METHOD("set_break_loop_at_end", "enable"), &AnimationNodeOneShot::set_break_loop_at_end);
ClassDB::bind_method(D_METHOD("is_loop_broken_at_end"), &AnimationNodeOneShot::is_loop_broken_at_end);
ClassDB::bind_method(D_METHOD("set_autorestart", "active"), &AnimationNodeOneShot::set_auto_restart_enabled);
ClassDB::bind_method(D_METHOD("has_autorestart"), &AnimationNodeOneShot::is_auto_restart_enabled);
@@ -526,6 +700,7 @@ void AnimationNodeOneShot::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fadein_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_fadein_curve", "get_fadein_curve");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadeout_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_fadeout_time", "get_fadeout_time");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fadeout_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_fadeout_curve", "get_fadeout_curve");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "break_loop_at_end"), "set_break_loop_at_end", "is_loop_broken_at_end");
ADD_GROUP("Auto Restart", "autorestart_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autorestart"), "set_autorestart", "has_autorestart");
@@ -550,10 +725,16 @@ AnimationNodeOneShot::AnimationNodeOneShot() {
////////////////////////////////////////////////
void AnimationNodeAdd2::get_parameter_list(List<PropertyInfo> *r_list) const {
AnimationNode::get_parameter_list(r_list);
r_list->push_back(PropertyInfo(Variant::FLOAT, add_amount, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater"));
}
Variant AnimationNodeAdd2::get_parameter_default_value(const StringName &p_parameter) const {
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
if (ret != Variant()) {
return ret;
}
return 0;
}
@@ -565,16 +746,16 @@ bool AnimationNodeAdd2::has_filter() const {
return true;
}
double AnimationNodeAdd2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
AnimationNode::NodeTimeInfo AnimationNodeAdd2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
double amount = get_parameter(add_amount);
AnimationMixer::PlaybackInfo pi = p_playback_info;
pi.weight = 1.0;
double rem0 = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
NodeTimeInfo nti = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
pi.weight = amount;
blend_input(1, pi, FILTER_PASS, sync, p_test_only);
return rem0;
return nti;
}
void AnimationNodeAdd2::_bind_methods() {
@@ -588,10 +769,16 @@ AnimationNodeAdd2::AnimationNodeAdd2() {
////////////////////////////////////////////////
void AnimationNodeAdd3::get_parameter_list(List<PropertyInfo> *r_list) const {
AnimationNode::get_parameter_list(r_list);
r_list->push_back(PropertyInfo(Variant::FLOAT, add_amount, PROPERTY_HINT_RANGE, "-1,1,0.01,or_less,or_greater"));
}
Variant AnimationNodeAdd3::get_parameter_default_value(const StringName &p_parameter) const {
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
if (ret != Variant()) {
return ret;
}
return 0;
}
@@ -603,18 +790,18 @@ bool AnimationNodeAdd3::has_filter() const {
return true;
}
double AnimationNodeAdd3::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
AnimationNode::NodeTimeInfo AnimationNodeAdd3::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
double amount = get_parameter(add_amount);
AnimationMixer::PlaybackInfo pi = p_playback_info;
pi.weight = MAX(0, -amount);
blend_input(0, pi, FILTER_PASS, sync, p_test_only);
pi.weight = 1.0;
double rem0 = blend_input(1, pi, FILTER_IGNORE, sync, p_test_only);
NodeTimeInfo nti = blend_input(1, pi, FILTER_IGNORE, sync, p_test_only);
pi.weight = MAX(0, amount);
blend_input(2, pi, FILTER_PASS, sync, p_test_only);
return rem0;
return nti;
}
void AnimationNodeAdd3::_bind_methods() {
@@ -629,10 +816,16 @@ AnimationNodeAdd3::AnimationNodeAdd3() {
/////////////////////////////////////////////
void AnimationNodeBlend2::get_parameter_list(List<PropertyInfo> *r_list) const {
AnimationNode::get_parameter_list(r_list);
r_list->push_back(PropertyInfo(Variant::FLOAT, blend_amount, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater"));
}
Variant AnimationNodeBlend2::get_parameter_default_value(const StringName &p_parameter) const {
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
if (ret != Variant()) {
return ret;
}
return 0; // For blend amount.
}
@@ -640,16 +833,16 @@ String AnimationNodeBlend2::get_caption() const {
return "Blend2";
}
double AnimationNodeBlend2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
AnimationNode::NodeTimeInfo AnimationNodeBlend2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
double amount = get_parameter(blend_amount);
AnimationMixer::PlaybackInfo pi = p_playback_info;
pi.weight = 1.0 - amount;
double rem0 = blend_input(0, pi, FILTER_BLEND, sync, p_test_only);
NodeTimeInfo nti0 = blend_input(0, pi, FILTER_BLEND, sync, p_test_only);
pi.weight = amount;
double rem1 = blend_input(1, pi, FILTER_PASS, sync, p_test_only);
NodeTimeInfo nti1 = blend_input(1, pi, FILTER_PASS, sync, p_test_only);
return amount > 0.5 ? rem1 : rem0; // Hacky but good enough.
return amount > 0.5 ? nti1 : nti0; // Hacky but good enough.
}
bool AnimationNodeBlend2::has_filter() const {
@@ -667,10 +860,16 @@ AnimationNodeBlend2::AnimationNodeBlend2() {
//////////////////////////////////////
void AnimationNodeBlend3::get_parameter_list(List<PropertyInfo> *r_list) const {
AnimationNode::get_parameter_list(r_list);
r_list->push_back(PropertyInfo(Variant::FLOAT, blend_amount, PROPERTY_HINT_RANGE, "-1,1,0.01,or_less,or_greater"));
}
Variant AnimationNodeBlend3::get_parameter_default_value(const StringName &p_parameter) const {
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
if (ret != Variant()) {
return ret;
}
return 0; // For blend amount.
}
@@ -678,18 +877,18 @@ String AnimationNodeBlend3::get_caption() const {
return "Blend3";
}
double AnimationNodeBlend3::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
AnimationNode::NodeTimeInfo AnimationNodeBlend3::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
double amount = get_parameter(blend_amount);
AnimationMixer::PlaybackInfo pi = p_playback_info;
pi.weight = MAX(0, -amount);
double rem0 = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
NodeTimeInfo nti0 = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
pi.weight = 1.0 - ABS(amount);
double rem1 = blend_input(1, pi, FILTER_IGNORE, sync, p_test_only);
NodeTimeInfo nti1 = blend_input(1, pi, FILTER_IGNORE, sync, p_test_only);
pi.weight = MAX(0, amount);
double rem2 = blend_input(2, pi, FILTER_IGNORE, sync, p_test_only);
NodeTimeInfo nti2 = blend_input(2, pi, FILTER_IGNORE, sync, p_test_only);
return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); // Hacky but good enough.
return amount > 0.5 ? nti2 : (amount < -0.5 ? nti0 : nti1); // Hacky but good enough.
}
void AnimationNodeBlend3::_bind_methods() {
@@ -704,10 +903,16 @@ AnimationNodeBlend3::AnimationNodeBlend3() {
////////////////////////////////////////////////
void AnimationNodeSub2::get_parameter_list(List<PropertyInfo> *r_list) const {
AnimationNode::get_parameter_list(r_list);
r_list->push_back(PropertyInfo(Variant::FLOAT, sub_amount, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater"));
}
Variant AnimationNodeSub2::get_parameter_default_value(const StringName &p_parameter) const {
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
if (ret != Variant()) {
return ret;
}
return 0;
}
@@ -719,7 +924,7 @@ bool AnimationNodeSub2::has_filter() const {
return true;
}
double AnimationNodeSub2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
AnimationNode::NodeTimeInfo AnimationNodeSub2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
double amount = get_parameter(sub_amount);
AnimationMixer::PlaybackInfo pi = p_playback_info;
@@ -742,10 +947,16 @@ AnimationNodeSub2::AnimationNodeSub2() {
/////////////////////////////////
void AnimationNodeTimeScale::get_parameter_list(List<PropertyInfo> *r_list) const {
AnimationNode::get_parameter_list(r_list);
r_list->push_back(PropertyInfo(Variant::FLOAT, scale, PROPERTY_HINT_RANGE, "-32,32,0.01,or_less,or_greater"));
}
Variant AnimationNodeTimeScale::get_parameter_default_value(const StringName &p_parameter) const {
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
if (ret != Variant()) {
return ret;
}
return 1.0; // Initial timescale.
}
@@ -753,13 +964,13 @@ String AnimationNodeTimeScale::get_caption() const {
return "TimeScale";
}
double AnimationNodeTimeScale::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
AnimationNode::NodeTimeInfo AnimationNodeTimeScale::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
double cur_scale = get_parameter(scale);
AnimationMixer::PlaybackInfo pi = p_playback_info;
pi.weight = 1.0;
if (!pi.seeked) {
pi.time *= cur_scale;
pi.delta *= cur_scale;
}
return blend_input(0, pi, FILTER_IGNORE, true, p_test_only);
@@ -775,10 +986,16 @@ AnimationNodeTimeScale::AnimationNodeTimeScale() {
////////////////////////////////////
void AnimationNodeTimeSeek::get_parameter_list(List<PropertyInfo> *r_list) const {
AnimationNode::get_parameter_list(r_list);
r_list->push_back(PropertyInfo(Variant::FLOAT, seek_pos_request, PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater")); // It will be reset to -1 after seeking the position immediately.
}
Variant AnimationNodeTimeSeek::get_parameter_default_value(const StringName &p_parameter) const {
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
if (ret != Variant()) {
return ret;
}
return -1.0; // Initial seek request.
}
@@ -786,7 +1003,7 @@ String AnimationNodeTimeSeek::get_caption() const {
return "TimeSeek";
}
double AnimationNodeTimeSeek::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
AnimationNode::NodeTimeInfo AnimationNodeTimeSeek::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
double cur_seek_pos = get_parameter(seek_pos_request);
AnimationMixer::PlaybackInfo pi = p_playback_info;
@@ -833,6 +1050,8 @@ bool AnimationNodeTransition::_set(const StringName &p_path, const Variant &p_va
set_input_name(which, p_value);
} else if (what == "auto_advance") {
set_input_as_auto_advance(which, p_value);
} else if (what == "break_loop_at_end") {
set_input_break_loop_at_end(which, p_value);
} else if (what == "reset") {
set_input_reset(which, p_value);
} else {
@@ -858,6 +1077,8 @@ bool AnimationNodeTransition::_get(const StringName &p_path, Variant &r_ret) con
r_ret = get_input_name(which);
} else if (what == "auto_advance") {
r_ret = is_input_set_as_auto_advance(which);
} else if (what == "break_loop_at_end") {
r_ret = is_input_loop_broken_at_end(which);
} else if (what == "reset") {
r_ret = is_input_reset(which);
} else {
@@ -868,6 +1089,7 @@ bool AnimationNodeTransition::_get(const StringName &p_path, Variant &r_ret) con
}
void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) const {
AnimationNode::get_parameter_list(r_list);
String anims;
for (int i = 0; i < get_input_count(); i++) {
if (i > 0) {
@@ -880,12 +1102,16 @@ void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) con
r_list->push_back(PropertyInfo(Variant::STRING, transition_request, PROPERTY_HINT_ENUM, anims)); // For transition request. It will be cleared after setting the value immediately.
r_list->push_back(PropertyInfo(Variant::INT, current_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY)); // To avoid finding the index every frame, use this internally.
r_list->push_back(PropertyInfo(Variant::INT, prev_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, prev_xfading, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
}
Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p_parameter) const {
if (p_parameter == time || p_parameter == prev_xfading) {
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
if (ret != Variant()) {
return ret;
}
if (p_parameter == prev_xfading) {
return 0.0;
} else if (p_parameter == prev_index || p_parameter == current_index) {
return -1;
@@ -895,6 +1121,10 @@ Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p
}
bool AnimationNodeTransition::is_parameter_read_only(const StringName &p_parameter) const {
if (AnimationNode::is_parameter_read_only(p_parameter)) {
return true;
}
if (p_parameter == current_state || p_parameter == current_index) {
return true;
}
@@ -947,6 +1177,16 @@ bool AnimationNodeTransition::is_input_set_as_auto_advance(int p_input) const {
return input_data[p_input].auto_advance;
}
void AnimationNodeTransition::set_input_break_loop_at_end(int p_input, bool p_enable) {
ERR_FAIL_INDEX(p_input, get_input_count());
input_data.write[p_input].break_loop_at_end = p_enable;
}
bool AnimationNodeTransition::is_input_loop_broken_at_end(int p_input) const {
ERR_FAIL_INDEX_V(p_input, get_input_count(), false);
return input_data[p_input].break_loop_at_end;
}
void AnimationNodeTransition::set_input_reset(int p_input, bool p_enable) {
ERR_FAIL_INDEX(p_input, get_input_count());
input_data.write[p_input].reset = p_enable;
@@ -981,12 +1221,12 @@ bool AnimationNodeTransition::is_allow_transition_to_self() const {
return allow_transition_to_self;
}
double AnimationNodeTransition::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
AnimationNode::NodeTimeInfo AnimationNodeTransition::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
String cur_transition_request = get_parameter(transition_request);
int cur_current_index = get_parameter(current_index);
int cur_prev_index = get_parameter(prev_index);
double cur_time = get_parameter(time);
NodeTimeInfo cur_nti = get_node_time_info();
double cur_prev_xfading = get_parameter(prev_xfading);
bool switched = false;
@@ -1052,7 +1292,6 @@ double AnimationNodeTransition::_process(const AnimationMixer::PlaybackInfo p_pl
// Special case for restart.
if (restart) {
set_parameter(time, 0);
pi.time = 0;
pi.seeked = true;
pi.weight = 1.0;
@@ -1061,16 +1300,12 @@ double AnimationNodeTransition::_process(const AnimationMixer::PlaybackInfo p_pl
if (switched) {
cur_prev_xfading = xfade_time;
cur_time = 0;
}
if (cur_current_index < 0 || cur_current_index >= get_input_count() || cur_prev_index >= get_input_count()) {
return 0;
return NodeTimeInfo();
}
double rem = 0.0;
double abs_time = Math::abs(p_time);
if (sync) {
pi.weight = 0;
for (int i = 0; i < get_input_count(); i++) {
@@ -1081,20 +1316,11 @@ double AnimationNodeTransition::_process(const AnimationMixer::PlaybackInfo p_pl
}
if (cur_prev_index < 0) { // Process current animation, check for transition.
pi.weight = 1.0;
rem = blend_input(cur_current_index, pi, FILTER_IGNORE, true, p_test_only);
if (p_seek) {
cur_time = abs_time;
} else {
cur_time += abs_time;
}
if (input_data[cur_current_index].auto_advance && rem <= xfade_time) {
cur_nti = blend_input(cur_current_index, pi, FILTER_IGNORE, true, p_test_only);
if (input_data[cur_current_index].auto_advance && cur_nti.get_remain(input_data[cur_current_index].break_loop_at_end) <= xfade_time) {
set_parameter(transition_request, get_input_name((cur_current_index + 1) % get_input_count()));
}
} else { // Cross-fading from prev to current.
real_t blend = 0.0;
@@ -1117,33 +1343,30 @@ double AnimationNodeTransition::_process(const AnimationMixer::PlaybackInfo p_pl
pi.time = 0;
pi.seeked = true;
}
rem = blend_input(cur_current_index, pi, FILTER_IGNORE, true, p_test_only);
cur_nti = blend_input(cur_current_index, pi, FILTER_IGNORE, true, p_test_only);
pi = p_playback_info;
pi.seeked &= use_blend;
pi.weight = blend;
blend_input(cur_prev_index, pi, FILTER_IGNORE, true, p_test_only);
if (p_seek) {
cur_time = abs_time;
} else {
cur_time += abs_time;
cur_prev_xfading -= abs_time;
if (cur_prev_xfading < 0) {
if (!p_seek) {
if (cur_prev_xfading <= 0) {
set_parameter(prev_index, -1);
}
cur_prev_xfading -= Math::abs(p_playback_info.delta);
}
}
set_parameter(time, cur_time);
set_parameter(prev_xfading, cur_prev_xfading);
return rem;
return cur_nti;
}
void AnimationNodeTransition::_get_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < get_input_count(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING, "input_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/auto_advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/break_loop_at_end", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/reset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
}
}
@@ -1154,6 +1377,9 @@ void AnimationNodeTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_input_as_auto_advance", "input", "enable"), &AnimationNodeTransition::set_input_as_auto_advance);
ClassDB::bind_method(D_METHOD("is_input_set_as_auto_advance", "input"), &AnimationNodeTransition::is_input_set_as_auto_advance);
ClassDB::bind_method(D_METHOD("set_input_break_loop_at_end", "input", "enable"), &AnimationNodeTransition::set_input_break_loop_at_end);
ClassDB::bind_method(D_METHOD("is_input_loop_broken_at_end", "input"), &AnimationNodeTransition::is_input_loop_broken_at_end);
ClassDB::bind_method(D_METHOD("set_input_reset", "input", "enable"), &AnimationNodeTransition::set_input_reset);
ClassDB::bind_method(D_METHOD("is_input_reset", "input"), &AnimationNodeTransition::is_input_reset);
@@ -1181,7 +1407,7 @@ String AnimationNodeOutput::get_caption() const {
return "Output";
}
double AnimationNodeOutput::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
AnimationNode::NodeTimeInfo AnimationNodeOutput::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
AnimationMixer::PlaybackInfo pi = p_playback_info;
pi.weight = 1.0;
return blend_input(0, pi, FILTER_IGNORE, true, p_test_only);
@@ -1400,10 +1626,10 @@ String AnimationNodeBlendTree::get_caption() const {
return "BlendTree";
}
double AnimationNodeBlendTree::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
AnimationNode::NodeTimeInfo AnimationNodeBlendTree::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output].node;
node_state.connections = nodes[SceneStringNames::get_singleton()->output].connections;
ERR_FAIL_COND_V(output.is_null(), 0);
ERR_FAIL_COND_V(output.is_null(), NodeTimeInfo());
AnimationMixer::PlaybackInfo pi = p_playback_info;
pi.weight = 1.0;