1
0
Fork 0

Refactor process of animation to retrive keys more exactly

This commit is contained in:
Silc Renew 2022-11-29 18:51:45 +09:00
parent 0bb1e89fb7
commit 1fc3833617
10 changed files with 394 additions and 275 deletions

View File

@ -616,5 +616,14 @@
<constant name="LOOP_PINGPONG" value="2" enum="LoopMode"> <constant name="LOOP_PINGPONG" value="2" enum="LoopMode">
Repeats playback and reverse playback at both ends of the animation. Repeats playback and reverse playback at both ends of the animation.
</constant> </constant>
<constant name="LOOPED_FLAG_NONE" value="0" enum="LoopedFlag">
This flag indicates that the animation proceeds without any looping.
</constant>
<constant name="LOOPED_FLAG_END" value="1" enum="LoopedFlag">
This flag indicates that the animation has reached the end of the animation and just after loop processed.
</constant>
<constant name="LOOPED_FLAG_START" value="2" enum="LoopedFlag">
This flag indicates that the animation has reached the start of the animation and just after loop processed.
</constant>
</constants> </constants>
</class> </class>

View File

@ -75,9 +75,10 @@
<param index="3" name="seeked" type="bool" /> <param index="3" name="seeked" type="bool" />
<param index="4" name="is_external_seeking" type="bool" /> <param index="4" name="is_external_seeking" type="bool" />
<param index="5" name="blend" type="float" /> <param index="5" name="blend" type="float" />
<param index="6" name="pingponged" type="int" default="0" /> <param index="6" name="looped_flag" type="int" enum="Animation.LoopedFlag" default="0" />
<description> <description>
Blend an animation by [param blend] amount (name must be valid in the linked [AnimationPlayer]). A [param time] and [param delta] may be passed, as well as whether [param seeked] happened. Blend an animation by [param blend] amount (name must be valid in the linked [AnimationPlayer]). A [param time] and [param delta] may be passed, as well as whether [param seeked] happened.
A [param looped_flag] is used by internal processing immediately after the loop. See also [enum Animation.LoopedFlag].
</description> </description>
</method> </method>
<method name="blend_input"> <method name="blend_input">

View File

@ -87,36 +87,38 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_is_ext
double anim_size = (double)anim->get_length(); double anim_size = (double)anim->get_length();
double step = 0.0; double step = 0.0;
double prev_time = cur_time; double prev_time = cur_time;
int pingponged = 0; Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
bool current_backward = signbit(p_time); bool node_backward = play_mode == PLAY_MODE_BACKWARD;
if (p_seek) { if (p_seek) {
step = p_time - cur_time; step = p_time - cur_time;
cur_time = p_time; cur_time = p_time;
} else { } else {
p_time *= backward ? -1.0 : 1.0; p_time *= backward ? -1.0 : 1.0;
if (!(cur_time == anim_size && !current_backward) && !(cur_time == 0 && current_backward)) { cur_time = cur_time + p_time;
cur_time = cur_time + p_time; step = p_time;
step = p_time;
}
} }
if (anim->get_loop_mode() == Animation::LOOP_PINGPONG) { if (anim->get_loop_mode() == Animation::LOOP_PINGPONG) {
if (!Math::is_zero_approx(anim_size)) { if (!Math::is_zero_approx(anim_size)) {
if ((int)Math::floor(abs(cur_time - prev_time) / anim_size) % 2 == 0) { if (prev_time >= 0 && cur_time < 0) {
if (prev_time >= 0 && cur_time < 0) { backward = !backward;
backward = !backward; looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
pingponged = -1; }
} if (prev_time <= anim_size && cur_time > anim_size) {
if (prev_time <= anim_size && cur_time > anim_size) { backward = !backward;
backward = !backward; looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
pingponged = 1;
}
} }
cur_time = Math::pingpong(cur_time, anim_size); cur_time = Math::pingpong(cur_time, anim_size);
} }
} else if (anim->get_loop_mode() == Animation::LOOP_LINEAR) { } else if (anim->get_loop_mode() == Animation::LOOP_LINEAR) {
if (!Math::is_zero_approx(anim_size)) { 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); cur_time = Math::fposmod(cur_time, anim_size);
} }
backward = false; backward = false;
@ -145,9 +147,9 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_is_ext
} }
if (play_mode == PLAY_MODE_FORWARD) { if (play_mode == PLAY_MODE_FORWARD) {
blend_animation(animation, cur_time, step, p_seek, p_is_external_seeking, 1.0, pingponged); blend_animation(animation, cur_time, step, p_seek, p_is_external_seeking, 1.0, looped_flag);
} else { } else {
blend_animation(animation, anim_size - cur_time, -step, p_seek, p_is_external_seeking, 1.0, pingponged); blend_animation(animation, anim_size - cur_time, -step, p_seek, p_is_external_seeking, 1.0, looped_flag);
} }
set_parameter(time, cur_time); set_parameter(time, cur_time);
@ -309,9 +311,7 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_exter
set_parameter(time_to_restart, cur_time_to_restart); set_parameter(time_to_restart, cur_time_to_restart);
} }
if (!cur_active) { return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync);
return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync);
}
} }
bool os_seek = p_seek; bool os_seek = p_seek;
@ -349,10 +349,9 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_exter
if (mix == MIX_MODE_ADD) { if (mix == MIX_MODE_ADD) {
main_rem = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync); main_rem = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync);
} else { } else {
main_rem = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0 - blend, FILTER_BLEND, sync); main_rem = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0 - blend, FILTER_BLEND, sync); // Unlike below, processing this edge is a corner case.
} }
double os_rem = blend_input(1, os_seek ? cur_time : p_time, os_seek, p_is_external_seeking, MAX(CMP_EPSILON, blend), FILTER_PASS, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
double os_rem = blend_input(1, os_seek ? cur_time : p_time, os_seek, p_is_external_seeking, blend, FILTER_PASS, true);
if (do_start) { if (do_start) {
cur_remaining = os_rem; cur_remaining = os_rem;
@ -769,17 +768,18 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex
blend = xfade_curve->sample(blend); blend = xfade_curve->sample(blend);
} }
// Blend values must be more than CMP_EPSILON to process discrete keys in edge.
if (from_start && !p_seek && switched) { //just switched, seek to start of current if (from_start && !p_seek && switched) { //just switched, seek to start of current
rem = blend_input(cur_current, 0, true, p_is_external_seeking, 1.0 - blend, FILTER_IGNORE, true); rem = blend_input(cur_current, 0, true, p_is_external_seeking, MAX(CMP_EPSILON, 1.0 - blend), FILTER_IGNORE, true);
} else { } else {
rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, 1.0 - blend, FILTER_IGNORE, true); rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, MAX(CMP_EPSILON, 1.0 - blend), FILTER_IGNORE, true);
} }
if (p_seek) { if (p_seek) {
blend_input(cur_prev, p_time, true, p_is_external_seeking, blend, FILTER_IGNORE, true); blend_input(cur_prev, p_time, true, p_is_external_seeking, MAX(CMP_EPSILON, blend), FILTER_IGNORE, true);
cur_time = p_time; cur_time = p_time;
} else { } else {
blend_input(cur_prev, p_time, false, p_is_external_seeking, blend, FILTER_IGNORE, true); blend_input(cur_prev, p_time, false, p_is_external_seeking, MAX(CMP_EPSILON, blend), FILTER_IGNORE, true);
cur_time += p_time; cur_time += p_time;
cur_prev_xfading -= p_time; cur_prev_xfading -= p_time;
if (cur_prev_xfading < 0) { if (cur_prev_xfading < 0) {

View File

@ -433,10 +433,10 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
if (current_curve.is_valid()) { if (current_curve.is_valid()) {
fade_blend = current_curve->sample(fade_blend); fade_blend = current_curve->sample(fade_blend);
} }
float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, fade_blend, AnimationNode::FILTER_IGNORE, true); float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, MAX(CMP_EPSILON, fade_blend), AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
if (fading_from != StringName()) { if (fading_from != StringName()) {
p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, true); p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, MAX(CMP_EPSILON, 1.0 - fade_blend), AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
} }
//guess playback position //guess playback position
@ -599,13 +599,11 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
current = next; current = next;
len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, CMP_EPSILON, AnimationNode::FILTER_IGNORE, true); // Process next node's first key in here.
if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) { if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) {
len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true);
pos_current = MIN(pos_current, len_current); pos_current = MIN(pos_current, len_current);
p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true); p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true);
} else { } else {
len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true);
pos_current = 0; pos_current = 0;
} }

