summaryrefslogtreecommitdiffstats
path: root/scene/resources/animation.cpp
diff options
context:
space:
mode:
authorSilc Lizard (Tokage) Renew <61938263+TokageItLab@users.noreply.github.com>2023-11-10 21:29:45 +0900
committerSilc Lizard (Tokage) Renew <61938263+TokageItLab@users.noreply.github.com>2023-11-16 21:37:18 +0900
commit80c9533810de435ba0b7ed5d7a0d5ae0f8e95e55 (patch)
treeeb322f1367027235ce6a38e9661a522a9f4ca464 /scene/resources/animation.cpp
parentd5217b68db68d52a2f393e935fb38dde6a0f802a (diff)
downloadredot-engine-80c9533810de435ba0b7ed5d7a0d5ae0f8e95e55.tar.gz
Rework blending method in Variant animation for Int/Array/String
Diffstat (limited to 'scene/resources/animation.cpp')
-rw-r--r--scene/resources/animation.cpp787
1 files changed, 470 insertions, 317 deletions
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index af1f9da2b5..b1b3bab937 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -5476,469 +5476,622 @@ bool Animation::_fetch_compressed_by_index(uint32_t p_compressed_track, int p_in
}
// Helper math functions for Variant.
+bool Animation::is_variant_interpolatable(const Variant p_value) {
+ Variant::Type type = p_value.get_type();
+ return (type >= Variant::BOOL && type <= Variant::STRING_NAME) || type == Variant::ARRAY || type >= Variant::PACKED_INT32_ARRAY; // PackedByteArray is unsigned, so it would be better to ignore since blending uses float.
+}
+
+Variant Animation::cast_to_blendwise(const Variant p_value) {
+ switch (p_value.get_type()) {
+ case Variant::BOOL:
+ case Variant::INT: {
+ return p_value.operator real_t();
+ } break;
+ case Variant::STRING:
+ case Variant::STRING_NAME: {
+ return string_to_array(p_value);
+ } break;
+ case Variant::RECT2I: {
+ return p_value.operator Rect2();
+ } break;
+ case Variant::VECTOR2I: {
+ return p_value.operator Vector2();
+ } break;
+ case Variant::VECTOR3I: {
+ return p_value.operator Vector3();
+ } break;
+ case Variant::VECTOR4I: {
+ return p_value.operator Vector4();
+ } break;
+ case Variant::PACKED_INT32_ARRAY: {
+ return p_value.operator PackedFloat32Array();
+ } break;
+ case Variant::PACKED_INT64_ARRAY: {
+ return p_value.operator PackedFloat64Array();
+ } break;
+ default: {
+ } break;
+ }
+ return p_value;
+}
+
+Variant Animation::cast_from_blendwise(const Variant p_value, const Variant::Type p_type) {
+ switch (p_type) {
+ case Variant::BOOL: {
+ return p_value.operator real_t() >= 0.5;
+ } break;
+ case Variant::INT: {
+ return (int)Math::round(p_value.operator real_t());
+ } break;
+ case Variant::STRING: {
+ return array_to_string(p_value);
+ } break;
+ case Variant::STRING_NAME: {
+ return StringName(array_to_string(p_value));
+ } break;
+ case Variant::RECT2I: {
+ return Rect2i(p_value.operator Rect2().round());
+ } break;
+ case Variant::VECTOR2I: {
+ return Vector2i(p_value.operator Vector2().round());
+ } break;
+ case Variant::VECTOR3I: {
+ return Vector3i(p_value.operator Vector3().round());
+ } break;
+ case Variant::VECTOR4I: {
+ return Vector4i(p_value.operator Vector4().round());
+ } break;
+ case Variant::PACKED_INT32_ARRAY: {
+ PackedFloat32Array old_val = p_value.operator PackedFloat32Array();
+ PackedInt32Array new_val;
+ new_val.resize(old_val.size());
+ int *new_val_w = new_val.ptrw();
+ for (int i = 0; i < old_val.size(); i++) {
+ new_val_w[i] = (int32_t)Math::round(old_val[i]);
+ }
+ return new_val;
+ } break;
+ case Variant::PACKED_INT64_ARRAY: {
+ PackedFloat64Array old_val = p_value.operator PackedFloat64Array();
+ PackedInt64Array new_val;
+ for (int i = 0; i < old_val.size(); i++) {
+ new_val.push_back((int64_t)Math::round(old_val[i]));
+ }
+ return new_val;
+ } break;
+ default: {
+ } break;
+ }
+ return p_value;
+}
+
+Variant Animation::string_to_array(const Variant p_value) {
+ if (!p_value.is_string()) {
+ return p_value;
+ };
+ const String &str = p_value.operator String();
+ PackedFloat32Array arr;
+ for (int i = 0; i < str.length(); i++) {
+ arr.push_back((float)str[i]);
+ }
+ return arr;
+}
+
+Variant Animation::array_to_string(const Variant p_value) {
+ if (!p_value.is_array()) {
+ return p_value;
+ };
+ const PackedFloat32Array &arr = p_value.operator PackedFloat32Array();
+ String str;
+ for (int i = 0; i < arr.size(); i++) {
+ char32_t c = (char32_t)Math::round(arr[i]);
+ if (c == 0 || (c & 0xfffff800) == 0xd800 || c > 0x10ffff) {
+ c = ' ';
+ }
+ str += c;
+ }
+ return str;
+}
+
Variant Animation::add_variant(const Variant &a, const Variant &b) {
- if (a.get_type() != b.get_type()) {
+ if (a.get_type() != b.get_type() && !a.is_array()) {
return a;
}
switch (a.get_type()) {
case Variant::NIL: {
return Variant();
- }
- case Variant::BOOL: {
- return (a.operator real_t()) + (b.operator real_t()); // It is cast for interpolation.
- }
+ } break;
+ case Variant::FLOAT: {
+ return (a.operator real_t()) + (b.operator real_t());
+ } break;
case Variant::RECT2: {
const Rect2 ra = a.operator Rect2();
const Rect2 rb = b.operator Rect2();
return Rect2(ra.position + rb.position, ra.size + rb.size);
- }
- case Variant::RECT2I: {
- const Rect2i ra = a.operator Rect2i();
- const Rect2i rb = b.operator Rect2i();
- return Rect2i(ra.position + rb.position, ra.size + rb.size);
- }
+ } break;
case Variant::PLANE: {
const Plane pa = a.operator Plane();
const Plane pb = b.operator Plane();
return Plane(pa.normal + pb.normal, pa.d + pb.d);
- }
+ } break;
case Variant::AABB: {
const ::AABB aa = a.operator ::AABB();
const ::AABB ab = b.operator ::AABB();
return ::AABB(aa.position + ab.position, aa.size + ab.size);
- }
+ } break;
case Variant::BASIS: {
return (a.operator Basis()) * (b.operator Basis());
- }
+ } break;
case Variant::QUATERNION: {
return (a.operator Quaternion()) * (b.operator Quaternion());
- }
+ } break;
case Variant::TRANSFORM2D: {
return (a.operator Transform2D()) * (b.operator Transform2D());
- }
+ } break;
case Variant::TRANSFORM3D: {
return (a.operator Transform3D()) * (b.operator Transform3D());
- }
+ } break;
+ case Variant::INT:
+ case Variant::RECT2I:
+ case Variant::VECTOR2I:
+ case Variant::VECTOR3I:
+ case Variant::VECTOR4I:
+ case Variant::PACKED_INT32_ARRAY:
+ case Variant::PACKED_INT64_ARRAY: {
+ // Fallback the interpolatable value which needs casting.
+ return cast_from_blendwise(add_variant(cast_to_blendwise(a), cast_to_blendwise(b)), a.get_type());
+ } break;
+ case Variant::BOOL:
+ case Variant::STRING:
+ case Variant::STRING_NAME: {
+ // Specialized for Tween.
+ return b;
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ // Skip.
+ } break;
default: {
- return Variant::evaluate(Variant::OP_ADD, a, b);
- }
+ if (a.is_array()) {
+ const Array arr_a = a.operator Array();
+ const Array arr_b = b.operator Array();
+
+ int min_size = arr_a.size();
+ int max_size = arr_b.size();
+ bool is_a_larger = inform_variant_array(min_size, max_size);
+
+ Array result;
+ result.set_typed(MAX(arr_a.get_typed_builtin(), arr_b.get_typed_builtin()), StringName(), Variant());
+ result.resize(min_size);
+ int i = 0;
+ for (; i < min_size; i++) {
+ result[i] = add_variant(arr_a[i], arr_b[i]);
+ }
+ if (min_size != max_size) {
+ // Process with last element of the lesser array.
+ // This is pretty funny and bizarre, but artists like to use it for polygon animation.
+ Variant lesser_last;
+ result.resize(max_size);
+ if (is_a_larger) {
+ if (i > 0) {
+ lesser_last = arr_b[i - 1];
+ } else {
+ Variant vz = arr_a[i];
+ vz.zero();
+ lesser_last = vz;
+ }
+ for (; i < max_size; i++) {
+ result[i] = add_variant(arr_a[i], lesser_last);
+ }
+ } else {
+ if (i > 0) {
+ lesser_last = arr_a[i - 1];
+ } else {
+ Variant vz = arr_b[i];
+ vz.zero();
+ lesser_last = vz;
+ }
+ for (; i < max_size; i++) {
+ result[i] = add_variant(lesser_last, arr_b[i]);
+ }
+ }
+ }
+ return result;
+ }
+ } break;
}
+ return Variant::evaluate(Variant::OP_ADD, a, b);
}
Variant Animation::subtract_variant(const Variant &a, const Variant &b) {
- if (a.get_type() != b.get_type()) {
+ if (a.get_type() != b.get_type() && !a.is_array()) {
return a;
}
switch (a.get_type()) {
case Variant::NIL: {
return Variant();
- }
- case Variant::BOOL: {
- return (a.operator real_t()) - (b.operator real_t()); // It is cast for interpolation.
- }
+ } break;
+ case Variant::FLOAT: {
+ return (a.operator real_t()) - (b.operator real_t());
+ } break;
case Variant::RECT2: {
const Rect2 ra = a.operator Rect2();
const Rect2 rb = b.operator Rect2();
return Rect2(ra.position - rb.position, ra.size - rb.size);
- }
- case Variant::RECT2I: {
- const Rect2i ra = a.operator Rect2i();
- const Rect2i rb = b.operator Rect2i();
- return Rect2i(ra.position - rb.position, ra.size - rb.size);
- }
+ } break;
case Variant::PLANE: {
const Plane pa = a.operator Plane();
const Plane pb = b.operator Plane();
return Plane(pa.normal - pb.normal, pa.d - pb.d);
- }
+ } break;
case Variant::AABB: {
const ::AABB aa = a.operator ::AABB();
const ::AABB ab = b.operator ::AABB();
return ::AABB(aa.position - ab.position, aa.size - ab.size);
- }
+ } break;
case Variant::BASIS: {
return (b.operator Basis()).inverse() * (a.operator Basis());
- }
+ } break;
case Variant::QUATERNION: {
return (b.operator Quaternion()).inverse() * (a.operator Quaternion());
- }
+ } break;
case Variant::TRANSFORM2D: {
return (b.operator Transform2D()).affine_inverse() * (a.operator Transform2D());
- }
+ } break;
case Variant::TRANSFORM3D: {
return (b.operator Transform3D()).affine_inverse() * (a.operator Transform3D());
- }
+ } break;
+ case Variant::INT:
+ case Variant::RECT2I:
+ case Variant::VECTOR2I:
+ case Variant::VECTOR3I:
+ case Variant::VECTOR4I:
+ case Variant::PACKED_INT32_ARRAY:
+ case Variant::PACKED_INT64_ARRAY: {
+ // Fallback the interpolatable value which needs casting.
+ return cast_from_blendwise(subtract_variant(cast_to_blendwise(a), cast_to_blendwise(b)), a.get_type());
+ } break;
+ case Variant::BOOL:
+ case Variant::STRING:
+ case Variant::STRING_NAME: {
+ // Specialized for Tween.
+ return a;
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ // Skip.
+ } break;
default: {
- return Variant::evaluate(Variant::OP_SUBTRACT, a, b);
- }
+ if (a.is_array()) {
+ const Array arr_a = a.operator Array();
+ const Array arr_b = b.operator Array();
+
+ int min_size = arr_a.size();
+ int max_size = arr_b.size();
+ bool is_a_larger = inform_variant_array(min_size, max_size);
+
+ Array result;
+ result.set_typed(MAX(arr_a.get_typed_builtin(), arr_b.get_typed_builtin()), StringName(), Variant());
+ result.resize(min_size);
+ int i = 0;
+ for (; i < min_size; i++) {
+ result[i] = subtract_variant(arr_a[i], arr_b[i]);
+ }
+ if (min_size != max_size) {
+ // Process with last element of the lesser array.
+ // This is pretty funny and bizarre, but artists like to use it for polygon animation.
+ Variant lesser_last;
+ result.resize(max_size);
+ if (is_a_larger) {
+ if (i > 0) {
+ lesser_last = arr_b[i - 1];
+ } else {
+ Variant vz = arr_a[i];
+ vz.zero();
+ lesser_last = vz;
+ }
+ for (; i < max_size; i++) {
+ result[i] = subtract_variant(arr_a[i], lesser_last);
+ }
+ } else {
+ if (i > 0) {
+ lesser_last = arr_a[i - 1];
+ } else {
+ Variant vz = arr_b[i];
+ vz.zero();
+ lesser_last = vz;
+ }
+ for (; i < max_size; i++) {
+ result[i] = subtract_variant(lesser_last, arr_b[i]);
+ }
+ }
+ }
+ return result;
+ }
+ } break;
}
+ return Variant::evaluate(Variant::OP_SUBTRACT, a, b);
}
Variant Animation::blend_variant(const Variant &a, const Variant &b, float c) {
- if (a.get_type() != b.get_type()) {
- if (a.is_num() && b.is_num()) {
- double va = a;
- double vb = b;
- return va + vb * c;
- }
+ if (a.get_type() != b.get_type() && !a.is_array()) {
return a;
}
switch (a.get_type()) {
case Variant::NIL: {
return Variant();
- }
- case Variant::INT: {
- return int64_t((a.operator int64_t()) + (b.operator int64_t()) * c + 0.5);
- }
+ } break;
case Variant::FLOAT: {
- return (a.operator double()) + (b.operator double()) * c;
- }
+ return (a.operator real_t()) + (b.operator real_t()) * c;
+ } break;
case Variant::VECTOR2: {
return (a.operator Vector2()) + (b.operator Vector2()) * c;
- }
- case Variant::VECTOR2I: {
- const Vector2i va = a.operator Vector2i();
- const Vector2i vb = b.operator Vector2i();
- return Vector2i(int32_t(va.x + vb.x * c + 0.5), int32_t(va.y + vb.y * c + 0.5));
- }
+ } break;
case Variant::RECT2: {
const Rect2 ra = a.operator Rect2();
const Rect2 rb = b.operator Rect2();
return Rect2(ra.position + rb.position * c, ra.size + rb.size * c);
- }
- case Variant::RECT2I: {
- const Rect2i ra = a.operator Rect2i();
- const Rect2i rb = b.operator Rect2i();
- return Rect2i(int32_t(ra.position.x + rb.position.x * c + 0.5), int32_t(ra.position.y + rb.position.y * c + 0.5), int32_t(ra.size.x + rb.size.x * c + 0.5), int32_t(ra.size.y + rb.size.y * c + 0.5));
- }
+ } break;
case Variant::VECTOR3: {
return (a.operator Vector3()) + (b.operator Vector3()) * c;
- }
- case Variant::VECTOR3I: {
- const Vector3i va = a.operator Vector3i();
- const Vector3i vb = b.operator Vector3i();
- return Vector3i(int32_t(va.x + vb.x * c + 0.5), int32_t(va.y + vb.y * c + 0.5), int32_t(va.z + vb.z * c + 0.5));
- }
+ } break;
case Variant::VECTOR4: {
return (a.operator Vector4()) + (b.operator Vector4()) * c;
- }
- case Variant::VECTOR4I: {
- const Vector4i va = a.operator Vector4i();
- const Vector4i vb = b.operator Vector4i();
- return Vector4i(int32_t(va.x + vb.x * c + 0.5), int32_t(va.y + vb.y * c + 0.5), int32_t(va.z + vb.z * c + 0.5), int32_t(va.w + vb.w * c + 0.5));
- }
+ } break;
case Variant::PLANE: {
const Plane pa = a.operator Plane();
const Plane pb = b.operator Plane();
return Plane(pa.normal + pb.normal * c, pa.d + pb.d * c);
- }
+ } break;
case Variant::COLOR: {
return (a.operator Color()) + (b.operator Color()) * c;
- }
+ } break;
case Variant::AABB: {
const ::AABB aa = a.operator ::AABB();
const ::AABB ab = b.operator ::AABB();
return ::AABB(aa.position + ab.position * c, aa.size + ab.size * c);
- }
+ } break;
case Variant::BASIS: {
return (a.operator Basis()) + (b.operator Basis()) * c;
- }
+ } break;
case Variant::QUATERNION: {
return (a.operator Quaternion()) * Quaternion().slerp((b.operator Quaternion()), c);
- }
+ } break;
case Variant::TRANSFORM2D: {
return (a.operator Transform2D()) * Transform2D().interpolate_with((b.operator Transform2D()), c);
- }
+ } break;
case Variant::TRANSFORM3D: {
return (a.operator Transform3D()) * Transform3D().interpolate_with((b.operator Transform3D()), c);
- }
+ } break;
+ case Variant::BOOL:
+ case Variant::INT:
+ case Variant::RECT2I:
+ case Variant::VECTOR2I:
+ case Variant::VECTOR3I:
+ case Variant::VECTOR4I:
+ case Variant::PACKED_INT32_ARRAY:
+ case Variant::PACKED_INT64_ARRAY: {
+ // Fallback the interpolatable value which needs casting.
+ return cast_from_blendwise(blend_variant(cast_to_blendwise(a), cast_to_blendwise(b), c), a.get_type());
+ } break;
+ case Variant::STRING:
+ case Variant::STRING_NAME: {
+ Array arr_a = cast_to_blendwise(a);
+ Array arr_b = cast_to_blendwise(b);
+ int min_size = arr_a.size();
+ int max_size = arr_b.size();
+ bool is_a_larger = inform_variant_array(min_size, max_size);
+ int mid_size = interpolate_variant(arr_a.size(), arr_b.size(), c);
+ if (is_a_larger) {
+ arr_a.resize(mid_size);
+ } else {
+ arr_b.resize(mid_size);
+ }
+ return cast_from_blendwise(blend_variant(arr_a, arr_b, c), a.get_type());
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ // Skip.
+ } break;
default: {
- return c < 0.5 ? a : b;
- }
+ if (a.is_array()) {
+ const Array arr_a = a.operator Array();
+ const Array arr_b = b.operator Array();
+
+ int min_size = arr_a.size();
+ int max_size = arr_b.size();
+ bool is_a_larger = inform_variant_array(min_size, max_size);
+
+ Array result;
+ result.set_typed(MAX(arr_a.get_typed_builtin(), arr_b.get_typed_builtin()), StringName(), Variant());
+ result.resize(min_size);
+ int i = 0;
+ for (; i < min_size; i++) {
+ result[i] = blend_variant(arr_a[i], arr_b[i], c);
+ }
+ if (min_size != max_size) {
+ // Process with last element of the lesser array.
+ // This is pretty funny and bizarre, but artists like to use it for polygon animation.
+ Variant lesser_last;
+ if (is_a_larger && !Math::is_equal_approx(c, 1.0f)) {
+ result.resize(max_size);
+ if (i > 0) {
+ lesser_last = arr_b[i - 1];
+ } else {
+ Variant vz = arr_a[i];
+ vz.zero();
+ lesser_last = vz;
+ }
+ for (; i < max_size; i++) {
+ result[i] = blend_variant(arr_a[i], lesser_last, c);
+ }
+ } else if (!is_a_larger && !Math::is_zero_approx(c)) {
+ result.resize(max_size);
+ if (i > 0) {
+ lesser_last = arr_a[i - 1];
+ } else {
+ Variant vz = arr_b[i];
+ vz.zero();
+ lesser_last = vz;
+ }
+ for (; i < max_size; i++) {
+ result[i] = blend_variant(lesser_last, arr_b[i], c);
+ }
+ }
+ }
+ return result;
+ }
+ } break;
}
+ return c < 0.5 ? a : b;
}
-Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float c) {
- if (a.get_type() != b.get_type()) {
- if (a.is_num() && b.is_num()) {
- double va = a;
- double vb = b;
- return va + (vb - va) * c;
- }
+Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float c, bool p_snap_array_element) {
+ if (a.get_type() != b.get_type() && !a.is_array()) {
return a;
}
switch (a.get_type()) {
case Variant::NIL: {
return Variant();
- }
- case Variant::INT: {
- const int64_t va = a.operator int64_t();
- return int64_t(va + ((b.operator int64_t()) - va) * c);
- }
+ } break;
case Variant::FLOAT: {
- const double va = a.operator double();
- return va + ((b.operator double()) - va) * c;
- }
+ const real_t va = a.operator real_t();
+ return va + ((b.operator real_t()) - va) * c;
+ } break;
case Variant::VECTOR2: {
return (a.operator Vector2()).lerp(b.operator Vector2(), c);
- }
- case Variant::VECTOR2I: {
- const Vector2i va = a.operator Vector2i();
- const Vector2i vb = b.operator Vector2i();
- return Vector2i(int32_t(va.x + (vb.x - va.x) * c), int32_t(va.y + (vb.y - va.y) * c));
- }
+ } break;
case Variant::RECT2: {
const Rect2 ra = a.operator Rect2();
const Rect2 rb = b.operator Rect2();
return Rect2(ra.position.lerp(rb.position, c), ra.size.lerp(rb.size, c));
- }
- case Variant::RECT2I: {
- const Rect2i ra = a.operator Rect2i();
- const Rect2i rb = b.operator Rect2i();
- return Rect2i(int32_t(ra.position.x + (rb.position.x - ra.position.x) * c), int32_t(ra.position.y + (rb.position.y - ra.position.y) * c), int32_t(ra.size.x + (rb.size.x - ra.size.x) * c), int32_t(ra.size.y + (rb.size.y - ra.size.y) * c));
- }
+ } break;
case Variant::VECTOR3: {
return (a.operator Vector3()).lerp(b.operator Vector3(), c);
- }
- case Variant::VECTOR3I: {
- const Vector3i va = a.operator Vector3i();
- const Vector3i vb = b.operator Vector3i();
- return Vector3i(int32_t(va.x + (vb.x - va.x) * c), int32_t(va.y + (vb.y - va.y) * c), int32_t(va.z + (vb.z - va.z) * c));
- }
+ } break;
case Variant::VECTOR4: {
return (a.operator Vector4()).lerp(b.operator Vector4(), c);
- }
- case Variant::VECTOR4I: {
- const Vector4i va = a.operator Vector4i();
- const Vector4i vb = b.operator Vector4i();
- return Vector4i(int32_t(va.x + (vb.x - va.x) * c), int32_t(va.y + (vb.y - va.y) * c), int32_t(va.z + (vb.z - va.z) * c), int32_t(va.w + (vb.w - va.w) * c));
- }
+ } break;
case Variant::PLANE: {
const Plane pa = a.operator Plane();
const Plane pb = b.operator Plane();
return Plane(pa.normal.lerp(pb.normal, c), pa.d + (pb.d - pa.d) * c);
- }
+ } break;
case Variant::COLOR: {
return (a.operator Color()).lerp(b.operator Color(), c);
- }
+ } break;
case Variant::AABB: {
const ::AABB aa = a.operator ::AABB();
const ::AABB ab = b.operator ::AABB();
return ::AABB(aa.position.lerp(ab.position, c), aa.size.lerp(ab.size, c));
- }
+ } break;
case Variant::BASIS: {
return (a.operator Basis()).lerp(b.operator Basis(), c);
- }
+ } break;
case Variant::QUATERNION: {
return (a.operator Quaternion()).slerp(b.operator Quaternion(), c);
- }
+ } break;
case Variant::TRANSFORM2D: {
return (a.operator Transform2D()).interpolate_with(b.operator Transform2D(), c);
- }
+ } break;
case Variant::TRANSFORM3D: {
return (a.operator Transform3D()).interpolate_with(b.operator Transform3D(), c);
- }
- case Variant::STRING: {
- // This is pretty funny and bizarre, but artists like to use it for typewriter effects.
- const String sa = a.operator String();
- const String sb = b.operator String();
- String dst;
- int sa_len = sa.length();
- int sb_len = sb.length();
- int csize = sa_len + (sb_len - sa_len) * c;
- if (csize == 0) {
- return "";
- }
- dst.resize(csize + 1);
- dst[csize] = 0;
- int split = csize / 2;
-
- for (int i = 0; i < csize; i++) {
- char32_t chr = ' ';
-
- if (i < split) {
- if (i < sa.length()) {
- chr = sa[i];
- } else if (i < sb.length()) {
- chr = sb[i];
- }
-
- } else {
- if (i < sb.length()) {
- chr = sb[i];
- } else if (i < sa.length()) {
- chr = sa[i];
- }
- }
-
- dst[i] = chr;
- }
-
- return dst;
- }
- case Variant::PACKED_INT32_ARRAY: {
- const Vector<int32_t> arr_a = a;
- const Vector<int32_t> arr_b = b;
- int sz = arr_a.size();
- if (sz == 0 || arr_b.size() != sz) {
- return a;
- } else {
- Vector<int32_t> v;
- v.resize(sz);
- {
- int32_t *vw = v.ptrw();
- const int32_t *ar = arr_a.ptr();
- const int32_t *br = arr_b.ptr();
-
- Variant va;
- for (int i = 0; i < sz; i++) {
- va = interpolate_variant(ar[i], br[i], c);
- vw[i] = va;
- }
- }
- return v;
- }
- }
+ } break;
+ case Variant::BOOL:
+ case Variant::INT:
+ case Variant::RECT2I:
+ case Variant::VECTOR2I:
+ case Variant::VECTOR3I:
+ case Variant::VECTOR4I:
+ case Variant::PACKED_INT32_ARRAY:
case Variant::PACKED_INT64_ARRAY: {
- const Vector<int64_t> arr_a = a;
- const Vector<int64_t> arr_b = b;
- int sz = arr_a.size();
- if (sz == 0 || arr_b.size() != sz) {
- return a;
- } else {
- Vector<int64_t> v;
- v.resize(sz);
- {
- int64_t *vw = v.ptrw();
- const int64_t *ar = arr_a.ptr();
- const int64_t *br = arr_b.ptr();
-
- Variant va;
- for (int i = 0; i < sz; i++) {
- va = interpolate_variant(ar[i], br[i], c);
- vw[i] = va;
- }
- }
- return v;
- }
- }
- case Variant::PACKED_FLOAT32_ARRAY: {
- const Vector<float> arr_a = a;
- const Vector<float> arr_b = b;
- int sz = arr_a.size();
- if (sz == 0 || arr_b.size() != sz) {
- return a;
+ // Fallback the interpolatable value which needs casting.
+ return cast_from_blendwise(interpolate_variant(cast_to_blendwise(a), cast_to_blendwise(b), c), a.get_type());
+ } break;
+ case Variant::STRING:
+ case Variant::STRING_NAME: {
+ Array arr_a = cast_to_blendwise(a);
+ Array arr_b = cast_to_blendwise(b);
+ int min_size = arr_a.size();
+ int max_size = arr_b.size();
+ bool is_a_larger = inform_variant_array(min_size, max_size);
+ int mid_size = interpolate_variant(arr_a.size(), arr_b.size(), c);
+ if (is_a_larger) {
+ arr_a.resize(mid_size);
} else {
- Vector<float> v;
- v.resize(sz);
- {
- float *vw = v.ptrw();
- const float *ar = arr_a.ptr();
- const float *br = arr_b.ptr();
-
- Variant va;
- for (int i = 0; i < sz; i++) {
- va = interpolate_variant(ar[i], br[i], c);
- vw[i] = va;
- }
- }
- return v;
+ arr_b.resize(mid_size);
}
- }
- case Variant::PACKED_FLOAT64_ARRAY: {
- const Vector<double> arr_a = a;
- const Vector<double> arr_b = b;
- int sz = arr_a.size();
- if (sz == 0 || arr_b.size() != sz) {
- return a;
- } else {
- Vector<double> v;
- v.resize(sz);
- {
- double *vw = v.ptrw();
- const double *ar = arr_a.ptr();
- const double *br = arr_b.ptr();
-
- Variant va;
- for (int i = 0; i < sz; i++) {
- va = interpolate_variant(ar[i], br[i], c);
- vw[i] = va;
- }
- }
- return v;
- }
- }
- case Variant::PACKED_VECTOR2_ARRAY: {
- const Vector<Vector2> arr_a = a;
- const Vector<Vector2> arr_b = b;
- int sz = arr_a.size();
- if (sz == 0 || arr_b.size() != sz) {
- return a;
- } else {
- Vector<Vector2> v;
- v.resize(sz);
- {
- Vector2 *vw = v.ptrw();
- const Vector2 *ar = arr_a.ptr();
- const Vector2 *br = arr_b.ptr();
-
- for (int i = 0; i < sz; i++) {
- vw[i] = ar[i].lerp(br[i], c);
- }
+ return cast_from_blendwise(interpolate_variant(arr_a, arr_b, c, true), a.get_type());
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ // Skip.
+ } break;
+ default: {
+ if (a.is_array()) {
+ const Array arr_a = a.operator Array();
+ const Array arr_b = b.operator Array();
+
+ int min_size = arr_a.size();
+ int max_size = arr_b.size();
+ bool is_a_larger = inform_variant_array(min_size, max_size);
+
+ Array result;
+ result.set_typed(MAX(arr_a.get_typed_builtin(), arr_b.get_typed_builtin()), StringName(), Variant());
+ result.resize(min_size);
+ int i = 0;
+ for (; i < min_size; i++) {
+ result[i] = interpolate_variant(arr_a[i], arr_b[i], c);
}
- return v;
- }
- }
- case Variant::PACKED_VECTOR3_ARRAY: {
- const Vector<Vector3> arr_a = a;
- const Vector<Vector3> arr_b = b;
- int sz = arr_a.size();
- if (sz == 0 || arr_b.size() != sz) {
- return a;
- } else {
- Vector<Vector3> v;
- v.resize(sz);
- {
- Vector3 *vw = v.ptrw();
- const Vector3 *ar = arr_a.ptr();
- const Vector3 *br = arr_b.ptr();
-
- for (int i = 0; i < sz; i++) {
- vw[i] = ar[i].lerp(br[i], c);
+ if (min_size != max_size) {
+ // Process with last element of the lesser array.
+ // This is pretty funny and bizarre, but artists like to use it for polygon animation.
+ Variant lesser_last;
+ if (is_a_larger && !Math::is_equal_approx(c, 1.0f)) {
+ result.resize(max_size);
+ if (p_snap_array_element) {
+ c = 0;
+ }
+ if (i > 0) {
+ lesser_last = arr_b[i - 1];
+ } else {
+ Variant vz = arr_a[i];
+ vz.zero();
+ lesser_last = vz;
+ }
+ for (; i < max_size; i++) {
+ result[i] = interpolate_variant(arr_a[i], lesser_last, c);
+ }
+ } else if (!is_a_larger && !Math::is_zero_approx(c)) {
+ result.resize(max_size);
+ if (p_snap_array_element) {
+ c = 1;
+ }
+ if (i > 0) {
+ lesser_last = arr_a[i - 1];
+ } else {
+ Variant vz = arr_b[i];
+ vz.zero();
+ lesser_last = vz;
+ }
+ for (; i < max_size; i++) {
+ result[i] = interpolate_variant(lesser_last, arr_b[i], c);
+ }
}
}
- return v;
+ return result;
}
- }
- case Variant::PACKED_COLOR_ARRAY: {
- const Vector<Color> arr_a = a;
- const Vector<Color> arr_b = b;
- int sz = arr_a.size();
- if (sz == 0 || arr_b.size() != sz) {
- return a;
- } else {
- Vector<Color> v;
- v.resize(sz);
- {
- Color *vw = v.ptrw();
- const Color *ar = arr_a.ptr();
- const Color *br = arr_b.ptr();
+ } break;
+ }
+ return c < 0.5 ? a : b;
+}
- for (int i = 0; i < sz; i++) {
- vw[i] = ar[i].lerp(br[i], c);
- }
- }
- return v;
- }
- }
- default: {
- return c < 0.5 ? a : b;
- }
+bool Animation::inform_variant_array(int &r_min, int &r_max) {
+ if (r_min <= r_max) {
+ return false;
}
+ SWAP(r_min, r_max);
+ return true;
}
Animation::Animation() {