Merge pull request #64924 from TokageItLab/2drot

Add linear/cubic angle interpolation to `Animation::InterpolationType` for shortest 2D rotation
This commit is contained in:
Rémi Verschelde 2022-08-27 08:09:25 +02:00 committed by GitHub
commit c6516cfa3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 238 additions and 18 deletions

View File

@ -253,6 +253,35 @@ public:
(-p_pre + 3.0f * p_from - 3.0f * p_to + p_post) * (p_weight * p_weight * p_weight));
}
static _ALWAYS_INLINE_ double cubic_interpolate_angle(double p_from, double p_to, double p_pre, double p_post, double p_weight) {
double from_rot = fmod(p_from, Math_TAU);
double pre_diff = fmod(p_pre - from_rot, Math_TAU);
double pre_rot = from_rot + fmod(2.0 * pre_diff, Math_TAU) - pre_diff;
double to_diff = fmod(p_to - from_rot, Math_TAU);
double to_rot = from_rot + fmod(2.0 * to_diff, Math_TAU) - to_diff;
double post_diff = fmod(p_post - to_rot, Math_TAU);
double post_rot = to_rot + fmod(2.0 * post_diff, Math_TAU) - post_diff;
return cubic_interpolate(from_rot, to_rot, pre_rot, post_rot, p_weight);
}
static _ALWAYS_INLINE_ float cubic_interpolate_angle(float p_from, float p_to, float p_pre, float p_post, float p_weight) {
float from_rot = fmod(p_from, (float)Math_TAU);
float pre_diff = fmod(p_pre - from_rot, (float)Math_TAU);
float pre_rot = from_rot + fmod(2.0f * pre_diff, (float)Math_TAU) - pre_diff;
float to_diff = fmod(p_to - from_rot, (float)Math_TAU);
float to_rot = from_rot + fmod(2.0f * to_diff, (float)Math_TAU) - to_diff;
float post_diff = fmod(p_post - to_rot, (float)Math_TAU);
float post_rot = to_rot + fmod(2.0f * post_diff, (float)Math_TAU) - post_diff;
return cubic_interpolate(from_rot, to_rot, pre_rot, post_rot, p_weight);
}
static _ALWAYS_INLINE_ double cubic_interpolate_in_time(double p_from, double p_to, double p_pre, double p_post, double p_weight,
double p_to_t, double p_pre_t, double p_post_t) {
/* Barry-Goldman method */
@ -276,6 +305,37 @@ public:
return Math::lerp(b1, b2, p_to_t == 0 ? 0.5f : t / p_to_t);
}
static _ALWAYS_INLINE_ double cubic_interpolate_angle_in_time(double p_from, double p_to, double p_pre, double p_post, double p_weight,
double p_to_t, double p_pre_t, double p_post_t) {
double from_rot = fmod(p_from, Math_TAU);
double pre_diff = fmod(p_pre - from_rot, Math_TAU);
double pre_rot = from_rot + fmod(2.0 * pre_diff, Math_TAU) - pre_diff;
double to_diff = fmod(p_to - from_rot, Math_TAU);
double to_rot = from_rot + fmod(2.0 * to_diff, Math_TAU) - to_diff;
double post_diff = fmod(p_post - to_rot, Math_TAU);
double post_rot = to_rot + fmod(2.0 * post_diff, Math_TAU) - post_diff;
return cubic_interpolate_in_time(from_rot, to_rot, pre_rot, post_rot, p_weight, p_to_t, p_pre_t, p_post_t);
}
static _ALWAYS_INLINE_ float cubic_interpolate_angle_in_time(float p_from, float p_to, float p_pre, float p_post, float p_weight,
float p_to_t, float p_pre_t, float p_post_t) {
float from_rot = fmod(p_from, (float)Math_TAU);
float pre_diff = fmod(p_pre - from_rot, (float)Math_TAU);
float pre_rot = from_rot + fmod(2.0f * pre_diff, (float)Math_TAU) - pre_diff;
float to_diff = fmod(p_to - from_rot, (float)Math_TAU);
float to_rot = from_rot + fmod(2.0f * to_diff, (float)Math_TAU) - to_diff;
float post_diff = fmod(p_post - to_rot, (float)Math_TAU);
float post_rot = to_rot + fmod(2.0f * post_diff, (float)Math_TAU) - post_diff;
return cubic_interpolate_in_time(from_rot, to_rot, pre_rot, post_rot, p_weight, p_to_t, p_pre_t, p_post_t);
}
static _ALWAYS_INLINE_ double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) {
/* Formula from Wikipedia article on Bezier curves. */
double omt = (1.0 - p_t);

View File

@ -367,11 +367,20 @@ struct VariantUtilityFunctions {
return Math::cubic_interpolate(from, to, pre, post, weight);
}
static inline double cubic_interpolate_angle(double from, double to, double pre, double post, double weight) {
return Math::cubic_interpolate_angle(from, to, pre, post, weight);
}
static inline double cubic_interpolate_in_time(double from, double to, double pre, double post, double weight,
double to_t, double pre_t, double post_t) {
return Math::cubic_interpolate_in_time(from, to, pre, post, weight, to_t, pre_t, post_t);
}
static inline double cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight,
double to_t, double pre_t, double post_t) {
return Math::cubic_interpolate_angle_in_time(from, to, pre, post, weight, to_t, pre_t, post_t);
}
static inline double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) {
return Math::bezier_interpolate(p_start, p_control_1, p_control_2, p_end, p_t);
}
@ -1419,7 +1428,9 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDVR3(lerp, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(lerpf, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(cubic_interpolate, sarray("from", "to", "pre", "post", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(cubic_interpolate_angle, sarray("from", "to", "pre", "post", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(cubic_interpolate_in_time, sarray("from", "to", "pre", "post", "weight", "to_t", "pre_t", "post_t"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(cubic_interpolate_angle_in_time, sarray("from", "to", "pre", "post", "weight", "to_t", "pre_t", "post_t"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(bezier_interpolate, sarray("start", "control_1", "control_2", "end", "t"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(lerp_angle, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(inverse_lerp, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);

View File

@ -260,6 +260,32 @@
Cubic interpolates between two values by the factor defined in [param weight] with pre and post values.
</description>
</method>
<method name="cubic_interpolate_angle">
<return type="float" />
<param index="0" name="from" type="float" />
<param index="1" name="to" type="float" />
<param index="2" name="pre" type="float" />
<param index="3" name="post" type="float" />
<param index="4" name="weight" type="float" />
<description>
Cubic interpolates between two rotation values with shortest path by the factor defined in [param weight] with pre and post values. See also [method lerp_angle].
</description>
</method>
<method name="cubic_interpolate_angle_in_time">
<return type="float" />
<param index="0" name="from" type="float" />
<param index="1" name="to" type="float" />
<param index="2" name="pre" type="float" />
<param index="3" name="post" type="float" />
<param index="4" name="weight" type="float" />
<param index="5" name="to_t" type="float" />
<param index="6" name="pre_t" type="float" />
<param index="7" name="post_t" type="float" />
<description>
Cubic interpolates between two rotation values with shortest path by the factor defined in [param weight] with pre and post values. See also [method lerp_angle].
It can perform smoother interpolation than [code]cubic_interpolate()[/code] by the time values.
</description>
</method>
<method name="cubic_interpolate_in_time">
<return type="float" />
<param index="0" name="from" type="float" />

View File

@ -600,6 +600,14 @@
<constant name="INTERPOLATION_CUBIC" value="2" enum="InterpolationType">
Cubic interpolation.
</constant>
<constant name="INTERPOLATION_LINEAR_ANGLE" value="3" enum="InterpolationType">
Linear interpolation with shortest path rotation.
[b]Note:[/b] The result value is always normalized and may not match the key value.
</constant>
<constant name="INTERPOLATION_CUBIC_ANGLE" value="4" enum="InterpolationType">
Cubic interpolation with shortest path rotation.
[b]Note:[/b] The result value is always normalized and may not match the key value.
</constant>
<constant name="UPDATE_CONTINUOUS" value="0" enum="UpdateMode">
Update between keyframes.
</constant>

View File

@ -2122,10 +2122,12 @@ void AnimationTrackEdit::_notification(int p_what) {
get_theme_icon(SNAME("InterpWrapClamp"), SNAME("EditorIcons")),
get_theme_icon(SNAME("InterpWrapLoop"), SNAME("EditorIcons")),
};
Ref<Texture2D> interp_icon[3] = {
Ref<Texture2D> interp_icon[5] = {
get_theme_icon(SNAME("InterpRaw"), SNAME("EditorIcons")),
get_theme_icon(SNAME("InterpLinear"), SNAME("EditorIcons")),
get_theme_icon(SNAME("InterpCubic"), SNAME("EditorIcons")),
get_theme_icon(SNAME("InterpLinearAngle"), SNAME("EditorIcons")),
get_theme_icon(SNAME("InterpCubicAngle"), SNAME("EditorIcons")),
};
Ref<Texture2D> cont_icon[4] = {
get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")),
@ -2848,6 +2850,23 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
menu->add_icon_item(get_theme_icon(SNAME("InterpRaw"), SNAME("EditorIcons")), TTR("Nearest"), MENU_INTERPOLATION_NEAREST);
menu->add_icon_item(get_theme_icon(SNAME("InterpLinear"), SNAME("EditorIcons")), TTR("Linear"), MENU_INTERPOLATION_LINEAR);
menu->add_icon_item(get_theme_icon(SNAME("InterpCubic"), SNAME("EditorIcons")), TTR("Cubic"), MENU_INTERPOLATION_CUBIC);
// Check is angle property.
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
if (ape) {
AnimationPlayer *ap = ape->get_player();
if (ap) {
NodePath path = animation->track_get_path(track);
Node *nd = ap->get_node(ap->get_root())->get_node(NodePath(path.get_concatenated_names()));
StringName prop = path.get_concatenated_subnames();
PropertyInfo prop_info;
ClassDB::get_property_info(nd->get_class(), prop, &prop_info);
bool is_angle = prop_info.type == Variant::FLOAT && prop_info.hint_string.find("radians") != -1;
if (is_angle) {
menu->add_icon_item(get_theme_icon(SNAME("InterpLinearAngle"), SNAME("EditorIcons")), TTR("Linear Angle"), MENU_INTERPOLATION_LINEAR_ANGLE);
menu->add_icon_item(get_theme_icon(SNAME("InterpCubicAngle"), SNAME("EditorIcons")), TTR("Cubic Angle"), MENU_INTERPOLATION_CUBIC_ANGLE);
}
}
}
menu->reset_size();
Vector2 popup_pos = get_screen_position() + interp_mode_rect.position + Vector2(0, interp_mode_rect.size.height);
@ -3188,7 +3207,9 @@ void AnimationTrackEdit::_menu_selected(int p_index) {
} break;
case MENU_INTERPOLATION_NEAREST:
case MENU_INTERPOLATION_LINEAR:
case MENU_INTERPOLATION_CUBIC: {
case MENU_INTERPOLATION_CUBIC:
case MENU_INTERPOLATION_LINEAR_ANGLE:
case MENU_INTERPOLATION_CUBIC_ANGLE: {
Animation::InterpolationType interp_mode = Animation::InterpolationType(p_index - MENU_INTERPOLATION_NEAREST);
undo_redo->create_action(TTR("Change Animation Interpolation Mode"));
undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_type", track, interp_mode);
@ -6042,6 +6063,9 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
Vector<int> keys = E->value;
int len = keys.size() - 1;
// Special case for angle interpolation.
bool is_using_angle = animation->track_get_interpolation_type(track) == Animation::INTERPOLATION_LINEAR_ANGLE || animation->track_get_interpolation_type(track) == Animation::INTERPOLATION_CUBIC_ANGLE;
// Make insert queue.
Vector<Pair<double, Variant>> insert_queue;
for (int i = 0; i < len; i++) {
@ -6051,6 +6075,12 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
double to_t = animation->track_get_key_time(track, keys[i + 1]);
Variant from_v = animation->track_get_key_value(track, keys[i]);
Variant to_v = animation->track_get_key_value(track, keys[i + 1]);
if (is_using_angle) {
real_t a = from_v;
real_t b = to_v;
real_t to_diff = fmod(b - a, Math_TAU);
to_v = a + fmod(2.0 * to_diff, Math_TAU) - to_diff;
}
Variant delta_v;
Variant::sub(to_v, from_v, delta_v);
double duration = to_t - from_t;
@ -6192,10 +6222,14 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
do_bake |= b_bs && type == Animation::TYPE_BLEND_SHAPE;
do_bake |= b_v && type == Animation::TYPE_VALUE;
if (do_bake && !animation->track_is_compressed(i)) {
if (animation->track_get_interpolation_type(i) == Animation::INTERPOLATION_NEAREST) {
continue; // Nearest interpolation cannot be baked.
Animation::InterpolationType it = animation->track_get_interpolation_type(i);
if (it == Animation::INTERPOLATION_NEAREST) {
continue; // Nearest and Angle interpolation cannot be baked.
}
// Special case for angle interpolation.
bool is_using_angle = it == Animation::INTERPOLATION_LINEAR_ANGLE || it == Animation::INTERPOLATION_CUBIC_ANGLE;
// Make insert queue.
Vector<Pair<double, Variant>> insert_queue;
@ -6259,7 +6293,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
}
// Insert keys.
undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_type", i, Animation::INTERPOLATION_LINEAR);
undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_type", i, is_using_angle ? Animation::INTERPOLATION_LINEAR_ANGLE : Animation::INTERPOLATION_LINEAR);
for (int j = insert_queue.size() - 1; j >= 0; j--) {
undo_redo->add_do_method(animation.ptr(), "track_insert_key", i, insert_queue[j].first, insert_queue[j].second);
undo_redo->add_undo_method(animation.ptr(), "track_remove_key", i, j);

View File

@ -144,6 +144,8 @@ class AnimationTrackEdit : public Control {
MENU_INTERPOLATION_NEAREST,
MENU_INTERPOLATION_LINEAR,
MENU_INTERPOLATION_CUBIC,
MENU_INTERPOLATION_LINEAR_ANGLE,
MENU_INTERPOLATION_CUBIC_ANGLE,
MENU_LOOP_WRAP,
MENU_LOOP_CLAMP,
MENU_KEY_INSERT,
@ -500,7 +502,7 @@ class AnimationTrackEditor : public VBoxContainer {
NodePath full_path;
NodePath base_path;
Animation::TrackType track_type = Animation::TYPE_ANIMATION;
Animation::InterpolationType interp_type = Animation::INTERPOLATION_CUBIC;
Animation::InterpolationType interp_type = Animation::INTERPOLATION_CUBIC_ANGLE;
Animation::UpdateMode update_mode = Animation::UPDATE_CAPTURE;
Animation::LoopMode loop_mode = Animation::LOOP_PINGPONG;
bool loop_wrap = false;

View File

@ -0,0 +1 @@
<svg enable-background="new 0 0 16 8" height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#5fff95" stroke-linecap="round"><path d="m2 6c5 0 3-4 6-4s1 4 6 4" stroke-width="2"/><circle cx="14" cy="2" r="1.5" stroke-linejoin="round"/></g></svg>

After

Width:  |  Height:  |  Size: 289 B

View File

@ -0,0 +1 @@
<svg enable-background="new 0 0 16 8" height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#fd995f" stroke-linecap="round" stroke-linejoin="round"><path d="m2 6 6-4 6 4" stroke-width="2"/><circle cx="14" cy="2" r="1.5"/></g></svg>

After

Width:  |  Height:  |  Size: 277 B

View File

@ -602,6 +602,8 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track_value->object = child;
}
track_value->is_using_angle = anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE;
track_value->subpath = leftover_path;
track_value->object_id = track_value->object->get_instance_id();
@ -804,6 +806,10 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
default: {
}
}
} else if (track_cache_type == Animation::TYPE_VALUE) {
// If it has at least one angle interpolation, it also uses angle interpolation for blending.
TrackCacheValue *track_value = memnew(TrackCacheValue);
track_value->is_using_angle |= anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE;
}
track->setup_pass = setup_pass;
@ -1353,8 +1359,28 @@ void AnimationTree::_process_graph(double p_delta) {
t->value = t->init_value;
}
Variant::sub(value, t->init_value, value);
Variant::blend(t->value, value, blend, t->value);
// Special case for angle interpolation.
if (t->is_using_angle) {
// For blending consistency, it prevents rotation of more than 180 degrees from init_value.
// This is the same as for Quaternion blends.
float rot_a = t->value;
float rot_b = value;
float rot_init = t->init_value;
rot_a = Math::fposmod(rot_a, (float)Math_TAU);
rot_b = Math::fposmod(rot_b, (float)Math_TAU);
rot_init = Math::fposmod(rot_init, (float)Math_TAU);
if (rot_init < Math_PI) {
rot_a = rot_a > rot_init + Math_PI ? rot_a - Math_TAU : rot_a;
rot_b = rot_b > rot_init + Math_PI ? rot_b - Math_TAU : rot_b;
} else {
rot_a = rot_a < rot_init - Math_PI ? rot_a + Math_TAU : rot_a;
rot_b = rot_b < rot_init - Math_PI ? rot_b + Math_TAU : rot_b;
}
t->value = Math::fposmod(rot_a + (rot_b - rot_init) * (float)blend, (float)Math_TAU);
} else {
Variant::sub(value, t->init_value, value);
Variant::blend(t->value, value, blend, t->value);
}
} else {
if (blend < CMP_EPSILON) {
continue; //nothing to blend

View File

@ -233,6 +233,7 @@ private:
Variant init_value;
Variant value;
Vector<StringName> subpath;
bool is_using_angle = false;
TrackCacheValue() { type = Animation::TYPE_VALUE; }
};

View File

@ -2323,7 +2323,20 @@ Variant Animation::_interpolate(const Variant &p_a, const Variant &p_b, real_t p
}
real_t Animation::_interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const {
return p_a * (1.0 - p_c) + p_b * p_c;
return Math::lerp(p_a, p_b, p_c);
}
Variant Animation::_interpolate_angle(const Variant &p_a, const Variant &p_b, real_t p_c) const {
Variant::Type type_a = p_a.get_type();
Variant::Type type_b = p_b.get_type();
uint32_t vformat = 1 << type_a;
vformat |= 1 << type_b;
if (vformat == ((1 << Variant::INT) | (1 << Variant::FLOAT)) || vformat == (1 << Variant::FLOAT)) {
real_t a = p_a;
real_t b = p_b;
return Math::fposmod((float)Math::lerp_angle(a, b, p_c), (float)Math_TAU);
}
return _interpolate(p_a, p_b, p_c);
}
// Cubic interpolation for anytype.
@ -2413,6 +2426,25 @@ Variant Animation::_cubic_interpolate_in_time(const Variant &p_pre_a, const Vari
}
real_t Animation::_cubic_interpolate_in_time(const real_t &p_pre_a, const real_t &p_a, const real_t &p_b, const real_t &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const {
return Math::cubic_interpolate_in_time(p_a, p_b, p_pre_a, p_post_b, p_c, p_b_t, p_pre_a_t, p_post_b_t);
}
Variant Animation::_cubic_interpolate_angle_in_time(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const {
Variant::Type type_a = p_a.get_type();
Variant::Type type_b = p_b.get_type();
Variant::Type type_pa = p_pre_a.get_type();
Variant::Type type_pb = p_post_b.get_type();
uint32_t vformat = 1 << type_a;
vformat |= 1 << type_b;
vformat |= 1 << type_pa;
vformat |= 1 << type_pb;
if (vformat == ((1 << Variant::INT) | (1 << Variant::FLOAT)) || vformat == (1 << Variant::FLOAT)) {
real_t a = p_a;
real_t b = p_b;
real_t pa = p_pre_a;
real_t pb = p_post_b;
return Math::fposmod((float)Math::cubic_interpolate_angle_in_time(a, b, pa, pb, p_c, p_b_t, p_pre_a_t, p_post_b_t), (float)Math_TAU);
}
return _interpolate(p_a, p_b, p_c);
}
@ -2595,7 +2627,11 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
case INTERPOLATION_LINEAR: {
return _interpolate(p_keys[idx].value, p_keys[next].value, c);
} break;
case INTERPOLATION_CUBIC: {
case INTERPOLATION_LINEAR_ANGLE: {
return _interpolate_angle(p_keys[idx].value, p_keys[next].value, c);
} break;
case INTERPOLATION_CUBIC:
case INTERPOLATION_CUBIC_ANGLE: {
int pre = 0;
int post = 0;
if (!p_backward) {
@ -2634,19 +2670,27 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
}
}
real_t pre_t = 0.0;
real_t to_t = 0.0;
real_t post_t = 0.0;
if (loop_mode == LOOP_LINEAR && p_loop_wrap) {
return _cubic_interpolate_in_time(
p_keys[pre].value, p_keys[idx].value, p_keys[next].value, p_keys[post].value, c,
pre > idx ? -length + p_keys[pre].time - p_keys[idx].time : p_keys[pre].time - p_keys[idx].time,
next < idx ? length + p_keys[next].time - p_keys[idx].time : p_keys[next].time - p_keys[idx].time,
next < idx || post <= idx ? length + p_keys[post].time - p_keys[idx].time : p_keys[post].time - p_keys[idx].time);
pre_t = pre > idx ? -length + p_keys[pre].time - p_keys[idx].time : p_keys[pre].time - p_keys[idx].time;
to_t = next < idx ? length + p_keys[next].time - p_keys[idx].time : p_keys[next].time - p_keys[idx].time;
post_t = next < idx || post <= idx ? length + p_keys[post].time - p_keys[idx].time : p_keys[post].time - p_keys[idx].time;
} else {
pre_t = p_keys[pre].time - p_keys[idx].time;
to_t = p_keys[next].time - p_keys[idx].time;
post_t = p_keys[post].time - p_keys[idx].time;
}
if (p_interp == INTERPOLATION_CUBIC_ANGLE) {
return _cubic_interpolate_angle_in_time(
p_keys[pre].value, p_keys[idx].value, p_keys[next].value, p_keys[post].value, c,
pre_t, to_t, post_t);
}
return _cubic_interpolate_in_time(
p_keys[pre].value, p_keys[idx].value, p_keys[next].value, p_keys[post].value, c,
p_keys[pre].time - p_keys[idx].time,
p_keys[next].time - p_keys[idx].time,
p_keys[post].time - p_keys[idx].time);
pre_t, to_t, post_t);
} break;
default:
return p_keys[idx].value;
@ -3976,6 +4020,8 @@ void Animation::_bind_methods() {
BIND_ENUM_CONSTANT(INTERPOLATION_NEAREST);
BIND_ENUM_CONSTANT(INTERPOLATION_LINEAR);
BIND_ENUM_CONSTANT(INTERPOLATION_CUBIC);
BIND_ENUM_CONSTANT(INTERPOLATION_LINEAR_ANGLE);
BIND_ENUM_CONSTANT(INTERPOLATION_CUBIC_ANGLE);
BIND_ENUM_CONSTANT(UPDATE_CONTINUOUS);
BIND_ENUM_CONSTANT(UPDATE_DISCRETE);

View File

@ -57,6 +57,8 @@ public:
INTERPOLATION_NEAREST,
INTERPOLATION_LINEAR,
INTERPOLATION_CUBIC,
INTERPOLATION_LINEAR_ANGLE,
INTERPOLATION_CUBIC_ANGLE,
};
enum UpdateMode {
@ -236,11 +238,13 @@ private:
_FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const;
_FORCE_INLINE_ Variant _interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const;
_FORCE_INLINE_ real_t _interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const;
_FORCE_INLINE_ Variant _interpolate_angle(const Variant &p_a, const Variant &p_b, real_t p_c) const;
_FORCE_INLINE_ Vector3 _cubic_interpolate_in_time(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const;
_FORCE_INLINE_ Quaternion _cubic_interpolate_in_time(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const;
_FORCE_INLINE_ Variant _cubic_interpolate_in_time(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const;
_FORCE_INLINE_ real_t _cubic_interpolate_in_time(const real_t &p_pre_a, const real_t &p_a, const real_t &p_b, const real_t &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const;
_FORCE_INLINE_ Variant _cubic_interpolate_angle_in_time(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const;
template <class T>
_FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok, bool p_backward = false) const;