Merge pull request #53819 from TokageItLab/re-implement-ping-pong

Reimplement ping-pong animation and reverse playback
This commit is contained in:
Rémi Verschelde
2021-11-09 22:11:04 +01:00
committed by GitHub
29 changed files with 1047 additions and 446 deletions

View File

@ -449,8 +449,8 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
return true;
} else if (name == "length") {
r_ret = length;
} else if (name == "loop") {
r_ret = loop;
} else if (name == "loop_mode") {
r_ret = loop_mode;
} else if (name == "step") {
r_ret = step;
} else if (name.begins_with("tracks/")) {
@ -2231,7 +2231,7 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr
}
template <class K>
int Animation::_find(const Vector<K> &p_keys, double p_time) const {
int Animation::_find(const Vector<K> &p_keys, double p_time, bool p_backward) const {
int len = p_keys.size();
if (len == 0) {
return -2;
@ -2261,8 +2261,14 @@ int Animation::_find(const Vector<K> &p_keys, double p_time) const {
}
}
if (keys[middle].time > p_time) {
middle--;
if (!p_backward) {
if (keys[middle].time > p_time) {
middle--;
}
} else {
if (keys[middle].time < p_time) {
middle++;
}
}
return middle;
@ -2385,7 +2391,7 @@ real_t Animation::_cubic_interpolate(const real_t &p_pre_a, const real_t &p_a, c
}
template <class T>
T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const {
T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok, bool p_backward) const {
int len = _find(p_keys, length) + 1; // try to find last key (there may be more past the end)
if (len <= 0) {
@ -2403,7 +2409,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
return p_keys[0].value;
}
int idx = _find(p_keys, p_time);
int idx = _find(p_keys, p_time, p_backward);
ERR_FAIL_COND_V(idx == -2, T());
@ -2412,24 +2418,42 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
real_t c = 0.0;
// prepare for all cases of interpolation
if (loop && p_loop_wrap) {
if ((loop_mode == LOOP_LINEAR || loop_mode == LOOP_PINGPONG) && p_loop_wrap) {
// loop
if (idx >= 0) {
if ((idx + 1) < len) {
next = idx + 1;
real_t delta = p_keys[next].time - p_keys[idx].time;
real_t from = p_time - p_keys[idx].time;
if (!p_backward) {
// no backward
if (idx >= 0) {
if (idx < len - 1) {
next = idx + 1;
real_t delta = p_keys[next].time - p_keys[idx].time;
real_t from = p_time - p_keys[idx].time;
if (Math::is_zero_approx(delta)) {
c = 0;
if (Math::is_zero_approx(delta)) {
c = 0;
} else {
c = from / delta;
}
} else {
c = from / delta;
}
next = 0;
real_t delta = (length - p_keys[idx].time) + p_keys[next].time;
real_t from = p_time - p_keys[idx].time;
if (Math::is_zero_approx(delta)) {
c = 0;
} else {
c = from / delta;
}
}
} else {
// on loop, behind first key
idx = len - 1;
next = 0;
real_t delta = (length - p_keys[idx].time) + p_keys[next].time;
real_t from = p_time - p_keys[idx].time;
real_t endtime = (length - p_keys[idx].time);
if (endtime < 0) { // may be keys past the end
endtime = 0;
}
real_t delta = endtime + p_keys[next].time;
real_t from = endtime + p_time;
if (Math::is_zero_approx(delta)) {
c = 0;
@ -2437,49 +2461,81 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
c = from / delta;
}
}
} else {
// on loop, behind first key
idx = len - 1;
next = 0;
real_t endtime = (length - p_keys[idx].time);
if (endtime < 0) { // may be keys past the end
endtime = 0;
}
real_t delta = endtime + p_keys[next].time;
real_t from = endtime + p_time;
// backward
if (idx <= len - 1) {
if (idx > 0) {
next = idx - 1;
real_t delta = (length - p_keys[next].time) - (length - p_keys[idx].time);
real_t from = (length - p_time) - (length - p_keys[idx].time);
if (Math::is_zero_approx(delta)) {
c = 0;
if (Math::is_zero_approx(delta))
c = 0;
else
c = from / delta;
} else {
next = len - 1;
real_t delta = p_keys[idx].time + (length - p_keys[next].time);
real_t from = (length - p_time) - (length - p_keys[idx].time);
if (Math::is_zero_approx(delta))
c = 0;
else
c = from / delta;
}
} else {
c = from / delta;
// on loop, in front of last key
idx = 0;
next = len - 1;
real_t endtime = p_keys[idx].time;
if (endtime > length) // may be keys past the end
endtime = length;
real_t delta = p_keys[next].time - endtime;
real_t from = p_time - endtime;
if (Math::is_zero_approx(delta))
c = 0;
else
c = from / delta;
}
}
} else { // no loop
if (!p_backward) {
if (idx >= 0) {
if (idx < len - 1) {
next = idx + 1;
real_t delta = p_keys[next].time - p_keys[idx].time;
real_t from = p_time - p_keys[idx].time;
if (idx >= 0) {
if ((idx + 1) < len) {
next = idx + 1;
real_t delta = p_keys[next].time - p_keys[idx].time;
real_t from = p_time - p_keys[idx].time;
if (Math::is_zero_approx(delta)) {
c = 0;
if (Math::is_zero_approx(delta)) {
c = 0;
} else {
c = from / delta;
}
} else {
c = from / delta;
next = idx;
}
} else {
next = idx;
}
} else {
// only allow extending first key to anim start if looping
if (loop) {
idx = next = 0;
}
} else {
if (idx <= len - 1) {
if (idx > 0) {
next = idx - 1;
real_t delta = (length - p_keys[next].time) - (length - p_keys[idx].time);
real_t from = (length - p_time) - (length - p_keys[idx].time);
if (Math::is_zero_approx(delta)) {
c = 0;
} else {
c = from / delta;
}
} else {
next = idx;
}
} else {
result = false;
idx = next = len - 1;
}
}
}
@ -2583,7 +2639,7 @@ void Animation::_value_track_get_key_indices_in_range(const ValueTrack *vt, doub
}
}
void Animation::value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
void Animation::value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_VALUE);
@ -2597,30 +2653,50 @@ void Animation::value_track_get_key_indices(int p_track, double p_time, double p
SWAP(from_time, to_time);
}
if (loop) {
from_time = Math::fposmod(from_time, length);
to_time = Math::fposmod(to_time, length);
switch (loop_mode) {
case LOOP_NONE: {
if (from_time < 0) {
from_time = 0;
}
if (from_time > length) {
from_time = length;
}
if (from_time > to_time) {
// handle loop by splitting
_value_track_get_key_indices_in_range(vt, from_time, length, p_indices);
_value_track_get_key_indices_in_range(vt, 0, to_time, p_indices);
return;
}
} else {
if (from_time < 0) {
from_time = 0;
}
if (from_time > length) {
from_time = length;
}
if (to_time < 0) {
to_time = 0;
}
if (to_time > length) {
to_time = length;
}
} break;
case LOOP_LINEAR: {
from_time = Math::fposmod(from_time, length);
to_time = Math::fposmod(to_time, length);
if (to_time < 0) {
to_time = 0;
}
if (to_time > length) {
to_time = length;
}
if (from_time > to_time) {
// handle loop by splitting
_value_track_get_key_indices_in_range(vt, from_time, length, p_indices);
_value_track_get_key_indices_in_range(vt, 0, to_time, p_indices);
return;
}
} break;
case LOOP_PINGPONG: {
from_time = Math::pingpong(from_time, length);
to_time = Math::pingpong(to_time, length);
if (p_pingponged == -1) {
// handle loop by splitting
_value_track_get_key_indices_in_range(vt, 0, from_time, p_indices);
_value_track_get_key_indices_in_range(vt, 0, to_time, p_indices);
return;
}
if (p_pingponged == 1) {
// handle loop by splitting
_value_track_get_key_indices_in_range(vt, from_time, length, p_indices);
_value_track_get_key_indices_in_range(vt, to_time, length, p_indices);
return;
}
} break;
}
_value_track_get_key_indices_in_range(vt, from_time, to_time, p_indices);
@ -2679,7 +2755,7 @@ void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, double
}
}
void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
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 {
ERR_FAIL_INDEX(p_track, tracks.size());
const Track *t = tracks[p_track];
@ -2690,114 +2766,255 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
SWAP(from_time, to_time);
}
if (loop) {
if (from_time > length || from_time < 0) {
from_time = Math::fposmod(from_time, length);
}
if (to_time > length || to_time < 0) {
to_time = Math::fposmod(to_time, length);
}
if (from_time > to_time) {
// handle loop by splitting
switch (t->type) {
case TYPE_POSITION_3D: {
const PositionTrack *tt = static_cast<const PositionTrack *>(t);
if (tt->compressed_track >= 0) {
_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);
} else {
_track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
_track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices);
}
} break;
case TYPE_ROTATION_3D: {
const RotationTrack *rt = static_cast<const RotationTrack *>(t);
if (rt->compressed_track >= 0) {
_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);
} else {
_track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
_track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices);
}
} break;
case TYPE_SCALE_3D: {
const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
if (st->compressed_track >= 0) {
_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);
} else {
_track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
_track_get_key_indices_in_range(st->scales, 0, to_time, p_indices);
}
} break;
case TYPE_BLEND_SHAPE: {
const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
if (bst->compressed_track >= 0) {
_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);
} else {
_track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices);
_track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices);
}
} break;
case TYPE_VALUE: {
const ValueTrack *vt = static_cast<const ValueTrack *>(t);
_track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
_track_get_key_indices_in_range(vt->values, 0, to_time, p_indices);
} break;
case TYPE_METHOD: {
const MethodTrack *mt = static_cast<const MethodTrack *>(t);
_track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
_track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices);
} break;
case TYPE_BEZIER: {
const BezierTrack *bz = static_cast<const BezierTrack *>(t);
_track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
_track_get_key_indices_in_range(bz->values, 0, to_time, p_indices);
} break;
case TYPE_AUDIO: {
const AudioTrack *ad = static_cast<const AudioTrack *>(t);
_track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
_track_get_key_indices_in_range(ad->values, 0, to_time, p_indices);
} break;
case TYPE_ANIMATION: {
const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
_track_get_key_indices_in_range(an->values, from_time, length, p_indices);
_track_get_key_indices_in_range(an->values, 0, to_time, p_indices);
} break;
switch (loop_mode) {
case LOOP_NONE: {
if (from_time < 0) {
from_time = 0;
}
if (from_time > length) {
from_time = length;
}
return;
}
} else {
if (from_time < 0) {
from_time = 0;
}
if (from_time > length) {
from_time = length;
}
if (to_time < 0) {
to_time = 0;
}
if (to_time > length) {
to_time = length;
}
if (to_time < 0) {
to_time = 0;
}
if (to_time > length) {
to_time = length;
}
} break;
case LOOP_LINEAR: {
if (from_time > length || from_time < 0) {
from_time = Math::fposmod(from_time, length);
}
if (to_time > length || to_time < 0) {
to_time = Math::fposmod(to_time, length);
}
if (from_time > to_time) {
// handle loop by splitting
switch (t->type) {
case TYPE_POSITION_3D: {
const PositionTrack *tt = static_cast<const PositionTrack *>(t);
if (tt->compressed_track >= 0) {
_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);
} else {
_track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
_track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices);
}
} break;
case TYPE_ROTATION_3D: {
const RotationTrack *rt = static_cast<const RotationTrack *>(t);
if (rt->compressed_track >= 0) {
_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);
} else {
_track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
_track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices);
}
} break;
case TYPE_SCALE_3D: {
const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
if (st->compressed_track >= 0) {
_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);
} else {
_track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
_track_get_key_indices_in_range(st->scales, 0, to_time, p_indices);
}
} break;
case TYPE_BLEND_SHAPE: {
const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
if (bst->compressed_track >= 0) {
_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);
} else {
_track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices);
_track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices);
}
} break;
case TYPE_VALUE: {
const ValueTrack *vt = static_cast<const ValueTrack *>(t);
_track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
_track_get_key_indices_in_range(vt->values, 0, to_time, p_indices);
} break;
case TYPE_METHOD: {
const MethodTrack *mt = static_cast<const MethodTrack *>(t);
_track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
_track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices);
} break;
case TYPE_BEZIER: {
const BezierTrack *bz = static_cast<const BezierTrack *>(t);
_track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
_track_get_key_indices_in_range(bz->values, 0, to_time, p_indices);
} break;
case TYPE_AUDIO: {
const AudioTrack *ad = static_cast<const AudioTrack *>(t);
_track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
_track_get_key_indices_in_range(ad->values, 0, to_time, p_indices);
} break;
case TYPE_ANIMATION: {
const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
_track_get_key_indices_in_range(an->values, from_time, length, p_indices);
_track_get_key_indices_in_range(an->values, 0, to_time, p_indices);
} break;
}
return;
}
} break;
case LOOP_PINGPONG: {
if (from_time > length || from_time < 0) {
from_time = Math::pingpong(from_time, length);
}
if (to_time > length || to_time < 0) {
to_time = Math::pingpong(to_time, length);
}
if ((int)Math::floor(abs(p_delta) / length) % 2 == 0) {
if (p_pingponged == -1) {
// handle loop by splitting
switch (t->type) {
case TYPE_POSITION_3D: {
const PositionTrack *tt = static_cast<const PositionTrack *>(t);
if (tt->compressed_track >= 0) {
_get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, p_indices);
_get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices);
} else {
_track_get_key_indices_in_range(tt->positions, 0, from_time, p_indices);
_track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices);
}
} break;
case TYPE_ROTATION_3D: {
const RotationTrack *rt = static_cast<const RotationTrack *>(t);
if (rt->compressed_track >= 0) {
_get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, p_indices);
_get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices);
} else {
_track_get_key_indices_in_range(rt->rotations, 0, from_time, p_indices);
_track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices);
}
} break;
case TYPE_SCALE_3D: {
const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
if (st->compressed_track >= 0) {
_get_compressed_key_indices_in_range<3>(st->compressed_track, 0, from_time, p_indices);
_get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices);
} else {
_track_get_key_indices_in_range(st->scales, 0, from_time, p_indices);
_track_get_key_indices_in_range(st->scales, 0, to_time, p_indices);
}
} break;
case TYPE_BLEND_SHAPE: {
const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
if (bst->compressed_track >= 0) {
_get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, from_time, p_indices);
_get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices);
} else {
_track_get_key_indices_in_range(bst->blend_shapes, 0, from_time, p_indices);
_track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices);
}
} break;
case TYPE_VALUE: {
const ValueTrack *vt = static_cast<const ValueTrack *>(t);
_track_get_key_indices_in_range(vt->values, 0, from_time, p_indices);
_track_get_key_indices_in_range(vt->values, 0, to_time, p_indices);
} break;
case TYPE_METHOD: {
const MethodTrack *mt = static_cast<const MethodTrack *>(t);
_track_get_key_indices_in_range(mt->methods, 0, from_time, p_indices);
_track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices);
} break;
case TYPE_BEZIER: {
const BezierTrack *bz = static_cast<const BezierTrack *>(t);
_track_get_key_indices_in_range(bz->values, 0, from_time, p_indices);
_track_get_key_indices_in_range(bz->values, 0, to_time, p_indices);
} break;
case TYPE_AUDIO: {
const AudioTrack *ad = static_cast<const AudioTrack *>(t);
_track_get_key_indices_in_range(ad->values, 0, from_time, p_indices);
_track_get_key_indices_in_range(ad->values, 0, to_time, p_indices);
} break;
case TYPE_ANIMATION: {
const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
_track_get_key_indices_in_range(an->values, 0, from_time, p_indices);
_track_get_key_indices_in_range(an->values, 0, to_time, p_indices);
} break;
}
return;
}
if (p_pingponged == 1) {
// handle loop by splitting
switch (t->type) {
case TYPE_POSITION_3D: {
const PositionTrack *tt = static_cast<const PositionTrack *>(t);
if (tt->compressed_track >= 0) {
_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, to_time, length, p_indices);
} else {
_track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
_track_get_key_indices_in_range(tt->positions, to_time, length, p_indices);
}
} break;
case TYPE_ROTATION_3D: {
const RotationTrack *rt = static_cast<const RotationTrack *>(t);
if (rt->compressed_track >= 0) {
_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, to_time, length, p_indices);
} else {
_track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
_track_get_key_indices_in_range(rt->rotations, to_time, length, p_indices);
}
} break;
case TYPE_SCALE_3D: {
const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
if (st->compressed_track >= 0) {
_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, to_time, length, p_indices);
} else {
_track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
_track_get_key_indices_in_range(st->scales, to_time, length, p_indices);
}
} break;
case TYPE_BLEND_SHAPE: {
const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
if (bst->compressed_track >= 0) {
_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, to_time, length, p_indices);
} else {
_track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices);
_track_get_key_indices_in_range(bst->blend_shapes, to_time, length, p_indices);
}
} break;
case TYPE_VALUE: {
const ValueTrack *vt = static_cast<const ValueTrack *>(t);
_track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
_track_get_key_indices_in_range(vt->values, to_time, length, p_indices);
} break;
case TYPE_METHOD: {
const MethodTrack *mt = static_cast<const MethodTrack *>(t);
_track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
_track_get_key_indices_in_range(mt->methods, to_time, length, p_indices);
} break;
case TYPE_BEZIER: {
const BezierTrack *bz = static_cast<const BezierTrack *>(t);
_track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
_track_get_key_indices_in_range(bz->values, to_time, length, p_indices);
} break;
case TYPE_AUDIO: {
const AudioTrack *ad = static_cast<const AudioTrack *>(t);
_track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
_track_get_key_indices_in_range(ad->values, to_time, length, p_indices);
} break;
case TYPE_ANIMATION: {
const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
_track_get_key_indices_in_range(an->values, from_time, length, p_indices);
_track_get_key_indices_in_range(an->values, to_time, length, p_indices);
} break;
}
return;
}
}
} break;
}
switch (t->type) {
@ -2808,7 +3025,6 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
} else {
_track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices);
}
} break;
case TYPE_ROTATION_3D: {
const RotationTrack *rt = static_cast<const RotationTrack *>(t);
@ -2817,7 +3033,6 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
} else {
_track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices);
}
} break;
case TYPE_SCALE_3D: {
const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
@ -2826,7 +3041,6 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
} else {
_track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices);
}
} break;
case TYPE_BLEND_SHAPE: {
const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
@ -2835,32 +3049,26 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
} else {
_track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices);
}
} break;
case TYPE_VALUE: {
const ValueTrack *vt = static_cast<const ValueTrack *>(t);
_track_get_key_indices_in_range(vt->values, from_time, to_time, p_indices);
} break;
case TYPE_METHOD: {
const MethodTrack *mt = static_cast<const MethodTrack *>(t);
_track_get_key_indices_in_range(mt->methods, from_time, to_time, p_indices);
} break;
case TYPE_BEZIER: {
const BezierTrack *bz = static_cast<const BezierTrack *>(t);
_track_get_key_indices_in_range(bz->values, from_time, to_time, p_indices);
} break;
case TYPE_AUDIO: {
const AudioTrack *ad = static_cast<const AudioTrack *>(t);
_track_get_key_indices_in_range(ad->values, from_time, to_time, p_indices);
} break;
case TYPE_ANIMATION: {
const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
_track_get_key_indices_in_range(an->values, from_time, to_time, p_indices);
} break;
}
}
@ -2898,7 +3106,7 @@ void Animation::_method_track_get_key_indices_in_range(const MethodTrack *mt, do
}
}
void Animation::method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
void Animation::method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_METHOD);
@ -2912,35 +3120,58 @@ void Animation::method_track_get_key_indices(int p_track, double p_time, double
SWAP(from_time, to_time);
}
if (loop) {
if (from_time > length || from_time < 0) {
from_time = Math::fposmod(from_time, length);
}
switch (loop_mode) {
case LOOP_NONE: {
if (from_time < 0) {
from_time = 0;
}
if (from_time > length) {
from_time = length;
}
if (to_time > length || to_time < 0) {
to_time = Math::fposmod(to_time, length);
}
if (to_time < 0) {
to_time = 0;
}
if (to_time > length) {
to_time = length;
}
} break;
case LOOP_LINEAR: {
if (from_time > length || from_time < 0) {
from_time = Math::fposmod(from_time, length);
}
if (to_time > length || to_time < 0) {
to_time = Math::fposmod(to_time, length);
}
if (from_time > to_time) {
// handle loop by splitting
_method_track_get_key_indices_in_range(mt, from_time, length, p_indices);
_method_track_get_key_indices_in_range(mt, 0, to_time, p_indices);
return;
}
} else {
if (from_time < 0) {
from_time = 0;
}
if (from_time > length) {
from_time = length;
}
if (from_time > to_time) {
// handle loop by splitting
_method_track_get_key_indices_in_range(mt, from_time, length, p_indices);
_method_track_get_key_indices_in_range(mt, 0, to_time, p_indices);
return;
}
} break;
case LOOP_PINGPONG: {
if (from_time > length || from_time < 0) {
from_time = Math::pingpong(from_time, length);
}
if (to_time > length || to_time < 0) {
to_time = Math::pingpong(to_time, length);
}
if (to_time < 0) {
to_time = 0;
}
if (to_time > length) {
to_time = length;
}
if (p_pingponged == -1) {
_method_track_get_key_indices_in_range(mt, 0, from_time, p_indices);
_method_track_get_key_indices_in_range(mt, 0, to_time, p_indices);
return;
}
if (p_pingponged == 1) {
_method_track_get_key_indices_in_range(mt, from_time, length, p_indices);
_method_track_get_key_indices_in_range(mt, to_time, length, p_indices);
return;
}
} break;
default:
break;
}
_method_track_get_key_indices_in_range(mt, from_time, to_time, p_indices);
@ -3326,13 +3557,13 @@ real_t Animation::get_length() const {
return length;
}
void Animation::set_loop(bool p_enabled) {
loop = p_enabled;
void Animation::set_loop_mode(Animation::LoopMode p_loop_mode) {
loop_mode = p_loop_mode;
emit_changed();
}
bool Animation::has_loop() const {
return loop;
Animation::LoopMode Animation::get_loop_mode() const {
return loop_mode;
}
void Animation::track_set_imported(int p_track, bool p_imported) {
@ -3514,8 +3745,8 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_length", "time_sec"), &Animation::set_length);
ClassDB::bind_method(D_METHOD("get_length"), &Animation::get_length);
ClassDB::bind_method(D_METHOD("set_loop", "enabled"), &Animation::set_loop);
ClassDB::bind_method(D_METHOD("has_loop"), &Animation::has_loop);
ClassDB::bind_method(D_METHOD("set_loop_mode", "loop_mode"), &Animation::set_loop_mode);
ClassDB::bind_method(D_METHOD("get_loop_mode"), &Animation::get_loop_mode);
ClassDB::bind_method(D_METHOD("set_step", "size_sec"), &Animation::set_step);
ClassDB::bind_method(D_METHOD("get_step"), &Animation::get_step);
@ -3526,7 +3757,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("compress", "page_size", "fps", "split_tolerance"), &Animation::compress, DEFVAL(8192), DEFVAL(120), DEFVAL(4.0));
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001"), "set_length", "get_length");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode"), "set_loop_mode", "get_loop_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "step", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_step", "get_step");
ADD_SIGNAL(MethodInfo("tracks_changed"));
@ -3549,6 +3780,10 @@ void Animation::_bind_methods() {
BIND_ENUM_CONSTANT(UPDATE_DISCRETE);
BIND_ENUM_CONSTANT(UPDATE_TRIGGER);
BIND_ENUM_CONSTANT(UPDATE_CAPTURE);
BIND_ENUM_CONSTANT(LOOP_NONE);
BIND_ENUM_CONSTANT(LOOP_LINEAR);
BIND_ENUM_CONSTANT(LOOP_PINGPONG);
}
void Animation::clear() {
@ -3556,7 +3791,7 @@ void Animation::clear() {
memdelete(tracks[i]);
}
tracks.clear();
loop = false;
loop_mode = LOOP_NONE;
length = 1;
compression.enabled = false;
compression.bounds.clear();