Merge pull request #53819 from TokageItLab/re-implement-ping-pong
Reimplement ping-pong animation and reverse playback
This commit is contained in:
@ -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();
|
||||
|
||||
Reference in New Issue
Block a user