-
-
Notifications
You must be signed in to change notification settings - Fork 21.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow Tween to accept Arrays & Packed*Arrays #66771
Conversation
|
@TokageItLab Let me reiterate, the previous support in |
Fixing bugs is fine, but I would like to see it applied to more than just Color for consistency. |
Yep, indeed, of course, it's a draft for that reason. |
@Mickeon Oh what a good surprise !! Thank you for trying to implement this feature ! I'm looking forward to seeing the end result !!! |
a05dbd5
to
bef86fe
Compare
Updated this PR to support all Packed*Arrays and even Arrays, as well. However, I really, really need a technical review, if that may asked, because I am afraid that the way I avoided to write duplicate code, by converting every Packed*Array, is way too slow. I tried using templates, and that would work, actually, but it'd still duplicate code in a way that felt unnecessarily bloated to me. To keep it in mind, open this to see the alternative method.if (a.is_array() && a.get_type() != Variant::ARRAY) {
switch (a.get_type()) {
case Variant::PACKED_COLOR_ARRAY: {
return interpolate_packed_array(PackedColorArray(a), PackedColorArray(b), c);
}
case Variant::PACKED_VECTOR2_ARRAY: {
return interpolate_packed_array(PackedVector2Array(a), PackedVector2Array(b), c);
}
}
template <class T>
Vector<T> Animation::interpolate_packed_array(Vector<T> a, Vector<T> b, float c) {
int size = a.size();
//WARN_PRINT(vformat("%d, %d", a.size(), b.size()));
if (size == 0 || b.size() != size) {
return a;
} else {
Vector<T> result;
result.resize(size);
T *result_ptr = result.ptrw();
const T *a_ptr = a.ptr();
const T *b_ptr = b.ptr();
for (int i = 0; i < size; i++) {
result_ptr[i] = interpolate_variant(a_ptr[i], b_ptr[i], c);
}
//WARN_PRINT(vformat("%s, %s", a[0], result[0]));
return result;
}
} |
bef86fe
to
bddefdf
Compare
That implementation would result in processing a large number of unnecessary switch/case statements (but I understand that this is better than the current status that requires a for loop externally), so other approaches seem better IMO. Are @akien-mga or @reduz familiar with this area? |
I think the current impementation is not really acceptable, there should be no conversions when not needed.
Regarding the code duplication I'd say in this specific case (arrays so potentially big chunks of data) the execution speed should definitely be prioritized. So yes for reducing the code duplication but not if it would hurt the performance. |
Thank you very much for your insightful feedback, it was very necessary!
Conversion between float and int is pretty common everywhere, but I feel like for how "hardcoded" all Packed Arrays are, that there should be no subtle casting here. Casting between PackedInt64Array and PackedInt32Array within the Animation functions feels like asking for trouble, and may even warrant a more noticeable error so that the user can handle it more graciously.
It's niche, but it avoids some annoyance. It shouldn't be too difficult to make it work between TypedArrays of float and int, at least for now, as that would be consistent across the engine. |
bddefdf
to
79cc84b
Compare
Updated the PR following the suggestions above.
This had to be implemented here, otherwise interpolating between [0.5, 2.75] and [1, 0] would not work as expected, for example. |
79cc84b
to
e9dce9f
Compare
If the starting and final arrays have different sizes, tweening will not happen. If the arrays are typed and they're different, tweening will not happen, except between floats and ints.
e9dce9f
to
ad402af
Compare
|
||
Array result; | ||
|
||
if (arr_a.is_typed() && arr_b.is_typed()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (!arr_a.is_typed() || !arr_b.is_typed()) {
return a;
}
is maybe better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would the suggestion do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It just recommends an earlier return for non-typed arrays.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea was to allow interpolation of non-typed Arrays, so long as a[i]
and b[i]
are of the same type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So what happens when a[i]
and b[i]
have different type? Are they skipped? Is there an error?
I think it's ok to support untyped arrays if it doesn't affect performance of the typed ones.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current behavior of this PR is that, if a[i]
and b[i] are different types they are skipped altogether.
However, as I come back to this PR, I realise that these methods are also generally used by AnimationPlayer. So, I'm not sure if the current implementation break or introduce some odd and unnecessary code smells, right now.
int size = arr_a.size(); | ||
if (size == 0 || arr_b.size() != size) { | ||
return a; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
int size = MAX(arr_a.size(), arr_b.size());
Shouldn't we consider the maximum size? It seems that at least in blend_packed_array()
the maximum size should be considered if you implement it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If maximum size is used, it would go out of bounds when operating on one of the two arrays when the sizes do not match. If anything, it could be MIN(), and anything above that index is ignored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use initial values for elements out of bounds. Then if resize() is properly done, access out of bounds will not occur.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, if we do the following tween:
[1, 2] -> [2, 4, 2] -> [2, 1]
The result would be as follows:
When using MIN: [1, 2] -> [2, 4] -> [2, 1]
When using MAX: [1, 2] -> [2, 4, 2] -> [2, 1, 0]
If you use MIN, the final result may be smaller than the element in the middle of the tween, so someone may access out of bounds unless they know after the tween is over that it is smaller than the element in the middle of the tween. So it is safer to use MAX.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do understand your theoretical implementation. I'm just not sure it would be expected behavior to make a tweener capable of resizing the Array it ends up returning, on the first frame the tween starts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least interpolate_variant()
implicitly resizes the array for PackedStringArray.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried interpolating polygon from the default color array to another array and the result was that the value jumped to the final value at the end of animation. Would using MAX handle this more gracefully?
template <class T> | ||
static Vector<T> add_packed_array(Vector<T> a, Vector<T> b); | ||
template <class T> | ||
static Vector<T> subtract_packed_array(Vector<T> a, Vector<T> b); | ||
template <class T> | ||
static Vector<T> interpolate_packed_array(Vector<T> a, Vector<T> b, float c); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static Vector<T> blend_packed_array(Vector<T> a, Vector<T> b, float c);
Could we also add a blend_packed_array()
for consistency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could, although this PR is specifically for supporting tweening the Packed Arrays, which doesn't technically need the method.
Hello Hardworkers ! I come back to check if there are somes good news. |
4.1 is in feature freeze, so any new feature development can only be merged in 4.2 at the earliest. A 3.x backport for 3.6 is feasible, but the feature needs to be merged in Also, this PR needs additional work before it can be merged. |
As we mentions in #73342 and #83384, this issue came to the surface after implement AnimationMixer (#80813), so I will try to salvage this PR later. Godot 4.2 features have been frozen and @Calinou said this is a new feature. However, I believe there is no problem to include it in 4.2 as a bug fix as long as during beta, since there is no danger for breaking compatibility. Considering #83384, this is just a fix for regression. |
Superseded by #84815. |
See this discussion godotengine/godot-proposals#5501.
This PR allows Arrays, but most especially Packed*Arrays to be tweened with a single
tween_property()
call. For example:Interpolation fails if the starting array and the new array do not have the same size (this behavior can be further discussed).
By extension, it also fixes a crash that would occur when passing any Packed*Array.
It seemed like the internal
Animation.interpolate_variant()
had partial support for interpolating these arrays, already. There just was never any occasions to put the gears to work, so to speak. In fact, the previous casting was also kind of botched, as it would simply return nothing and later crash.