mirror of
https://github.com/godotengine/godot.git
synced 2024-11-22 04:06:14 +00:00
Rework AnimationNode process for retrieving the semantic time info
This commit is contained in:
parent
fe01776f05
commit
6dd410854c
@ -6,6 +6,13 @@
|
||||
<description>
|
||||
Base resource for [AnimationTree] nodes. In general, it's not used directly, but you can create custom ones with custom blending formulas.
|
||||
Inherit this when creating animation nodes mainly for use in [AnimationNodeBlendTree], otherwise [AnimationRootNode] should be used instead.
|
||||
You can access the time information as read-only parameter which is processed and stored in the previous frame for all nodes except [AnimationNodeOutput].
|
||||
[b]Note:[/b] If more than two inputs exist in the [AnimationNode], which time information takes precedence depends on the type of [AnimationNode].
|
||||
[codeblock]
|
||||
var current_length = $AnimationTree[parameters/AnimationNodeName/current_length]
|
||||
var current_position = $AnimationTree[parameters/AnimationNodeName/current_position]
|
||||
var current_delta = $AnimationTree[parameters/AnimationNodeName/current_delta]
|
||||
[/codeblock]
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
|
||||
@ -56,7 +63,7 @@
|
||||
When inheriting from [AnimationRootNode], implement this virtual method to return whether the [param parameter] is read-only. Parameters are custom local memory used for your animation nodes, given a resource can be reused in multiple trees.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_process" qualifiers="virtual const">
|
||||
<method name="_process" qualifiers="virtual const" deprecated="Currently this is mostly useless as there is a lack of many APIs to extend AnimationNode by GDScript. It is planned that a more flexible API using structures will be provided in the future.">
|
||||
<return type="float" />
|
||||
<param index="0" name="time" type="float" />
|
||||
<param index="1" name="seek" type="bool" />
|
||||
@ -65,7 +72,7 @@
|
||||
<description>
|
||||
When inheriting from [AnimationRootNode], implement this virtual method to run some code when this animation node is processed. The [param time] parameter is a relative delta, unless [param seek] is [code]true[/code], in which case it is absolute.
|
||||
Here, call the [method blend_input], [method blend_node] or [method blend_animation] functions. You can also use [method get_parameter] and [method set_parameter] to modify local memory.
|
||||
This function should return the time left for the current animation to finish (if unsure, pass the value from the main blend being called).
|
||||
This function should return the delta.
|
||||
</description>
|
||||
</method>
|
||||
<method name="add_input">
|
||||
|
@ -15,9 +15,27 @@
|
||||
<member name="animation" type="StringName" setter="set_animation" getter="get_animation" default="&""">
|
||||
Animation to use as an output. It is one of the animations provided by [member AnimationTree.anim_player].
|
||||
</member>
|
||||
<member name="loop_mode" type="int" setter="set_loop_mode" getter="get_loop_mode" enum="Animation.LoopMode">
|
||||
If [member use_custom_timeline] is [code]true[/code], override the loop settings of the original [Animation] resource with the value.
|
||||
</member>
|
||||
<member name="play_mode" type="int" setter="set_play_mode" getter="get_play_mode" enum="AnimationNodeAnimation.PlayMode" default="0">
|
||||
Determines the playback direction of the animation.
|
||||
</member>
|
||||
<member name="start_offset" type="float" setter="set_start_offset" getter="get_start_offset">
|
||||
If [member use_custom_timeline] is [code]true[/code], offset the start position of the animation.
|
||||
This is useful for adjusting which foot steps first in 3D walking animations.
|
||||
</member>
|
||||
<member name="stretch_time_scale" type="bool" setter="set_stretch_time_scale" getter="is_stretching_time_scale">
|
||||
If [code]true[/code], scales the time so that the length specified in [member timeline_length] is one cycle.
|
||||
This is useful for matching the periods of walking and running animations.
|
||||
If [code]false[/code], the original animation length is respected. If you set the loop to [member loop_mode], the animation will loop in [member timeline_length].
|
||||
</member>
|
||||
<member name="timeline_length" type="float" setter="set_timeline_length" getter="get_timeline_length">
|
||||
If [member use_custom_timeline] is [code]true[/code], offset the start position of the animation.
|
||||
</member>
|
||||
<member name="use_custom_timeline" type="bool" setter="set_use_custom_timeline" getter="is_using_custom_timeline" default="false">
|
||||
If [code]true[/code], [AnimationNode] provides an animation based on the [Animation] resource with some parameters adjusted.
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
<constant name="PLAY_MODE_FORWARD" value="0" enum="PlayMode">
|
||||
|
@ -66,17 +66,22 @@
|
||||
<member name="autorestart_random_delay" type="float" setter="set_autorestart_random_delay" getter="get_autorestart_random_delay" default="0.0">
|
||||
If [member autorestart] is [code]true[/code], a random additional delay (in seconds) between 0 and this value will be added to [member autorestart_delay].
|
||||
</member>
|
||||
<member name="break_loop_at_end" type="bool" setter="set_break_loop_at_end" getter="is_loop_broken_at_end" default="false">
|
||||
If [code]true[/code], breaks the loop at the end of the loop cycle for transition, even if the animation is looping.
|
||||
</member>
|
||||
<member name="fadein_curve" type="Curve" setter="set_fadein_curve" getter="get_fadein_curve">
|
||||
Determines how cross-fading between animations is eased. If empty, the transition will be linear.
|
||||
</member>
|
||||
<member name="fadein_time" type="float" setter="set_fadein_time" getter="get_fadein_time" default="0.0">
|
||||
The fade-in duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a cross-fade that starts at 0 second and ends at 1 second during the animation.
|
||||
[b]Note:[/b] [AnimationNodeOneShot] transitions the current state after the end of the fading. When [AnimationNodeOutput] is considered as the most upstream, so the [member fadein_time] is scaled depending on the downstream delta. For example, if this value is set to [code]1.0[/code] and a [AnimationNodeTimeScale] with a value of [code]2.0[/code] is chained downstream, the actual processing time will be 0.5 second.
|
||||
</member>
|
||||
<member name="fadeout_curve" type="Curve" setter="set_fadeout_curve" getter="get_fadeout_curve">
|
||||
Determines how cross-fading between animations is eased. If empty, the transition will be linear.
|
||||
</member>
|
||||
<member name="fadeout_time" type="float" setter="set_fadeout_time" getter="get_fadeout_time" default="0.0">
|
||||
The fade-out duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a cross-fade that starts at 4 second and ends at 5 second during the animation.
|
||||
[b]Note:[/b] [AnimationNodeOneShot] transitions the current state after the end of the fading. When [AnimationNodeOutput] is considered as the most upstream, so the [member fadeout_time] is scaled depending on the downstream delta. For example, if this value is set to [code]1.0[/code] and an [AnimationNodeTimeScale] with a value of [code]2.0[/code] is chained downstream, the actual processing time will be 0.5 second.
|
||||
</member>
|
||||
<member name="mix_mode" type="int" setter="set_mix_mode" getter="get_mix_mode" enum="AnimationNodeOneShot.MixMode" default="0">
|
||||
The blend type.
|
||||
|
@ -28,6 +28,9 @@
|
||||
<member name="advance_mode" type="int" setter="set_advance_mode" getter="get_advance_mode" enum="AnimationNodeStateMachineTransition.AdvanceMode" default="1">
|
||||
Determines whether the transition should disabled, enabled when using [method AnimationNodeStateMachinePlayback.travel], or traversed automatically if the [member advance_condition] and [member advance_expression] checks are true (if assigned).
|
||||
</member>
|
||||
<member name="break_loop_at_end" type="bool" setter="set_break_loop_at_end" getter="is_loop_broken_at_end" default="false">
|
||||
If [code]true[/code], breaks the loop at the end of the loop cycle for transition, even if the animation is looping.
|
||||
</member>
|
||||
<member name="priority" type="int" setter="set_priority" getter="get_priority" default="1">
|
||||
Lower priority transitions are preferred when travelling through the tree via [method AnimationNodeStateMachinePlayback.travel] or [member advance_mode] is set to [constant ADVANCE_MODE_AUTO].
|
||||
</member>
|
||||
@ -42,6 +45,7 @@
|
||||
</member>
|
||||
<member name="xfade_time" type="float" setter="set_xfade_time" getter="get_xfade_time" default="0.0">
|
||||
The time to cross-fade between this state and the next.
|
||||
[b]Note:[/b] [AnimationNodeStateMachine] transitions the current state immediately after the start of the fading. The precise remaining time can only be inferred from the main animation. When [AnimationNodeOutput] is considered as the most upstream, so the [member xfade_time] is not scaled depending on the downstream delta. See also [member AnimationNodeOneShot.fadeout_time].
|
||||
</member>
|
||||
</members>
|
||||
<signals>
|
||||
|
@ -42,6 +42,13 @@
|
||||
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="is_input_loop_broken_at_end" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="input" type="int" />
|
||||
<description>
|
||||
Returns whether the animation breaks the loop at the end of the loop cycle for transition.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_input_reset" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="input" type="int" />
|
||||
@ -64,6 +71,14 @@
|
||||
Enables or disables auto-advance for the given [param input] index. If enabled, state changes to the next input after playing the animation once. If enabled for the last input state, it loops to the first.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_input_break_loop_at_end">
|
||||
<return type="void" />
|
||||
<param index="0" name="input" type="int" />
|
||||
<param index="1" name="enable" type="bool" />
|
||||
<description>
|
||||
If [code]true[/code], breaks the loop at the end of the loop cycle for transition, even if the animation is looping.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_input_reset">
|
||||
<return type="void" />
|
||||
<param index="0" name="input" type="int" />
|
||||
@ -85,6 +100,7 @@
|
||||
</member>
|
||||
<member name="xfade_time" type="float" setter="set_xfade_time" getter="get_xfade_time" default="0.0">
|
||||
Cross-fading time (in seconds) between each animation connected to the inputs.
|
||||
[b]Note:[/b] [AnimationNodeTransition] transitions the current state immediately after the start of the fading. The precise remaining time can only be inferred from the main animation. When [AnimationNodeOutput] is considered as the most upstream, so the [member xfade_time] is not scaled depending on the downstream delta. See also [member AnimationNodeOneShot.fadeout_time].
|
||||
</member>
|
||||
</members>
|
||||
</class>
|
||||
|
@ -256,10 +256,6 @@ void AnimationNodeBlendTreeEditor::update_graph() {
|
||||
options.push_back(F);
|
||||
}
|
||||
|
||||
if (tree->has_animation(anim->get_animation())) {
|
||||
pb->set_max(tree->get_animation(anim->get_animation())->get_length());
|
||||
}
|
||||
|
||||
pb->set_show_percentage(false);
|
||||
pb->set_custom_minimum_size(Vector2(0, 14) * EDSCALE);
|
||||
animations[E] = pb;
|
||||
@ -994,9 +990,10 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) {
|
||||
if (tree->has_animation(an->get_animation())) {
|
||||
Ref<Animation> anim = tree->get_animation(an->get_animation());
|
||||
if (anim.is_valid()) {
|
||||
E.value->set_max(anim->get_length());
|
||||
//StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + E.input_node;
|
||||
StringName time_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E.key) + "/time";
|
||||
StringName length_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E.key) + "/current_length";
|
||||
StringName time_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E.key) + "/current_position";
|
||||
E.value->set_max(tree->get(length_path));
|
||||
E.value->set_value(tree->get(time_path));
|
||||
}
|
||||
}
|
||||
|
@ -1247,14 +1247,14 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw_all() {
|
||||
{
|
||||
float len = MAX(0.0001, current_length);
|
||||
float pos = CLAMP(current_play_pos, 0, len);
|
||||
float c = current_length == HUGE_LENGTH ? 1 : (pos / len);
|
||||
float c = pos / len;
|
||||
_state_machine_pos_draw_individual(playback->get_current_node(), c);
|
||||
}
|
||||
|
||||
{
|
||||
float len = MAX(0.0001, fade_from_length);
|
||||
float pos = CLAMP(fade_from_current_play_pos, 0, len);
|
||||
float c = fade_from_length == HUGE_LENGTH ? 1 : (pos / len);
|
||||
float c = pos / len;
|
||||
_state_machine_pos_draw_individual(playback->get_fading_from_node(), c);
|
||||
}
|
||||
}
|
||||
|
@ -33,12 +33,17 @@
|
||||
#include "animation_blend_tree.h"
|
||||
|
||||
void AnimationNodeBlendSpace1D::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, blend_position));
|
||||
r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, length_internal, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
}
|
||||
|
||||
Variant AnimationNodeBlendSpace1D::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
|
||||
if (ret != Variant()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (p_parameter == closest) {
|
||||
return -1;
|
||||
} else {
|
||||
@ -272,9 +277,9 @@ void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref<Animatio
|
||||
}
|
||||
}
|
||||
|
||||
double AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
if (blend_points_used == 0) {
|
||||
return 0.0;
|
||||
return NodeTimeInfo();
|
||||
}
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
@ -287,8 +292,7 @@ double AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_
|
||||
|
||||
double blend_pos = get_parameter(blend_position);
|
||||
int cur_closest = get_parameter(closest);
|
||||
double cur_length_internal = get_parameter(length_internal);
|
||||
double max_time_remaining = 0.0;
|
||||
NodeTimeInfo mind;
|
||||
|
||||
if (blend_mode == BLEND_MODE_INTERPOLATED) {
|
||||
int point_lower = -1;
|
||||
@ -341,12 +345,17 @@ double AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_
|
||||
}
|
||||
|
||||
// actually blend the animations now
|
||||
|
||||
bool first = true;
|
||||
double max_weight = 0.0;
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
if (i == point_lower || i == point_higher) {
|
||||
pi.weight = weights[i];
|
||||
double remaining = blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
max_time_remaining = MAX(max_time_remaining, remaining);
|
||||
NodeTimeInfo t = blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
if (first || pi.weight > max_weight) {
|
||||
max_weight = pi.weight;
|
||||
mind = t;
|
||||
first = false;
|
||||
}
|
||||
} else if (sync) {
|
||||
pi.weight = 0;
|
||||
blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
@ -365,7 +374,7 @@ double AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_
|
||||
}
|
||||
|
||||
if (new_closest != cur_closest && new_closest != -1) {
|
||||
double from = 0.0;
|
||||
NodeTimeInfo from;
|
||||
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) {
|
||||
//for ping-pong loop
|
||||
Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node);
|
||||
@ -376,18 +385,17 @@ double AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_
|
||||
//see how much animation remains
|
||||
pi.seeked = false;
|
||||
pi.weight = 0;
|
||||
from = cur_length_internal - blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
}
|
||||
|
||||
pi.time = from;
|
||||
pi.time = from.position;
|
||||
pi.seeked = true;
|
||||
pi.weight = 1.0;
|
||||
max_time_remaining = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
cur_length_internal = from + max_time_remaining;
|
||||
mind = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
cur_closest = new_closest;
|
||||
} else {
|
||||
pi.weight = 1.0;
|
||||
max_time_remaining = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
mind = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
@ -402,8 +410,7 @@ double AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_
|
||||
}
|
||||
|
||||
set_parameter(closest, cur_closest);
|
||||
set_parameter(length_internal, cur_length_internal);
|
||||
return max_time_remaining;
|
||||
return mind;
|
||||
}
|
||||
|
||||
String AnimationNodeBlendSpace1D::get_caption() const {
|
||||
|
@ -68,7 +68,6 @@ protected:
|
||||
|
||||
StringName blend_position = "blend_position";
|
||||
StringName closest = "closest";
|
||||
StringName length_internal = "length_internal";
|
||||
|
||||
BlendMode blend_mode = BLEND_MODE_INTERPOLATED;
|
||||
|
||||
@ -114,7 +113,7 @@ public:
|
||||
void set_use_sync(bool p_sync);
|
||||
bool is_using_sync() const;
|
||||
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
String get_caption() const override;
|
||||
|
||||
Ref<AnimationNode> get_child_by_name(const StringName &p_name) const override;
|
||||
|
@ -34,16 +34,19 @@
|
||||
#include "core/math/geometry_2d.h"
|
||||
|
||||
void AnimationNodeBlendSpace2D::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
r_list->push_back(PropertyInfo(Variant::VECTOR2, blend_position));
|
||||
r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, length_internal, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
}
|
||||
|
||||
Variant AnimationNodeBlendSpace2D::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
|
||||
if (ret != Variant()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (p_parameter == closest) {
|
||||
return -1;
|
||||
} else if (p_parameter == length_internal) {
|
||||
return 0;
|
||||
} else {
|
||||
return Vector2();
|
||||
}
|
||||
@ -442,19 +445,18 @@ void AnimationNodeBlendSpace2D::_blend_triangle(const Vector2 &p_pos, const Vect
|
||||
r_weights[2] = w;
|
||||
}
|
||||
|
||||
double AnimationNodeBlendSpace2D::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
_update_triangles();
|
||||
|
||||
Vector2 blend_pos = get_parameter(blend_position);
|
||||
int cur_closest = get_parameter(closest);
|
||||
double cur_length_internal = get_parameter(length_internal);
|
||||
double mind = 0.0; //time of min distance point
|
||||
NodeTimeInfo mind; //time of min distance point
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
|
||||
if (blend_mode == BLEND_MODE_INTERPOLATED) {
|
||||
if (triangles.size() == 0) {
|
||||
return 0;
|
||||
return NodeTimeInfo();
|
||||
}
|
||||
|
||||
Vector2 best_point;
|
||||
@ -500,7 +502,7 @@ double AnimationNodeBlendSpace2D::_process(const AnimationMixer::PlaybackInfo p_
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(blend_triangle == -1, 0); //should never reach here
|
||||
ERR_FAIL_COND_V(blend_triangle == -1, NodeTimeInfo()); //should never reach here
|
||||
|
||||
int triangle_points[3];
|
||||
for (int j = 0; j < 3; j++) {
|
||||
@ -509,15 +511,17 @@ double AnimationNodeBlendSpace2D::_process(const AnimationMixer::PlaybackInfo p_
|
||||
|
||||
first = true;
|
||||
|
||||
bool found = false;
|
||||
double max_weight = 0.0;
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
bool found = false;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
if (i == triangle_points[j]) {
|
||||
//blend with the given weight
|
||||
pi.weight = blend_weights[j];
|
||||
double t = blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
if (first || t < mind) {
|
||||
NodeTimeInfo t = blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
if (first || pi.weight > max_weight) {
|
||||
mind = t;
|
||||
max_weight = pi.weight;
|
||||
first = false;
|
||||
}
|
||||
found = true;
|
||||
@ -543,7 +547,7 @@ double AnimationNodeBlendSpace2D::_process(const AnimationMixer::PlaybackInfo p_
|
||||
}
|
||||
|
||||
if (new_closest != cur_closest && new_closest != -1) {
|
||||
double from = 0.0;
|
||||
NodeTimeInfo from;
|
||||
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) {
|
||||
//for ping-pong loop
|
||||
Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node);
|
||||
@ -554,14 +558,13 @@ double AnimationNodeBlendSpace2D::_process(const AnimationMixer::PlaybackInfo p_
|
||||
//see how much animation remains
|
||||
pi.seeked = false;
|
||||
pi.weight = 0;
|
||||
from = cur_length_internal - blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
}
|
||||
|
||||
pi.time = from;
|
||||
pi.time = from.position;
|
||||
pi.seeked = true;
|
||||
pi.weight = 1.0;
|
||||
mind = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
cur_length_internal = from + mind;
|
||||
cur_closest = new_closest;
|
||||
} else {
|
||||
pi.weight = 1.0;
|
||||
@ -580,7 +583,6 @@ double AnimationNodeBlendSpace2D::_process(const AnimationMixer::PlaybackInfo p_
|
||||
}
|
||||
|
||||
set_parameter(closest, cur_closest);
|
||||
set_parameter(length_internal, cur_length_internal);
|
||||
return mind;
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,6 @@ protected:
|
||||
|
||||
StringName blend_position = "blend_position";
|
||||
StringName closest = "closest";
|
||||
StringName length_internal = "length_internal";
|
||||
Vector2 max_space = Vector2(1, 1);
|
||||
Vector2 min_space = Vector2(-1, -1);
|
||||
Vector2 snap = Vector2(0.1, 0.1);
|
||||
@ -129,7 +128,7 @@ public:
|
||||
void set_y_label(const String &p_label);
|
||||
String get_y_label() const;
|
||||
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual String get_caption() const override;
|
||||
|
||||
Vector2 get_closest_point(const Vector2 &p_point);
|
||||
|
@ -44,7 +44,26 @@ StringName AnimationNodeAnimation::get_animation() const {
|
||||
Vector<String> (*AnimationNodeAnimation::get_editable_animation_list)() = nullptr;
|
||||
|
||||
void AnimationNodeAnimation::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
}
|
||||
|
||||
AnimationNode::NodeTimeInfo AnimationNodeAnimation::get_node_time_info() const {
|
||||
NodeTimeInfo nti;
|
||||
if (!process_state->tree->has_animation(animation)) {
|
||||
return nti;
|
||||
}
|
||||
|
||||
if (use_custom_timeline) {
|
||||
nti.length = timeline_length;
|
||||
nti.loop_mode = loop_mode;
|
||||
} else {
|
||||
Ref<Animation> anim = process_state->tree->get_animation(animation);
|
||||
nti.length = (double)anim->get_length();
|
||||
nti.loop_mode = anim->get_loop_mode();
|
||||
}
|
||||
nti.position = get_parameter(current_position);
|
||||
|
||||
return nti;
|
||||
}
|
||||
|
||||
void AnimationNodeAnimation::_validate_property(PropertyInfo &p_property) const {
|
||||
@ -62,11 +81,34 @@ void AnimationNodeAnimation::_validate_property(PropertyInfo &p_property) const
|
||||
p_property.hint_string = anims;
|
||||
}
|
||||
}
|
||||
|
||||
if (!use_custom_timeline) {
|
||||
if (p_property.name == "timeline_length" || p_property.name == "start_offset" || p_property.name == "loop_mode" || p_property.name == "stretch_time_scale") {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double AnimationNodeAnimation::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
double cur_time = get_parameter(time);
|
||||
AnimationNode::NodeTimeInfo AnimationNodeAnimation::process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
process_state->is_testing = p_test_only;
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
if (p_playback_info.seeked) {
|
||||
pi.delta = get_node_time_info().position - p_playback_info.time;
|
||||
} else {
|
||||
pi.time = get_node_time_info().position + (backward ? -p_playback_info.delta : p_playback_info.delta);
|
||||
}
|
||||
|
||||
NodeTimeInfo nti = _process(pi, p_test_only);
|
||||
|
||||
if (!p_test_only) {
|
||||
set_node_time_info(nti);
|
||||
}
|
||||
|
||||
return nti;
|
||||
}
|
||||
|
||||
AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
if (!process_state->tree->has_animation(animation)) {
|
||||
AnimationNodeBlendTree *tree = Object::cast_to<AnimationNodeBlendTree>(node_state.parent);
|
||||
if (tree) {
|
||||
@ -77,87 +119,129 @@ double AnimationNodeAnimation::_process(const AnimationMixer::PlaybackInfo p_pla
|
||||
make_invalid(vformat(RTR("Animation not found: '%s'"), animation));
|
||||
}
|
||||
|
||||
return 0;
|
||||
return NodeTimeInfo();
|
||||
}
|
||||
|
||||
Ref<Animation> anim = process_state->tree->get_animation(animation);
|
||||
double anim_size = (double)anim->get_length();
|
||||
double step = 0.0;
|
||||
double prev_time = cur_time;
|
||||
|
||||
NodeTimeInfo cur_nti = get_node_time_info();
|
||||
double cur_len = cur_nti.length;
|
||||
double cur_time = p_playback_info.time;
|
||||
double cur_delta = p_playback_info.delta;
|
||||
|
||||
Animation::LoopMode cur_loop_mode = cur_nti.loop_mode;
|
||||
double prev_time = cur_nti.position;
|
||||
|
||||
Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
|
||||
bool node_backward = play_mode == PLAY_MODE_BACKWARD;
|
||||
|
||||
double p_time = p_playback_info.time;
|
||||
bool p_seek = p_playback_info.seeked;
|
||||
bool p_is_external_seeking = p_playback_info.is_external_seeking;
|
||||
|
||||
if (p_playback_info.seeked) {
|
||||
step = p_time - cur_time;
|
||||
cur_time = p_time;
|
||||
} else {
|
||||
p_time *= backward ? -1.0 : 1.0;
|
||||
cur_time = cur_time + p_time;
|
||||
step = p_time;
|
||||
}
|
||||
bool is_just_looped = false;
|
||||
|
||||
bool is_looping = false;
|
||||
if (anim->get_loop_mode() == Animation::LOOP_PINGPONG) {
|
||||
if (!Math::is_zero_approx(anim_size)) {
|
||||
if (prev_time >= 0 && cur_time < 0) {
|
||||
backward = !backward;
|
||||
looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
|
||||
// 1. Progress for AnimationNode.
|
||||
if (cur_loop_mode != Animation::LOOP_NONE) {
|
||||
if (cur_loop_mode == Animation::LOOP_LINEAR) {
|
||||
if (!Math::is_zero_approx(cur_len)) {
|
||||
if (prev_time <= cur_len && cur_time > cur_len) {
|
||||
is_just_looped = true; // Don't break with negative timescale since remain will not be 0.
|
||||
}
|
||||
cur_time = Math::fposmod(cur_time, cur_len);
|
||||
}
|
||||
if (prev_time <= anim_size && cur_time > anim_size) {
|
||||
backward = !backward;
|
||||
looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
|
||||
backward = false;
|
||||
} else {
|
||||
if (!Math::is_zero_approx(cur_len)) {
|
||||
if (prev_time >= 0 && cur_time < 0) {
|
||||
backward = !backward;
|
||||
} else if (prev_time <= cur_len && cur_time > cur_len) {
|
||||
backward = !backward;
|
||||
is_just_looped = true; // Don't break with negative timescale since remain will not be 0.
|
||||
}
|
||||
cur_time = Math::pingpong(cur_time, cur_len);
|
||||
}
|
||||
cur_time = Math::pingpong(cur_time, anim_size);
|
||||
}
|
||||
is_looping = true;
|
||||
} else if (anim->get_loop_mode() == Animation::LOOP_LINEAR) {
|
||||
if (!Math::is_zero_approx(anim_size)) {
|
||||
if (prev_time >= 0 && cur_time < 0) {
|
||||
looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
|
||||
}
|
||||
if (prev_time <= anim_size && cur_time > anim_size) {
|
||||
looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
|
||||
}
|
||||
cur_time = Math::fposmod(cur_time, anim_size);
|
||||
}
|
||||
backward = false;
|
||||
is_looping = true;
|
||||
} else {
|
||||
if (cur_time < 0) {
|
||||
step += cur_time;
|
||||
cur_delta += cur_time;
|
||||
cur_time = 0;
|
||||
} else if (cur_time > anim_size) {
|
||||
step += anim_size - cur_time;
|
||||
cur_time = anim_size;
|
||||
} else if (cur_time > cur_len) {
|
||||
cur_delta += cur_time - cur_len;
|
||||
cur_time = cur_len;
|
||||
}
|
||||
backward = false;
|
||||
|
||||
// If ended, don't progress animation. So set delta to 0.
|
||||
if (p_time > 0) {
|
||||
// If ended, don't progress AnimationNode. So set delta to 0.
|
||||
if (!Math::is_zero_approx(cur_delta)) {
|
||||
if (play_mode == PLAY_MODE_FORWARD) {
|
||||
if (prev_time >= anim_size) {
|
||||
step = 0;
|
||||
if (prev_time >= cur_len) {
|
||||
cur_delta = 0;
|
||||
}
|
||||
} else {
|
||||
if (prev_time <= 0) {
|
||||
step = 0;
|
||||
cur_delta = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. For return, store "AnimationNode" time info here, not "Animation" time info as below.
|
||||
NodeTimeInfo nti;
|
||||
nti.length = cur_len;
|
||||
nti.position = cur_time;
|
||||
nti.delta = cur_delta;
|
||||
nti.loop_mode = cur_loop_mode;
|
||||
nti.is_just_looped = is_just_looped;
|
||||
|
||||
// 3. Progress for Animation.
|
||||
double prev_playback_time = prev_time - start_offset;
|
||||
double cur_playback_time = cur_time - start_offset;
|
||||
if (stretch_time_scale) {
|
||||
double mlt = anim_size / cur_len;
|
||||
cur_playback_time *= mlt;
|
||||
cur_delta *= mlt;
|
||||
}
|
||||
if (cur_loop_mode == Animation::LOOP_LINEAR) {
|
||||
if (!Math::is_zero_approx(anim_size)) {
|
||||
prev_playback_time = Math::fposmod(prev_playback_time, anim_size);
|
||||
cur_playback_time = Math::fposmod(cur_playback_time, anim_size);
|
||||
if (prev_playback_time >= 0 && cur_playback_time < 0) {
|
||||
looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
|
||||
}
|
||||
if (prev_playback_time <= anim_size && cur_playback_time > anim_size) {
|
||||
looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
|
||||
}
|
||||
}
|
||||
} else if (cur_loop_mode == Animation::LOOP_PINGPONG) {
|
||||
if (!Math::is_zero_approx(anim_size)) {
|
||||
if (Math::fposmod(cur_playback_time, anim_size * 2.0) >= anim_size) {
|
||||
cur_delta = -cur_delta; // Needed for retrieveing discrete keys correctly.
|
||||
}
|
||||
prev_playback_time = Math::pingpong(prev_playback_time, anim_size);
|
||||
cur_playback_time = Math::pingpong(cur_playback_time, anim_size);
|
||||
if (prev_playback_time >= 0 && cur_playback_time < 0) {
|
||||
looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
|
||||
}
|
||||
if (prev_playback_time <= anim_size && cur_playback_time > anim_size) {
|
||||
looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cur_playback_time < 0) {
|
||||
cur_playback_time = 0;
|
||||
} else if (cur_playback_time > anim_size) {
|
||||
cur_playback_time = anim_size;
|
||||
}
|
||||
|
||||
// Emit start & finish signal. Internally, the detections are the same for backward.
|
||||
// We should use call_deferred since the track keys are still being processed.
|
||||
if (process_state->tree && !p_test_only) {
|
||||
// AnimationTree uses seek to 0 "internally" to process the first key of the animation, which is used as the start detection.
|
||||
if (p_seek && !p_is_external_seeking && cur_time == 0) {
|
||||
if (p_seek && !p_is_external_seeking && cur_playback_time == 0) {
|
||||
process_state->tree->call_deferred(SNAME("emit_signal"), "animation_started", animation);
|
||||
}
|
||||
// Finished.
|
||||
if (prev_time < anim_size && cur_time >= anim_size) {
|
||||
if (prev_time - start_offset < anim_size && cur_playback_time >= anim_size) {
|
||||
process_state->tree->call_deferred(SNAME("emit_signal"), "animation_finished", animation);
|
||||
}
|
||||
}
|
||||
@ -166,19 +250,18 @@ double AnimationNodeAnimation::_process(const AnimationMixer::PlaybackInfo p_pla
|
||||
if (!p_test_only) {
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
if (play_mode == PLAY_MODE_FORWARD) {
|
||||
pi.time = cur_time;
|
||||
pi.delta = step;
|
||||
pi.time = cur_playback_time;
|
||||
pi.delta = cur_delta;
|
||||
} else {
|
||||
pi.time = anim_size - cur_time;
|
||||
pi.delta = -step;
|
||||
pi.time = anim_size - cur_playback_time;
|
||||
pi.delta = -cur_delta;
|
||||
}
|
||||
pi.weight = 1.0;
|
||||
pi.looped_flag = looped_flag;
|
||||
blend_animation(animation, pi);
|
||||
}
|
||||
set_parameter(time, cur_time);
|
||||
|
||||
return is_looping ? HUGE_LENGTH : anim_size - cur_time;
|
||||
return nti;
|
||||
}
|
||||
|
||||
String AnimationNodeAnimation::get_caption() const {
|
||||
@ -201,6 +284,48 @@ bool AnimationNodeAnimation::is_backward() const {
|
||||
return backward;
|
||||
}
|
||||
|
||||
void AnimationNodeAnimation::set_use_custom_timeline(bool p_use_custom_timeline) {
|
||||
use_custom_timeline = p_use_custom_timeline;
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
bool AnimationNodeAnimation::is_using_custom_timeline() const {
|
||||
return use_custom_timeline;
|
||||
}
|
||||
|
||||
void AnimationNodeAnimation::set_timeline_length(double p_length) {
|
||||
timeline_length = p_length;
|
||||
}
|
||||
|
||||
double AnimationNodeAnimation::get_timeline_length() const {
|
||||
return timeline_length;
|
||||
}
|
||||
|
||||
void AnimationNodeAnimation::set_stretch_time_scale(bool p_strech_time_scale) {
|
||||
stretch_time_scale = p_strech_time_scale;
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
bool AnimationNodeAnimation::is_stretching_time_scale() const {
|
||||
return stretch_time_scale;
|
||||
}
|
||||
|
||||
void AnimationNodeAnimation::set_start_offset(double p_offset) {
|
||||
start_offset = p_offset;
|
||||
}
|
||||
|
||||
double AnimationNodeAnimation::get_start_offset() const {
|
||||
return start_offset;
|
||||
}
|
||||
|
||||
void AnimationNodeAnimation::set_loop_mode(Animation::LoopMode p_loop_mode) {
|
||||
loop_mode = p_loop_mode;
|
||||
}
|
||||
|
||||
Animation::LoopMode AnimationNodeAnimation::get_loop_mode() const {
|
||||
return loop_mode;
|
||||
}
|
||||
|
||||
void AnimationNodeAnimation::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_animation", "name"), &AnimationNodeAnimation::set_animation);
|
||||
ClassDB::bind_method(D_METHOD("get_animation"), &AnimationNodeAnimation::get_animation);
|
||||
@ -208,8 +333,28 @@ void AnimationNodeAnimation::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_play_mode", "mode"), &AnimationNodeAnimation::set_play_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_play_mode"), &AnimationNodeAnimation::get_play_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_use_custom_timeline", "use_custom_timeline"), &AnimationNodeAnimation::set_use_custom_timeline);
|
||||
ClassDB::bind_method(D_METHOD("is_using_custom_timeline"), &AnimationNodeAnimation::is_using_custom_timeline);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_timeline_length", "timeline_length"), &AnimationNodeAnimation::set_timeline_length);
|
||||
ClassDB::bind_method(D_METHOD("get_timeline_length"), &AnimationNodeAnimation::get_timeline_length);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_stretch_time_scale", "stretch_time_scale"), &AnimationNodeAnimation::set_stretch_time_scale);
|
||||
ClassDB::bind_method(D_METHOD("is_stretching_time_scale"), &AnimationNodeAnimation::is_stretching_time_scale);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_start_offset", "start_offset"), &AnimationNodeAnimation::set_start_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_start_offset"), &AnimationNodeAnimation::get_start_offset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_loop_mode", "loop_mode"), &AnimationNodeAnimation::set_loop_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_loop_mode"), &AnimationNodeAnimation::get_loop_mode);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "play_mode", PROPERTY_HINT_ENUM, "Forward,Backward"), "set_play_mode", "get_play_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_custom_timeline"), "set_use_custom_timeline", "is_using_custom_timeline");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "timeline_length", PROPERTY_HINT_RANGE, "0.001,60,0.001,or_greater,or_less,hide_slider,suffix:s"), "set_timeline_length", "get_timeline_length");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stretch_time_scale"), "set_stretch_time_scale", "is_stretching_time_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "start_offset", PROPERTY_HINT_RANGE, "-60,60,0.001,or_greater,or_less,hide_slider,suffix:s"), "set_start_offset", "get_start_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Ping-Pong"), "set_loop_mode", "get_loop_mode");
|
||||
|
||||
BIND_ENUM_CONSTANT(PLAY_MODE_FORWARD);
|
||||
BIND_ENUM_CONSTANT(PLAY_MODE_BACKWARD);
|
||||
@ -240,16 +385,21 @@ AnimationNodeSync::AnimationNodeSync() {
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
void AnimationNodeOneShot::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
r_list->push_back(PropertyInfo(Variant::BOOL, active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY));
|
||||
r_list->push_back(PropertyInfo(Variant::BOOL, internal_active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY));
|
||||
r_list->push_back(PropertyInfo(Variant::INT, request, PROPERTY_HINT_ENUM, ",Fire,Abort,Fade Out"));
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, fade_in_remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, fade_out_remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, time_to_restart, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
}
|
||||
|
||||
Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
|
||||
if (ret != Variant()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (p_parameter == request) {
|
||||
return ONE_SHOT_REQUEST_NONE;
|
||||
} else if (p_parameter == active || p_parameter == internal_active) {
|
||||
@ -262,6 +412,10 @@ Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_pa
|
||||
}
|
||||
|
||||
bool AnimationNodeOneShot::is_parameter_read_only(const StringName &p_parameter) const {
|
||||
if (AnimationNode::is_parameter_read_only(p_parameter)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (p_parameter == active || p_parameter == internal_active) {
|
||||
return true;
|
||||
}
|
||||
@ -332,6 +486,14 @@ AnimationNodeOneShot::MixMode AnimationNodeOneShot::get_mix_mode() const {
|
||||
return mix;
|
||||
}
|
||||
|
||||
void AnimationNodeOneShot::set_break_loop_at_end(bool p_enable) {
|
||||
break_loop_at_end = p_enable;
|
||||
}
|
||||
|
||||
bool AnimationNodeOneShot::is_loop_broken_at_end() const {
|
||||
return break_loop_at_end;
|
||||
}
|
||||
|
||||
String AnimationNodeOneShot::get_caption() const {
|
||||
return "OneShot";
|
||||
}
|
||||
@ -340,14 +502,14 @@ bool AnimationNodeOneShot::has_filter() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
OneShotRequest cur_request = static_cast<OneShotRequest>((int)get_parameter(request));
|
||||
bool cur_active = get_parameter(active);
|
||||
bool cur_internal_active = get_parameter(internal_active);
|
||||
double cur_time = get_parameter(time);
|
||||
double cur_remaining = get_parameter(remaining);
|
||||
double cur_fade_out_remaining = get_parameter(fade_out_remaining);
|
||||
NodeTimeInfo cur_nti = get_node_time_info();
|
||||
double cur_time_to_restart = get_parameter(time_to_restart);
|
||||
double cur_fade_in_remaining = get_parameter(fade_in_remaining);
|
||||
double cur_fade_out_remaining = get_parameter(fade_out_remaining);
|
||||
|
||||
set_parameter(request, ONE_SHOT_REQUEST_NONE);
|
||||
|
||||
@ -356,6 +518,8 @@ double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playb
|
||||
bool is_fading_out = cur_active == true && cur_internal_active == false;
|
||||
|
||||
double p_time = p_playback_info.time;
|
||||
double p_delta = p_playback_info.delta;
|
||||
double abs_delta = Math::abs(p_delta);
|
||||
bool p_seek = p_playback_info.seeked;
|
||||
bool p_is_external_seeking = p_playback_info.is_external_seeking;
|
||||
|
||||
@ -374,6 +538,7 @@ double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playb
|
||||
// Request fading.
|
||||
is_fading_out = true;
|
||||
cur_fade_out_remaining = fade_out;
|
||||
cur_fade_in_remaining = 0;
|
||||
} else {
|
||||
// Shot is ended, do nothing.
|
||||
is_shooting = false;
|
||||
@ -382,7 +547,7 @@ double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playb
|
||||
set_parameter(time_to_restart, -1);
|
||||
} else if (!do_start && !cur_active) {
|
||||
if (cur_time_to_restart >= 0.0 && !p_seek) {
|
||||
cur_time_to_restart -= p_time;
|
||||
cur_time_to_restart -= abs_delta;
|
||||
if (cur_time_to_restart < 0) {
|
||||
do_start = true; // Restart.
|
||||
}
|
||||
@ -413,8 +578,11 @@ double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playb
|
||||
}
|
||||
|
||||
if (do_start) {
|
||||
cur_time = 0;
|
||||
os_seek = true;
|
||||
if (!cur_internal_active) {
|
||||
cur_fade_in_remaining = fade_in; // If already active, don't fade-in again.
|
||||
}
|
||||
cur_internal_active = true;
|
||||
set_parameter(request, ONE_SHOT_REQUEST_NONE);
|
||||
set_parameter(internal_active, true);
|
||||
set_parameter(active, true);
|
||||
@ -422,20 +590,17 @@ double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playb
|
||||
|
||||
real_t blend = 1.0;
|
||||
bool use_blend = sync;
|
||||
if (cur_time < fade_in) {
|
||||
|
||||
if (cur_fade_in_remaining > 0) {
|
||||
if (fade_in > 0) {
|
||||
use_blend = true;
|
||||
blend = cur_time / fade_in;
|
||||
blend = (fade_in - cur_fade_in_remaining) / fade_in;
|
||||
if (fade_in_curve.is_valid()) {
|
||||
blend = fade_in_curve->sample(blend);
|
||||
}
|
||||
} else {
|
||||
blend = 0; // Should not happen.
|
||||
}
|
||||
} else if (!do_start && !is_fading_out && cur_remaining <= fade_out) {
|
||||
is_fading_out = true;
|
||||
cur_fade_out_remaining = cur_remaining;
|
||||
set_parameter(internal_active, false);
|
||||
}
|
||||
|
||||
if (is_fading_out) {
|
||||
@ -451,34 +616,36 @@ double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playb
|
||||
}
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
double main_rem = 0.0;
|
||||
NodeTimeInfo main_nti;
|
||||
if (mix == MIX_MODE_ADD) {
|
||||
pi.weight = 1.0;
|
||||
main_rem = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
|
||||
main_nti = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
|
||||
} else {
|
||||
pi.seeked &= use_blend;
|
||||
pi.weight = 1.0 - blend;
|
||||
main_rem = blend_input(0, pi, FILTER_BLEND, sync, p_test_only); // Unlike below, processing this edge is a corner case.
|
||||
main_nti = blend_input(0, pi, FILTER_BLEND, sync, p_test_only); // Unlike below, processing this edge is a corner case.
|
||||
}
|
||||
|
||||
pi = p_playback_info;
|
||||
if (os_seek) {
|
||||
pi.time = cur_time;
|
||||
if (do_start) {
|
||||
pi.time = 0;
|
||||
} else if (os_seek) {
|
||||
pi.time = cur_nti.position;
|
||||
}
|
||||
pi.seeked = os_seek;
|
||||
pi.weight = Math::is_zero_approx(blend) ? CMP_EPSILON : blend;
|
||||
double os_rem = blend_input(1, pi, FILTER_PASS, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
|
||||
|
||||
if (do_start) {
|
||||
cur_remaining = os_rem;
|
||||
NodeTimeInfo os_nti = blend_input(1, pi, FILTER_PASS, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
|
||||
|
||||
if (cur_fade_in_remaining <= 0 && !do_start && !is_fading_out && os_nti.get_remain(break_loop_at_end) <= fade_out) {
|
||||
is_fading_out = true;
|
||||
cur_fade_out_remaining = os_nti.get_remain(break_loop_at_end);
|
||||
cur_fade_in_remaining = 0;
|
||||
set_parameter(internal_active, false);
|
||||
}
|
||||
|
||||
if (p_seek) {
|
||||
cur_time = p_time;
|
||||
} else {
|
||||
cur_time += p_time;
|
||||
cur_remaining = os_rem;
|
||||
cur_fade_out_remaining -= p_time;
|
||||
if (cur_remaining <= 0 || (is_fading_out && cur_fade_out_remaining <= 0)) {
|
||||
if (!p_seek) {
|
||||
if (os_nti.get_remain(break_loop_at_end) <= 0 || (is_fading_out && cur_fade_out_remaining <= 0)) {
|
||||
set_parameter(internal_active, false);
|
||||
set_parameter(active, false);
|
||||
if (auto_restart) {
|
||||
@ -486,13 +653,17 @@ double AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playb
|
||||
set_parameter(time_to_restart, restart_sec);
|
||||
}
|
||||
}
|
||||
double d = Math::abs(os_nti.delta);
|
||||
if (!do_start) {
|
||||
cur_fade_in_remaining = MAX(0, cur_fade_in_remaining - d); // Don't consider seeked delta by restart.
|
||||
}
|
||||
cur_fade_out_remaining = MAX(0, cur_fade_out_remaining - d);
|
||||
}
|
||||
|
||||
set_parameter(time, cur_time);
|
||||
set_parameter(remaining, cur_remaining);
|
||||
set_parameter(fade_in_remaining, cur_fade_in_remaining);
|
||||
set_parameter(fade_out_remaining, cur_fade_out_remaining);
|
||||
|
||||
return MAX(main_rem, cur_remaining);
|
||||
return cur_internal_active ? os_nti : main_nti;
|
||||
}
|
||||
|
||||
void AnimationNodeOneShot::_bind_methods() {
|
||||
@ -508,6 +679,9 @@ void AnimationNodeOneShot::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_fadeout_curve", "curve"), &AnimationNodeOneShot::set_fade_out_curve);
|
||||
ClassDB::bind_method(D_METHOD("get_fadeout_curve"), &AnimationNodeOneShot::get_fade_out_curve);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_break_loop_at_end", "enable"), &AnimationNodeOneShot::set_break_loop_at_end);
|
||||
ClassDB::bind_method(D_METHOD("is_loop_broken_at_end"), &AnimationNodeOneShot::is_loop_broken_at_end);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_autorestart", "active"), &AnimationNodeOneShot::set_auto_restart_enabled);
|
||||
ClassDB::bind_method(D_METHOD("has_autorestart"), &AnimationNodeOneShot::is_auto_restart_enabled);
|
||||
|
||||
@ -526,6 +700,7 @@ void AnimationNodeOneShot::_bind_methods() {
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fadein_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_fadein_curve", "get_fadein_curve");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadeout_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_fadeout_time", "get_fadeout_time");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fadeout_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_fadeout_curve", "get_fadeout_curve");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "break_loop_at_end"), "set_break_loop_at_end", "is_loop_broken_at_end");
|
||||
|
||||
ADD_GROUP("Auto Restart", "autorestart_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autorestart"), "set_autorestart", "has_autorestart");
|
||||
@ -550,10 +725,16 @@ AnimationNodeOneShot::AnimationNodeOneShot() {
|
||||
////////////////////////////////////////////////
|
||||
|
||||
void AnimationNodeAdd2::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, add_amount, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater"));
|
||||
}
|
||||
|
||||
Variant AnimationNodeAdd2::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
|
||||
if (ret != Variant()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -565,16 +746,16 @@ bool AnimationNodeAdd2::has_filter() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
double AnimationNodeAdd2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeAdd2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
double amount = get_parameter(add_amount);
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
pi.weight = 1.0;
|
||||
double rem0 = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
|
||||
NodeTimeInfo nti = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
|
||||
pi.weight = amount;
|
||||
blend_input(1, pi, FILTER_PASS, sync, p_test_only);
|
||||
|
||||
return rem0;
|
||||
return nti;
|
||||
}
|
||||
|
||||
void AnimationNodeAdd2::_bind_methods() {
|
||||
@ -588,10 +769,16 @@ AnimationNodeAdd2::AnimationNodeAdd2() {
|
||||
////////////////////////////////////////////////
|
||||
|
||||
void AnimationNodeAdd3::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, add_amount, PROPERTY_HINT_RANGE, "-1,1,0.01,or_less,or_greater"));
|
||||
}
|
||||
|
||||
Variant AnimationNodeAdd3::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
|
||||
if (ret != Variant()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -603,18 +790,18 @@ bool AnimationNodeAdd3::has_filter() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
double AnimationNodeAdd3::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeAdd3::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
double amount = get_parameter(add_amount);
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
pi.weight = MAX(0, -amount);
|
||||
blend_input(0, pi, FILTER_PASS, sync, p_test_only);
|
||||
pi.weight = 1.0;
|
||||
double rem0 = blend_input(1, pi, FILTER_IGNORE, sync, p_test_only);
|
||||
NodeTimeInfo nti = blend_input(1, pi, FILTER_IGNORE, sync, p_test_only);
|
||||
pi.weight = MAX(0, amount);
|
||||
blend_input(2, pi, FILTER_PASS, sync, p_test_only);
|
||||
|
||||
return rem0;
|
||||
return nti;
|
||||
}
|
||||
|
||||
void AnimationNodeAdd3::_bind_methods() {
|
||||
@ -629,10 +816,16 @@ AnimationNodeAdd3::AnimationNodeAdd3() {
|
||||
/////////////////////////////////////////////
|
||||
|
||||
void AnimationNodeBlend2::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, blend_amount, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater"));
|
||||
}
|
||||
|
||||
Variant AnimationNodeBlend2::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
|
||||
if (ret != Variant()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0; // For blend amount.
|
||||
}
|
||||
|
||||
@ -640,16 +833,16 @@ String AnimationNodeBlend2::get_caption() const {
|
||||
return "Blend2";
|
||||
}
|
||||
|
||||
double AnimationNodeBlend2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeBlend2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
double amount = get_parameter(blend_amount);
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
pi.weight = 1.0 - amount;
|
||||
double rem0 = blend_input(0, pi, FILTER_BLEND, sync, p_test_only);
|
||||
NodeTimeInfo nti0 = blend_input(0, pi, FILTER_BLEND, sync, p_test_only);
|
||||
pi.weight = amount;
|
||||
double rem1 = blend_input(1, pi, FILTER_PASS, sync, p_test_only);
|
||||
NodeTimeInfo nti1 = blend_input(1, pi, FILTER_PASS, sync, p_test_only);
|
||||
|
||||
return amount > 0.5 ? rem1 : rem0; // Hacky but good enough.
|
||||
return amount > 0.5 ? nti1 : nti0; // Hacky but good enough.
|
||||
}
|
||||
|
||||
bool AnimationNodeBlend2::has_filter() const {
|
||||
@ -667,10 +860,16 @@ AnimationNodeBlend2::AnimationNodeBlend2() {
|
||||
//////////////////////////////////////
|
||||
|
||||
void AnimationNodeBlend3::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, blend_amount, PROPERTY_HINT_RANGE, "-1,1,0.01,or_less,or_greater"));
|
||||
}
|
||||
|
||||
Variant AnimationNodeBlend3::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
|
||||
if (ret != Variant()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0; // For blend amount.
|
||||
}
|
||||
|
||||
@ -678,18 +877,18 @@ String AnimationNodeBlend3::get_caption() const {
|
||||
return "Blend3";
|
||||
}
|
||||
|
||||
double AnimationNodeBlend3::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeBlend3::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
double amount = get_parameter(blend_amount);
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
pi.weight = MAX(0, -amount);
|
||||
double rem0 = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
|
||||
NodeTimeInfo nti0 = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
|
||||
pi.weight = 1.0 - ABS(amount);
|
||||
double rem1 = blend_input(1, pi, FILTER_IGNORE, sync, p_test_only);
|
||||
NodeTimeInfo nti1 = blend_input(1, pi, FILTER_IGNORE, sync, p_test_only);
|
||||
pi.weight = MAX(0, amount);
|
||||
double rem2 = blend_input(2, pi, FILTER_IGNORE, sync, p_test_only);
|
||||
NodeTimeInfo nti2 = blend_input(2, pi, FILTER_IGNORE, sync, p_test_only);
|
||||
|
||||
return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); // Hacky but good enough.
|
||||
return amount > 0.5 ? nti2 : (amount < -0.5 ? nti0 : nti1); // Hacky but good enough.
|
||||
}
|
||||
|
||||
void AnimationNodeBlend3::_bind_methods() {
|
||||
@ -704,10 +903,16 @@ AnimationNodeBlend3::AnimationNodeBlend3() {
|
||||
////////////////////////////////////////////////
|
||||
|
||||
void AnimationNodeSub2::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, sub_amount, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater"));
|
||||
}
|
||||
|
||||
Variant AnimationNodeSub2::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
|
||||
if (ret != Variant()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -719,7 +924,7 @@ bool AnimationNodeSub2::has_filter() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
double AnimationNodeSub2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeSub2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
double amount = get_parameter(sub_amount);
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
@ -742,10 +947,16 @@ AnimationNodeSub2::AnimationNodeSub2() {
|
||||
/////////////////////////////////
|
||||
|
||||
void AnimationNodeTimeScale::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, scale, PROPERTY_HINT_RANGE, "-32,32,0.01,or_less,or_greater"));
|
||||
}
|
||||
|
||||
Variant AnimationNodeTimeScale::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
|
||||
if (ret != Variant()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 1.0; // Initial timescale.
|
||||
}
|
||||
|
||||
@ -753,13 +964,13 @@ String AnimationNodeTimeScale::get_caption() const {
|
||||
return "TimeScale";
|
||||
}
|
||||
|
||||
double AnimationNodeTimeScale::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeTimeScale::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
double cur_scale = get_parameter(scale);
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
pi.weight = 1.0;
|
||||
if (!pi.seeked) {
|
||||
pi.time *= cur_scale;
|
||||
pi.delta *= cur_scale;
|
||||
}
|
||||
|
||||
return blend_input(0, pi, FILTER_IGNORE, true, p_test_only);
|
||||
@ -775,10 +986,16 @@ AnimationNodeTimeScale::AnimationNodeTimeScale() {
|
||||
////////////////////////////////////
|
||||
|
||||
void AnimationNodeTimeSeek::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, seek_pos_request, PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater")); // It will be reset to -1 after seeking the position immediately.
|
||||
}
|
||||
|
||||
Variant AnimationNodeTimeSeek::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
|
||||
if (ret != Variant()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return -1.0; // Initial seek request.
|
||||
}
|
||||
|
||||
@ -786,7 +1003,7 @@ String AnimationNodeTimeSeek::get_caption() const {
|
||||
return "TimeSeek";
|
||||
}
|
||||
|
||||
double AnimationNodeTimeSeek::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeTimeSeek::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
double cur_seek_pos = get_parameter(seek_pos_request);
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
@ -833,6 +1050,8 @@ bool AnimationNodeTransition::_set(const StringName &p_path, const Variant &p_va
|
||||
set_input_name(which, p_value);
|
||||
} else if (what == "auto_advance") {
|
||||
set_input_as_auto_advance(which, p_value);
|
||||
} else if (what == "break_loop_at_end") {
|
||||
set_input_break_loop_at_end(which, p_value);
|
||||
} else if (what == "reset") {
|
||||
set_input_reset(which, p_value);
|
||||
} else {
|
||||
@ -858,6 +1077,8 @@ bool AnimationNodeTransition::_get(const StringName &p_path, Variant &r_ret) con
|
||||
r_ret = get_input_name(which);
|
||||
} else if (what == "auto_advance") {
|
||||
r_ret = is_input_set_as_auto_advance(which);
|
||||
} else if (what == "break_loop_at_end") {
|
||||
r_ret = is_input_loop_broken_at_end(which);
|
||||
} else if (what == "reset") {
|
||||
r_ret = is_input_reset(which);
|
||||
} else {
|
||||
@ -868,6 +1089,7 @@ bool AnimationNodeTransition::_get(const StringName &p_path, Variant &r_ret) con
|
||||
}
|
||||
|
||||
void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
String anims;
|
||||
for (int i = 0; i < get_input_count(); i++) {
|
||||
if (i > 0) {
|
||||
@ -880,12 +1102,16 @@ void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) con
|
||||
r_list->push_back(PropertyInfo(Variant::STRING, transition_request, PROPERTY_HINT_ENUM, anims)); // For transition request. It will be cleared after setting the value immediately.
|
||||
r_list->push_back(PropertyInfo(Variant::INT, current_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY)); // To avoid finding the index every frame, use this internally.
|
||||
r_list->push_back(PropertyInfo(Variant::INT, prev_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, prev_xfading, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
}
|
||||
|
||||
Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
if (p_parameter == time || p_parameter == prev_xfading) {
|
||||
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
|
||||
if (ret != Variant()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (p_parameter == prev_xfading) {
|
||||
return 0.0;
|
||||
} else if (p_parameter == prev_index || p_parameter == current_index) {
|
||||
return -1;
|
||||
@ -895,6 +1121,10 @@ Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p
|
||||
}
|
||||
|
||||
bool AnimationNodeTransition::is_parameter_read_only(const StringName &p_parameter) const {
|
||||
if (AnimationNode::is_parameter_read_only(p_parameter)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (p_parameter == current_state || p_parameter == current_index) {
|
||||
return true;
|
||||
}
|
||||
@ -947,6 +1177,16 @@ bool AnimationNodeTransition::is_input_set_as_auto_advance(int p_input) const {
|
||||
return input_data[p_input].auto_advance;
|
||||
}
|
||||
|
||||
void AnimationNodeTransition::set_input_break_loop_at_end(int p_input, bool p_enable) {
|
||||
ERR_FAIL_INDEX(p_input, get_input_count());
|
||||
input_data.write[p_input].break_loop_at_end = p_enable;
|
||||
}
|
||||
|
||||
bool AnimationNodeTransition::is_input_loop_broken_at_end(int p_input) const {
|
||||
ERR_FAIL_INDEX_V(p_input, get_input_count(), false);
|
||||
return input_data[p_input].break_loop_at_end;
|
||||
}
|
||||
|
||||
void AnimationNodeTransition::set_input_reset(int p_input, bool p_enable) {
|
||||
ERR_FAIL_INDEX(p_input, get_input_count());
|
||||
input_data.write[p_input].reset = p_enable;
|
||||
@ -981,12 +1221,12 @@ bool AnimationNodeTransition::is_allow_transition_to_self() const {
|
||||
return allow_transition_to_self;
|
||||
}
|
||||
|
||||
double AnimationNodeTransition::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeTransition::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
String cur_transition_request = get_parameter(transition_request);
|
||||
int cur_current_index = get_parameter(current_index);
|
||||
int cur_prev_index = get_parameter(prev_index);
|
||||
|
||||
double cur_time = get_parameter(time);
|
||||
NodeTimeInfo cur_nti = get_node_time_info();
|
||||
double cur_prev_xfading = get_parameter(prev_xfading);
|
||||
|
||||
bool switched = false;
|
||||
@ -1052,7 +1292,6 @@ double AnimationNodeTransition::_process(const AnimationMixer::PlaybackInfo p_pl
|
||||
|
||||
// Special case for restart.
|
||||
if (restart) {
|
||||
set_parameter(time, 0);
|
||||
pi.time = 0;
|
||||
pi.seeked = true;
|
||||
pi.weight = 1.0;
|
||||
@ -1061,16 +1300,12 @@ double AnimationNodeTransition::_process(const AnimationMixer::PlaybackInfo p_pl
|
||||
|
||||
if (switched) {
|
||||
cur_prev_xfading = xfade_time;
|
||||
cur_time = 0;
|
||||
}
|
||||
|
||||
if (cur_current_index < 0 || cur_current_index >= get_input_count() || cur_prev_index >= get_input_count()) {
|
||||
return 0;
|
||||
return NodeTimeInfo();
|
||||
}
|
||||
|
||||
double rem = 0.0;
|
||||
double abs_time = Math::abs(p_time);
|
||||
|
||||
if (sync) {
|
||||
pi.weight = 0;
|
||||
for (int i = 0; i < get_input_count(); i++) {
|
||||
@ -1081,20 +1316,11 @@ double AnimationNodeTransition::_process(const AnimationMixer::PlaybackInfo p_pl
|
||||
}
|
||||
|
||||
if (cur_prev_index < 0) { // Process current animation, check for transition.
|
||||
|
||||
pi.weight = 1.0;
|
||||
rem = blend_input(cur_current_index, pi, FILTER_IGNORE, true, p_test_only);
|
||||
|
||||
if (p_seek) {
|
||||
cur_time = abs_time;
|
||||
} else {
|
||||
cur_time += abs_time;
|
||||
}
|
||||
|
||||
if (input_data[cur_current_index].auto_advance && rem <= xfade_time) {
|
||||
cur_nti = blend_input(cur_current_index, pi, FILTER_IGNORE, true, p_test_only);
|
||||
if (input_data[cur_current_index].auto_advance && cur_nti.get_remain(input_data[cur_current_index].break_loop_at_end) <= xfade_time) {
|
||||
set_parameter(transition_request, get_input_name((cur_current_index + 1) % get_input_count()));
|
||||
}
|
||||
|
||||
} else { // Cross-fading from prev to current.
|
||||
|
||||
real_t blend = 0.0;
|
||||
@ -1117,33 +1343,30 @@ double AnimationNodeTransition::_process(const AnimationMixer::PlaybackInfo p_pl
|
||||
pi.time = 0;
|
||||
pi.seeked = true;
|
||||
}
|
||||
rem = blend_input(cur_current_index, pi, FILTER_IGNORE, true, p_test_only);
|
||||
cur_nti = blend_input(cur_current_index, pi, FILTER_IGNORE, true, p_test_only);
|
||||
|
||||
pi = p_playback_info;
|
||||
pi.seeked &= use_blend;
|
||||
pi.weight = blend;
|
||||
blend_input(cur_prev_index, pi, FILTER_IGNORE, true, p_test_only);
|
||||
if (p_seek) {
|
||||
cur_time = abs_time;
|
||||
} else {
|
||||
cur_time += abs_time;
|
||||
cur_prev_xfading -= abs_time;
|
||||
if (cur_prev_xfading < 0) {
|
||||
if (!p_seek) {
|
||||
if (cur_prev_xfading <= 0) {
|
||||
set_parameter(prev_index, -1);
|
||||
}
|
||||
cur_prev_xfading -= Math::abs(p_playback_info.delta);
|
||||
}
|
||||
}
|
||||
|
||||
set_parameter(time, cur_time);
|
||||
set_parameter(prev_xfading, cur_prev_xfading);
|
||||
|
||||
return rem;
|
||||
return cur_nti;
|
||||
}
|
||||
|
||||
void AnimationNodeTransition::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
for (int i = 0; i < get_input_count(); i++) {
|
||||
p_list->push_back(PropertyInfo(Variant::STRING, "input_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/auto_advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/break_loop_at_end", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/reset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
|
||||
}
|
||||
}
|
||||
@ -1154,6 +1377,9 @@ void AnimationNodeTransition::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_input_as_auto_advance", "input", "enable"), &AnimationNodeTransition::set_input_as_auto_advance);
|
||||
ClassDB::bind_method(D_METHOD("is_input_set_as_auto_advance", "input"), &AnimationNodeTransition::is_input_set_as_auto_advance);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_input_break_loop_at_end", "input", "enable"), &AnimationNodeTransition::set_input_break_loop_at_end);
|
||||
ClassDB::bind_method(D_METHOD("is_input_loop_broken_at_end", "input"), &AnimationNodeTransition::is_input_loop_broken_at_end);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_input_reset", "input", "enable"), &AnimationNodeTransition::set_input_reset);
|
||||
ClassDB::bind_method(D_METHOD("is_input_reset", "input"), &AnimationNodeTransition::is_input_reset);
|
||||
|
||||
@ -1181,7 +1407,7 @@ String AnimationNodeOutput::get_caption() const {
|
||||
return "Output";
|
||||
}
|
||||
|
||||
double AnimationNodeOutput::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeOutput::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
pi.weight = 1.0;
|
||||
return blend_input(0, pi, FILTER_IGNORE, true, p_test_only);
|
||||
@ -1400,10 +1626,10 @@ String AnimationNodeBlendTree::get_caption() const {
|
||||
return "BlendTree";
|
||||
}
|
||||
|
||||
double AnimationNodeBlendTree::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeBlendTree::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output].node;
|
||||
node_state.connections = nodes[SceneStringNames::get_singleton()->output].connections;
|
||||
ERR_FAIL_COND_V(output.is_null(), 0);
|
||||
ERR_FAIL_COND_V(output.is_null(), NodeTimeInfo());
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
pi.weight = 1.0;
|
||||
|
@ -37,7 +37,12 @@ class AnimationNodeAnimation : public AnimationRootNode {
|
||||
GDCLASS(AnimationNodeAnimation, AnimationRootNode);
|
||||
|
||||
StringName animation;
|
||||
StringName time = "time";
|
||||
|
||||
bool use_custom_timeline = false;
|
||||
double timeline_length = 1.0;
|
||||
Animation::LoopMode loop_mode = Animation::LOOP_NONE;
|
||||
bool stretch_time_scale = true;
|
||||
double start_offset = 0.0;
|
||||
|
||||
uint64_t last_version = 0;
|
||||
bool skip = false;
|
||||
@ -50,10 +55,13 @@ public:
|
||||
|
||||
void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
|
||||
virtual NodeTimeInfo get_node_time_info() const override; // Wrapper of get_parameter().
|
||||
|
||||
static Vector<String> (*get_editable_animation_list)();
|
||||
|
||||
virtual String get_caption() const override;
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
void set_animation(const StringName &p_name);
|
||||
StringName get_animation() const;
|
||||
@ -64,6 +72,21 @@ public:
|
||||
void set_backward(bool p_backward);
|
||||
bool is_backward() const;
|
||||
|
||||
void set_use_custom_timeline(bool p_use_custom_timeline);
|
||||
bool is_using_custom_timeline() const;
|
||||
|
||||
void set_timeline_length(double p_length);
|
||||
double get_timeline_length() const;
|
||||
|
||||
void set_stretch_time_scale(bool p_strech_time_scale);
|
||||
bool is_stretching_time_scale() const;
|
||||
|
||||
void set_start_offset(double p_offset);
|
||||
double get_start_offset() const;
|
||||
|
||||
void set_loop_mode(Animation::LoopMode p_loop_mode);
|
||||
Animation::LoopMode get_loop_mode() const;
|
||||
|
||||
AnimationNodeAnimation();
|
||||
|
||||
protected:
|
||||
@ -118,12 +141,12 @@ private:
|
||||
double auto_restart_delay = 1.0;
|
||||
double auto_restart_random_delay = 0.0;
|
||||
MixMode mix = MIX_MODE_BLEND;
|
||||
bool break_loop_at_end = false;
|
||||
|
||||
StringName request = PNAME("request");
|
||||
StringName active = PNAME("active");
|
||||
StringName internal_active = PNAME("internal_active");
|
||||
StringName time = "time";
|
||||
StringName remaining = "remaining";
|
||||
StringName fade_in_remaining = "fade_in_remaining";
|
||||
StringName fade_out_remaining = "fade_out_remaining";
|
||||
StringName time_to_restart = "time_to_restart";
|
||||
|
||||
@ -160,8 +183,11 @@ public:
|
||||
void set_mix_mode(MixMode p_mix);
|
||||
MixMode get_mix_mode() const;
|
||||
|
||||
void set_break_loop_at_end(bool p_enable);
|
||||
bool is_loop_broken_at_end() const;
|
||||
|
||||
virtual bool has_filter() const override;
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
AnimationNodeOneShot();
|
||||
};
|
||||
@ -184,7 +210,7 @@ public:
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual bool has_filter() const override;
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
AnimationNodeAdd2();
|
||||
};
|
||||
@ -204,7 +230,7 @@ public:
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual bool has_filter() const override;
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
AnimationNodeAdd3();
|
||||
};
|
||||
@ -222,7 +248,7 @@ public:
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
||||
virtual String get_caption() const override;
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
virtual bool has_filter() const override;
|
||||
AnimationNodeBlend2();
|
||||
@ -242,7 +268,7 @@ public:
|
||||
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
AnimationNodeBlend3();
|
||||
};
|
||||
|
||||
@ -261,7 +287,7 @@ public:
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual bool has_filter() const override;
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
AnimationNodeSub2();
|
||||
};
|
||||
@ -280,7 +306,7 @@ public:
|
||||
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
AnimationNodeTimeScale();
|
||||
};
|
||||
@ -299,7 +325,7 @@ public:
|
||||
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
AnimationNodeTimeSeek();
|
||||
};
|
||||
@ -309,11 +335,11 @@ class AnimationNodeTransition : public AnimationNodeSync {
|
||||
|
||||
struct InputData {
|
||||
bool auto_advance = false;
|
||||
bool break_loop_at_end = false;
|
||||
bool reset = true;
|
||||
};
|
||||
Vector<InputData> input_data;
|
||||
|
||||
StringName time = "time";
|
||||
StringName prev_xfading = "prev_xfading";
|
||||
StringName prev_index = "prev_index";
|
||||
StringName current_index = PNAME("current_index");
|
||||
@ -351,6 +377,9 @@ public:
|
||||
void set_input_as_auto_advance(int p_input, bool p_enable);
|
||||
bool is_input_set_as_auto_advance(int p_input) const;
|
||||
|
||||
void set_input_break_loop_at_end(int p_input, bool p_enable);
|
||||
bool is_input_loop_broken_at_end(int p_input) const;
|
||||
|
||||
void set_input_reset(int p_input, bool p_enable);
|
||||
bool is_input_reset(int p_input) const;
|
||||
|
||||
@ -363,7 +392,7 @@ public:
|
||||
void set_allow_transition_to_self(bool p_enable);
|
||||
bool is_allow_transition_to_self() const;
|
||||
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
AnimationNodeTransition();
|
||||
};
|
||||
@ -373,7 +402,7 @@ class AnimationNodeOutput : public AnimationNode {
|
||||
|
||||
public:
|
||||
virtual String get_caption() const override;
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
AnimationNodeOutput();
|
||||
};
|
||||
|
||||
@ -445,7 +474,7 @@ public:
|
||||
void get_node_connections(List<NodeConnection> *r_connections) const;
|
||||
|
||||
virtual String get_caption() const override;
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
void get_node_list(List<StringName> *r_list);
|
||||
|
||||
|
@ -101,12 +101,22 @@ float AnimationNodeStateMachineTransition::get_xfade_time() const {
|
||||
|
||||
void AnimationNodeStateMachineTransition::set_xfade_curve(const Ref<Curve> &p_curve) {
|
||||
xfade_curve = p_curve;
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
Ref<Curve> AnimationNodeStateMachineTransition::get_xfade_curve() const {
|
||||
return xfade_curve;
|
||||
}
|
||||
|
||||
void AnimationNodeStateMachineTransition::set_break_loop_at_end(bool p_enable) {
|
||||
break_loop_at_end = p_enable;
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
bool AnimationNodeStateMachineTransition::is_loop_broken_at_end() const {
|
||||
return break_loop_at_end;
|
||||
}
|
||||
|
||||
void AnimationNodeStateMachineTransition::set_reset(bool p_reset) {
|
||||
reset = p_reset;
|
||||
emit_changed();
|
||||
@ -141,6 +151,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeStateMachineTransition::set_xfade_curve);
|
||||
ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeStateMachineTransition::get_xfade_curve);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_break_loop_at_end", "enable"), &AnimationNodeStateMachineTransition::set_break_loop_at_end);
|
||||
ClassDB::bind_method(D_METHOD("is_loop_broken_at_end"), &AnimationNodeStateMachineTransition::is_loop_broken_at_end);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_reset", "reset"), &AnimationNodeStateMachineTransition::set_reset);
|
||||
ClassDB::bind_method(D_METHOD("is_reset"), &AnimationNodeStateMachineTransition::is_reset);
|
||||
|
||||
@ -153,6 +166,7 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01,suffix:s"), "set_xfade_time", "get_xfade_time");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve");
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "break_loop_at_end"), "set_break_loop_at_end", "is_loop_broken_at_end");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset"), "set_reset", "is_reset");
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority");
|
||||
@ -310,19 +324,19 @@ TypedArray<StringName> AnimationNodeStateMachinePlayback::_get_travel_path() con
|
||||
}
|
||||
|
||||
float AnimationNodeStateMachinePlayback::get_current_play_pos() const {
|
||||
return pos_current;
|
||||
return current_nti.position;
|
||||
}
|
||||
|
||||
float AnimationNodeStateMachinePlayback::get_current_length() const {
|
||||
return len_current;
|
||||
return current_nti.length;
|
||||
}
|
||||
|
||||
float AnimationNodeStateMachinePlayback::get_fade_from_play_pos() const {
|
||||
return pos_fade_from;
|
||||
return fadeing_from_nti.position;
|
||||
}
|
||||
|
||||
float AnimationNodeStateMachinePlayback::get_fade_from_length() const {
|
||||
return len_fade_from;
|
||||
return fadeing_from_nti.length;
|
||||
}
|
||||
|
||||
float AnimationNodeStateMachinePlayback::get_fading_time() const {
|
||||
@ -665,21 +679,22 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree,
|
||||
}
|
||||
}
|
||||
|
||||
double AnimationNodeStateMachinePlayback::process(const String &p_base_path, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
double rem = _process(p_base_path, p_state_machine, p_playback_info, p_test_only);
|
||||
AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::process(const String &p_base_path, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo nti = _process(p_base_path, p_state_machine, p_playback_info, p_test_only);
|
||||
start_request = StringName();
|
||||
next_request = false;
|
||||
stop_request = false;
|
||||
reset_request_on_teleport = false;
|
||||
return rem;
|
||||
return nti;
|
||||
}
|
||||
|
||||
double AnimationNodeStateMachinePlayback::_process(const String &p_base_path, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(const String &p_base_path, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
_set_base_path(p_base_path);
|
||||
|
||||
AnimationTree *tree = p_state_machine->process_state->tree;
|
||||
|
||||
double p_time = p_playback_info.time;
|
||||
double p_delta = p_playback_info.delta;
|
||||
bool p_seek = p_playback_info.seeked;
|
||||
bool p_is_external_seeking = p_playback_info.is_external_seeking;
|
||||
|
||||
@ -690,8 +705,8 @@ double AnimationNodeStateMachinePlayback::_process(const String &p_base_path, An
|
||||
if (p_state_machine->get_state_machine_type() != AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
|
||||
path.clear();
|
||||
_clear_path_children(tree, p_state_machine, p_test_only);
|
||||
_start(p_state_machine);
|
||||
}
|
||||
_start(p_state_machine);
|
||||
reset_request = true;
|
||||
} else {
|
||||
// Reset current state.
|
||||
@ -705,11 +720,11 @@ double AnimationNodeStateMachinePlayback::_process(const String &p_base_path, An
|
||||
travel_request = StringName();
|
||||
path.clear();
|
||||
playing = false;
|
||||
return 0;
|
||||
return AnimationNode::NodeTimeInfo();
|
||||
}
|
||||
|
||||
if (!playing && start_request != StringName() && travel_request != StringName()) {
|
||||
return 0;
|
||||
return AnimationNode::NodeTimeInfo();
|
||||
}
|
||||
|
||||
// Process start/travel request.
|
||||
@ -732,7 +747,7 @@ double AnimationNodeStateMachinePlayback::_process(const String &p_base_path, An
|
||||
_start(p_state_machine);
|
||||
} else {
|
||||
StringName node = start_request;
|
||||
ERR_FAIL_V_MSG(0, "No such node: '" + node + "'");
|
||||
ERR_FAIL_V_MSG(AnimationNode::NodeTimeInfo(), "No such node: '" + node + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@ -766,7 +781,7 @@ double AnimationNodeStateMachinePlayback::_process(const String &p_base_path, An
|
||||
teleport_request = true;
|
||||
}
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(0, "No such node: '" + temp_travel_request + "'");
|
||||
ERR_FAIL_V_MSG(AnimationNode::NodeTimeInfo(), "No such node: '" + temp_travel_request + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -777,16 +792,14 @@ double AnimationNodeStateMachinePlayback::_process(const String &p_base_path, An
|
||||
teleport_request = false;
|
||||
// Clear fadeing on teleport.
|
||||
fading_from = StringName();
|
||||
fadeing_from_nti = AnimationNode::NodeTimeInfo();
|
||||
fading_pos = 0;
|
||||
// Init current length.
|
||||
pos_current = 0; // Overwritten suddenly in main process.
|
||||
|
||||
pi.time = 0;
|
||||
pi.seeked = true;
|
||||
pi.is_external_seeking = false;
|
||||
pi.weight = 0;
|
||||
|
||||
len_current = p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, true);
|
||||
current_nti = p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, true);
|
||||
// Don't process first node if not necessary, insteads process next node.
|
||||
_transition_to_next_recursive(tree, p_state_machine, p_test_only);
|
||||
}
|
||||
@ -795,7 +808,7 @@ double AnimationNodeStateMachinePlayback::_process(const String &p_base_path, An
|
||||
if (!p_state_machine->states.has(current)) {
|
||||
playing = false; // Current does not exist.
|
||||
_set_current(p_state_machine, StringName());
|
||||
return 0;
|
||||
return AnimationNode::NodeTimeInfo();
|
||||
}
|
||||
|
||||
// Special case for grouped state machine Start/End to make priority with parent blend (means don't treat Start and End states as RESET animations).
|
||||
@ -813,7 +826,7 @@ double AnimationNodeStateMachinePlayback::_process(const String &p_base_path, An
|
||||
fading_from = StringName();
|
||||
} else {
|
||||
if (!p_seek) {
|
||||
fading_pos += p_time;
|
||||
fading_pos += Math::abs(p_delta);
|
||||
}
|
||||
fade_blend = MIN(1.0, fading_pos / fading_time);
|
||||
}
|
||||
@ -829,18 +842,14 @@ double AnimationNodeStateMachinePlayback::_process(const String &p_base_path, An
|
||||
}
|
||||
|
||||
// Main process.
|
||||
double rem = 0.0;
|
||||
pi = p_playback_info;
|
||||
pi.weight = fade_blend;
|
||||
if (reset_request) {
|
||||
reset_request = false;
|
||||
pi.time = 0;
|
||||
pi.seeked = true;
|
||||
len_current = p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, p_test_only);
|
||||
rem = len_current;
|
||||
} else {
|
||||
rem = p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
|
||||
}
|
||||
current_nti = p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
|
||||
|
||||
// Cross-fade process.
|
||||
if (fading_from != StringName()) {
|
||||
@ -852,7 +861,6 @@ double AnimationNodeStateMachinePlayback::_process(const String &p_base_path, An
|
||||
fade_blend_inv = 1.0;
|
||||
}
|
||||
|
||||
float fading_from_rem = 0.0;
|
||||
pi = p_playback_info;
|
||||
pi.weight = fade_blend_inv;
|
||||
if (_reset_request_for_fading_from) {
|
||||
@ -860,57 +868,41 @@ double AnimationNodeStateMachinePlayback::_process(const String &p_base_path, An
|
||||
pi.time = 0;
|
||||
pi.seeked = true;
|
||||
}
|
||||
fading_from_rem = p_state_machine->blend_node(p_state_machine->states[fading_from].node, fading_from, pi, AnimationNode::FILTER_IGNORE, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
|
||||
|
||||
// Guess playback position.
|
||||
if (fading_from_rem > len_fade_from) { /// Weird but ok.
|
||||
len_fade_from = fading_from_rem;
|
||||
}
|
||||
pos_fade_from = len_fade_from - fading_from_rem;
|
||||
fadeing_from_nti = p_state_machine->blend_node(p_state_machine->states[fading_from].node, fading_from, pi, AnimationNode::FILTER_IGNORE, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
|
||||
|
||||
if (fading_pos >= fading_time) {
|
||||
fading_from = StringName(); // Finish fading.
|
||||
// Finish fading.
|
||||
fading_from = StringName();
|
||||
fadeing_from_nti = AnimationNode::NodeTimeInfo();
|
||||
}
|
||||
}
|
||||
|
||||
// Guess playback position.
|
||||
if (rem > len_current) { // Weird but ok.
|
||||
len_current = rem;
|
||||
}
|
||||
pos_current = len_current - rem;
|
||||
|
||||
// Find next and see when to transition.
|
||||
_transition_to_next_recursive(tree, p_state_machine, p_test_only);
|
||||
|
||||
// Predict remaining time.
|
||||
double remain = rem; // If we can't predict the end of state machine, the time remaining must be INFINITY.
|
||||
|
||||
if (p_state_machine->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_NESTED) {
|
||||
// There is no next transition.
|
||||
if (!p_state_machine->has_transition_from(current)) {
|
||||
if (fading_from != StringName()) {
|
||||
remain = MAX(rem, fading_time - fading_pos);
|
||||
} else {
|
||||
remain = rem;
|
||||
return current_nti.get_remain() > fadeing_from_nti.get_remain() ? current_nti : fadeing_from_nti;
|
||||
}
|
||||
return remain;
|
||||
return current_nti;
|
||||
}
|
||||
}
|
||||
|
||||
if (current == p_state_machine->end_node) {
|
||||
if (fading_from != StringName()) {
|
||||
remain = MAX(0, fading_time - fading_pos);
|
||||
} else {
|
||||
remain = 0;
|
||||
if (fading_from != StringName() && fadeing_from_nti.get_remain() > 0) {
|
||||
return fadeing_from_nti;
|
||||
}
|
||||
return remain;
|
||||
return AnimationNode::NodeTimeInfo();
|
||||
}
|
||||
|
||||
if (!is_end()) {
|
||||
return HUGE_LENGTH;
|
||||
current_nti.is_infinity = true;
|
||||
}
|
||||
|
||||
return remain;
|
||||
return current_nti;
|
||||
}
|
||||
|
||||
bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_test_only) {
|
||||
@ -952,6 +944,7 @@ bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationT
|
||||
p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, p_test_only);
|
||||
}
|
||||
fading_from = StringName();
|
||||
fadeing_from_nti = AnimationNode::NodeTimeInfo();
|
||||
fading_time = 0;
|
||||
fading_pos = 0;
|
||||
}
|
||||
@ -968,11 +961,10 @@ bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationT
|
||||
_reset_request_for_fading_from = reset_request; // To avoid processing doubly, it must be reset in the fading process within _process().
|
||||
reset_request = next.is_reset;
|
||||
|
||||
pos_fade_from = pos_current;
|
||||
len_fade_from = len_current;
|
||||
fadeing_from_nti = current_nti;
|
||||
|
||||
if (next.switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) {
|
||||
pi.time = MIN(pos_current, len_current);
|
||||
pi.time = current_nti.position;
|
||||
pi.seeked = true;
|
||||
pi.is_external_seeking = false;
|
||||
pi.weight = 0;
|
||||
@ -980,24 +972,11 @@ bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationT
|
||||
}
|
||||
|
||||
// Just get length to find next recursive.
|
||||
double rem = 0.0;
|
||||
pi.time = 0;
|
||||
pi.is_external_seeking = false;
|
||||
pi.weight = 0;
|
||||
if (next.is_reset) {
|
||||
pi.seeked = true;
|
||||
len_current = p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, true); // Just retrieve remain length, don't process.
|
||||
rem = len_current;
|
||||
} else {
|
||||
pi.seeked = false;
|
||||
rem = p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, true); // Just retrieve remain length, don't process.
|
||||
}
|
||||
|
||||
// Guess playback position.
|
||||
if (rem > len_current) { // Weird but ok.
|
||||
len_current = rem;
|
||||
}
|
||||
pos_current = len_current - rem;
|
||||
pi.seeked = next.is_reset;
|
||||
current_nti = p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, true); // Just retrieve remain length, don't process.
|
||||
|
||||
// Fading must be processed.
|
||||
if (fading_time) {
|
||||
@ -1028,6 +1007,7 @@ bool AnimationNodeStateMachinePlayback::_can_transition_to_next(AnimationTree *p
|
||||
playback->_next_main();
|
||||
// Then, fadeing should be end.
|
||||
fading_from = StringName();
|
||||
fadeing_from_nti = AnimationNode::NodeTimeInfo();
|
||||
fading_pos = 0;
|
||||
} else {
|
||||
return true;
|
||||
@ -1039,7 +1019,7 @@ bool AnimationNodeStateMachinePlayback::_can_transition_to_next(AnimationTree *p
|
||||
}
|
||||
|
||||
if (current != p_state_machine->start_node && p_next.switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_AT_END) {
|
||||
return pos_current >= len_current - p_next.xfade;
|
||||
return current_nti.get_remain(p_next.break_loop_at_end) <= p_next.xfade;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1084,6 +1064,7 @@ AnimationNodeStateMachinePlayback::NextInfo AnimationNodeStateMachinePlayback::_
|
||||
next.curve = ref_transition->get_xfade_curve();
|
||||
next.switch_mode = ref_transition->get_switch_mode();
|
||||
next.is_reset = ref_transition->is_reset();
|
||||
next.break_loop_at_end = ref_transition->is_loop_broken_at_end();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1113,6 +1094,7 @@ AnimationNodeStateMachinePlayback::NextInfo AnimationNodeStateMachinePlayback::_
|
||||
next.curve = ref_transition->get_xfade_curve();
|
||||
next.switch_mode = ref_transition->get_switch_mode();
|
||||
next.is_reset = ref_transition->is_reset();
|
||||
next.break_loop_at_end = ref_transition->is_loop_broken_at_end();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1233,6 +1215,7 @@ AnimationNodeStateMachinePlayback::AnimationNodeStateMachinePlayback() {
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
void AnimationNodeStateMachine::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
r_list->push_back(PropertyInfo(Variant::OBJECT, playback, PROPERTY_HINT_RESOURCE_TYPE, "AnimationNodeStateMachinePlayback", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ALWAYS_DUPLICATE)); // Don't store this object in .tres, it always needs to be made as unique object.
|
||||
List<StringName> advance_conditions;
|
||||
for (int i = 0; i < transitions.size(); i++) {
|
||||
@ -1249,6 +1232,11 @@ void AnimationNodeStateMachine::get_parameter_list(List<PropertyInfo> *r_list) c
|
||||
}
|
||||
|
||||
Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
|
||||
if (ret != Variant()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (p_parameter == playback) {
|
||||
Ref<AnimationNodeStateMachinePlayback> p;
|
||||
p.instantiate();
|
||||
@ -1259,6 +1247,10 @@ Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName
|
||||
}
|
||||
|
||||
bool AnimationNodeStateMachine::is_parameter_read_only(const StringName &p_parameter) const {
|
||||
if (AnimationNode::is_parameter_read_only(p_parameter)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (p_parameter == playback) {
|
||||
return true;
|
||||
}
|
||||
@ -1622,9 +1614,9 @@ Vector2 AnimationNodeStateMachine::get_graph_offset() const {
|
||||
return graph_offset;
|
||||
}
|
||||
|
||||
double AnimationNodeStateMachine::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNodeStateMachine::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
Ref<AnimationNodeStateMachinePlayback> playback_new = get_parameter(playback);
|
||||
ERR_FAIL_COND_V(playback_new.is_null(), 0.0);
|
||||
ERR_FAIL_COND_V(playback_new.is_null(), AnimationNode::NodeTimeInfo());
|
||||
playback_new->_set_grouped(state_machine_type == STATE_MACHINE_TYPE_GROUPED);
|
||||
if (p_test_only) {
|
||||
playback_new = playback_new->duplicate(); // Don't process original when testing.
|
||||
|
@ -57,6 +57,7 @@ private:
|
||||
StringName advance_condition_name;
|
||||
float xfade_time = 0.0;
|
||||
Ref<Curve> xfade_curve;
|
||||
bool break_loop_at_end = false;
|
||||
bool reset = true;
|
||||
int priority = 1;
|
||||
String advance_expression;
|
||||
@ -85,6 +86,9 @@ public:
|
||||
void set_xfade_time(float p_xfade);
|
||||
float get_xfade_time() const;
|
||||
|
||||
void set_break_loop_at_end(bool p_enable);
|
||||
bool is_loop_broken_at_end() const;
|
||||
|
||||
void set_reset(bool p_reset);
|
||||
bool is_reset() const;
|
||||
|
||||
@ -210,7 +214,7 @@ public:
|
||||
void set_graph_offset(const Vector2 &p_offset);
|
||||
Vector2 get_graph_offset() const;
|
||||
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name) const override;
|
||||
@ -246,6 +250,7 @@ class AnimationNodeStateMachinePlayback : public Resource {
|
||||
Ref<Curve> curve;
|
||||
AnimationNodeStateMachineTransition::SwitchMode switch_mode;
|
||||
bool is_reset;
|
||||
bool break_loop_at_end;
|
||||
};
|
||||
|
||||
struct ChildStateMachineInfo {
|
||||
@ -257,18 +262,14 @@ class AnimationNodeStateMachinePlayback : public Resource {
|
||||
Ref<AnimationNodeStateMachineTransition> default_transition;
|
||||
String base_path;
|
||||
|
||||
double len_fade_from = 0.0;
|
||||
double pos_fade_from = 0.0;
|
||||
|
||||
double len_current = 0.0;
|
||||
double pos_current = 0.0;
|
||||
|
||||
AnimationNode::NodeTimeInfo current_nti;
|
||||
StringName current;
|
||||
Ref<Curve> current_curve;
|
||||
|
||||
Ref<AnimationNodeStateMachineTransition> group_start_transition;
|
||||
Ref<AnimationNodeStateMachineTransition> group_end_transition;
|
||||
|
||||
AnimationNode::NodeTimeInfo fadeing_from_nti;
|
||||
StringName fading_from;
|
||||
float fading_time = 0.0;
|
||||
float fading_pos = 0.0;
|
||||
@ -301,8 +302,8 @@ class AnimationNodeStateMachinePlayback : public Resource {
|
||||
bool _travel_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const String &p_path, bool p_is_allow_transition_to_self, bool p_is_parent_same_state, bool p_test_only);
|
||||
void _start_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const String &p_path, bool p_test_only);
|
||||
|
||||
double process(const String &p_base_path, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only);
|
||||
double _process(const String &p_base_path, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only);
|
||||
AnimationNode::NodeTimeInfo process(const String &p_base_path, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only);
|
||||
AnimationNode::NodeTimeInfo _process(const String &p_base_path, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only);
|
||||
|
||||
bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
|
||||
bool _transition_to_next_recursive(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_test_only);
|
||||
|
@ -46,6 +46,10 @@ void AnimationNode::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
r_list->push_back(PropertyInfo::from_dict(d));
|
||||
}
|
||||
}
|
||||
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, current_length, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_READ_ONLY));
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, current_position, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_READ_ONLY));
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, current_delta, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_READ_ONLY));
|
||||
}
|
||||
|
||||
Variant AnimationNode::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
@ -56,8 +60,15 @@ Variant AnimationNode::get_parameter_default_value(const StringName &p_parameter
|
||||
|
||||
bool AnimationNode::is_parameter_read_only(const StringName &p_parameter) const {
|
||||
bool ret = false;
|
||||
GDVIRTUAL_CALL(_is_parameter_read_only, p_parameter, ret);
|
||||
return ret;
|
||||
if (GDVIRTUAL_CALL(_is_parameter_read_only, p_parameter, ret) && ret) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (p_parameter == current_length || p_parameter == current_position || p_parameter == current_delta) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AnimationNode::set_parameter(const StringName &p_name, const Variant &p_value) {
|
||||
@ -81,6 +92,20 @@ Variant AnimationNode::get_parameter(const StringName &p_name) const {
|
||||
return process_state->tree->property_map[path].first;
|
||||
}
|
||||
|
||||
void AnimationNode::set_node_time_info(const NodeTimeInfo &p_node_time_info) {
|
||||
set_parameter(current_length, p_node_time_info.length);
|
||||
set_parameter(current_position, p_node_time_info.position);
|
||||
set_parameter(current_delta, p_node_time_info.delta);
|
||||
}
|
||||
|
||||
AnimationNode::NodeTimeInfo AnimationNode::get_node_time_info() const {
|
||||
NodeTimeInfo nti;
|
||||
nti.length = get_parameter(current_length);
|
||||
nti.position = get_parameter(current_position);
|
||||
nti.delta = get_parameter(current_delta);
|
||||
return nti;
|
||||
}
|
||||
|
||||
void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) {
|
||||
Dictionary cn;
|
||||
if (GDVIRTUAL_CALL(_get_child_nodes, cn)) {
|
||||
@ -101,11 +126,11 @@ void AnimationNode::blend_animation(const StringName &p_animation, AnimationMixe
|
||||
process_state->tree->make_animation_instance(p_animation, p_playback_info);
|
||||
}
|
||||
|
||||
double AnimationNode::_pre_process(ProcessState *p_process_state, AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNode::_pre_process(ProcessState *p_process_state, AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
process_state = p_process_state;
|
||||
double t = process(p_playback_info, p_test_only);
|
||||
NodeTimeInfo nti = process(p_playback_info, p_test_only);
|
||||
process_state = nullptr;
|
||||
return t;
|
||||
return nti;
|
||||
}
|
||||
|
||||
void AnimationNode::make_invalid(const String &p_reason) {
|
||||
@ -122,11 +147,11 @@ AnimationTree *AnimationNode::get_animation_tree() const {
|
||||
return process_state->tree;
|
||||
}
|
||||
|
||||
double AnimationNode::blend_input(int p_input, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only) {
|
||||
ERR_FAIL_INDEX_V(p_input, inputs.size(), 0);
|
||||
AnimationNode::NodeTimeInfo AnimationNode::blend_input(int p_input, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only) {
|
||||
ERR_FAIL_INDEX_V(p_input, inputs.size(), NodeTimeInfo());
|
||||
|
||||
AnimationNodeBlendTree *blend_tree = Object::cast_to<AnimationNodeBlendTree>(node_state.parent);
|
||||
ERR_FAIL_NULL_V(blend_tree, 0);
|
||||
ERR_FAIL_NULL_V(blend_tree, NodeTimeInfo());
|
||||
|
||||
// Update connections.
|
||||
StringName current_name = blend_tree->get_node_name(Ref<AnimationNode>(this));
|
||||
@ -136,32 +161,31 @@ double AnimationNode::blend_input(int p_input, AnimationMixer::PlaybackInfo p_pl
|
||||
StringName node_name = node_state.connections[p_input];
|
||||
if (!blend_tree->has_node(node_name)) {
|
||||
make_invalid(vformat(RTR("Nothing connected to input '%s' of node '%s'."), get_input_name(p_input), current_name));
|
||||
return 0;
|
||||
return NodeTimeInfo();
|
||||
}
|
||||
|
||||
Ref<AnimationNode> node = blend_tree->get_node(node_name);
|
||||
ERR_FAIL_COND_V(node.is_null(), 0);
|
||||
ERR_FAIL_COND_V(node.is_null(), NodeTimeInfo());
|
||||
|
||||
real_t activity = 0.0;
|
||||
Vector<AnimationTree::Activity> *activity_ptr = process_state->tree->input_activity_map.getptr(node_state.base_path);
|
||||
double ret = _blend_node(node, node_name, nullptr, p_playback_info, p_filter, p_sync, p_test_only, &activity);
|
||||
NodeTimeInfo nti = _blend_node(node, node_name, nullptr, p_playback_info, p_filter, p_sync, p_test_only, &activity);
|
||||
|
||||
if (activity_ptr && p_input < activity_ptr->size()) {
|
||||
activity_ptr->write[p_input].last_pass = process_state->last_pass;
|
||||
activity_ptr->write[p_input].activity = activity;
|
||||
}
|
||||
return ret;
|
||||
return nti;
|
||||
}
|
||||
|
||||
double AnimationNode::blend_node(Ref<AnimationNode> p_node, const StringName &p_subpath, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only) {
|
||||
ERR_FAIL_COND_V(p_node.is_null(), 0);
|
||||
|
||||
AnimationNode::NodeTimeInfo AnimationNode::blend_node(Ref<AnimationNode> p_node, const StringName &p_subpath, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only) {
|
||||
ERR_FAIL_COND_V(p_node.is_null(), NodeTimeInfo());
|
||||
p_node->node_state.connections.clear();
|
||||
return _blend_node(p_node, p_subpath, this, p_playback_info, p_filter, p_sync, p_test_only, nullptr);
|
||||
}
|
||||
|
||||
double AnimationNode::_blend_node(Ref<AnimationNode> p_node, const StringName &p_subpath, AnimationNode *p_new_parent, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only, real_t *r_activity) {
|
||||
ERR_FAIL_NULL_V(process_state, 0);
|
||||
AnimationNode::NodeTimeInfo AnimationNode::_blend_node(Ref<AnimationNode> p_node, const StringName &p_subpath, AnimationNode *p_new_parent, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only, real_t *r_activity) {
|
||||
ERR_FAIL_NULL_V(process_state, NodeTimeInfo());
|
||||
|
||||
int blend_count = node_state.track_weights.size();
|
||||
|
||||
@ -261,7 +285,7 @@ double AnimationNode::_blend_node(Ref<AnimationNode> p_node, const StringName &p
|
||||
new_parent = p_new_parent;
|
||||
new_path = String(node_state.base_path) + String(p_subpath) + "/";
|
||||
} else {
|
||||
ERR_FAIL_NULL_V(node_state.parent, 0);
|
||||
ERR_FAIL_NULL_V(node_state.parent, NodeTimeInfo());
|
||||
new_parent = node_state.parent;
|
||||
new_path = String(new_parent->node_state.base_path) + String(p_subpath) + "/";
|
||||
}
|
||||
@ -271,7 +295,7 @@ double AnimationNode::_blend_node(Ref<AnimationNode> p_node, const StringName &p
|
||||
p_node->node_state.base_path = new_path;
|
||||
p_node->node_state.parent = new_parent;
|
||||
if (!p_playback_info.seeked && !p_sync && !any_valid) {
|
||||
p_playback_info.time = 0.0;
|
||||
p_playback_info.delta = 0.0;
|
||||
return p_node->_pre_process(process_state, p_playback_info, p_test_only);
|
||||
}
|
||||
return p_node->_pre_process(process_state, p_playback_info, p_test_only);
|
||||
@ -328,15 +352,31 @@ int AnimationNode::find_input(const String &p_name) const {
|
||||
return idx;
|
||||
}
|
||||
|
||||
double AnimationNode::process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
AnimationNode::NodeTimeInfo AnimationNode::process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
process_state->is_testing = p_test_only;
|
||||
return _process(p_playback_info, p_test_only);
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
if (p_playback_info.seeked) {
|
||||
pi.delta = get_node_time_info().position - p_playback_info.time;
|
||||
} else {
|
||||
pi.time = get_node_time_info().position + p_playback_info.delta;
|
||||
}
|
||||
|
||||
NodeTimeInfo nti = _process(pi, p_test_only);
|
||||
|
||||
if (!p_test_only) {
|
||||
set_node_time_info(nti);
|
||||
}
|
||||
|
||||
return nti;
|
||||
}
|
||||
|
||||
double AnimationNode::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
double ret = 0;
|
||||
GDVIRTUAL_CALL(_process, p_playback_info.time, p_playback_info.seeked, p_playback_info.is_external_seeking, p_test_only, ret);
|
||||
return ret;
|
||||
AnimationNode::NodeTimeInfo AnimationNode::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
double r_ret = 0.0;
|
||||
GDVIRTUAL_CALL(_process, p_playback_info.time, p_playback_info.seeked, p_playback_info.is_external_seeking, p_test_only, r_ret);
|
||||
NodeTimeInfo nti;
|
||||
nti.delta = r_ret;
|
||||
return nti;
|
||||
}
|
||||
|
||||
void AnimationNode::set_filter_path(const NodePath &p_path, bool p_enable) {
|
||||
@ -432,7 +472,8 @@ double AnimationNode::blend_node_ex(const StringName &p_sub_path, Ref<AnimationN
|
||||
info.seeked = p_seek;
|
||||
info.is_external_seeking = p_is_external_seeking;
|
||||
info.weight = p_blend;
|
||||
return blend_node(p_node, p_sub_path, info, p_filter, p_sync, p_test_only);
|
||||
NodeTimeInfo nti = blend_node(p_node, p_sub_path, info, p_filter, p_sync, p_test_only);
|
||||
return nti.length - nti.position;
|
||||
}
|
||||
|
||||
double AnimationNode::blend_input_ex(int p_input, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter, bool p_sync, bool p_test_only) {
|
||||
@ -441,7 +482,8 @@ double AnimationNode::blend_input_ex(int p_input, double p_time, bool p_seek, bo
|
||||
info.seeked = p_seek;
|
||||
info.is_external_seeking = p_is_external_seeking;
|
||||
info.weight = p_blend;
|
||||
return blend_input(p_input, info, p_filter, p_sync, p_test_only);
|
||||
NodeTimeInfo nti = blend_input(p_input, info, p_filter, p_sync, p_test_only);
|
||||
return nti.length - nti.position;
|
||||
}
|
||||
|
||||
void AnimationNode::_bind_methods() {
|
||||
@ -568,11 +610,12 @@ bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const
|
||||
if (started) {
|
||||
// If started, seek.
|
||||
pi.seeked = true;
|
||||
pi.delta = p_delta;
|
||||
root_animation_node->_pre_process(&process_state, pi, false);
|
||||
started = false;
|
||||
} else {
|
||||
pi.seeked = false;
|
||||
pi.time = p_delta;
|
||||
pi.delta = p_delta;
|
||||
root_animation_node->_pre_process(&process_state, pi, false);
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,34 @@ public:
|
||||
HashMap<NodePath, bool> filter;
|
||||
bool filter_enabled = false;
|
||||
|
||||
// To propagate information from upstream for use in estimation of playback progress.
|
||||
// These values must be taken from the result of blend_node() or blend_input() and must be essentially read-only.
|
||||
// For example, if you want to change the position, you need to change the pi.time value of PlaybackInfo passed to blend_input(pi) and get the result.
|
||||
struct NodeTimeInfo {
|
||||
// Retain the previous frame values. These are stored into the AnimationTree's Map and exposing them as read-only values.
|
||||
double length = 0.0;
|
||||
double position = 0.0;
|
||||
double delta = 0.0;
|
||||
|
||||
// Needs internally to estimate remain time, the previous frame values are not retained.
|
||||
Animation::LoopMode loop_mode = Animation::LOOP_NONE;
|
||||
bool is_just_looped = false; // For breaking loop, it is true when just looped.
|
||||
bool is_infinity = false; // For unpredictable state machine's end.
|
||||
|
||||
bool is_looping() {
|
||||
return loop_mode != Animation::LOOP_NONE;
|
||||
}
|
||||
double get_remain(bool p_break_loop = false) {
|
||||
if ((is_looping() && !p_break_loop) || is_infinity) {
|
||||
return HUGE_LENGTH;
|
||||
}
|
||||
if (p_break_loop && is_just_looped) {
|
||||
return 0;
|
||||
}
|
||||
return length - position;
|
||||
}
|
||||
};
|
||||
|
||||
// Temporary state for blending process which needs to be stored in each AnimationNodes.
|
||||
struct NodeState {
|
||||
StringName base_path;
|
||||
@ -84,16 +112,23 @@ public:
|
||||
Array _get_filters() const;
|
||||
void _set_filters(const Array &p_filters);
|
||||
friend class AnimationNodeBlendTree;
|
||||
double _blend_node(Ref<AnimationNode> p_node, const StringName &p_subpath, AnimationNode *p_new_parent, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false, real_t *r_activity = nullptr);
|
||||
double _pre_process(ProcessState *p_process_state, AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false);
|
||||
|
||||
// The time information is passed from upstream to downstream by AnimationMixer::PlaybackInfo::p_playback_info until AnimationNodeAnimation processes it.
|
||||
// Conversely, AnimationNodeAnimation returns the processed result as NodeTimeInfo from downstream to upstream.
|
||||
NodeTimeInfo _blend_node(Ref<AnimationNode> p_node, const StringName &p_subpath, AnimationNode *p_new_parent, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false, real_t *r_activity = nullptr);
|
||||
NodeTimeInfo _pre_process(ProcessState *p_process_state, AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false);
|
||||
|
||||
protected:
|
||||
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false);
|
||||
double process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false);
|
||||
StringName current_length = "current_length";
|
||||
StringName current_position = "current_position";
|
||||
StringName current_delta = "current_delta";
|
||||
|
||||
virtual NodeTimeInfo process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false); // To organize time information. Virtualizing for especially AnimationNodeAnimation needs to take "backward" into account.
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false); // Main process.
|
||||
|
||||
void blend_animation(const StringName &p_animation, AnimationMixer::PlaybackInfo p_playback_info);
|
||||
double blend_node(Ref<AnimationNode> p_node, const StringName &p_subpath, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false);
|
||||
double blend_input(int p_input, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false);
|
||||
NodeTimeInfo blend_node(Ref<AnimationNode> p_node, const StringName &p_subpath, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false);
|
||||
NodeTimeInfo blend_input(int p_input, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false);
|
||||
|
||||
// Bind-able methods to expose for compatibility, moreover AnimationMixer::PlaybackInfo is not exposed.
|
||||
void blend_animation_ex(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_is_external_seeking, real_t p_blend, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE);
|
||||
@ -124,6 +159,9 @@ public:
|
||||
void set_parameter(const StringName &p_name, const Variant &p_value);
|
||||
Variant get_parameter(const StringName &p_name) const;
|
||||
|
||||
void set_node_time_info(const NodeTimeInfo &p_node_time_info); // Wrapper of set_parameter().
|
||||
virtual NodeTimeInfo get_node_time_info() const; // Wrapper of get_parameter().
|
||||
|
||||
struct ChildNode {
|
||||
StringName name;
|
||||
Ref<AnimationNode> node;
|
||||
|
@ -75,6 +75,7 @@ public:
|
||||
LOOP_PINGPONG,
|
||||
};
|
||||
|
||||
// LoopedFlag is used in Animataion to "process the keys at both ends correct".
|
||||
enum LoopedFlag {
|
||||
LOOPED_FLAG_NONE,
|
||||
LOOPED_FLAG_END,
|
||||
@ -187,6 +188,7 @@ private:
|
||||
};
|
||||
|
||||
/* BEZIER TRACK */
|
||||
|
||||
struct BezierKey {
|
||||
Vector2 in_handle; // Relative (x always <0)
|
||||
Vector2 out_handle; // Relative (x always >0)
|
||||
@ -223,7 +225,7 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
/* AUDIO TRACK */
|
||||
/* ANIMATION TRACK */
|
||||
|
||||
struct AnimationTrack : public Track {
|
||||
Vector<TKey<StringName>> values;
|
||||
|
Loading…
Reference in New Issue
Block a user