View File

@ -468,7 +468,7 @@ Variant AnimationPlayer::_post_process_key_value(const Ref<Animation> &p_anim, i
return p_value; return p_value;
} }
void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started, int p_pingponged) { void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_prev_time, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started, Animation::LoopedFlag p_looped_flag) {
_ensure_node_caches(p_anim); _ensure_node_caches(p_anim);
ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count()); ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count());
@ -683,7 +683,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else if (p_is_current && p_delta != 0) { } else if (p_is_current && p_delta != 0) {
List<int> indices; List<int> indices;
a->track_get_key_indices_in_range(i, p_time, p_delta, &indices, p_pingponged); if (p_started) {
int first_key = a->track_find_key(i, p_prev_time, true);
if (first_key >= 0) {
indices.push_back(first_key);
}
}
a->track_get_key_indices_in_range(i, p_time, p_delta, &indices, p_looped_flag);
for (int &F : indices) { for (int &F : indices) {
Variant value = a->track_get_key_value(i, F); Variant value = a->track_get_key_value(i, F);
@ -742,7 +748,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} }
List<int> indices; List<int> indices;
a->track_get_key_indices_in_range(i, p_time, p_delta, &indices, p_pingponged); if (p_started) {
int first_key = a->track_find_key(i, p_prev_time, true);
if (first_key >= 0) {
indices.push_back(first_key);
}
}
a->track_get_key_indices_in_range(i, p_time, p_delta, &indices, p_looped_flag);
for (int &E : indices) { for (int &E : indices) {
StringName method = a->method_track_get_name(i, E); StringName method = a->method_track_get_name(i, E);
@ -832,7 +844,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else { } else {
//find stuff to play //find stuff to play
List<int> to_play; List<int> to_play;
a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_pingponged); if (p_started) {
int first_key = a->track_find_key(i, p_prev_time, true);
if (first_key >= 0) {
to_play.push_back(first_key);
}
}
a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_looped_flag);
if (to_play.size()) { if (to_play.size()) {
int idx = to_play.back()->get(); int idx = to_play.back()->get();
@ -939,7 +957,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else { } else {
//find stuff to play //find stuff to play
List<int> to_play; List<int> to_play;
a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_pingponged); if (p_started) {
int first_key = a->track_find_key(i, p_prev_time, true);
if (first_key >= 0) {
to_play.push_back(first_key);
}
}
a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_looped_flag);
if (to_play.size()) { if (to_play.size()) {
int idx = to_play.back()->get(); int idx = to_play.back()->get();
@ -969,7 +993,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
double next_pos = cd.pos + delta; double next_pos = cd.pos + delta;
real_t len = cd.from->animation->get_length(); real_t len = cd.from->animation->get_length();
int pingponged = 0; Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
switch (cd.from->animation->get_loop_mode()) { switch (cd.from->animation->get_loop_mode()) {
case Animation::LOOP_NONE: { case Animation::LOOP_NONE: {
@ -998,44 +1022,33 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
} break; } break;
case Animation::LOOP_LINEAR: { case Animation::LOOP_LINEAR: {
double looped_next_pos = Math::fposmod(next_pos, (double)len); if (next_pos < 0 && cd.pos >= 0) {
if (looped_next_pos == 0 && next_pos != 0) { looped_flag = Animation::LOOPED_FLAG_START;
// Loop multiples of the length to it, rather than 0
// so state at time=length is previewable in the editor
next_pos = len;
} else {
next_pos = looped_next_pos;
} }
if (next_pos > len && cd.pos <= len) {
looped_flag = Animation::LOOPED_FLAG_END;
}
next_pos = Math::fposmod(next_pos, (double)len);
} break; } break;
case Animation::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) {
if (next_pos < 0 && cd.pos >= 0) { cd.speed_scale *= -1.0;
cd.speed_scale *= -1.0; looped_flag = Animation::LOOPED_FLAG_START;
pingponged = -1;
}
if (next_pos > len && cd.pos <= len) {
cd.speed_scale *= -1.0;
pingponged = 1;
}
} }
double looped_next_pos = Math::pingpong(next_pos, (double)len); if (next_pos > len && cd.pos <= len) {
if (looped_next_pos == 0 && next_pos != 0) { cd.speed_scale *= -1.0;
// Loop multiples of the length to it, rather than 0 looped_flag = Animation::LOOPED_FLAG_END;
// so state at time=length is previewable in the editor
next_pos = len;
} else {
next_pos = looped_next_pos;
} }
next_pos = Math::pingpong(next_pos, (double)len);
} break; } break;
default: default:
break; break;
} }
_animation_process_animation(cd.from, cd.pos, next_pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started, looped_flag);
cd.pos = next_pos; cd.pos = next_pos;
_animation_process_animation(cd.from, cd.pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started, pingponged);
} }
void AnimationPlayer::_animation_process2(double p_delta, bool p_started) { void AnimationPlayer::_animation_process2(double p_delta, bool p_started) {

View File

@ -268,7 +268,7 @@ private:
NodePath root; NodePath root;
void _animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current = true, bool p_seeked = false, bool p_started = false, int p_pingponged = 0); void _animation_process_animation(AnimationData *p_anim, double p_prev_time, double p_time, double p_delta, float p_interp, bool p_is_current = true, bool p_seeked = false, bool p_started = false, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE);
void _ensure_node_caches(AnimationData *p_anim, Node *p_root_override = nullptr); void _ensure_node_caches(AnimationData *p_anim, Node *p_root_override = nullptr);
void _animation_process_data(PlaybackData &cd, double p_delta, float p_blend, bool p_seeked, bool p_started); void _animation_process_data(PlaybackData &cd, double p_delta, float p_blend, bool p_seeked, bool p_started);

View File

@ -86,7 +86,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, bool p_is_external_seeking, 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_is_external_seeking, real_t p_blend, Animation::LoopedFlag p_looped_flag) {
ERR_FAIL_COND(!state); ERR_FAIL_COND(!state);
ERR_FAIL_COND(!state->player->has_animation(p_animation)); ERR_FAIL_COND(!state->player->has_animation(p_animation));
@ -112,7 +112,7 @@ void AnimationNode::blend_animation(const StringName &p_animation, double p_time
anim_state.time = p_time; anim_state.time = p_time;
anim_state.animation = animation; anim_state.animation = animation;
anim_state.seeked = p_seeked; anim_state.seeked = p_seeked;
anim_state.pingponged = p_pingponged; anim_state.looped_flag = p_looped_flag;
anim_state.is_external_seeking = p_is_external_seeking; anim_state.is_external_seeking = p_is_external_seeking;
state->animation_states.push_back(anim_state); state->animation_states.push_back(anim_state);
@ -413,7 +413,7 @@ void AnimationNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters); 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("_get_filters"), &AnimationNode::_get_filters);
ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "is_external_seeking", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0)); ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "is_external_seeking", "blend", "looped_flag"), &AnimationNode::blend_animation, DEFVAL(Animation::LOOPED_FLAG_NONE));
ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "is_external_seeking", "blend", "filter", "sync"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true)); ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "is_external_seeking", "blend", "filter", "sync"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true));
ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "is_external_seeking", "blend", "filter", "sync"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true)); ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "is_external_seeking", "blend", "filter", "sync"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true));
@ -1019,7 +1019,7 @@ void AnimationTree::_process_graph(double p_delta) {
double delta = as.delta; double delta = as.delta;
real_t weight = as.blend; real_t weight = as.blend;
bool seeked = as.seeked; bool seeked = as.seeked;
int pingponged = as.pingponged; Animation::LoopedFlag looped_flag = as.looped_flag;
bool is_external_seeking = as.is_external_seeking; bool is_external_seeking = as.is_external_seeking;
#ifndef _3D_DISABLED #ifndef _3D_DISABLED
bool backward = signbit(delta); // This flag is required only for the root motion since it calculates the difference between the previous and current frames. bool backward = signbit(delta); // This flag is required only for the root motion since it calculates the difference between the previous and current frames.
@ -1391,7 +1391,7 @@ void AnimationTree::_process_graph(double p_delta) {
t->object->set_indexed(t->subpath, value); t->object->set_indexed(t->subpath, value);
} else { } else {
List<int> indices; List<int> indices;
a->track_get_key_indices_in_range(i, time, delta, &indices, pingponged); a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag);
for (int &F : indices) { for (int &F : indices) {
Variant value = a->track_get_key_value(i, F); Variant value = a->track_get_key_value(i, F);
value = _post_process_key_value(a, i, value, t->object); value = _post_process_key_value(a, i, value, t->object);
@ -1416,7 +1416,7 @@ void AnimationTree::_process_graph(double p_delta) {
} }
} else { } else {
List<int> indices; List<int> indices;
a->track_get_key_indices_in_range(i, time, delta, &indices, pingponged); a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag);
for (int &F : indices) { for (int &F : indices) {
StringName method = a->method_track_get_name(i, F); StringName method = a->method_track_get_name(i, F);
Vector<Variant> params = a->method_track_get_params(i, F); Vector<Variant> params = a->method_track_get_params(i, F);
@ -1479,7 +1479,7 @@ void AnimationTree::_process_graph(double p_delta) {
} else { } else {
//find stuff to play //find stuff to play
List<int> to_play; List<int> to_play;
a->track_get_key_indices_in_range(i, time, delta, &to_play, pingponged); a->track_get_key_indices_in_range(i, time, delta, &to_play, looped_flag);
if (to_play.size()) { if (to_play.size()) {
int idx = to_play.back()->get(); int idx = to_play.back()->get();
@ -1594,7 +1594,7 @@ void AnimationTree::_process_graph(double p_delta) {
} else { } else {
//find stuff to play //find stuff to play
List<int> to_play; List<int> to_play;
a->track_get_key_indices_in_range(i, time, delta, &to_play, pingponged); a->track_get_key_indices_in_range(i, time, delta, &to_play, looped_flag);
if (to_play.size()) { if (to_play.size()) {
int idx = to_play.back()->get(); int idx = to_play.back()->get();

View File

@ -69,7 +69,7 @@ public:
real_t blend = 0.0; real_t blend = 0.0;
bool seeked = false; bool seeked = false;
bool is_external_seeking = false; bool is_external_seeking = false;
int pingponged = 0; Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
}; };
struct State { struct State {
@ -102,7 +102,7 @@ public:
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_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = 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_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, real_t *r_max = nullptr);
protected: protected:
void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_is_external_seeking, real_t p_blend, int p_pingponged = 0); void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_is_external_seeking, real_t p_blend, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE);
double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true); double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true);
double blend_input(int p_input, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true); double blend_input(int p_input, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true);

View File

@ -2726,40 +2726,60 @@ Animation::UpdateMode Animation::value_track_get_update_mode(int p_track) const
} }
template <class T> template <class T>
void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const { void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices, bool p_is_backward) const {
if (to_time == length) { int len = p_array.size();
to_time = length + CMP_EPSILON; //include a little more if at the end if (len == 0) {
return;
} }
int to = _find(p_array, to_time); int from = 0;
int to = len - 1;
// can't really send the events == time, will be sent in the next frame. if (!p_is_backward) {
// if event>=len then it will probably never be requested by the anim player. while (p_array[from].time < from_time || Math::is_equal_approx(p_array[from].time, from_time)) {
from++;
if (to >= 0 && p_array[to].time >= to_time) { if (to < from) {
to--; return;
}
}
while (p_array[to].time > to_time && !Math::is_equal_approx(p_array[to].time, to_time)) {
to--;
if (to < from) {
return;
}
}
} else {
while (p_array[from].time < from_time && !Math::is_equal_approx(p_array[from].time, from_time)) {
from++;
if (to < from) {
return;
}
}
while (p_array[to].time > to_time || Math::is_equal_approx(p_array[to].time, to_time)) {
to--;
if (to < from) {
return;
}
}
} }
if (to < 0) { if (from == to) {
return; // not bother p_indices->push_back(from);
return;
} }
int from = _find(p_array, from_time); if (!p_is_backward) {
for (int i = from; i <= to; i++) {
// position in the right first event.+ p_indices->push_back(i);
if (from < 0 || p_array[from].time < from_time) { }
from++; } else {
} for (int i = to; i >= to; i--) {
p_indices->push_back(i);
int max = p_array.size(); }
for (int i = from; i <= to; i++) {
ERR_CONTINUE(i < 0 || i >= max); // shouldn't happen
p_indices->push_back(i);
} }
} }
void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const { void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, Animation::LoopedFlag p_looped_flag) const {
ERR_FAIL_INDEX(p_track, tracks.size()); ERR_FAIL_INDEX(p_track, tracks.size());
if (p_delta == 0) { if (p_delta == 0) {
@ -2771,7 +2791,9 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
double from_time = p_time - p_delta; double from_time = p_time - p_delta;
double to_time = p_time; double to_time = p_time;
bool is_backward = false;
if (from_time > to_time) { if (from_time > to_time) {
is_backward = true;
SWAP(from_time, to_time); SWAP(from_time, to_time);
} }
@ -2800,7 +2822,10 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
} }
if (from_time > to_time) { if (from_time > to_time) {
// handle loop by splitting // Handle loop by splitting.
double anim_end = length + CMP_EPSILON;
double anim_start = -CMP_EPSILON;
switch (t->type) { switch (t->type) {
case TYPE_POSITION_3D: { case TYPE_POSITION_3D: {
const PositionTrack *tt = static_cast<const PositionTrack *>(t); const PositionTrack *tt = static_cast<const PositionTrack *>(t);
@ -2808,8 +2833,13 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
_get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices); _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices);
_get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices); _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices);
} else { } else {
_track_get_key_indices_in_range(tt->positions, from_time, length, p_indices); if (!is_backward) {
_track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices); _track_get_key_indices_in_range(tt->positions, from_time, anim_end, p_indices, is_backward);
_track_get_key_indices_in_range(tt->positions, anim_start, to_time, p_indices, is_backward);
} else {
_track_get_key_indices_in_range(tt->positions, anim_start, to_time, p_indices, is_backward);
_track_get_key_indices_in_range(tt->positions, from_time, anim_end, p_indices, is_backward);
}
} }
} break; } break;
case TYPE_ROTATION_3D: { case TYPE_ROTATION_3D: {
@ -2818,8 +2848,13 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
_get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices); _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices);
_get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices); _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices);
} else { } else {
_track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices); if (!is_backward) {
_track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices); _track_get_key_indices_in_range(rt->rotations, from_time, anim_end, p_indices, is_backward);
_track_get_key_indices_in_range(rt->rotations, anim_start, to_time, p_indices, is_backward);
} else {
_track_get_key_indices_in_range(rt->rotations, anim_start, to_time, p_indices, is_backward);
_track_get_key_indices_in_range(rt->rotations, from_time, anim_end, p_indices, is_backward);
}
} }
} break; } break;
case TYPE_SCALE_3D: { case TYPE_SCALE_3D: {
@ -2828,8 +2863,13 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
_get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices); _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices);
_get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices); _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices);
} else { } else {
_track_get_key_indices_in_range(st->scales, from_time, length, p_indices); if (!is_backward) {
_track_get_key_indices_in_range(st->scales, 0, to_time, p_indices); _track_get_key_indices_in_range(st->scales, from_time, anim_end, p_indices, is_backward);
_track_get_key_indices_in_range(st->scales, anim_start, to_time, p_indices, is_backward);
} else {
_track_get_key_indices_in_range(st->scales, anim_start, to_time, p_indices, is_backward);
_track_get_key_indices_in_range(st->scales, from_time, anim_end, p_indices, is_backward);
}
} }
} break; } break;
case TYPE_BLEND_SHAPE: { case TYPE_BLEND_SHAPE: {
@ -2838,38 +2878,83 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
_get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices); _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
_get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices); _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices);
} else { } else {
_track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices); if (!is_backward) {
_track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices); _track_get_key_indices_in_range(bst->blend_shapes, from_time, anim_end, p_indices, is_backward);
_track_get_key_indices_in_range(bst->blend_shapes, anim_start, to_time, p_indices, is_backward);
} else {
_track_get_key_indices_in_range(bst->blend_shapes, anim_start, to_time, p_indices, is_backward);
_track_get_key_indices_in_range(bst->blend_shapes, from_time, anim_end, p_indices, is_backward);
}
} }
} break; } break;
case TYPE_VALUE: { case TYPE_VALUE: {
const ValueTrack *vt = static_cast<const ValueTrack *>(t); const ValueTrack *vt = static_cast<const ValueTrack *>(t);
_track_get_key_indices_in_range(vt->values, from_time, length, p_indices); if (!is_backward) {
_track_get_key_indices_in_range(vt->values, 0, to_time, p_indices); _track_get_key_indices_in_range(vt->values, from_time, anim_end, p_indices, is_backward);
_track_get_key_indices_in_range(vt->values, anim_start, to_time, p_indices, is_backward);
} else {
_track_get_key_indices_in_range(vt->values, anim_start, to_time, p_indices, is_backward);
_track_get_key_indices_in_range(vt->values, from_time, anim_end, p_indices, is_backward);
}
} break; } break;
case TYPE_METHOD: { case TYPE_METHOD: {
const MethodTrack *mt = static_cast<const MethodTrack *>(t); const MethodTrack *mt = static_cast<const MethodTrack *>(t);
_track_get_key_indices_in_range(mt->methods, from_time, length, p_indices); if (!is_backward) {
_track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices); _track_get_key_indices_in_range(mt->methods, from_time, anim_end, p_indices, is_backward);
_track_get_key_indices_in_range(mt->methods, anim_start, to_time, p_indices, is_backward);
} else {
_track_get_key_indices_in_range(mt->methods, anim_start, to_time, p_indices, is_backward);
_track_get_key_indices_in_range(mt->methods, from_time, anim_end, p_indices, is_backward);
}
} break; } break;
case TYPE_BEZIER: { case TYPE_BEZIER: {
const BezierTrack *bz = static_cast<const BezierTrack *>(t); const BezierTrack *bz = static_cast<const BezierTrack *>(t);
_track_get_key_indices_in_range(bz->values, from_time, length, p_indices); if (!is_backward) {
_track_get_key_indices_in_range(bz->values, 0, to_time, p_indices); _track_get_key_indices_in_range(bz->values, from_time, anim_end, p_indices, is_backward);
_track_get_key_indices_in_range(bz->values, anim_start, to_time, p_indices, is_backward);
} else {
_track_get_key_indices_in_range(bz->values, anim_start, to_time, p_indices, is_backward);
_track_get_key_indices_in_range(bz->values, from_time, anim_end, p_indices, is_backward);
}
} break; } break;
case TYPE_AUDIO: { case TYPE_AUDIO: {
const AudioTrack *ad = static_cast<const AudioTrack *>(t); const AudioTrack *ad = static_cast<const AudioTrack *>(t);
_track_get_key_indices_in_range(ad->values, from_time, length, p_indices); if (!is_backward) {
_track_get_key_indices_in_range(ad->values, 0, to_time, p_indices); _track_get_key_indices_in_range(ad->values, from_time, anim_end, p_indices, is_backward);
_track_get_key_indices_in_range(ad->values, anim_start, to_time, p_indices, is_backward);
} else {
_track_get_key_indices_in_range(ad->values, anim_start, to_time, p_indices, is_backward);
_track_get_key_indices_in_range(ad->values, from_time, anim_end, p_indices, is_backward);
}
} break; } break;
case TYPE_ANIMATION: { case TYPE_ANIMATION: {
const AnimationTrack *an = static_cast<const AnimationTrack *>(t); const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
_track_get_key_indices_in_range(an->values, from_time, length, p_indices); if (!is_backward) {
_track_get_key_indices_in_range(an->values, 0, to_time, p_indices); _track_get_key_indices_in_range(an->values, from_time, anim_end, p_indices, is_backward);
_track_get_key_indices_in_range(an->values, anim_start, to_time, p_indices, is_backward);
} else {
_track_get_key_indices_in_range(an->values, anim_start, to_time, p_indices, is_backward);
_track_get_key_indices_in_range(an->values, from_time, anim_end, p_indices, is_backward);
}
} break; } break;
} }
return; return;
} }
// Not from_time > to_time but most recent of looping...
if (p_looped_flag != Animation::LOOPED_FLAG_NONE) {
if (!is_backward && Math::is_equal_approx(from_time, 0)) {
int edge = track_find_key(p_track, 0, true);
if (edge >= 0) {
p_indices->push_back(edge);
}
} else if (is_backward && Math::is_equal_approx(to_time, length)) {
int edge = track_find_key(p_track, length, true);
if (edge >= 0) {
p_indices->push_back(edge);
}
}
}
} break; } break;
case LOOP_PINGPONG: { case LOOP_PINGPONG: {
if (from_time > length || from_time < 0) { if (from_time > length || from_time < 0) {
@ -2879,162 +2964,164 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
to_time = Math::pingpong(to_time, length); to_time = Math::pingpong(to_time, length);
} }
if ((int)Math::floor(abs(p_delta) / length) % 2 == 0) { if (p_looped_flag == Animation::LOOPED_FLAG_START) {
if (p_pingponged == -1) { // Handle loop by splitting.
// handle loop by splitting switch (t->type) {
to_time = MAX(CMP_EPSILON, to_time); // To avoid overlapping keys at the turnaround point, one of the point will needs to be shifted slightly. case TYPE_POSITION_3D: {
switch (t->type) { const PositionTrack *tt = static_cast<const PositionTrack *>(t);
case TYPE_POSITION_3D: { if (tt->compressed_track >= 0) {
const PositionTrack *tt = static_cast<const PositionTrack *>(t); _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, p_indices);
if (tt->compressed_track >= 0) { _get_compressed_key_indices_in_range<3>(tt->compressed_track, CMP_EPSILON, to_time, p_indices);
_get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, p_indices); } else {
_get_compressed_key_indices_in_range<3>(tt->compressed_track, CMP_EPSILON, to_time, p_indices); _track_get_key_indices_in_range(tt->positions, 0, from_time, p_indices, true);
} else { _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices, false);
_track_get_key_indices_in_range(tt->positions, 0, from_time, p_indices); }
_track_get_key_indices_in_range(tt->positions, CMP_EPSILON, to_time, p_indices); } break;
} case TYPE_ROTATION_3D: {
} break; const RotationTrack *rt = static_cast<const RotationTrack *>(t);
case TYPE_ROTATION_3D: { if (rt->compressed_track >= 0) {
const RotationTrack *rt = static_cast<const RotationTrack *>(t); _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, p_indices);
if (rt->compressed_track >= 0) { _get_compressed_key_indices_in_range<3>(rt->compressed_track, CMP_EPSILON, to_time, p_indices);
_get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, p_indices); } else {
_get_compressed_key_indices_in_range<3>(rt->compressed_track, CMP_EPSILON, to_time, p_indices); _track_get_key_indices_in_range(rt->rotations, 0, from_time, p_indices, true);
} else { _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices, false);
_track_get_key_indices_in_range(rt->rotations, 0, from_time, p_indices); }
_track_get_key_indices_in_range(rt->rotations, CMP_EPSILON, to_time, p_indices); } break;
} case TYPE_SCALE_3D: {
} break; const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
case TYPE_SCALE_3D: { if (st->compressed_track >= 0) {
const ScaleTrack *st = static_cast<const ScaleTrack *>(t); _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, from_time, p_indices);
if (st->compressed_track >= 0) { _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices);
_get_compressed_key_indices_in_range<3>(st->compressed_track, 0, from_time, p_indices); } else {
_get_compressed_key_indices_in_range<3>(st->compressed_track, CMP_EPSILON, to_time, p_indices); _track_get_key_indices_in_range(st->scales, 0, from_time, p_indices, true);
} else { _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices, false);
_track_get_key_indices_in_range(st->scales, 0, from_time, p_indices); }
_track_get_key_indices_in_range(st->scales, CMP_EPSILON, to_time, p_indices); } break;
} case TYPE_BLEND_SHAPE: {
} break; const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
case TYPE_BLEND_SHAPE: { if (bst->compressed_track >= 0) {
const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t); _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, from_time, p_indices);
if (bst->compressed_track >= 0) { _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices);
_get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, from_time, p_indices); } else {
_get_compressed_key_indices_in_range<1>(bst->compressed_track, CMP_EPSILON, to_time, p_indices); _track_get_key_indices_in_range(bst->blend_shapes, 0, from_time, p_indices, true);
} else { _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices, false);
_track_get_key_indices_in_range(bst->blend_shapes, 0, from_time, p_indices); }
_track_get_key_indices_in_range(bst->blend_shapes, CMP_EPSILON, to_time, p_indices); } break;
} case TYPE_VALUE: {
} break; const ValueTrack *vt = static_cast<const ValueTrack *>(t);
case TYPE_VALUE: { _track_get_key_indices_in_range(vt->values, 0, from_time, p_indices, true);
const ValueTrack *vt = static_cast<const ValueTrack *>(t); _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices, false);
_track_get_key_indices_in_range(vt->values, 0, from_time, p_indices); } break;
_track_get_key_indices_in_range(vt->values, CMP_EPSILON, to_time, p_indices); case TYPE_METHOD: {
} break; const MethodTrack *mt = static_cast<const MethodTrack *>(t);
case TYPE_METHOD: { _track_get_key_indices_in_range(mt->methods, 0, from_time, p_indices, true);
const MethodTrack *mt = static_cast<const MethodTrack *>(t); _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices, false);
_track_get_key_indices_in_range(mt->methods, 0, from_time, p_indices); } break;
_track_get_key_indices_in_range(mt->methods, CMP_EPSILON, to_time, p_indices); case TYPE_BEZIER: {
} break; const BezierTrack *bz = static_cast<const BezierTrack *>(t);
case TYPE_BEZIER: { _track_get_key_indices_in_range(bz->values, 0, from_time, p_indices, true);
const BezierTrack *bz = static_cast<const BezierTrack *>(t); _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices, false);
_track_get_key_indices_in_range(bz->values, 0, from_time, p_indices); } break;
_track_get_key_indices_in_range(bz->values, CMP_EPSILON, to_time, p_indices); case TYPE_AUDIO: {
} break; const AudioTrack *ad = static_cast<const AudioTrack *>(t);
case TYPE_AUDIO: { _track_get_key_indices_in_range(ad->values, 0, from_time, p_indices, true);
const AudioTrack *ad = static_cast<const AudioTrack *>(t); _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices, false);
_track_get_key_indices_in_range(ad->values, 0, from_time, p_indices); } break;
_track_get_key_indices_in_range(ad->values, CMP_EPSILON, to_time, p_indices); case TYPE_ANIMATION: {
} break; const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
case TYPE_ANIMATION: { _track_get_key_indices_in_range(an->values, 0, from_time, p_indices, true);
const AnimationTrack *an = static_cast<const AnimationTrack *>(t); _track_get_key_indices_in_range(an->values, 0, to_time, p_indices, false);
_track_get_key_indices_in_range(an->values, 0, from_time, p_indices); } break;
_track_get_key_indices_in_range(an->values, CMP_EPSILON, to_time, p_indices);
} break;
}
return;
} }
if (p_pingponged == 1) { return;
// handle loop by splitting }
to_time = MIN(length - CMP_EPSILON, to_time); if (p_looped_flag == Animation::LOOPED_FLAG_END) {
switch (t->type) { // Handle loop by splitting.
case TYPE_POSITION_3D: { switch (t->type) {
const PositionTrack *tt = static_cast<const PositionTrack *>(t); case TYPE_POSITION_3D: {
if (tt->compressed_track >= 0) { const PositionTrack *tt = static_cast<const PositionTrack *>(t);
_get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices); if (tt->compressed_track >= 0) {
_get_compressed_key_indices_in_range<3>(tt->compressed_track, to_time, length - CMP_EPSILON, p_indices); _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices);
} else { _get_compressed_key_indices_in_range<3>(tt->compressed_track, to_time, length, p_indices);
_track_get_key_indices_in_range(tt->positions, from_time, length, p_indices); } else {
_track_get_key_indices_in_range(tt->positions, to_time, length - CMP_EPSILON, p_indices); _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices, false);
} _track_get_key_indices_in_range(tt->positions, to_time, length, p_indices, true);
} break; }
case TYPE_ROTATION_3D: { } break;
const RotationTrack *rt = static_cast<const RotationTrack *>(t); case TYPE_ROTATION_3D: {
if (rt->compressed_track >= 0) { const RotationTrack *rt = static_cast<const RotationTrack *>(t);
_get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices); if (rt->compressed_track >= 0) {
_get_compressed_key_indices_in_range<3>(rt->compressed_track, to_time, length, p_indices); _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices);
} else { _get_compressed_key_indices_in_range<3>(rt->compressed_track, to_time, length, p_indices);
_track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices); } else {
_track_get_key_indices_in_range(rt->rotations, to_time, length - CMP_EPSILON, p_indices); _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices, false);
} _track_get_key_indices_in_range(rt->rotations, to_time, length, p_indices, true);
} break; }
case TYPE_SCALE_3D: { } break;
const ScaleTrack *st = static_cast<const ScaleTrack *>(t); case TYPE_SCALE_3D: {
if (st->compressed_track >= 0) { const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
_get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices); if (st->compressed_track >= 0) {
_get_compressed_key_indices_in_range<3>(st->compressed_track, to_time, length, p_indices); _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices);
} else { _get_compressed_key_indices_in_range<3>(st->compressed_track, to_time, length, p_indices);
_track_get_key_indices_in_range(st->scales, from_time, length, p_indices); } else {
_track_get_key_indices_in_range(st->scales, to_time, length - CMP_EPSILON, p_indices); _track_get_key_indices_in_range(st->scales, from_time, length, p_indices, false);
} _track_get_key_indices_in_range(st->scales, to_time, length, p_indices, true);
} break; }
case TYPE_BLEND_SHAPE: { } break;
const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t); case TYPE_BLEND_SHAPE: {
if (bst->compressed_track >= 0) { const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
_get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices); if (bst->compressed_track >= 0) {
_get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length - CMP_EPSILON, p_indices); _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
} else { _get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length - CMP_EPSILON, p_indices);
_track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices); } else {
_track_get_key_indices_in_range(bst->blend_shapes, to_time, length - CMP_EPSILON, p_indices); _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices, false);
} _track_get_key_indices_in_range(bst->blend_shapes, to_time, length, p_indices, true);
} break; }
case TYPE_VALUE: { } break;
const ValueTrack *vt = static_cast<const ValueTrack *>(t); case TYPE_VALUE: {
_track_get_key_indices_in_range(vt->values, from_time, length, p_indices); const ValueTrack *vt = static_cast<const ValueTrack *>(t);
_track_get_key_indices_in_range(vt->values, to_time, length - CMP_EPSILON, p_indices); _track_get_key_indices_in_range(vt->values, from_time, length, p_indices, false);
} break; _track_get_key_indices_in_range(vt->values, to_time, length, p_indices, true);
case TYPE_METHOD: { } break;
const MethodTrack *mt = static_cast<const MethodTrack *>(t); case TYPE_METHOD: {
_track_get_key_indices_in_range(mt->methods, from_time, length, p_indices); const MethodTrack *mt = static_cast<const MethodTrack *>(t);
_track_get_key_indices_in_range(mt->methods, to_time, length - CMP_EPSILON, p_indices); _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices, false);
} break; _track_get_key_indices_in_range(mt->methods, to_time, length, p_indices, true);
case TYPE_BEZIER: { } break;
const BezierTrack *bz = static_cast<const BezierTrack *>(t); case TYPE_BEZIER: {
_track_get_key_indices_in_range(bz->values, from_time, length, p_indices); const BezierTrack *bz = static_cast<const BezierTrack *>(t);
_track_get_key_indices_in_range(bz->values, to_time, length - CMP_EPSILON, p_indices); _track_get_key_indices_in_range(bz->values, from_time, length, p_indices, false);
} break; _track_get_key_indices_in_range(bz->values, to_time, length, p_indices, true);
case TYPE_AUDIO: { } break;
const AudioTrack *ad = static_cast<const AudioTrack *>(t); case TYPE_AUDIO: {
_track_get_key_indices_in_range(ad->values, from_time, length, p_indices); const AudioTrack *ad = static_cast<const AudioTrack *>(t);
_track_get_key_indices_in_range(ad->values, to_time, length - CMP_EPSILON, p_indices); _track_get_key_indices_in_range(ad->values, from_time, length, p_indices, false);
} break; _track_get_key_indices_in_range(ad->values, to_time, length, p_indices, true);
case TYPE_ANIMATION: { } break;
const AnimationTrack *an = static_cast<const AnimationTrack *>(t); case TYPE_ANIMATION: {
_track_get_key_indices_in_range(an->values, from_time, length, p_indices); const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
_track_get_key_indices_in_range(an->values, to_time, length - CMP_EPSILON, p_indices); _track_get_key_indices_in_range(an->values, from_time, length, p_indices, false);
} break; _track_get_key_indices_in_range(an->values, to_time, length, p_indices, true);
} } break;
return;
} }
return;
}
// The edge will be pingponged in the next frame and processed there, so let's ignore it now...
if (!is_backward && Math::is_equal_approx(to_time, length)) {
to_time = length - CMP_EPSILON;
} else if (is_backward && Math::is_equal_approx(from_time, 0)) {
from_time = CMP_EPSILON;
} }
} break; } break;
} }
switch (t->type) { switch (t->type) {
case TYPE_POSITION_3D: { case TYPE_POSITION_3D: {
const PositionTrack *tt = static_cast<const PositionTrack *>(t); const PositionTrack *tt = static_cast<const PositionTrack *>(t);
if (tt->compressed_track >= 0) { if (tt->compressed_track >= 0) {
_get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, to_time - from_time, p_indices); _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, to_time - from_time, p_indices);
} else { } else {
_track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices); _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices, is_backward);
} }
} break; } break;
case TYPE_ROTATION_3D: { case TYPE_ROTATION_3D: {
@ -3042,7 +3129,7 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
if (rt->compressed_track >= 0) { if (rt->compressed_track >= 0) {
_get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, to_time - from_time, p_indices); _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, to_time - from_time, p_indices);
} else { } else {
_track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices); _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices, is_backward);
} }
} break; } break;
case TYPE_SCALE_3D: { case TYPE_SCALE_3D: {
@ -3050,7 +3137,7 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
if (st->compressed_track >= 0) { if (st->compressed_track >= 0) {
_get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, to_time - from_time, p_indices); _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, to_time - from_time, p_indices);
} else { } else {
_track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices); _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices, is_backward);
} }
} break; } break;
case TYPE_BLEND_SHAPE: { case TYPE_BLEND_SHAPE: {
@ -3058,28 +3145,28 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
if (bst->compressed_track >= 0) { if (bst->compressed_track >= 0) {
_get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, to_time - from_time, p_indices); _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, to_time - from_time, p_indices);
} else { } else {
_track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices); _track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices, is_backward);
} }
} break; } break;
case TYPE_VALUE: { case TYPE_VALUE: {
const ValueTrack *vt = static_cast<const ValueTrack *>(t); const ValueTrack *vt = static_cast<const ValueTrack *>(t);
_track_get_key_indices_in_range(vt->values, from_time, to_time, p_indices); _track_get_key_indices_in_range(vt->values, from_time, to_time, p_indices, is_backward);
} break; } break;
case TYPE_METHOD: { case TYPE_METHOD: {
const MethodTrack *mt = static_cast<const MethodTrack *>(t); const MethodTrack *mt = static_cast<const MethodTrack *>(t);
_track_get_key_indices_in_range(mt->methods, from_time, to_time, p_indices); _track_get_key_indices_in_range(mt->methods, from_time, to_time, p_indices, is_backward);
} break; } break;
case TYPE_BEZIER: { case TYPE_BEZIER: {
const BezierTrack *bz = static_cast<const BezierTrack *>(t); const BezierTrack *bz = static_cast<const BezierTrack *>(t);
_track_get_key_indices_in_range(bz->values, from_time, to_time, p_indices); _track_get_key_indices_in_range(bz->values, from_time, to_time, p_indices, is_backward);
} break; } break;
case TYPE_AUDIO: { case TYPE_AUDIO: {
const AudioTrack *ad = static_cast<const AudioTrack *>(t); const AudioTrack *ad = static_cast<const AudioTrack *>(t);
_track_get_key_indices_in_range(ad->values, from_time, to_time, p_indices); _track_get_key_indices_in_range(ad->values, from_time, to_time, p_indices, is_backward);
} break; } break;
case TYPE_ANIMATION: { case TYPE_ANIMATION: {
const AnimationTrack *an = static_cast<const AnimationTrack *>(t); const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
_track_get_key_indices_in_range(an->values, from_time, to_time, p_indices); _track_get_key_indices_in_range(an->values, from_time, to_time, p_indices, is_backward);
} break; } break;
} }
} }
@ -3815,6 +3902,10 @@ void Animation::_bind_methods() {
BIND_ENUM_CONSTANT(LOOP_NONE); BIND_ENUM_CONSTANT(LOOP_NONE);
BIND_ENUM_CONSTANT(LOOP_LINEAR); BIND_ENUM_CONSTANT(LOOP_LINEAR);
BIND_ENUM_CONSTANT(LOOP_PINGPONG); BIND_ENUM_CONSTANT(LOOP_PINGPONG);
BIND_ENUM_CONSTANT(LOOPED_FLAG_NONE);
BIND_ENUM_CONSTANT(LOOPED_FLAG_END);
BIND_ENUM_CONSTANT(LOOPED_FLAG_START);
} }
void Animation::clear() { void Animation::clear() {
@ -5798,7 +5889,8 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float
} }
} }
Animation::Animation() {} Animation::Animation() {
}
Animation::~Animation() { Animation::~Animation() {
for (int i = 0; i < tracks.size(); i++) { for (int i = 0; i < tracks.size(); i++) {

View File

@ -74,6 +74,12 @@ public:
LOOP_PINGPONG, LOOP_PINGPONG,
}; };
enum LoopedFlag {
LOOPED_FLAG_NONE,
LOOPED_FLAG_END,
LOOPED_FLAG_START,
};
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
enum HandleMode { enum HandleMode {
HANDLE_MODE_FREE, HANDLE_MODE_FREE,
@ -250,12 +256,11 @@ private:
_FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok, bool p_backward = false) const; _FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok, bool p_backward = false) const;
template <class T> template <class T>
_FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const; _FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices, bool p_is_backward) const;
double length = 1.0; double length = 1.0;
real_t step = 0.1; real_t step = 0.1;
LoopMode loop_mode = LOOP_NONE; LoopMode loop_mode = LOOP_NONE;
int pingponged = 0;
/* Animation compression page format (version 1): /* Animation compression page format (version 1):
* *
@ -454,7 +459,7 @@ public:
void copy_track(int p_track, Ref<Animation> p_to_animation); void copy_track(int p_track, Ref<Animation> p_to_animation);
void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged = 0) const; void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE) const;
void set_length(real_t p_length); void set_length(real_t p_length);
real_t get_length() const; real_t get_length() const;
@ -484,6 +489,7 @@ VARIANT_ENUM_CAST(Animation::TrackType);
VARIANT_ENUM_CAST(Animation::InterpolationType); VARIANT_ENUM_CAST(Animation::InterpolationType);
VARIANT_ENUM_CAST(Animation::UpdateMode); VARIANT_ENUM_CAST(Animation::UpdateMode);
VARIANT_ENUM_CAST(Animation::LoopMode); VARIANT_ENUM_CAST(Animation::LoopMode);
VARIANT_ENUM_CAST(Animation::LoopedFlag);
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
VARIANT_ENUM_CAST(Animation::HandleMode); VARIANT_ENUM_CAST(Animation::HandleMode);
VARIANT_ENUM_CAST(Animation::HandleSetMode); VARIANT_ENUM_CAST(Animation::HandleSetMode);