|
@@ -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 (Math::is_zero_approx(delta)) {
|
|
|
- c = 0;
|
|
|
+ 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;
|
|
|
+ } 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;
|
|
|
-
|
|
|
- if (Math::is_zero_approx(delta)) {
|
|
|
- c = 0;
|
|
|
+ // 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;
|
|
|
+ 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 (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 (!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 (Math::is_zero_approx(delta)) {
|
|
|
+ c = 0;
|
|
|
+ } else {
|
|
|
+ c = from / delta;
|
|
|
+ }
|
|
|
} else {
|
|
|
- c = from / delta;
|
|
|
+ next = idx;
|
|
|
}
|
|
|
-
|
|
|
} else {
|
|
|
- next = idx;
|
|
|
+ idx = next = 0;
|
|
|
}
|
|
|
-
|
|
|
} else {
|
|
|
- // only allow extending first key to anim start if looping
|
|
|
- if (loop) {
|
|
|
- idx = next = 0;
|
|
|
+ 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);
|
|
|
+ switch (loop_mode) {
|
|
|
+ case LOOP_NONE: {
|
|
|
+ if (from_time < 0) {
|
|
|
+ from_time = 0;
|
|
|
+ }
|
|
|
+ if (from_time > length) {
|
|
|
+ from_time = length;
|
|
|
+ }
|
|
|
|
|
|
- } 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);
|
|
|
- }
|
|
|
+ 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);
|
|
|
+ }
|
|
|
|
|
|
- } 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);
|
|
|
+ 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);
|
|
|
+ }
|
|
|
|
|
|
- } 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);
|
|
|
+ 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;
|
|
|
}
|
|
|
-
|
|
|
- } 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);
|
|
|
+ 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;
|
|
|
}
|
|
|
-
|
|
|
- } 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;
|
|
|
+ }
|
|
|
}
|
|
|
- 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;
|
|
|
}
|
|
|
|
|
|
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();
|