feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -374,21 +374,20 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationM
|
|||
}
|
||||
|
||||
if (new_closest != cur_closest && new_closest != -1) {
|
||||
NodeTimeInfo from;
|
||||
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) {
|
||||
//for ping-pong loop
|
||||
NodeTimeInfo from;
|
||||
// For ping-pong loop.
|
||||
Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node);
|
||||
Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node);
|
||||
if (!na_c.is_null() && !na_n.is_null()) {
|
||||
if (na_c.is_valid() && na_n.is_valid()) {
|
||||
na_n->set_backward(na_c->is_backward());
|
||||
}
|
||||
//see how much animation remains
|
||||
// See how much animation remains.
|
||||
pi.seeked = false;
|
||||
pi.weight = 0;
|
||||
from = 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, true);
|
||||
pi.time = from.position;
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ void AnimationNodeBlendSpace2D::_set_triangles(const Vector<int> &p_triangles) {
|
|||
|
||||
Vector<int> AnimationNodeBlendSpace2D::_get_triangles() const {
|
||||
Vector<int> t;
|
||||
if (auto_triangles && trianges_dirty) {
|
||||
if (auto_triangles && triangles_dirty) {
|
||||
return t;
|
||||
}
|
||||
|
||||
|
|
@ -330,20 +330,20 @@ Vector<int> AnimationNodeBlendSpace2D::_get_triangles() const {
|
|||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::_queue_auto_triangles() {
|
||||
if (!auto_triangles || trianges_dirty) {
|
||||
if (!auto_triangles || triangles_dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
trianges_dirty = true;
|
||||
triangles_dirty = true;
|
||||
callable_mp(this, &AnimationNodeBlendSpace2D::_update_triangles).call_deferred();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::_update_triangles() {
|
||||
if (!auto_triangles || !trianges_dirty) {
|
||||
if (!auto_triangles || !triangles_dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
trianges_dirty = false;
|
||||
triangles_dirty = false;
|
||||
triangles.clear();
|
||||
if (blend_points_used < 3) {
|
||||
emit_signal(SNAME("triangles_updated"));
|
||||
|
|
@ -551,21 +551,20 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationM
|
|||
}
|
||||
|
||||
if (new_closest != cur_closest && new_closest != -1) {
|
||||
NodeTimeInfo from;
|
||||
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) {
|
||||
//for ping-pong loop
|
||||
NodeTimeInfo from;
|
||||
// For ping-pong loop.
|
||||
Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node);
|
||||
Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node);
|
||||
if (!na_c.is_null() && !na_n.is_null()) {
|
||||
if (na_c.is_valid() && na_n.is_valid()) {
|
||||
na_n->set_backward(na_c->is_backward());
|
||||
}
|
||||
//see how much animation remains
|
||||
// See how much animation remains.
|
||||
pi.seeked = false;
|
||||
pi.weight = 0;
|
||||
from = 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, true);
|
||||
pi.time = from.position;
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ protected:
|
|||
void _blend_triangle(const Vector2 &p_pos, const Vector2 *p_points, float *r_weights);
|
||||
|
||||
bool auto_triangles = true;
|
||||
bool trianges_dirty = false;
|
||||
bool triangles_dirty = false;
|
||||
|
||||
void _update_triangles();
|
||||
void _queue_auto_triangles();
|
||||
|
|
|
|||
|
|
@ -44,6 +44,18 @@ Vector<String> (*AnimationNodeAnimation::get_editable_animation_list)() = nullpt
|
|||
|
||||
void AnimationNodeAnimation::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
r_list->push_back(PropertyInfo(Variant::BOOL, backward, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
}
|
||||
|
||||
Variant AnimationNodeAnimation::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 == backward) {
|
||||
return false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
AnimationNode::NodeTimeInfo AnimationNodeAnimation::get_node_time_info() const {
|
||||
|
|
@ -93,9 +105,11 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::process(const AnimationMixer
|
|||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
if (p_playback_info.seeked) {
|
||||
pi.delta = get_node_time_info().position - p_playback_info.time;
|
||||
if (p_playback_info.is_external_seeking) {
|
||||
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);
|
||||
pi.time = get_node_time_info().position + (get_parameter(backward) ? -p_playback_info.delta : p_playback_info.delta);
|
||||
}
|
||||
|
||||
NodeTimeInfo nti = _process(pi, p_test_only);
|
||||
|
|
@ -128,6 +142,7 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe
|
|||
double cur_len = cur_nti.length;
|
||||
double cur_time = p_playback_info.time;
|
||||
double cur_delta = p_playback_info.delta;
|
||||
bool cur_backward = get_parameter(backward);
|
||||
|
||||
Animation::LoopMode cur_loop_mode = cur_nti.loop_mode;
|
||||
double prev_time = cur_nti.position;
|
||||
|
|
@ -140,18 +155,24 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe
|
|||
|
||||
// 1. Progress for AnimationNode.
|
||||
bool will_end = Animation::is_greater_or_equal_approx(cur_time + cur_delta, cur_len);
|
||||
bool is_started = p_seek && !p_is_external_seeking && Math::is_zero_approx(cur_time);
|
||||
|
||||
// 1. Progress for AnimationNode.
|
||||
if (is_started && advance_on_start) {
|
||||
cur_time = cur_delta;
|
||||
}
|
||||
if (cur_loop_mode != Animation::LOOP_NONE) {
|
||||
if (cur_loop_mode == Animation::LOOP_LINEAR) {
|
||||
if (!Math::is_zero_approx(cur_len)) {
|
||||
cur_time = Math::fposmod(cur_time, cur_len);
|
||||
}
|
||||
backward = false;
|
||||
cur_backward = false;
|
||||
} else {
|
||||
if (!Math::is_zero_approx(cur_len)) {
|
||||
if (Animation::is_greater_or_equal_approx(prev_time, 0) && Animation::is_less_approx(cur_time, 0)) {
|
||||
backward = !backward;
|
||||
cur_backward = !cur_backward;
|
||||
} else if (Animation::is_less_or_equal_approx(prev_time, cur_len) && Animation::is_greater_approx(cur_time, cur_len)) {
|
||||
backward = !backward;
|
||||
cur_backward = !cur_backward;
|
||||
}
|
||||
cur_time = Math::pingpong(cur_time, cur_len);
|
||||
}
|
||||
|
|
@ -164,7 +185,7 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe
|
|||
cur_delta += cur_time - cur_len;
|
||||
cur_time = cur_len;
|
||||
}
|
||||
backward = false;
|
||||
cur_backward = false;
|
||||
// If ended, don't progress AnimationNode. So set delta to 0.
|
||||
if (!Math::is_zero_approx(cur_delta)) {
|
||||
if (play_mode == PLAY_MODE_FORWARD) {
|
||||
|
|
@ -232,7 +253,7 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe
|
|||
// 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 && Math::is_zero_approx(cur_playback_time)) {
|
||||
if (is_started) {
|
||||
process_state->tree->call_deferred(SNAME("emit_signal"), SceneStringName(animation_started), animation);
|
||||
}
|
||||
// Finished.
|
||||
|
|
@ -245,6 +266,8 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe
|
|||
|
||||
if (!p_test_only) {
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
pi.start = 0.0;
|
||||
pi.end = cur_len;
|
||||
if (play_mode == PLAY_MODE_FORWARD) {
|
||||
pi.time = cur_playback_time;
|
||||
pi.delta = cur_delta;
|
||||
|
|
@ -257,6 +280,8 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe
|
|||
blend_animation(animation, pi);
|
||||
}
|
||||
|
||||
set_parameter(backward, cur_backward);
|
||||
|
||||
return nti;
|
||||
}
|
||||
|
||||
|
|
@ -273,11 +298,19 @@ AnimationNodeAnimation::PlayMode AnimationNodeAnimation::get_play_mode() const {
|
|||
}
|
||||
|
||||
void AnimationNodeAnimation::set_backward(bool p_backward) {
|
||||
backward = p_backward;
|
||||
set_parameter(backward, p_backward);
|
||||
}
|
||||
|
||||
bool AnimationNodeAnimation::is_backward() const {
|
||||
return backward;
|
||||
return get_parameter(backward);
|
||||
}
|
||||
|
||||
void AnimationNodeAnimation::set_advance_on_start(bool p_advance_on_start) {
|
||||
advance_on_start = p_advance_on_start;
|
||||
}
|
||||
|
||||
bool AnimationNodeAnimation::is_advance_on_start() const {
|
||||
return advance_on_start;
|
||||
}
|
||||
|
||||
void AnimationNodeAnimation::set_use_custom_timeline(bool p_use_custom_timeline) {
|
||||
|
|
@ -297,8 +330,8 @@ 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;
|
||||
void AnimationNodeAnimation::set_stretch_time_scale(bool p_stretch_time_scale) {
|
||||
stretch_time_scale = p_stretch_time_scale;
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
|
|
@ -329,6 +362,9 @@ 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_advance_on_start", "advance_on_start"), &AnimationNodeAnimation::set_advance_on_start);
|
||||
ClassDB::bind_method(D_METHOD("is_advance_on_start"), &AnimationNodeAnimation::is_advance_on_start);
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -346,6 +382,7 @@ void AnimationNodeAnimation::_bind_methods() {
|
|||
|
||||
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, "advance_on_start"), "set_advance_on_start", "is_advance_on_start");
|
||||
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");
|
||||
|
|
@ -754,9 +791,6 @@ AnimationNode::NodeTimeInfo AnimationNodeAdd2::_process(const AnimationMixer::Pl
|
|||
return nti;
|
||||
}
|
||||
|
||||
void AnimationNodeAdd2::_bind_methods() {
|
||||
}
|
||||
|
||||
AnimationNodeAdd2::AnimationNodeAdd2() {
|
||||
add_input("in");
|
||||
add_input("add");
|
||||
|
|
@ -800,9 +834,6 @@ AnimationNode::NodeTimeInfo AnimationNodeAdd3::_process(const AnimationMixer::Pl
|
|||
return nti;
|
||||
}
|
||||
|
||||
void AnimationNodeAdd3::_bind_methods() {
|
||||
}
|
||||
|
||||
AnimationNodeAdd3::AnimationNodeAdd3() {
|
||||
add_input("-add");
|
||||
add_input("in");
|
||||
|
|
@ -845,9 +876,6 @@ bool AnimationNodeBlend2::has_filter() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
void AnimationNodeBlend2::_bind_methods() {
|
||||
}
|
||||
|
||||
AnimationNodeBlend2::AnimationNodeBlend2() {
|
||||
add_input("in");
|
||||
add_input("blend");
|
||||
|
|
@ -887,9 +915,6 @@ AnimationNode::NodeTimeInfo AnimationNodeBlend3::_process(const AnimationMixer::
|
|||
return amount > 0.5 ? nti2 : (amount < -0.5 ? nti0 : nti1); // Hacky but good enough.
|
||||
}
|
||||
|
||||
void AnimationNodeBlend3::_bind_methods() {
|
||||
}
|
||||
|
||||
AnimationNodeBlend3::AnimationNodeBlend3() {
|
||||
add_input("-blend");
|
||||
add_input("in");
|
||||
|
|
@ -932,9 +957,6 @@ AnimationNode::NodeTimeInfo AnimationNodeSub2::_process(const AnimationMixer::Pl
|
|||
return blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
|
||||
}
|
||||
|
||||
void AnimationNodeSub2::_bind_methods() {
|
||||
}
|
||||
|
||||
AnimationNodeSub2::AnimationNodeSub2() {
|
||||
add_input("in");
|
||||
add_input("sub");
|
||||
|
|
@ -972,9 +994,6 @@ AnimationNode::NodeTimeInfo AnimationNodeTimeScale::_process(const AnimationMixe
|
|||
return blend_input(0, pi, FILTER_IGNORE, true, p_test_only);
|
||||
}
|
||||
|
||||
void AnimationNodeTimeScale::_bind_methods() {
|
||||
}
|
||||
|
||||
AnimationNodeTimeScale::AnimationNodeTimeScale() {
|
||||
add_input("in");
|
||||
}
|
||||
|
|
@ -999,6 +1018,14 @@ String AnimationNodeTimeSeek::get_caption() const {
|
|||
return "TimeSeek";
|
||||
}
|
||||
|
||||
void AnimationNodeTimeSeek::set_explicit_elapse(bool p_enable) {
|
||||
explicit_elapse = p_enable;
|
||||
}
|
||||
|
||||
bool AnimationNodeTimeSeek::is_explicit_elapse() const {
|
||||
return explicit_elapse;
|
||||
}
|
||||
|
||||
AnimationNode::NodeTimeInfo AnimationNodeTimeSeek::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
double cur_seek_pos = get_parameter(seek_pos_request);
|
||||
|
||||
|
|
@ -1007,20 +1034,23 @@ AnimationNode::NodeTimeInfo AnimationNodeTimeSeek::_process(const AnimationMixer
|
|||
if (Animation::is_greater_or_equal_approx(cur_seek_pos, 0)) {
|
||||
pi.time = cur_seek_pos;
|
||||
pi.seeked = true;
|
||||
pi.is_external_seeking = true;
|
||||
pi.is_external_seeking = explicit_elapse;
|
||||
set_parameter(seek_pos_request, -1.0); // Reset.
|
||||
}
|
||||
|
||||
return blend_input(0, pi, FILTER_IGNORE, true, p_test_only);
|
||||
}
|
||||
|
||||
void AnimationNodeTimeSeek::_bind_methods() {
|
||||
}
|
||||
|
||||
AnimationNodeTimeSeek::AnimationNodeTimeSeek() {
|
||||
add_input("in");
|
||||
}
|
||||
|
||||
void AnimationNodeTimeSeek::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_explicit_elapse", "enable"), &AnimationNodeTimeSeek::set_explicit_elapse);
|
||||
ClassDB::bind_method(D_METHOD("is_explicit_elapse"), &AnimationNodeTimeSeek::is_explicit_elapse);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "explicit_elapse"), "set_explicit_elapse", "is_explicit_elapse");
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
bool AnimationNodeTransition::_set(const StringName &p_path, const Variant &p_value) {
|
||||
|
|
@ -1160,12 +1190,16 @@ void AnimationNodeTransition::remove_input(int p_index) {
|
|||
|
||||
bool AnimationNodeTransition::set_input_name(int p_input, const String &p_name) {
|
||||
pending_update = true;
|
||||
return AnimationNode::set_input_name(p_input, p_name);
|
||||
if (!AnimationNode::set_input_name(p_input, p_name)) {
|
||||
return false;
|
||||
}
|
||||
emit_signal(SNAME("tree_changed")); // For updating enum options.
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnimationNodeTransition::set_input_as_auto_advance(int p_input, bool p_enable) {
|
||||
ERR_FAIL_INDEX(p_input, get_input_count());
|
||||
input_data.write[p_input].auto_advance = p_enable;
|
||||
input_data[p_input].auto_advance = p_enable;
|
||||
}
|
||||
|
||||
bool AnimationNodeTransition::is_input_set_as_auto_advance(int p_input) const {
|
||||
|
|
@ -1175,7 +1209,7 @@ bool AnimationNodeTransition::is_input_set_as_auto_advance(int p_input) const {
|
|||
|
||||
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;
|
||||
input_data[p_input].break_loop_at_end = p_enable;
|
||||
}
|
||||
|
||||
bool AnimationNodeTransition::is_input_loop_broken_at_end(int p_input) const {
|
||||
|
|
@ -1185,7 +1219,7 @@ bool AnimationNodeTransition::is_input_loop_broken_at_end(int p_input) const {
|
|||
|
||||
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;
|
||||
input_data[p_input].reset = p_enable;
|
||||
}
|
||||
|
||||
bool AnimationNodeTransition::is_input_reset(int p_input) const {
|
||||
|
|
@ -1418,7 +1452,7 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNod
|
|||
ERR_FAIL_COND(nodes.has(p_name));
|
||||
ERR_FAIL_COND(p_node.is_null());
|
||||
ERR_FAIL_COND(p_name == SceneStringName(output));
|
||||
ERR_FAIL_COND(String(p_name).contains("/"));
|
||||
ERR_FAIL_COND(String(p_name).contains_char('/'));
|
||||
|
||||
Node n;
|
||||
n.node = p_node;
|
||||
|
|
|
|||
|
|
@ -36,8 +36,12 @@
|
|||
class AnimationNodeAnimation : public AnimationRootNode {
|
||||
GDCLASS(AnimationNodeAnimation, AnimationRootNode);
|
||||
|
||||
StringName backward = "backward"; // Only used by pingpong animation.
|
||||
|
||||
StringName animation;
|
||||
|
||||
bool advance_on_start = false;
|
||||
|
||||
bool use_custom_timeline = false;
|
||||
double timeline_length = 1.0;
|
||||
Animation::LoopMode loop_mode = Animation::LOOP_NONE;
|
||||
|
|
@ -54,6 +58,7 @@ public:
|
|||
};
|
||||
|
||||
void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
||||
virtual NodeTimeInfo get_node_time_info() const override; // Wrapper of get_parameter().
|
||||
|
||||
|
|
@ -72,13 +77,16 @@ public:
|
|||
void set_backward(bool p_backward);
|
||||
bool is_backward() const;
|
||||
|
||||
void set_advance_on_start(bool p_advance_on_start);
|
||||
bool is_advance_on_start() 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);
|
||||
void set_stretch_time_scale(bool p_stretch_time_scale);
|
||||
bool is_stretching_time_scale() const;
|
||||
|
||||
void set_start_offset(double p_offset);
|
||||
|
|
@ -95,7 +103,6 @@ protected:
|
|||
|
||||
private:
|
||||
PlayMode play_mode = PLAY_MODE_FORWARD;
|
||||
bool backward = false; // Only used by pingpong animation.
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationNodeAnimation::PlayMode)
|
||||
|
|
@ -200,9 +207,6 @@ class AnimationNodeAdd2 : public AnimationNodeSync {
|
|||
|
||||
StringName add_amount = PNAME("add_amount");
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
|
@ -220,9 +224,6 @@ class AnimationNodeAdd3 : public AnimationNodeSync {
|
|||
|
||||
StringName add_amount = PNAME("add_amount");
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
|
@ -240,9 +241,6 @@ class AnimationNodeBlend2 : public AnimationNodeSync {
|
|||
|
||||
StringName blend_amount = PNAME("blend_amount");
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
|
@ -259,9 +257,6 @@ class AnimationNodeBlend3 : public AnimationNodeSync {
|
|||
|
||||
StringName blend_amount = PNAME("blend_amount");
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
|
@ -277,9 +272,6 @@ class AnimationNodeSub2 : public AnimationNodeSync {
|
|||
|
||||
StringName sub_amount = PNAME("sub_amount");
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
|
@ -297,9 +289,6 @@ class AnimationNodeTimeScale : public AnimationNode {
|
|||
|
||||
StringName scale = PNAME("scale");
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
|
@ -315,6 +304,7 @@ class AnimationNodeTimeSeek : public AnimationNode {
|
|||
GDCLASS(AnimationNodeTimeSeek, AnimationNode);
|
||||
|
||||
StringName seek_pos_request = PNAME("seek_request");
|
||||
bool explicit_elapse = true;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
|
@ -327,6 +317,9 @@ public:
|
|||
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
void set_explicit_elapse(bool p_enable);
|
||||
bool is_explicit_elapse() const;
|
||||
|
||||
AnimationNodeTimeSeek();
|
||||
};
|
||||
|
||||
|
|
@ -338,7 +331,7 @@ class AnimationNodeTransition : public AnimationNodeSync {
|
|||
bool break_loop_at_end = false;
|
||||
bool reset = true;
|
||||
};
|
||||
Vector<InputData> input_data;
|
||||
LocalVector<InputData> input_data;
|
||||
|
||||
StringName prev_xfading = "prev_xfading";
|
||||
StringName prev_index = "prev_index";
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include "core/config/engine.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/string/string_name.h"
|
||||
#include "scene/2d/audio_stream_player_2d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/audio/audio_stream_player.h"
|
||||
|
|
@ -45,11 +46,9 @@
|
|||
#include "scene/3d/mesh_instance_3d.h"
|
||||
#include "scene/3d/node_3d.h"
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
#include "scene/3d/skeleton_modifier_3d.h"
|
||||
#endif // _3D_DISABLED
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
|
|
@ -81,9 +80,8 @@ bool AnimationMixer::_set(const StringName &p_name, const Variant &p_value) {
|
|||
List<Variant> keys;
|
||||
d.get_key_list(&keys);
|
||||
for (const Variant &K : keys) {
|
||||
StringName lib_name = K;
|
||||
Ref<AnimationLibrary> lib = d[lib_name];
|
||||
add_animation_library(lib_name, lib);
|
||||
Ref<AnimationLibrary> lib = d[K];
|
||||
add_animation_library(K, lib);
|
||||
}
|
||||
emit_signal(SNAME("animation_libraries_updated"));
|
||||
|
||||
|
|
@ -128,6 +126,9 @@ void AnimationMixer::_validate_property(PropertyInfo &p_property) const {
|
|||
p_property.usage |= PROPERTY_USAGE_READ_ONLY;
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
if (root_motion_track.is_empty() && p_property.name == "root_motion_local") {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
|
@ -268,6 +269,16 @@ bool AnimationMixer::has_animation_library(const StringName &p_name) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
StringName AnimationMixer::get_animation_library_name(const Ref<AnimationLibrary> &p_animation_library) const {
|
||||
ERR_FAIL_COND_V(p_animation_library.is_null(), StringName());
|
||||
for (const AnimationLibraryData &lib : animation_libraries) {
|
||||
if (lib.library == p_animation_library) {
|
||||
return lib.name;
|
||||
}
|
||||
}
|
||||
return StringName();
|
||||
}
|
||||
|
||||
StringName AnimationMixer::find_animation_library(const Ref<Animation> &p_animation) const {
|
||||
for (const KeyValue<StringName, AnimationData> &E : animation_set) {
|
||||
if (E.value.animation == p_animation) {
|
||||
|
|
@ -280,7 +291,7 @@ StringName AnimationMixer::find_animation_library(const Ref<Animation> &p_animat
|
|||
Error AnimationMixer::add_animation_library(const StringName &p_name, const Ref<AnimationLibrary> &p_animation_library) {
|
||||
ERR_FAIL_COND_V(p_animation_library.is_null(), ERR_INVALID_PARAMETER);
|
||||
#ifdef DEBUG_ENABLED
|
||||
ERR_FAIL_COND_V_MSG(String(p_name).contains("/") || String(p_name).contains(":") || String(p_name).contains(",") || String(p_name).contains("["), ERR_INVALID_PARAMETER, "Invalid animation name: " + String(p_name) + ".");
|
||||
ERR_FAIL_COND_V_MSG(String(p_name).contains_char('/') || String(p_name).contains_char(':') || String(p_name).contains_char(',') || String(p_name).contains_char('['), ERR_INVALID_PARAMETER, "Invalid animation name: " + String(p_name) + ".");
|
||||
#endif
|
||||
|
||||
int insert_pos = 0;
|
||||
|
|
@ -342,7 +353,7 @@ void AnimationMixer::rename_animation_library(const StringName &p_name, const St
|
|||
return;
|
||||
}
|
||||
#ifdef DEBUG_ENABLED
|
||||
ERR_FAIL_COND_MSG(String(p_new_name).contains("/") || String(p_new_name).contains(":") || String(p_new_name).contains(",") || String(p_new_name).contains("["), "Invalid animation library name: " + String(p_new_name) + ".");
|
||||
ERR_FAIL_COND_MSG(String(p_new_name).contains_char('/') || String(p_new_name).contains_char(':') || String(p_new_name).contains_char(',') || String(p_new_name).contains_char('['), "Invalid animation library name: " + String(p_new_name) + ".");
|
||||
#endif
|
||||
|
||||
bool found = false;
|
||||
|
|
@ -564,6 +575,7 @@ void AnimationMixer::_clear_caches() {
|
|||
memdelete(K.value);
|
||||
}
|
||||
track_cache.clear();
|
||||
animation_track_num_to_track_cache.clear();
|
||||
cache_valid = false;
|
||||
capture_cache.clear();
|
||||
|
||||
|
|
@ -600,6 +612,22 @@ void AnimationMixer::_init_root_motion_cache() {
|
|||
root_motion_scale_accumulator = Vector3(1, 1, 1);
|
||||
}
|
||||
|
||||
void AnimationMixer::_create_track_num_to_track_cache_for_animation(Ref<Animation> &p_animation) {
|
||||
ERR_FAIL_COND(animation_track_num_to_track_cache.has(p_animation));
|
||||
LocalVector<TrackCache *> &track_num_to_track_cache = animation_track_num_to_track_cache.insert_new(p_animation, LocalVector<TrackCache *>())->value;
|
||||
const Vector<Animation::Track *> &tracks = p_animation->get_tracks();
|
||||
|
||||
track_num_to_track_cache.resize(tracks.size());
|
||||
for (int i = 0; i < tracks.size(); i++) {
|
||||
TrackCache **track_ptr = track_cache.getptr(tracks[i]->thash);
|
||||
if (track_ptr == nullptr) {
|
||||
track_num_to_track_cache[i] = nullptr;
|
||||
} else {
|
||||
track_num_to_track_cache[i] = *track_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AnimationMixer::_update_caches() {
|
||||
setup_pass++;
|
||||
|
||||
|
|
@ -717,6 +745,15 @@ bool AnimationMixer::_update_caches() {
|
|||
}
|
||||
}
|
||||
|
||||
if (is_value && callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) {
|
||||
if (child) {
|
||||
PropertyInfo prop_info;
|
||||
ClassDB::get_property_info(child->get_class_name(), path.get_concatenated_subnames(), &prop_info);
|
||||
if (prop_info.hint == PROPERTY_HINT_ONESHOT) {
|
||||
WARN_PRINT_ED(vformat("%s: '%s', Value Track: '%s' is oneshot property, but will be continuously updated. Consider setting a value other than ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS to AnimationMixer.callback_mode_dominant.", mixer_name, String(E), String(path)));
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case Animation::TYPE_POSITION_3D:
|
||||
case Animation::TYPE_ROTATION_3D:
|
||||
|
|
@ -923,6 +960,16 @@ bool AnimationMixer::_update_caches() {
|
|||
idx++;
|
||||
}
|
||||
|
||||
for (KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) {
|
||||
K.value->blend_idx = track_map[K.value->path];
|
||||
}
|
||||
|
||||
animation_track_num_to_track_cache.clear();
|
||||
for (const StringName &E : sname_list) {
|
||||
Ref<Animation> anim = get_animation(E);
|
||||
_create_track_num_to_track_cache_for_animation(anim);
|
||||
}
|
||||
|
||||
track_count = idx;
|
||||
|
||||
cache_valid = true;
|
||||
|
|
@ -947,15 +994,7 @@ void AnimationMixer::_process_animation(double p_delta, bool p_update_only) {
|
|||
clear_animation_instances();
|
||||
}
|
||||
|
||||
Variant AnimationMixer::post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx) {
|
||||
Variant res;
|
||||
if (GDVIRTUAL_CALL(_post_process_key_value, p_anim, p_track, p_value, p_object_id, p_object_sub_idx, res)) {
|
||||
return res;
|
||||
}
|
||||
return _post_process_key_value(p_anim, p_track, p_value, p_object_id, p_object_sub_idx);
|
||||
}
|
||||
|
||||
Variant AnimationMixer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx) {
|
||||
Variant AnimationMixer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant &p_value, ObjectID p_object_id, int p_object_sub_idx) {
|
||||
#ifndef _3D_DISABLED
|
||||
switch (p_anim->track_get_type(p_track)) {
|
||||
case Animation::TYPE_POSITION_3D: {
|
||||
|
|
@ -974,6 +1013,17 @@ Variant AnimationMixer::_post_process_key_value(const Ref<Animation> &p_anim, in
|
|||
return p_value;
|
||||
}
|
||||
|
||||
Variant AnimationMixer::post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx) {
|
||||
if (is_GDVIRTUAL_CALL_post_process_key_value) {
|
||||
Variant res;
|
||||
if (GDVIRTUAL_CALL(_post_process_key_value, p_anim, p_track, p_value, p_object_id, p_object_sub_idx, res)) {
|
||||
return res;
|
||||
}
|
||||
is_GDVIRTUAL_CALL_post_process_key_value = false;
|
||||
}
|
||||
return _post_process_key_value(p_anim, p_track, p_value, p_object_id, p_object_sub_idx);
|
||||
}
|
||||
|
||||
void AnimationMixer::_blend_init() {
|
||||
// Check all tracks, see if they need modification.
|
||||
root_motion_position = Vector3(0, 0, 0);
|
||||
|
|
@ -1031,7 +1081,7 @@ void AnimationMixer::_blend_init() {
|
|||
}
|
||||
}
|
||||
|
||||
bool AnimationMixer::_blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) {
|
||||
bool AnimationMixer::_blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1050,6 +1100,9 @@ void AnimationMixer::blend_capture(double p_delta) {
|
|||
|
||||
capture_cache.remain -= p_delta * capture_cache.step;
|
||||
if (Animation::is_less_or_equal_approx(capture_cache.remain, 0)) {
|
||||
if (capture_cache.animation.is_valid()) {
|
||||
animation_track_num_to_track_cache.erase(capture_cache.animation);
|
||||
}
|
||||
capture_cache.clear();
|
||||
return;
|
||||
}
|
||||
|
|
@ -1080,24 +1133,32 @@ void AnimationMixer::_blend_calc_total_weight() {
|
|||
for (const AnimationInstance &ai : animation_instances) {
|
||||
Ref<Animation> a = ai.animation_data.animation;
|
||||
real_t weight = ai.playback_info.weight;
|
||||
Vector<real_t> track_weights = ai.playback_info.track_weights;
|
||||
Vector<Animation::TypeHash> processed_hashes;
|
||||
for (int i = 0; i < a->get_track_count(); i++) {
|
||||
if (!a->track_is_enabled(i)) {
|
||||
const real_t *track_weights_ptr = ai.playback_info.track_weights.ptr();
|
||||
int track_weights_count = ai.playback_info.track_weights.size();
|
||||
ERR_CONTINUE_EDMSG(!animation_track_num_to_track_cache.has(a), "No animation in cache.");
|
||||
LocalVector<TrackCache *> &track_num_to_track_cache = animation_track_num_to_track_cache[a];
|
||||
thread_local HashSet<Animation::TypeHash, HashHasher> processed_hashes;
|
||||
processed_hashes.clear();
|
||||
const Vector<Animation::Track *> tracks = a->get_tracks();
|
||||
Animation::Track *const *tracks_ptr = tracks.ptr();
|
||||
int count = tracks.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
Animation::Track *animation_track = tracks_ptr[i];
|
||||
if (!animation_track->enabled) {
|
||||
continue;
|
||||
}
|
||||
Animation::TypeHash thash = a->track_get_type_hash(i);
|
||||
if (!track_cache.has(thash) || processed_hashes.has(thash)) {
|
||||
Animation::TypeHash thash = animation_track->thash;
|
||||
TrackCache *track = track_num_to_track_cache[i];
|
||||
if (track == nullptr || processed_hashes.has(thash)) {
|
||||
// No path, but avoid error spamming.
|
||||
// Or, there is the case different track type with same path; These can be distinguished by hash. So don't add the weight doubly.
|
||||
continue;
|
||||
}
|
||||
TrackCache *track = track_cache[thash];
|
||||
int blend_idx = track_map[track->path];
|
||||
int blend_idx = track->blend_idx;
|
||||
ERR_CONTINUE(blend_idx < 0 || blend_idx >= track_count);
|
||||
real_t blend = blend_idx < track_weights.size() ? track_weights[blend_idx] * weight : weight;
|
||||
real_t blend = blend_idx < track_weights_count ? track_weights_ptr[blend_idx] * weight : weight;
|
||||
track->total_weight += blend;
|
||||
processed_hashes.push_back(thash);
|
||||
processed_hashes.insert(thash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1111,30 +1172,37 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
Ref<Animation> a = ai.animation_data.animation;
|
||||
double time = ai.playback_info.time;
|
||||
double delta = ai.playback_info.delta;
|
||||
double start = ai.playback_info.start;
|
||||
double end = ai.playback_info.end;
|
||||
bool seeked = ai.playback_info.seeked;
|
||||
Animation::LoopedFlag looped_flag = ai.playback_info.looped_flag;
|
||||
bool is_external_seeking = ai.playback_info.is_external_seeking;
|
||||
real_t weight = ai.playback_info.weight;
|
||||
Vector<real_t> track_weights = ai.playback_info.track_weights;
|
||||
const real_t *track_weights_ptr = ai.playback_info.track_weights.ptr();
|
||||
int track_weights_count = ai.playback_info.track_weights.size();
|
||||
bool backward = signbit(delta); // This flag is used by the root motion calculates or detecting the end of audio stream.
|
||||
bool seeked_backward = signbit(p_delta);
|
||||
#ifndef _3D_DISABLED
|
||||
bool calc_root = !seeked || is_external_seeking;
|
||||
#endif // _3D_DISABLED
|
||||
|
||||
for (int i = 0; i < a->get_track_count(); i++) {
|
||||
if (!a->track_is_enabled(i)) {
|
||||
ERR_CONTINUE_EDMSG(!animation_track_num_to_track_cache.has(a), "No animation in cache.");
|
||||
LocalVector<TrackCache *> &track_num_to_track_cache = animation_track_num_to_track_cache[a];
|
||||
const Vector<Animation::Track *> tracks = a->get_tracks();
|
||||
Animation::Track *const *tracks_ptr = tracks.ptr();
|
||||
real_t a_length = a->get_length();
|
||||
int count = tracks.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
const Animation::Track *animation_track = tracks_ptr[i];
|
||||
if (!animation_track->enabled) {
|
||||
continue;
|
||||
}
|
||||
Animation::TypeHash thash = a->track_get_type_hash(i);
|
||||
if (!track_cache.has(thash)) {
|
||||
TrackCache *track = track_num_to_track_cache[i];
|
||||
if (track == nullptr) {
|
||||
continue; // No path, but avoid error spamming.
|
||||
}
|
||||
TrackCache *track = track_cache[thash];
|
||||
ERR_CONTINUE(!track_map.has(track->path));
|
||||
int blend_idx = track_map[track->path];
|
||||
int blend_idx = track->blend_idx;
|
||||
ERR_CONTINUE(blend_idx < 0 || blend_idx >= track_count);
|
||||
real_t blend = blend_idx < track_weights.size() ? track_weights[blend_idx] * weight : weight;
|
||||
real_t blend = blend_idx < track_weights_count ? track_weights_ptr[blend_idx] * weight : weight;
|
||||
if (!deterministic) {
|
||||
// If non-deterministic, do normalization.
|
||||
// It would be better to make this if statement outside the for loop, but come here since too much code...
|
||||
|
|
@ -1143,8 +1211,8 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
}
|
||||
blend = blend / track->total_weight;
|
||||
}
|
||||
Animation::TrackType ttype = a->track_get_type(i);
|
||||
track->root_motion = root_motion_track == a->track_get_path(i);
|
||||
Animation::TrackType ttype = animation_track->type;
|
||||
track->root_motion = root_motion_track == animation_track->path;
|
||||
switch (ttype) {
|
||||
case Animation::TYPE_POSITION_3D: {
|
||||
#ifndef _3D_DISABLED
|
||||
|
|
@ -1153,75 +1221,130 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
}
|
||||
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
|
||||
if (track->root_motion && calc_root) {
|
||||
int rot_track = -1;
|
||||
if (root_motion_local) {
|
||||
rot_track = a->find_track(a->track_get_path(i), Animation::TYPE_ROTATION_3D);
|
||||
}
|
||||
double prev_time = time - delta;
|
||||
if (!backward) {
|
||||
if (Animation::is_less_approx(prev_time, 0)) {
|
||||
if (Animation::is_less_approx(prev_time, start)) {
|
||||
switch (a->get_loop_mode()) {
|
||||
case Animation::LOOP_NONE: {
|
||||
prev_time = 0;
|
||||
prev_time = start;
|
||||
} break;
|
||||
case Animation::LOOP_LINEAR: {
|
||||
prev_time = Math::fposmod(prev_time, (double)a->get_length());
|
||||
prev_time = Math::fposmod(prev_time - start, end - start) + start;
|
||||
} break;
|
||||
case Animation::LOOP_PINGPONG: {
|
||||
prev_time = Math::pingpong(prev_time, (double)a->get_length());
|
||||
prev_time = Math::pingpong(prev_time - start, end - start) + start;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Animation::is_greater_approx(prev_time, (double)a->get_length())) {
|
||||
if (Animation::is_greater_approx(prev_time, end)) {
|
||||
switch (a->get_loop_mode()) {
|
||||
case Animation::LOOP_NONE: {
|
||||
prev_time = (double)a->get_length();
|
||||
prev_time = end;
|
||||
} break;
|
||||
case Animation::LOOP_LINEAR: {
|
||||
prev_time = Math::fposmod(prev_time, (double)a->get_length());
|
||||
prev_time = Math::fposmod(prev_time - start, end - start) + start;
|
||||
} break;
|
||||
case Animation::LOOP_PINGPONG: {
|
||||
prev_time = Math::pingpong(prev_time, (double)a->get_length());
|
||||
prev_time = Math::pingpong(prev_time - start, end - start) + start;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vector3 loc[2];
|
||||
if (!backward) {
|
||||
if (Animation::is_greater_approx(prev_time, time)) {
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
if (rot_track >= 0) {
|
||||
Vector3 loc[2];
|
||||
Quaternion rot;
|
||||
if (!backward) {
|
||||
if (Animation::is_greater_approx(prev_time, time)) {
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, end, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
|
||||
a->try_rotation_track_interpolate(rot_track, end, &rot);
|
||||
rot = post_process_key_value(a, rot_track, rot, t->object_id, t->bone_idx);
|
||||
|
||||
root_motion_cache.loc += rot.xform_inv(loc[1] - loc[0]) * blend;
|
||||
prev_time = start;
|
||||
}
|
||||
} else {
|
||||
if (Animation::is_less_approx(prev_time, time)) {
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, start, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
|
||||
a->try_rotation_track_interpolate(rot_track, start, &rot);
|
||||
rot = post_process_key_value(a, rot_track, rot, t->object_id, t->bone_idx);
|
||||
|
||||
root_motion_cache.loc += rot.xform_inv(loc[1] - loc[0]) * blend;
|
||||
prev_time = end;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, (double)a->get_length(), &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
|
||||
prev_time = 0;
|
||||
}
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, time, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
|
||||
a->try_rotation_track_interpolate(rot_track, time, &rot);
|
||||
rot = post_process_key_value(a, rot_track, rot, t->object_id, t->bone_idx);
|
||||
|
||||
root_motion_cache.loc += rot.xform_inv(loc[1] - loc[0]) * blend;
|
||||
prev_time = !backward ? start : end;
|
||||
} else {
|
||||
if (Animation::is_less_approx(prev_time, time)) {
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
Vector3 loc[2];
|
||||
if (!backward) {
|
||||
if (Animation::is_greater_approx(prev_time, time)) {
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, end, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
|
||||
prev_time = start;
|
||||
}
|
||||
} else {
|
||||
if (Animation::is_less_approx(prev_time, time)) {
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, start, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
|
||||
prev_time = end;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, 0, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
|
||||
prev_time = (double)a->get_length();
|
||||
}
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, time, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
|
||||
prev_time = !backward ? start : end;
|
||||
}
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, time, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
|
||||
prev_time = !backward ? 0 : (double)a->get_length();
|
||||
}
|
||||
{
|
||||
Vector3 loc;
|
||||
|
|
@ -1243,32 +1366,32 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
if (track->root_motion && calc_root) {
|
||||
double prev_time = time - delta;
|
||||
if (!backward) {
|
||||
if (Animation::is_less_approx(prev_time, 0)) {
|
||||
if (Animation::is_less_approx(prev_time, start)) {
|
||||
switch (a->get_loop_mode()) {
|
||||
case Animation::LOOP_NONE: {
|
||||
prev_time = 0;
|
||||
prev_time = start;
|
||||
} break;
|
||||
case Animation::LOOP_LINEAR: {
|
||||
prev_time = Math::fposmod(prev_time, (double)a->get_length());
|
||||
prev_time = Math::fposmod(prev_time - start, end - start) + start;
|
||||
} break;
|
||||
case Animation::LOOP_PINGPONG: {
|
||||
prev_time = Math::pingpong(prev_time, (double)a->get_length());
|
||||
prev_time = Math::pingpong(prev_time - start, end - start) + start;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Animation::is_greater_approx(prev_time, (double)a->get_length())) {
|
||||
if (Animation::is_greater_approx(prev_time, end)) {
|
||||
switch (a->get_loop_mode()) {
|
||||
case Animation::LOOP_NONE: {
|
||||
prev_time = (double)a->get_length();
|
||||
prev_time = end;
|
||||
} break;
|
||||
case Animation::LOOP_LINEAR: {
|
||||
prev_time = Math::fposmod(prev_time, (double)a->get_length());
|
||||
prev_time = Math::fposmod(prev_time - start, end - start) + start;
|
||||
} break;
|
||||
case Animation::LOOP_PINGPONG: {
|
||||
prev_time = Math::pingpong(prev_time, (double)a->get_length());
|
||||
prev_time = Math::pingpong(prev_time - start, end - start) + start;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -1283,10 +1406,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
continue;
|
||||
}
|
||||
rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx);
|
||||
a->try_rotation_track_interpolate(i, (double)a->get_length(), &rot[1]);
|
||||
a->try_rotation_track_interpolate(i, end, &rot[1]);
|
||||
rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
|
||||
prev_time = 0;
|
||||
prev_time = start;
|
||||
}
|
||||
} else {
|
||||
if (Animation::is_less_approx(prev_time, time)) {
|
||||
|
|
@ -1295,9 +1418,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
continue;
|
||||
}
|
||||
rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx);
|
||||
a->try_rotation_track_interpolate(i, 0, &rot[1]);
|
||||
a->try_rotation_track_interpolate(i, start, &rot[1]);
|
||||
rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
|
||||
prev_time = (double)a->get_length();
|
||||
prev_time = end;
|
||||
}
|
||||
}
|
||||
Error err = a->try_rotation_track_interpolate(i, prev_time, &rot[0]);
|
||||
|
|
@ -1308,7 +1432,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
a->try_rotation_track_interpolate(i, time, &rot[1]);
|
||||
rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
|
||||
prev_time = !backward ? 0 : (double)a->get_length();
|
||||
prev_time = !backward ? start : end;
|
||||
}
|
||||
{
|
||||
Quaternion rot;
|
||||
|
|
@ -1330,32 +1454,32 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
if (track->root_motion && calc_root) {
|
||||
double prev_time = time - delta;
|
||||
if (!backward) {
|
||||
if (Animation::is_less_approx(prev_time, 0)) {
|
||||
if (Animation::is_less_approx(prev_time, start)) {
|
||||
switch (a->get_loop_mode()) {
|
||||
case Animation::LOOP_NONE: {
|
||||
prev_time = 0;
|
||||
prev_time = start;
|
||||
} break;
|
||||
case Animation::LOOP_LINEAR: {
|
||||
prev_time = Math::fposmod(prev_time, (double)a->get_length());
|
||||
prev_time = Math::fposmod(prev_time - start, end - start) + start;
|
||||
} break;
|
||||
case Animation::LOOP_PINGPONG: {
|
||||
prev_time = Math::pingpong(prev_time, (double)a->get_length());
|
||||
prev_time = Math::pingpong(prev_time - start, end - start) + start;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Animation::is_greater_approx(prev_time, (double)a->get_length())) {
|
||||
if (Animation::is_greater_approx(prev_time, end)) {
|
||||
switch (a->get_loop_mode()) {
|
||||
case Animation::LOOP_NONE: {
|
||||
prev_time = (double)a->get_length();
|
||||
prev_time = end;
|
||||
} break;
|
||||
case Animation::LOOP_LINEAR: {
|
||||
prev_time = Math::fposmod(prev_time, (double)a->get_length());
|
||||
prev_time = Math::fposmod(prev_time - start, end - start) + start;
|
||||
} break;
|
||||
case Animation::LOOP_PINGPONG: {
|
||||
prev_time = Math::pingpong(prev_time, (double)a->get_length());
|
||||
prev_time = Math::pingpong(prev_time - start, end - start) + start;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -1370,10 +1494,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
continue;
|
||||
}
|
||||
scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx);
|
||||
a->try_scale_track_interpolate(i, (double)a->get_length(), &scale[1]);
|
||||
root_motion_cache.scale += (scale[1] - scale[0]) * blend;
|
||||
a->try_scale_track_interpolate(i, end, &scale[1]);
|
||||
scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx);
|
||||
prev_time = 0;
|
||||
root_motion_cache.scale += (scale[1] - scale[0]) * blend;
|
||||
prev_time = start;
|
||||
}
|
||||
} else {
|
||||
if (Animation::is_less_approx(prev_time, time)) {
|
||||
|
|
@ -1382,10 +1506,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
continue;
|
||||
}
|
||||
scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx);
|
||||
a->try_scale_track_interpolate(i, 0, &scale[1]);
|
||||
a->try_scale_track_interpolate(i, start, &scale[1]);
|
||||
scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.scale += (scale[1] - scale[0]) * blend;
|
||||
prev_time = (double)a->get_length();
|
||||
prev_time = end;
|
||||
}
|
||||
}
|
||||
Error err = a->try_scale_track_interpolate(i, prev_time, &scale[0]);
|
||||
|
|
@ -1396,7 +1520,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
a->try_scale_track_interpolate(i, time, &scale[1]);
|
||||
scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.scale += (scale[1] - scale[0]) * blend;
|
||||
prev_time = !backward ? 0 : (double)a->get_length();
|
||||
prev_time = !backward ? start : end;
|
||||
}
|
||||
{
|
||||
Vector3 scale;
|
||||
|
|
@ -1560,13 +1684,13 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
}
|
||||
|
||||
PlayingAudioTrackInfo &track_info = t->playing_streams[oid];
|
||||
track_info.length = a->get_length();
|
||||
track_info.length = a_length;
|
||||
track_info.time = time;
|
||||
track_info.volume += blend;
|
||||
track_info.loop = a->get_loop_mode() != Animation::LOOP_NONE;
|
||||
track_info.backward = backward;
|
||||
track_info.use_blend = a->audio_track_is_use_blend(i);
|
||||
HashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
|
||||
AHashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
|
||||
|
||||
// Main process to fire key is started from here.
|
||||
if (p_update_only) {
|
||||
|
|
@ -1623,6 +1747,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
}
|
||||
|
||||
if (t_obj->call(SNAME("get_is_sample"))) {
|
||||
if (t->audio_stream_playback->get_sample_playback().is_valid()) {
|
||||
AudioServer::get_singleton()->stop_sample_playback(t->audio_stream_playback->get_sample_playback());
|
||||
}
|
||||
Ref<AudioSamplePlayback> sample_playback;
|
||||
sample_playback.instantiate();
|
||||
sample_playback->stream = stream;
|
||||
|
|
@ -1655,6 +1782,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
if (!player2) {
|
||||
continue;
|
||||
}
|
||||
// TODO: Make it possible to embed section info in animation track keys.
|
||||
if (seeked) {
|
||||
// Seek.
|
||||
int idx = a->track_find_key(i, time, Animation::FIND_MODE_NEAREST, true);
|
||||
|
|
@ -1667,19 +1795,19 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
continue;
|
||||
}
|
||||
Ref<Animation> anim = player2->get_animation(anim_name);
|
||||
double at_anim_pos = 0.0;
|
||||
double at_anim_pos = start;
|
||||
switch (anim->get_loop_mode()) {
|
||||
case Animation::LOOP_NONE: {
|
||||
if (!is_external_seeking && ((!backward && Animation::is_greater_or_equal_approx(time, pos + (double)anim->get_length())) || (backward && Animation::is_less_or_equal_approx(time, pos)))) {
|
||||
if (!is_external_seeking && ((!backward && Animation::is_greater_or_equal_approx(time, pos + end)) || (backward && Animation::is_less_or_equal_approx(time, pos + start)))) {
|
||||
continue; // Do nothing if current time is outside of length when started.
|
||||
}
|
||||
at_anim_pos = MIN((double)anim->get_length(), time - pos); // Seek to end.
|
||||
at_anim_pos = MIN(end, time - pos); // Seek to end.
|
||||
} break;
|
||||
case Animation::LOOP_LINEAR: {
|
||||
at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); // Seek to loop.
|
||||
at_anim_pos = Math::fposmod(time - pos - start, end - start) + start; // Seek to loop.
|
||||
} break;
|
||||
case Animation::LOOP_PINGPONG: {
|
||||
at_anim_pos = Math::pingpong(time - pos, (double)a->get_length());
|
||||
at_anim_pos = Math::pingpong(time - pos - start, end - start) + start;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -1717,6 +1845,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
|||
}
|
||||
}
|
||||
}
|
||||
is_GDVIRTUAL_CALL_post_process_key_value = true;
|
||||
}
|
||||
|
||||
void AnimationMixer::_blend_apply() {
|
||||
|
|
@ -1830,7 +1959,7 @@ void AnimationMixer::_blend_apply() {
|
|||
PlayingAudioTrackInfo &track_info = L.value;
|
||||
float db = Math::linear_to_db(track_info.use_blend ? track_info.volume : 1.0);
|
||||
LocalVector<int> erase_streams;
|
||||
HashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
|
||||
AHashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
|
||||
for (const KeyValue<int, PlayingAudioStreamInfo> &M : map) {
|
||||
PlayingAudioStreamInfo pasi = M.value;
|
||||
|
||||
|
|
@ -1938,12 +2067,21 @@ void AnimationMixer::clear_caches() {
|
|||
|
||||
void AnimationMixer::set_root_motion_track(const NodePath &p_track) {
|
||||
root_motion_track = p_track;
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
NodePath AnimationMixer::get_root_motion_track() const {
|
||||
return root_motion_track;
|
||||
}
|
||||
|
||||
void AnimationMixer::set_root_motion_local(bool p_enabled) {
|
||||
root_motion_local = p_enabled;
|
||||
}
|
||||
|
||||
bool AnimationMixer::is_root_motion_local() const {
|
||||
return root_motion_local;
|
||||
}
|
||||
|
||||
Vector3 AnimationMixer::get_root_motion_position() const {
|
||||
return root_motion_position;
|
||||
}
|
||||
|
|
@ -2075,6 +2213,8 @@ Ref<AnimatedValuesBackup> AnimationMixer::make_backup() {
|
|||
PlaybackInfo pi;
|
||||
pi.time = 0;
|
||||
pi.delta = 0;
|
||||
pi.start = 0;
|
||||
pi.end = reset_anim->get_length();
|
||||
pi.seeked = true;
|
||||
pi.weight = 1.0;
|
||||
make_animation_instance(SceneStringName(RESET), pi);
|
||||
|
|
@ -2112,7 +2252,7 @@ void AnimationMixer::restore(const Ref<AnimatedValuesBackup> &p_backup) {
|
|||
ERR_FAIL_COND(p_backup.is_null());
|
||||
track_cache = p_backup->get_data();
|
||||
_blend_apply();
|
||||
track_cache = HashMap<Animation::TypeHash, AnimationMixer::TrackCache *>();
|
||||
track_cache = AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher>();
|
||||
cache_valid = false;
|
||||
}
|
||||
|
||||
|
|
@ -2159,6 +2299,9 @@ void AnimationMixer::capture(const StringName &p_name, double p_duration, Tween:
|
|||
capture_cache.step = 1.0 / p_duration;
|
||||
capture_cache.trans_type = p_trans_type;
|
||||
capture_cache.ease_type = p_ease_type;
|
||||
if (capture_cache.animation.is_valid()) {
|
||||
animation_track_num_to_track_cache.erase(capture_cache.animation);
|
||||
}
|
||||
capture_cache.animation.instantiate();
|
||||
|
||||
bool is_valid = false;
|
||||
|
|
@ -2182,6 +2325,8 @@ void AnimationMixer::capture(const StringName &p_name, double p_duration, Tween:
|
|||
}
|
||||
if (!is_valid) {
|
||||
capture_cache.clear();
|
||||
} else {
|
||||
_create_track_num_to_track_cache_for_animation(capture_cache.animation);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2282,6 +2427,8 @@ void AnimationMixer::_bind_methods() {
|
|||
/* ---- Root motion accumulator for Skeleton3D ---- */
|
||||
ClassDB::bind_method(D_METHOD("set_root_motion_track", "path"), &AnimationMixer::set_root_motion_track);
|
||||
ClassDB::bind_method(D_METHOD("get_root_motion_track"), &AnimationMixer::get_root_motion_track);
|
||||
ClassDB::bind_method(D_METHOD("set_root_motion_local", "enabled"), &AnimationMixer::set_root_motion_local);
|
||||
ClassDB::bind_method(D_METHOD("is_root_motion_local"), &AnimationMixer::is_root_motion_local);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_root_motion_position"), &AnimationMixer::get_root_motion_position);
|
||||
ClassDB::bind_method(D_METHOD("get_root_motion_rotation"), &AnimationMixer::get_root_motion_rotation);
|
||||
|
|
@ -2309,6 +2456,7 @@ void AnimationMixer::_bind_methods() {
|
|||
|
||||
ADD_GROUP("Root Motion", "root_motion_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_motion_track"), "set_root_motion_track", "get_root_motion_track");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "root_motion_local"), "set_root_motion_local", "is_root_motion_local");
|
||||
|
||||
ADD_GROUP("Audio", "audio_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_max_polyphony", PROPERTY_HINT_RANGE, "1,127,1"), "set_audio_max_polyphony", "get_audio_max_polyphony");
|
||||
|
|
@ -2348,7 +2496,7 @@ AnimationMixer::AnimationMixer() {
|
|||
AnimationMixer::~AnimationMixer() {
|
||||
}
|
||||
|
||||
void AnimatedValuesBackup::set_data(const HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> p_data) {
|
||||
void AnimatedValuesBackup::set_data(const AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> p_data) {
|
||||
clear_data();
|
||||
|
||||
for (const KeyValue<Animation::TypeHash, AnimationMixer::TrackCache *> &E : p_data) {
|
||||
|
|
@ -2361,7 +2509,7 @@ void AnimatedValuesBackup::set_data(const HashMap<Animation::TypeHash, Animation
|
|||
}
|
||||
}
|
||||
|
||||
HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> AnimatedValuesBackup::get_data() const {
|
||||
AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> AnimatedValuesBackup::get_data() const {
|
||||
HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> ret;
|
||||
for (const KeyValue<Animation::TypeHash, AnimationMixer::TrackCache *> &E : data) {
|
||||
AnimationMixer::TrackCache *track = get_cache_copy(E.value);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#ifndef ANIMATION_MIXER_H
|
||||
#define ANIMATION_MIXER_H
|
||||
|
||||
#include "core/templates/a_hash_map.h"
|
||||
#include "scene/animation/tween.h"
|
||||
#include "scene/main/node.h"
|
||||
#include "scene/resources/animation.h"
|
||||
|
|
@ -48,6 +49,7 @@ class AnimationMixer : public Node {
|
|||
#endif // TOOLS_ENABLED
|
||||
|
||||
bool reset_on_save = true;
|
||||
bool is_GDVIRTUAL_CALL_post_process_key_value = true;
|
||||
|
||||
public:
|
||||
enum AnimationCallbackModeProcess {
|
||||
|
|
@ -84,6 +86,8 @@ public:
|
|||
struct PlaybackInfo {
|
||||
double time = 0.0;
|
||||
double delta = 0.0;
|
||||
double start = 0.0;
|
||||
double end = 0.0;
|
||||
bool seeked = false;
|
||||
bool is_external_seeking = false;
|
||||
Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
|
||||
|
|
@ -99,7 +103,7 @@ public:
|
|||
protected:
|
||||
/* ---- Data lists ---- */
|
||||
LocalVector<AnimationLibraryData> animation_libraries;
|
||||
HashMap<StringName, AnimationData> animation_set; // HashMap<Library name + Animation name, AnimationData>
|
||||
AHashMap<StringName, AnimationData> animation_set; // HashMap<Library name + Animation name, AnimationData>
|
||||
|
||||
TypedArray<StringName> _get_animation_library_list() const;
|
||||
Vector<String> _get_animation_list() const {
|
||||
|
|
@ -145,6 +149,7 @@ protected:
|
|||
uint64_t setup_pass = 0;
|
||||
Animation::TrackType type = Animation::TrackType::TYPE_ANIMATION;
|
||||
NodePath path;
|
||||
int blend_idx = -1;
|
||||
ObjectID object_id;
|
||||
real_t total_weight = 0.0;
|
||||
|
||||
|
|
@ -266,7 +271,7 @@ protected:
|
|||
|
||||
// Audio track information for mixng and ending.
|
||||
struct PlayingAudioTrackInfo {
|
||||
HashMap<int, PlayingAudioStreamInfo> stream_info;
|
||||
AHashMap<int, PlayingAudioStreamInfo> stream_info;
|
||||
double length = 0.0;
|
||||
double time = 0.0;
|
||||
real_t volume = 0.0;
|
||||
|
|
@ -305,7 +310,8 @@ protected:
|
|||
};
|
||||
|
||||
RootMotionCache root_motion_cache;
|
||||
HashMap<Animation::TypeHash, TrackCache *> track_cache;
|
||||
AHashMap<Animation::TypeHash, TrackCache *, HashHasher> track_cache;
|
||||
AHashMap<Ref<Animation>, LocalVector<TrackCache *>> animation_track_num_to_track_cache;
|
||||
HashSet<TrackCache *> playing_caches;
|
||||
Vector<Node *> playing_audio_stream_players;
|
||||
|
||||
|
|
@ -315,18 +321,20 @@ protected:
|
|||
void _clear_playing_caches();
|
||||
void _init_root_motion_cache();
|
||||
bool _update_caches();
|
||||
void _create_track_num_to_track_cache_for_animation(Ref<Animation> &p_animation);
|
||||
|
||||
/* ---- Audio ---- */
|
||||
AudioServer::PlaybackType playback_type;
|
||||
|
||||
/* ---- Blending processor ---- */
|
||||
LocalVector<AnimationInstance> animation_instances;
|
||||
HashMap<NodePath, int> track_map;
|
||||
AHashMap<NodePath, int> track_map;
|
||||
int track_count = 0;
|
||||
bool deterministic = false;
|
||||
|
||||
/* ---- Root motion accumulator for Skeleton3D ---- */
|
||||
NodePath root_motion_track;
|
||||
bool root_motion_local = false;
|
||||
Vector3 root_motion_position = Vector3(0, 0, 0);
|
||||
Quaternion root_motion_rotation = Quaternion(0, 0, 0, 1);
|
||||
Vector3 root_motion_scale = Vector3(0, 0, 0);
|
||||
|
|
@ -356,14 +364,14 @@ protected:
|
|||
virtual void _process_animation(double p_delta, bool p_update_only = false);
|
||||
|
||||
// For post process with retrieved key value during blending.
|
||||
virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
|
||||
virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant &p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
|
||||
Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
|
||||
GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, ObjectID, int);
|
||||
|
||||
void _blend_init();
|
||||
virtual bool _blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map);
|
||||
virtual bool _blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map);
|
||||
virtual void _blend_capture(double p_delta);
|
||||
void _blend_calc_total_weight(); // For undeterministic blending.
|
||||
void _blend_calc_total_weight(); // For indeterministic blending.
|
||||
void _blend_process(double p_delta, bool p_update_only = false);
|
||||
void _blend_apply();
|
||||
virtual void _blend_post_process();
|
||||
|
|
@ -402,6 +410,7 @@ public:
|
|||
void get_animation_library_list(List<StringName> *p_animations) const;
|
||||
Ref<AnimationLibrary> get_animation_library(const StringName &p_name) const;
|
||||
bool has_animation_library(const StringName &p_name) const;
|
||||
StringName get_animation_library_name(const Ref<AnimationLibrary> &p_animation_library) const;
|
||||
StringName find_animation_library(const Ref<Animation> &p_animation) const;
|
||||
Error add_animation_library(const StringName &p_name, const Ref<AnimationLibrary> &p_animation_library);
|
||||
void remove_animation_library(const StringName &p_name);
|
||||
|
|
@ -439,6 +448,9 @@ public:
|
|||
void set_root_motion_track(const NodePath &p_track);
|
||||
NodePath get_root_motion_track() const;
|
||||
|
||||
void set_root_motion_local(bool p_enabled);
|
||||
bool is_root_motion_local() const;
|
||||
|
||||
Vector3 get_root_motion_position() const;
|
||||
Quaternion get_root_motion_rotation() const;
|
||||
Vector3 get_root_motion_scale() const;
|
||||
|
|
@ -482,11 +494,11 @@ public:
|
|||
class AnimatedValuesBackup : public RefCounted {
|
||||
GDCLASS(AnimatedValuesBackup, RefCounted);
|
||||
|
||||
HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> data;
|
||||
AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> data;
|
||||
|
||||
public:
|
||||
void set_data(const HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> p_data);
|
||||
HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> get_data() const;
|
||||
void set_data(const AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> p_data);
|
||||
AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> get_data() const;
|
||||
void clear_data();
|
||||
|
||||
AnimationMixer::TrackCache *get_cache_copy(AnimationMixer::TrackCache *p_cache) const;
|
||||
|
|
|
|||
83
engine/scene/animation/animation_node_extension.cpp
Normal file
83
engine/scene/animation/animation_node_extension.cpp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/**************************************************************************/
|
||||
/* animation_node_extension.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "animation_node_extension.h"
|
||||
|
||||
AnimationNode::NodeTimeInfo AnimationNodeExtension::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
PackedFloat32Array r_ret;
|
||||
|
||||
GDVIRTUAL_CALL(
|
||||
_process_animation_node,
|
||||
_playback_info_to_array(p_playback_info),
|
||||
p_test_only,
|
||||
r_ret);
|
||||
|
||||
return _array_to_node_time_info(r_ret);
|
||||
}
|
||||
|
||||
bool AnimationNodeExtension::is_looping(const PackedFloat32Array &p_node_info) {
|
||||
return _array_to_node_time_info(p_node_info).is_looping();
|
||||
}
|
||||
|
||||
double AnimationNodeExtension::get_remaining_time(const PackedFloat32Array &p_node_info, bool p_break_loop) {
|
||||
return _array_to_node_time_info(p_node_info).get_remain(p_break_loop);
|
||||
}
|
||||
|
||||
void AnimationNodeExtension::_bind_methods() {
|
||||
ClassDB::bind_static_method("AnimationNodeExtension", D_METHOD("is_looping", "node_info"), &AnimationNodeExtension::is_looping);
|
||||
ClassDB::bind_static_method("AnimationNodeExtension", D_METHOD("get_remaining_time", "node_info", "break_loop"), &AnimationNodeExtension::get_remaining_time);
|
||||
GDVIRTUAL_BIND(_process_animation_node, "playback_info", "test_only");
|
||||
}
|
||||
|
||||
AnimationNode::NodeTimeInfo AnimationNodeExtension::_array_to_node_time_info(const PackedFloat32Array &p_node_info) {
|
||||
ERR_FAIL_COND_V_MSG(p_node_info.size() != 6, AnimationNode::NodeTimeInfo(), "Invalid node info.");
|
||||
AnimationNode::NodeTimeInfo ret_val;
|
||||
ret_val.length = p_node_info[0];
|
||||
ret_val.position = p_node_info[1];
|
||||
ret_val.delta = p_node_info[2];
|
||||
ret_val.loop_mode = static_cast<Animation::LoopMode>(p_node_info[3]);
|
||||
ret_val.will_end = p_node_info[4] > 0.0;
|
||||
ret_val.is_infinity = p_node_info[5] > 0.0;
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
PackedFloat64Array AnimationNodeExtension::_playback_info_to_array(const AnimationMixer::PlaybackInfo &p_playback_info) {
|
||||
PackedFloat64Array playback_info_array;
|
||||
playback_info_array.push_back(p_playback_info.time);
|
||||
playback_info_array.push_back(p_playback_info.delta);
|
||||
playback_info_array.push_back(p_playback_info.start);
|
||||
playback_info_array.push_back(p_playback_info.end);
|
||||
playback_info_array.push_back(p_playback_info.seeked);
|
||||
playback_info_array.push_back(p_playback_info.is_external_seeking);
|
||||
playback_info_array.push_back(p_playback_info.looped_flag);
|
||||
playback_info_array.push_back(p_playback_info.weight);
|
||||
|
||||
return playback_info_array;
|
||||
}
|
||||
55
engine/scene/animation/animation_node_extension.h
Normal file
55
engine/scene/animation/animation_node_extension.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/**************************************************************************/
|
||||
/* animation_node_extension.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANIMATION_NODE_EXTENSION_H
|
||||
#define ANIMATION_NODE_EXTENSION_H
|
||||
|
||||
#include "scene/animation/animation_tree.h"
|
||||
|
||||
class AnimationNodeExtension : public AnimationNode {
|
||||
GDCLASS(AnimationNodeExtension, AnimationNode);
|
||||
|
||||
public:
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
static bool is_looping(const PackedFloat32Array &p_node_info);
|
||||
static double get_remaining_time(const PackedFloat32Array &p_node_info, bool p_break_loop = false);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
GDVIRTUAL2R_REQUIRED(PackedFloat32Array, _process_animation_node, PackedFloat64Array, bool);
|
||||
|
||||
private:
|
||||
static AnimationNode::NodeTimeInfo _array_to_node_time_info(const PackedFloat32Array &p_array);
|
||||
static PackedFloat64Array _playback_info_to_array(const AnimationMixer::PlaybackInfo &p_playback_info);
|
||||
};
|
||||
|
||||
#endif // ANIMATION_NODE_EXTENSION_H
|
||||
|
|
@ -29,10 +29,6 @@
|
|||
/**************************************************************************/
|
||||
|
||||
#include "animation_node_state_machine.h"
|
||||
#include "scene/main/window.h"
|
||||
|
||||
StringName AnimationNodeStateMachine::START_NODE;
|
||||
StringName AnimationNodeStateMachine::END_NODE;
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
|
|
@ -54,7 +50,7 @@ AnimationNodeStateMachineTransition::AdvanceMode AnimationNodeStateMachineTransi
|
|||
|
||||
void AnimationNodeStateMachineTransition::set_advance_condition(const StringName &p_condition) {
|
||||
String cs = p_condition;
|
||||
ERR_FAIL_COND(cs.contains("/") || cs.contains(":"));
|
||||
ERR_FAIL_COND(cs.contains_char('/') || cs.contains_char(':'));
|
||||
advance_condition = p_condition;
|
||||
if (!cs.is_empty()) {
|
||||
advance_condition_name = "conditions/" + cs;
|
||||
|
|
@ -205,7 +201,7 @@ void AnimationNodeStateMachinePlayback::_set_current(AnimationNodeStateMachine *
|
|||
}
|
||||
|
||||
Ref<AnimationNodeStateMachine> anodesm = p_state_machine->find_node_by_path(current);
|
||||
if (!anodesm.is_valid()) {
|
||||
if (anodesm.is_null()) {
|
||||
group_start_transition = Ref<AnimationNodeStateMachineTransition>();
|
||||
group_end_transition = Ref<AnimationNodeStateMachineTransition>();
|
||||
return;
|
||||
|
|
@ -229,9 +225,9 @@ void AnimationNodeStateMachinePlayback::_set_current(AnimationNodeStateMachine *
|
|||
|
||||
// Validation.
|
||||
if (anodesm->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
|
||||
indices = anodesm->find_transition_from(AnimationNodeStateMachine::START_NODE);
|
||||
indices = anodesm->find_transition_from(SceneStringName(Start));
|
||||
int anodesm_start_size = indices.size();
|
||||
indices = anodesm->find_transition_to(AnimationNodeStateMachine::END_NODE);
|
||||
indices = anodesm->find_transition_to(SceneStringName(End));
|
||||
int anodesm_end_size = indices.size();
|
||||
if (group_start_size > 1) {
|
||||
WARN_PRINT_ED("There are two or more transitions to the Grouped AnimationNodeStateMachine in AnimationNodeStateMachine: " + base_path + ", which may result in unintended transitions.");
|
||||
|
|
@ -307,7 +303,7 @@ bool AnimationNodeStateMachinePlayback::is_playing() const {
|
|||
}
|
||||
|
||||
bool AnimationNodeStateMachinePlayback::is_end() const {
|
||||
return current == AnimationNodeStateMachine::END_NODE && fading_from == StringName();
|
||||
return current == SceneStringName(End) && fading_from == StringName();
|
||||
}
|
||||
|
||||
StringName AnimationNodeStateMachinePlayback::get_current_node() const {
|
||||
|
|
@ -357,7 +353,7 @@ void AnimationNodeStateMachinePlayback::_clear_path_children(AnimationTree *p_tr
|
|||
Ref<AnimationNodeStateMachine> anodesm = child_node.node;
|
||||
if (anodesm.is_valid() && anodesm->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
|
||||
Ref<AnimationNodeStateMachinePlayback> playback = p_tree->get(base_path + child_node.name + "/playback");
|
||||
ERR_FAIL_COND(!playback.is_valid());
|
||||
ERR_FAIL_COND(playback.is_null());
|
||||
playback->_set_base_path(base_path + child_node.name + "/");
|
||||
if (p_test_only) {
|
||||
playback = playback->duplicate();
|
||||
|
|
@ -387,7 +383,7 @@ void AnimationNodeStateMachinePlayback::_start_children(AnimationTree *p_tree, A
|
|||
ERR_FAIL_MSG("Root/Nested AnimationNodeStateMachine can't have path from parent AnimationNodeStateMachine.");
|
||||
}
|
||||
Ref<AnimationNodeStateMachinePlayback> playback = p_tree->get(base_path + concatenated + "/playback");
|
||||
ERR_FAIL_COND(!playback.is_valid());
|
||||
ERR_FAIL_COND(playback.is_null());
|
||||
playback->_set_base_path(base_path + concatenated + "/");
|
||||
if (p_test_only) {
|
||||
playback = playback->duplicate();
|
||||
|
|
@ -419,7 +415,7 @@ bool AnimationNodeStateMachinePlayback::_travel_children(AnimationTree *p_tree,
|
|||
ERR_FAIL_V_MSG(false, "Root/Nested AnimationNodeStateMachine can't have path from parent AnimationNodeStateMachine.");
|
||||
}
|
||||
Ref<AnimationNodeStateMachinePlayback> playback = p_tree->get(base_path + concatenated + "/playback");
|
||||
ERR_FAIL_COND_V(!playback.is_valid(), false);
|
||||
ERR_FAIL_COND_V(playback.is_null(), false);
|
||||
playback->_set_base_path(base_path + concatenated + "/");
|
||||
if (p_test_only) {
|
||||
playback = playback->duplicate();
|
||||
|
|
@ -438,16 +434,16 @@ bool AnimationNodeStateMachinePlayback::_travel_children(AnimationTree *p_tree,
|
|||
String child_path = "/" + playback->get_current_node();
|
||||
while (true) {
|
||||
Ref<AnimationNodeStateMachine> child_anodesm = p_state_machine->find_node_by_path(concatenated + child_path);
|
||||
if (!child_anodesm.is_valid() || child_anodesm->get_state_machine_type() != AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
|
||||
if (child_anodesm.is_null() || child_anodesm->get_state_machine_type() != AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
|
||||
break;
|
||||
}
|
||||
Ref<AnimationNodeStateMachinePlayback> child_playback = p_tree->get(base_path + concatenated + child_path + "/playback");
|
||||
ERR_FAIL_COND_V(!child_playback.is_valid(), false);
|
||||
ERR_FAIL_COND_V(child_playback.is_null(), false);
|
||||
child_playback->_set_base_path(base_path + concatenated + "/");
|
||||
if (p_test_only) {
|
||||
child_playback = child_playback->duplicate();
|
||||
}
|
||||
child_playback->_travel_main(AnimationNodeStateMachine::END_NODE);
|
||||
child_playback->_travel_main(SceneStringName(End));
|
||||
child_found_route &= child_playback->_travel(p_tree, child_anodesm.ptr(), false, p_test_only);
|
||||
child_path += "/" + child_playback->get_current_node();
|
||||
}
|
||||
|
|
@ -490,7 +486,7 @@ bool AnimationNodeStateMachinePlayback::_travel_children(AnimationTree *p_tree,
|
|||
|
||||
void AnimationNodeStateMachinePlayback::_start(AnimationNodeStateMachine *p_state_machine) {
|
||||
playing = true;
|
||||
_set_current(p_state_machine, start_request != StringName() ? start_request : AnimationNodeStateMachine::START_NODE);
|
||||
_set_current(p_state_machine, start_request != StringName() ? start_request : SceneStringName(Start));
|
||||
teleport_request = true;
|
||||
stop_request = false;
|
||||
start_request = StringName();
|
||||
|
|
@ -502,12 +498,12 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationTree *p_tree, Animation
|
|||
|
||||
String AnimationNodeStateMachinePlayback::_validate_path(AnimationNodeStateMachine *p_state_machine, const String &p_path) {
|
||||
if (p_state_machine->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
|
||||
return p_path; // Grouped state machine doesn't allow validat-able request.
|
||||
return p_path; // Grouped state machine doesn't allow validate-able request.
|
||||
}
|
||||
String target = p_path;
|
||||
Ref<AnimationNodeStateMachine> anodesm = p_state_machine->find_node_by_path(target);
|
||||
while (anodesm.is_valid() && anodesm->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
|
||||
Vector<int> indices = anodesm->find_transition_from(AnimationNodeStateMachine::START_NODE);
|
||||
Vector<int> indices = anodesm->find_transition_from(SceneStringName(Start));
|
||||
if (indices.size()) {
|
||||
target = target + "/" + anodesm->get_transition_to(indices[0]); // Find next state of Start.
|
||||
} else {
|
||||
|
|
@ -649,7 +645,7 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree,
|
|||
Ref<AnimationNodeStateMachine> anodesm = p_state_machine->find_node_by_path(current_path);
|
||||
if (anodesm.is_valid() && anodesm->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
|
||||
Ref<AnimationNodeStateMachinePlayback> playback = p_tree->get(base_path + current_path + "/playback");
|
||||
ERR_FAIL_COND_V(!playback.is_valid(), false);
|
||||
ERR_FAIL_COND_V(playback.is_null(), false);
|
||||
playback->_set_base_path(base_path + current_path + "/");
|
||||
if (p_test_only) {
|
||||
playback = playback->duplicate();
|
||||
|
|
@ -660,7 +656,7 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree,
|
|||
if (i >= new_path.size()) {
|
||||
break; // Tracing has been finished, needs to break.
|
||||
}
|
||||
playback->_travel_main(AnimationNodeStateMachine::END_NODE);
|
||||
playback->_travel_main(SceneStringName(End));
|
||||
if (!playback->_travel(p_tree, anodesm.ptr(), false, p_test_only)) {
|
||||
found_route = false;
|
||||
break;
|
||||
|
|
@ -803,7 +799,7 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(const St
|
|||
pi.is_external_seeking = false;
|
||||
pi.weight = 0;
|
||||
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.
|
||||
// Don't process first node if not necessary, instead process next node.
|
||||
_transition_to_next_recursive(tree, p_state_machine, p_delta, p_test_only);
|
||||
}
|
||||
|
||||
|
|
@ -818,8 +814,8 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(const St
|
|||
bool is_start_of_group = false;
|
||||
bool is_end_of_group = false;
|
||||
if (!p_state_machine->are_ends_reset() || p_state_machine->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
|
||||
is_start_of_group = fading_from == AnimationNodeStateMachine::START_NODE;
|
||||
is_end_of_group = current == AnimationNodeStateMachine::END_NODE;
|
||||
is_start_of_group = fading_from == SceneStringName(Start);
|
||||
is_end_of_group = current == SceneStringName(End);
|
||||
}
|
||||
|
||||
// Calc blend amount by cross-fade.
|
||||
|
|
@ -881,7 +877,7 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(const St
|
|||
}
|
||||
|
||||
// Find next and see when to transition.
|
||||
bool will_end = _transition_to_next_recursive(tree, p_state_machine, p_delta, p_test_only) || current == AnimationNodeStateMachine::END_NODE;
|
||||
bool will_end = _transition_to_next_recursive(tree, p_state_machine, p_delta, p_test_only) || current == SceneStringName(End);
|
||||
|
||||
// Predict remaining time.
|
||||
if (will_end || ((p_state_machine->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_NESTED) && !p_state_machine->has_transition_from(current))) {
|
||||
|
|
@ -951,7 +947,7 @@ bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationT
|
|||
_set_current(p_state_machine, next.node);
|
||||
current_curve = next.curve;
|
||||
|
||||
if (current == AnimationNodeStateMachine::END_NODE) {
|
||||
if (current == SceneStringName(End)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -981,7 +977,7 @@ bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationT
|
|||
}
|
||||
}
|
||||
|
||||
return next.node == AnimationNodeStateMachine::END_NODE;
|
||||
return next.node == SceneStringName(End);
|
||||
}
|
||||
|
||||
bool AnimationNodeStateMachinePlayback::_can_transition_to_next(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, NextInfo p_next, bool p_test_only) {
|
||||
|
|
@ -996,7 +992,7 @@ bool AnimationNodeStateMachinePlayback::_can_transition_to_next(AnimationTree *p
|
|||
Ref<AnimationNodeStateMachine> anodesm = p_state_machine->find_node_by_path(current);
|
||||
if (anodesm.is_valid() && anodesm->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
|
||||
Ref<AnimationNodeStateMachinePlayback> playback = p_tree->get(base_path + current + "/playback");
|
||||
ERR_FAIL_COND_V(!playback.is_valid(), false);
|
||||
ERR_FAIL_COND_V(playback.is_null(), false);
|
||||
playback->_set_base_path(base_path + current + "/");
|
||||
if (p_test_only) {
|
||||
playback = playback->duplicate();
|
||||
|
|
@ -1015,7 +1011,7 @@ bool AnimationNodeStateMachinePlayback::_can_transition_to_next(AnimationTree *p
|
|||
return false;
|
||||
}
|
||||
|
||||
if (current != AnimationNodeStateMachine::START_NODE && p_next.switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_AT_END) {
|
||||
if (current != SceneStringName(Start) && p_next.switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_AT_END) {
|
||||
return Animation::is_less_or_equal_approx(current_nti.get_remain(p_next.break_loop_at_end), p_next.xfade);
|
||||
}
|
||||
return true;
|
||||
|
|
@ -1025,13 +1021,13 @@ Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachinePlayback::_che
|
|||
Ref<AnimationNodeStateMachineTransition> temp_transition;
|
||||
Ref<AnimationNodeStateMachinePlayback> parent_playback;
|
||||
if (r_state_machine->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
|
||||
if (p_transition.from == AnimationNodeStateMachine::START_NODE) {
|
||||
if (p_transition.from == SceneStringName(Start)) {
|
||||
parent_playback = _get_parent_playback(p_tree);
|
||||
if (parent_playback.is_valid()) {
|
||||
r_bypass = true;
|
||||
temp_transition = parent_playback->_get_group_start_transition();
|
||||
}
|
||||
} else if (p_transition.to == AnimationNodeStateMachine::END_NODE) {
|
||||
} else if (p_transition.to == SceneStringName(End)) {
|
||||
parent_playback = _get_parent_playback(p_tree);
|
||||
if (parent_playback.is_valid()) {
|
||||
temp_transition = parent_playback->_get_group_end_transition();
|
||||
|
|
@ -1152,7 +1148,7 @@ Ref<AnimationNodeStateMachinePlayback> AnimationNodeStateMachinePlayback::_get_p
|
|||
split.remove_at(split.size() - 2);
|
||||
String playback_path = String("/").join(split) + "playback";
|
||||
Ref<AnimationNodeStateMachinePlayback> playback = p_tree->get(playback_path);
|
||||
if (!playback.is_valid()) {
|
||||
if (playback.is_null()) {
|
||||
ERR_PRINT_ONCE("Can't get parent AnimationNodeStateMachinePlayback with path: " + playback_path + ". Maybe there is no Root/Nested AnimationNodeStateMachine in the parent of the Grouped AnimationNodeStateMachine.");
|
||||
return Ref<AnimationNodeStateMachinePlayback>();
|
||||
}
|
||||
|
|
@ -1257,7 +1253,7 @@ bool AnimationNodeStateMachine::is_parameter_read_only(const StringName &p_param
|
|||
void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position) {
|
||||
ERR_FAIL_COND(states.has(p_name));
|
||||
ERR_FAIL_COND(p_node.is_null());
|
||||
ERR_FAIL_COND(String(p_name).contains("/"));
|
||||
ERR_FAIL_COND(String(p_name).contains_char('/'));
|
||||
|
||||
State state_new;
|
||||
state_new.node = p_node;
|
||||
|
|
@ -1276,7 +1272,7 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation
|
|||
void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<AnimationNode> p_node) {
|
||||
ERR_FAIL_COND(states.has(p_name) == false);
|
||||
ERR_FAIL_COND(p_node.is_null());
|
||||
ERR_FAIL_COND(String(p_name).contains("/"));
|
||||
ERR_FAIL_COND(String(p_name).contains_char('/'));
|
||||
|
||||
{
|
||||
Ref<AnimationNode> node = states[p_name].node;
|
||||
|
|
@ -1326,7 +1322,8 @@ bool AnimationNodeStateMachine::are_ends_reset() const {
|
|||
|
||||
bool AnimationNodeStateMachine::can_edit_node(const StringName &p_name) const {
|
||||
if (states.has(p_name)) {
|
||||
return !(states[p_name].node->is_class("AnimationNodeStartState") || states[p_name].node->is_class("AnimationNodeEndState"));
|
||||
const AnimationNode *anode = states[p_name].node.ptr();
|
||||
return !(Object::cast_to<AnimationNodeStartState>(anode) || Object::cast_to<AnimationNodeEndState>(anode));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -1518,7 +1515,7 @@ void AnimationNodeStateMachine::add_transition(const StringName &p_from, const S
|
|||
return;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(p_from == END_NODE || p_to == START_NODE);
|
||||
ERR_FAIL_COND(p_from == SceneStringName(End) || p_to == SceneStringName(Start));
|
||||
ERR_FAIL_COND(p_from == p_to);
|
||||
ERR_FAIL_COND(!_can_connect(p_from));
|
||||
ERR_FAIL_COND(!_can_connect(p_to));
|
||||
|
|
@ -1560,7 +1557,7 @@ StringName AnimationNodeStateMachine::get_transition_to(int p_transition) const
|
|||
bool AnimationNodeStateMachine::is_transition_across_group(int p_transition) const {
|
||||
ERR_FAIL_INDEX_V(p_transition, transitions.size(), false);
|
||||
if (get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
|
||||
if (transitions[p_transition].from == START_NODE || transitions[p_transition].to == END_NODE) {
|
||||
if (transitions[p_transition].from == SceneStringName(Start) || transitions[p_transition].to == SceneStringName(End)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1582,16 +1579,8 @@ void AnimationNodeStateMachine::remove_transition(const StringName &p_from, cons
|
|||
|
||||
void AnimationNodeStateMachine::remove_transition_by_index(const int p_transition) {
|
||||
ERR_FAIL_INDEX(p_transition, transitions.size());
|
||||
Transition tr = transitions[p_transition];
|
||||
transitions.write[p_transition].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
|
||||
transitions.remove_at(p_transition);
|
||||
|
||||
Vector<String> path_from = String(tr.from).split("/");
|
||||
Vector<String> path_to = String(tr.to).split("/");
|
||||
|
||||
List<Vector<String>> paths;
|
||||
paths.push_back(path_from);
|
||||
paths.push_back(path_to);
|
||||
}
|
||||
|
||||
void AnimationNodeStateMachine::_remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition) {
|
||||
|
|
@ -1619,7 +1608,7 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachine::_process(const AnimationM
|
|||
playback_new = playback_new->duplicate(); // Don't process original when testing.
|
||||
}
|
||||
|
||||
return playback_new->process(node_state.base_path, this, p_playback_info, p_test_only);
|
||||
return playback_new->process(node_state.get_base_path(), this, p_playback_info, p_test_only);
|
||||
}
|
||||
|
||||
String AnimationNodeStateMachine::get_caption() const {
|
||||
|
|
@ -1745,14 +1734,14 @@ void AnimationNodeStateMachine::reset_state() {
|
|||
State start;
|
||||
start.node = s;
|
||||
start.position = Vector2(200, 100);
|
||||
states[START_NODE] = start;
|
||||
states[SceneStringName(Start)] = start;
|
||||
|
||||
Ref<AnimationNodeEndState> e;
|
||||
e.instantiate();
|
||||
State end;
|
||||
end.node = e;
|
||||
end.position = Vector2(900, 100);
|
||||
states[END_NODE] = end;
|
||||
states[SceneStringName(End)] = end;
|
||||
|
||||
emit_changed();
|
||||
emit_signal(SNAME("tree_changed"));
|
||||
|
|
@ -1841,21 +1830,38 @@ void AnimationNodeStateMachine::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(STATE_MACHINE_TYPE_GROUPED);
|
||||
}
|
||||
|
||||
AnimationNodeStateMachine::AnimationNodeStateMachine() {
|
||||
START_NODE = "Start";
|
||||
END_NODE = "End";
|
||||
Vector<StringName> AnimationNodeStateMachine::get_nodes_with_transitions_from(const StringName &p_node) const {
|
||||
Vector<StringName> result;
|
||||
for (const Transition &transition : transitions) {
|
||||
if (transition.from == p_node) {
|
||||
result.push_back(transition.to);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector<StringName> AnimationNodeStateMachine::get_nodes_with_transitions_to(const StringName &p_node) const {
|
||||
Vector<StringName> result;
|
||||
for (const Transition &transition : transitions) {
|
||||
if (transition.to == p_node) {
|
||||
result.push_back(transition.from);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
AnimationNodeStateMachine::AnimationNodeStateMachine() {
|
||||
Ref<AnimationNodeStartState> s;
|
||||
s.instantiate();
|
||||
State start;
|
||||
start.node = s;
|
||||
start.position = Vector2(200, 100);
|
||||
states[START_NODE] = start;
|
||||
states[SceneStringName(Start)] = start;
|
||||
|
||||
Ref<AnimationNodeEndState> e;
|
||||
e.instantiate();
|
||||
State end;
|
||||
end.node = e;
|
||||
end.position = Vector2(900, 100);
|
||||
states[END_NODE] = end;
|
||||
states[SceneStringName(End)] = end;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,9 +110,6 @@ class AnimationNodeStateMachine : public AnimationRootNode {
|
|||
GDCLASS(AnimationNodeStateMachine, AnimationRootNode);
|
||||
|
||||
public:
|
||||
static StringName START_NODE;
|
||||
static StringName END_NODE;
|
||||
|
||||
enum StateMachineType {
|
||||
STATE_MACHINE_TYPE_ROOT,
|
||||
STATE_MACHINE_TYPE_NESTED,
|
||||
|
|
@ -223,6 +220,9 @@ public:
|
|||
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
|
||||
#endif
|
||||
|
||||
Vector<StringName> get_nodes_with_transitions_from(const StringName &p_node) const;
|
||||
Vector<StringName> get_nodes_with_transitions_to(const StringName &p_node) const;
|
||||
|
||||
AnimationNodeStateMachine();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -58,14 +58,6 @@ void AnimationPlayer::_seek_bind_compat_80813(double p_time, bool p_update) {
|
|||
seek(p_time, p_update, false);
|
||||
}
|
||||
|
||||
void AnimationPlayer::_play_compat_84906(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) {
|
||||
play(p_name, p_custom_blend, p_custom_scale, p_from_end);
|
||||
}
|
||||
|
||||
void AnimationPlayer::_play_backwards_compat_84906(const StringName &p_name, double p_custom_blend) {
|
||||
play_backwards(p_name, p_custom_blend);
|
||||
}
|
||||
|
||||
void AnimationPlayer::_bind_compatibility_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_process_callback", "mode"), &AnimationPlayer::_set_process_callback_bind_compat_80813);
|
||||
ClassDB::bind_method(D_METHOD("get_process_callback"), &AnimationPlayer::_get_process_callback_bind_compat_80813);
|
||||
|
|
@ -74,8 +66,6 @@ void AnimationPlayer::_bind_compatibility_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_root", "path"), &AnimationPlayer::_set_root_bind_compat_80813);
|
||||
ClassDB::bind_method(D_METHOD("get_root"), &AnimationPlayer::_get_root_bind_compat_80813);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("seek", "seconds", "update"), &AnimationPlayer::_seek_bind_compat_80813, DEFVAL(false));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::_play_compat_84906, DEFVAL(""), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::_play_backwards_compat_84906, DEFVAL(""), DEFVAL(-1));
|
||||
BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS);
|
||||
BIND_ENUM_CONSTANT(ANIMATION_PROCESS_IDLE);
|
||||
BIND_ENUM_CONSTANT(ANIMATION_PROCESS_MANUAL);
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
|
|||
List<PropertyInfo> anim_names;
|
||||
|
||||
for (const KeyValue<StringName, AnimationData> &E : animation_set) {
|
||||
HashMap<StringName, StringName>::ConstIterator F = animation_next_set.find(E.key);
|
||||
AHashMap<StringName, StringName>::ConstIterator F = animation_next_set.find(E.key);
|
||||
if (F && F->value != StringName()) {
|
||||
anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
|
||||
}
|
||||
|
|
@ -164,39 +164,41 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
|
|||
double delta = p_started ? 0 : p_delta * speed;
|
||||
double next_pos = cd.pos + delta;
|
||||
|
||||
double len = cd.from->animation->get_length();
|
||||
double start = cd.get_start_time();
|
||||
double end = cd.get_end_time();
|
||||
|
||||
Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
|
||||
|
||||
switch (cd.from->animation->get_loop_mode()) {
|
||||
case Animation::LOOP_NONE: {
|
||||
if (Animation::is_less_approx(next_pos, 0)) {
|
||||
next_pos = 0;
|
||||
} else if (Animation::is_greater_approx(next_pos, len)) {
|
||||
next_pos = len;
|
||||
if (Animation::is_less_approx(next_pos, start)) {
|
||||
next_pos = start;
|
||||
} else if (Animation::is_greater_approx(next_pos, end)) {
|
||||
next_pos = end;
|
||||
}
|
||||
delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here).
|
||||
} break;
|
||||
|
||||
case Animation::LOOP_LINEAR: {
|
||||
if (Animation::is_less_approx(next_pos, 0) && Animation::is_greater_or_equal_approx(cd.pos, 0)) {
|
||||
if (Animation::is_less_approx(next_pos, start) && Animation::is_greater_or_equal_approx(cd.pos, start)) {
|
||||
looped_flag = Animation::LOOPED_FLAG_START;
|
||||
}
|
||||
if (Animation::is_greater_approx(next_pos, len) && Animation::is_less_or_equal_approx(cd.pos, len)) {
|
||||
if (Animation::is_greater_approx(next_pos, end) && Animation::is_less_or_equal_approx(cd.pos, end)) {
|
||||
looped_flag = Animation::LOOPED_FLAG_END;
|
||||
}
|
||||
next_pos = Math::fposmod(next_pos, (double)len);
|
||||
next_pos = Math::fposmod(next_pos - start, end - start) + start;
|
||||
} break;
|
||||
|
||||
case Animation::LOOP_PINGPONG: {
|
||||
if (Animation::is_less_approx(next_pos, 0) && Animation::is_greater_or_equal_approx(cd.pos, 0)) {
|
||||
if (Animation::is_less_approx(next_pos, start) && Animation::is_greater_or_equal_approx(cd.pos, start)) {
|
||||
cd.speed_scale *= -1.0;
|
||||
looped_flag = Animation::LOOPED_FLAG_START;
|
||||
}
|
||||
if (Animation::is_greater_approx(next_pos, len) && Animation::is_less_or_equal_approx(cd.pos, len)) {
|
||||
if (Animation::is_greater_approx(next_pos, end) && Animation::is_less_or_equal_approx(cd.pos, end)) {
|
||||
cd.speed_scale *= -1.0;
|
||||
looped_flag = Animation::LOOPED_FLAG_END;
|
||||
}
|
||||
next_pos = Math::pingpong(next_pos, (double)len);
|
||||
next_pos = Math::pingpong(next_pos - start, end - start) + start;
|
||||
} break;
|
||||
|
||||
default:
|
||||
|
|
@ -208,18 +210,18 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
|
|||
// End detection.
|
||||
if (p_is_current) {
|
||||
if (cd.from->animation->get_loop_mode() == Animation::LOOP_NONE) {
|
||||
if (!backwards && Animation::is_less_or_equal_approx(prev_pos, len) && Math::is_equal_approx(next_pos, len)) {
|
||||
if (!backwards && Animation::is_less_or_equal_approx(prev_pos, end) && Math::is_equal_approx(next_pos, end)) {
|
||||
// Playback finished.
|
||||
next_pos = len; // Snap to the edge.
|
||||
next_pos = end; // Snap to the edge.
|
||||
end_reached = true;
|
||||
end_notify = Animation::is_less_approx(prev_pos, len); // Notify only if not already at the end.
|
||||
end_notify = Animation::is_less_approx(prev_pos, end); // Notify only if not already at the end.
|
||||
p_blend = 1.0;
|
||||
}
|
||||
if (backwards && Animation::is_greater_or_equal_approx(prev_pos, 0) && Math::is_equal_approx(next_pos, 0)) {
|
||||
if (backwards && Animation::is_greater_or_equal_approx(prev_pos, start) && Math::is_equal_approx(next_pos, start)) {
|
||||
// Playback finished.
|
||||
next_pos = 0; // Snap to the edge.
|
||||
next_pos = start; // Snap to the edge.
|
||||
end_reached = true;
|
||||
end_notify = Animation::is_greater_approx(prev_pos, 0); // Notify only if not already at the beginning.
|
||||
end_notify = Animation::is_greater_approx(prev_pos, start); // Notify only if not already at the beginning.
|
||||
p_blend = 1.0;
|
||||
}
|
||||
}
|
||||
|
|
@ -231,10 +233,14 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
|
|||
if (p_started) {
|
||||
pi.time = prev_pos;
|
||||
pi.delta = 0;
|
||||
pi.start = start;
|
||||
pi.end = end;
|
||||
pi.seeked = true;
|
||||
} else {
|
||||
pi.time = next_pos;
|
||||
pi.delta = delta;
|
||||
pi.start = start;
|
||||
pi.end = end;
|
||||
pi.seeked = p_seeked;
|
||||
}
|
||||
if (Math::is_zero_approx(pi.delta) && backwards) {
|
||||
|
|
@ -293,7 +299,7 @@ void AnimationPlayer::_blend_playback_data(double p_delta, bool p_started) {
|
|||
}
|
||||
}
|
||||
|
||||
bool AnimationPlayer::_blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) {
|
||||
bool AnimationPlayer::_blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) {
|
||||
if (!playback.current.from) {
|
||||
_set_process(false);
|
||||
return false;
|
||||
|
|
@ -378,6 +384,14 @@ void AnimationPlayer::play_backwards(const StringName &p_name, double p_custom_b
|
|||
play(p_name, p_custom_blend, -1, true);
|
||||
}
|
||||
|
||||
void AnimationPlayer::play_section_with_markers_backwards(const StringName &p_name, const StringName &p_start_marker, const StringName &p_end_marker, double p_custom_blend) {
|
||||
play_section_with_markers(p_name, p_start_marker, p_end_marker, p_custom_blend, -1, true);
|
||||
}
|
||||
|
||||
void AnimationPlayer::play_section_backwards(const StringName &p_name, double p_start_time, double p_end_time, double p_custom_blend) {
|
||||
play_section(p_name, p_start_time, p_end_time, p_custom_blend, -1, true);
|
||||
}
|
||||
|
||||
void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) {
|
||||
if (auto_capture) {
|
||||
play_with_capture(p_name, auto_capture_duration, p_custom_blend, p_custom_scale, p_from_end, auto_capture_transition_type, auto_capture_ease_type);
|
||||
|
|
@ -387,6 +401,10 @@ void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, floa
|
|||
}
|
||||
|
||||
void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) {
|
||||
play_section_with_markers(p_name, StringName(), StringName(), p_custom_blend, p_custom_scale, p_from_end);
|
||||
}
|
||||
|
||||
void AnimationPlayer::play_section_with_markers(const StringName &p_name, const StringName &p_start_marker, const StringName &p_end_marker, double p_custom_blend, float p_custom_scale, bool p_from_end) {
|
||||
StringName name = p_name;
|
||||
|
||||
if (name == StringName()) {
|
||||
|
|
@ -395,6 +413,38 @@ void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, flo
|
|||
|
||||
ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));
|
||||
|
||||
Ref<Animation> animation = animation_set[name].animation;
|
||||
|
||||
ERR_FAIL_COND_MSG(p_start_marker == p_end_marker && p_start_marker, vformat("Start marker and end marker cannot be the same marker: %s.", p_start_marker));
|
||||
ERR_FAIL_COND_MSG(p_start_marker && !animation->has_marker(p_start_marker), vformat("Marker %s not found in animation: %s.", p_start_marker, name));
|
||||
ERR_FAIL_COND_MSG(p_end_marker && !animation->has_marker(p_end_marker), vformat("Marker %s not found in animation: %s.", p_end_marker, name));
|
||||
|
||||
double start_time = p_start_marker ? animation->get_marker_time(p_start_marker) : -1;
|
||||
double end_time = p_end_marker ? animation->get_marker_time(p_end_marker) : -1;
|
||||
|
||||
ERR_FAIL_COND_MSG(p_start_marker && p_end_marker && Animation::is_greater_approx(start_time, end_time), vformat("End marker %s is placed earlier than start marker %s in animation: %s.", p_end_marker, p_start_marker, name));
|
||||
|
||||
if (p_start_marker && Animation::is_less_approx(start_time, 0)) {
|
||||
WARN_PRINT_ED(vformat("Negative time start marker: %s is invalid in the section, so the start of the animation: %s is used instead.", p_start_marker, playback.current.from->animation->get_name()));
|
||||
}
|
||||
if (p_end_marker && Animation::is_less_approx(end_time, 0)) {
|
||||
WARN_PRINT_ED(vformat("Negative time end marker: %s is invalid in the section, so the end of the animation: %s is used instead.", p_end_marker, playback.current.from->animation->get_name()));
|
||||
}
|
||||
|
||||
play_section(name, start_time, end_time, p_custom_blend, p_custom_scale, p_from_end);
|
||||
}
|
||||
|
||||
void AnimationPlayer::play_section(const StringName &p_name, double p_start_time, double p_end_time, double p_custom_blend, float p_custom_scale, bool p_from_end) {
|
||||
StringName name = p_name;
|
||||
|
||||
if (name == StringName()) {
|
||||
name = playback.assigned;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));
|
||||
ERR_FAIL_COND_MSG(p_start_time >= 0 && p_end_time >= 0 && Math::is_equal_approx(p_start_time, p_end_time), "Start time and end time must not equal to each other.");
|
||||
ERR_FAIL_COND_MSG(p_start_time >= 0 && p_end_time >= 0 && Animation::is_greater_approx(p_start_time, p_end_time), vformat("Start time %f is greater than end time %f.", p_start_time, p_end_time));
|
||||
|
||||
Playback &c = playback;
|
||||
|
||||
if (c.current.from) {
|
||||
|
|
@ -442,22 +492,27 @@ void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, flo
|
|||
|
||||
c.current.from = &animation_set[name];
|
||||
c.current.speed_scale = p_custom_scale;
|
||||
c.current.start_time = p_start_time;
|
||||
c.current.end_time = p_end_time;
|
||||
|
||||
double start = playback.current.get_start_time();
|
||||
double end = playback.current.get_end_time();
|
||||
|
||||
if (!end_reached) {
|
||||
playback_queue.clear();
|
||||
}
|
||||
|
||||
if (c.assigned != name) { // Reset.
|
||||
c.current.pos = p_from_end ? c.current.from->animation->get_length() : 0;
|
||||
c.current.pos = p_from_end ? end : start;
|
||||
c.assigned = name;
|
||||
emit_signal(SNAME("current_animation_changed"), c.assigned);
|
||||
} else {
|
||||
if (p_from_end && Math::is_zero_approx(c.current.pos)) {
|
||||
if (p_from_end && Math::is_equal_approx(c.current.pos, start)) {
|
||||
// Animation reset but played backwards, set position to the end.
|
||||
seek_internal(c.current.from->animation->get_length(), true, true, true);
|
||||
} else if (!p_from_end && Math::is_equal_approx(c.current.pos, (double)c.current.from->animation->get_length())) {
|
||||
seek_internal(end, true, true, true);
|
||||
} else if (!p_from_end && Math::is_equal_approx(c.current.pos, end)) {
|
||||
// Animation resumed but already ended, set position to the beginning.
|
||||
seek_internal(0, true, true, true);
|
||||
seek_internal(start, true, true, true);
|
||||
} else if (playing) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -551,6 +606,8 @@ void AnimationPlayer::set_assigned_animation(const String &p_animation) {
|
|||
ERR_FAIL_COND_MSG(!animation_set.has(p_animation), vformat("Animation not found: %s.", p_animation));
|
||||
playback.current.pos = 0;
|
||||
playback.current.from = &animation_set[p_animation];
|
||||
playback.current.start_time = -1;
|
||||
playback.current.end_time = -1;
|
||||
playback.assigned = p_animation;
|
||||
emit_signal(SNAME("current_animation_changed"), playback.assigned);
|
||||
}
|
||||
|
|
@ -603,6 +660,12 @@ void AnimationPlayer::seek_internal(double p_time, bool p_update, bool p_update_
|
|||
}
|
||||
}
|
||||
|
||||
double start = playback.current.get_start_time();
|
||||
double end = playback.current.get_end_time();
|
||||
|
||||
// Clamp the seek position.
|
||||
p_time = CLAMP(p_time, start, end);
|
||||
|
||||
playback.seeked = true;
|
||||
playback.internal_seeked = p_is_internal_seek;
|
||||
|
||||
|
|
@ -641,6 +704,49 @@ double AnimationPlayer::get_current_animation_length() const {
|
|||
return playback.current.from->animation->get_length();
|
||||
}
|
||||
|
||||
void AnimationPlayer::set_section_with_markers(const StringName &p_start_marker, const StringName &p_end_marker) {
|
||||
ERR_FAIL_NULL_MSG(playback.current.from, "AnimationPlayer has no current animation.");
|
||||
ERR_FAIL_COND_MSG(p_start_marker == p_end_marker && p_start_marker, vformat("Start marker and end marker cannot be the same marker: %s.", p_start_marker));
|
||||
ERR_FAIL_COND_MSG(p_start_marker && !playback.current.from->animation->has_marker(p_start_marker), vformat("Marker %s not found in animation: %s.", p_start_marker, playback.current.from->animation->get_name()));
|
||||
ERR_FAIL_COND_MSG(p_end_marker && !playback.current.from->animation->has_marker(p_end_marker), vformat("Marker %s not found in animation: %s.", p_end_marker, playback.current.from->animation->get_name()));
|
||||
double start_time = p_start_marker ? playback.current.from->animation->get_marker_time(p_start_marker) : -1;
|
||||
double end_time = p_end_marker ? playback.current.from->animation->get_marker_time(p_end_marker) : -1;
|
||||
if (p_start_marker && Animation::is_less_approx(start_time, 0)) {
|
||||
WARN_PRINT_ONCE_ED(vformat("Marker %s time must be positive in animation: %s.", p_start_marker, playback.current.from->animation->get_name()));
|
||||
}
|
||||
if (p_end_marker && Animation::is_less_approx(end_time, 0)) {
|
||||
WARN_PRINT_ONCE_ED(vformat("Marker %s time must be positive in animation: %s.", p_end_marker, playback.current.from->animation->get_name()));
|
||||
}
|
||||
set_section(start_time, end_time);
|
||||
}
|
||||
|
||||
void AnimationPlayer::set_section(double p_start_time, double p_end_time) {
|
||||
ERR_FAIL_NULL_MSG(playback.current.from, "AnimationPlayer has no current animation.");
|
||||
ERR_FAIL_COND_MSG(Animation::is_greater_or_equal_approx(p_start_time, 0) && Animation::is_greater_or_equal_approx(p_end_time, 0) && Animation::is_greater_or_equal_approx(p_start_time, p_end_time), vformat("Start time %f is greater than end time %f.", p_start_time, p_end_time));
|
||||
playback.current.start_time = p_start_time;
|
||||
playback.current.end_time = p_end_time;
|
||||
playback.current.pos = CLAMP(playback.current.pos, playback.current.get_start_time(), playback.current.get_end_time());
|
||||
}
|
||||
|
||||
void AnimationPlayer::reset_section() {
|
||||
playback.current.start_time = -1;
|
||||
playback.current.end_time = -1;
|
||||
}
|
||||
|
||||
double AnimationPlayer::get_section_start_time() const {
|
||||
ERR_FAIL_NULL_V_MSG(playback.current.from, playback.current.start_time, "AnimationPlayer has no current animation.");
|
||||
return playback.current.get_start_time();
|
||||
}
|
||||
|
||||
double AnimationPlayer::get_section_end_time() const {
|
||||
ERR_FAIL_NULL_V_MSG(playback.current.from, playback.current.end_time, "AnimationPlayer has no current animation.");
|
||||
return playback.current.get_end_time();
|
||||
}
|
||||
|
||||
bool AnimationPlayer::has_section() const {
|
||||
return Animation::is_greater_or_equal_approx(playback.current.start_time, 0) || Animation::is_greater_or_equal_approx(playback.current.end_time, 0);
|
||||
}
|
||||
|
||||
void AnimationPlayer::set_autoplay(const String &p_name) {
|
||||
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) {
|
||||
WARN_PRINT("Setting autoplay after the node has been added to the scene has no effect.");
|
||||
|
|
@ -665,13 +771,14 @@ void AnimationPlayer::_stop_internal(bool p_reset, bool p_keep_state) {
|
|||
_clear_caches();
|
||||
Playback &c = playback;
|
||||
// c.blend.clear();
|
||||
double start = c.current.from ? playback.current.get_start_time() : 0;
|
||||
if (p_reset) {
|
||||
c.blend.clear();
|
||||
if (p_keep_state) {
|
||||
c.current.pos = 0;
|
||||
c.current.pos = start;
|
||||
} else {
|
||||
is_stopping = true;
|
||||
seek_internal(0, true, true, true);
|
||||
seek_internal(start, true, true, true);
|
||||
is_stopping = false;
|
||||
}
|
||||
c.current.from = nullptr;
|
||||
|
|
@ -863,7 +970,11 @@ void AnimationPlayer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_auto_capture_ease_type"), &AnimationPlayer::get_auto_capture_ease_type);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("play_section_with_markers", "name", "start_marker", "end_marker", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play_section_with_markers, DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("play_section", "name", "start_time", "end_time", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play_section, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::play_backwards, DEFVAL(StringName()), DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("play_section_with_markers_backwards", "name", "start_marker", "end_marker", "custom_blend"), &AnimationPlayer::play_section_with_markers_backwards, DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("play_section_backwards", "name", "start_time", "end_time", "custom_blend"), &AnimationPlayer::play_section_backwards, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("play_with_capture", "name", "duration", "custom_blend", "custom_speed", "from_end", "trans_type", "ease_type"), &AnimationPlayer::play_with_capture, DEFVAL(StringName()), DEFVAL(-1.0), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false), DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN));
|
||||
ClassDB::bind_method(D_METHOD("pause"), &AnimationPlayer::pause);
|
||||
ClassDB::bind_method(D_METHOD("stop", "keep_state"), &AnimationPlayer::stop, DEFVAL(false));
|
||||
|
|
@ -893,6 +1004,14 @@ void AnimationPlayer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_current_animation_position"), &AnimationPlayer::get_current_animation_position);
|
||||
ClassDB::bind_method(D_METHOD("get_current_animation_length"), &AnimationPlayer::get_current_animation_length);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_section_with_markers", "start_marker", "end_marker"), &AnimationPlayer::set_section_with_markers, DEFVAL(StringName()), DEFVAL(StringName()));
|
||||
ClassDB::bind_method(D_METHOD("set_section", "start_time", "end_time"), &AnimationPlayer::set_section, DEFVAL(-1), DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("reset_section"), &AnimationPlayer::reset_section);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_section_start_time"), &AnimationPlayer::get_section_start_time);
|
||||
ClassDB::bind_method(D_METHOD("get_section_end_time"), &AnimationPlayer::get_section_end_time);
|
||||
ClassDB::bind_method(D_METHOD("has_section"), &AnimationPlayer::has_section);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("seek", "seconds", "update", "update_only"), &AnimationPlayer::seek, DEFVAL(false), DEFVAL(false));
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR), "set_current_animation", "get_current_animation");
|
||||
|
|
@ -904,7 +1023,7 @@ void AnimationPlayer::_bind_methods() {
|
|||
ADD_GROUP("Playback Options", "playback_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playback_auto_capture"), "set_auto_capture", "is_auto_capture");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_auto_capture_duration", PROPERTY_HINT_NONE, "suffix:s"), "set_auto_capture_duration", "get_auto_capture_duration");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_auto_capture_transition_type", PROPERTY_HINT_ENUM, "Linear,Sine,Quint,Quart,Expo,Elastic,Cubic,Circ,Bounce,Back,Spring"), "set_auto_capture_transition_type", "get_auto_capture_transition_type");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_auto_capture_transition_type", PROPERTY_HINT_ENUM, "Linear,Sine,Quint,Quart,Quad,Expo,Elastic,Cubic,Circ,Bounce,Back,Spring"), "set_auto_capture_transition_type", "get_auto_capture_transition_type");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_auto_capture_ease_type", PROPERTY_HINT_ENUM, "In,Out,InOut,OutIn"), "set_auto_capture_ease_type", "get_auto_capture_ease_type");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_default_blend_time", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:s"), "set_default_blend_time", "get_default_blend_time");
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
#define ANIMATION_PLAYER_H
|
||||
|
||||
#include "animation_mixer.h"
|
||||
#include "scene/2d/node_2d.h"
|
||||
#include "scene/resources/animation.h"
|
||||
|
||||
class AnimationPlayer : public AnimationMixer {
|
||||
|
|
@ -52,7 +51,7 @@ public:
|
|||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
private:
|
||||
HashMap<StringName, StringName> animation_next_set; // For auto advance.
|
||||
AHashMap<StringName, StringName> animation_next_set; // For auto advance.
|
||||
|
||||
float speed_scale = 1.0;
|
||||
double default_blend_time = 0.0;
|
||||
|
|
@ -68,6 +67,20 @@ private:
|
|||
AnimationData *from = nullptr;
|
||||
double pos = 0.0;
|
||||
float speed_scale = 1.0;
|
||||
double start_time = 0.0;
|
||||
double end_time = 0.0;
|
||||
double get_start_time() const {
|
||||
if (from && (Animation::is_less_approx(start_time, 0) || Animation::is_greater_approx(start_time, from->animation->get_length()))) {
|
||||
return 0;
|
||||
}
|
||||
return start_time;
|
||||
}
|
||||
double get_end_time() const {
|
||||
if (from && (Animation::is_less_approx(end_time, 0) || Animation::is_greater_approx(end_time, from->animation->get_length()))) {
|
||||
return from->animation->get_length();
|
||||
}
|
||||
return end_time;
|
||||
}
|
||||
};
|
||||
|
||||
struct Blend {
|
||||
|
|
@ -136,7 +149,7 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
// Make animation instances.
|
||||
virtual bool _blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) override;
|
||||
virtual bool _blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) override;
|
||||
virtual void _blend_capture(double p_delta) override;
|
||||
virtual void _blend_post_process() override;
|
||||
|
||||
|
|
@ -176,8 +189,16 @@ public:
|
|||
void set_auto_capture_ease_type(Tween::EaseType p_auto_capture_ease_type);
|
||||
Tween::EaseType get_auto_capture_ease_type() const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
|
||||
#endif
|
||||
|
||||
void play(const StringName &p_name = StringName(), double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
|
||||
void play_section_with_markers(const StringName &p_name = StringName(), const StringName &p_start_marker = StringName(), const StringName &p_end_marker = StringName(), double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
|
||||
void play_section(const StringName &p_name = StringName(), double p_start_time = -1, double p_end_time = -1, double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
|
||||
void play_backwards(const StringName &p_name = StringName(), double p_custom_blend = -1);
|
||||
void play_section_with_markers_backwards(const StringName &p_name = StringName(), const StringName &p_start_marker = StringName(), const StringName &p_end_marker = StringName(), double p_custom_blend = -1);
|
||||
void play_section_backwards(const StringName &p_name = StringName(), double p_start_time = -1, double p_end_time = -1, double p_custom_blend = -1);
|
||||
void play_with_capture(const StringName &p_name = StringName(), double p_duration = -1.0, double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false, Tween::TransitionType p_trans_type = Tween::TRANS_LINEAR, Tween::EaseType p_ease_type = Tween::EASE_IN);
|
||||
void queue(const StringName &p_name);
|
||||
Vector<String> get_queue();
|
||||
|
|
@ -207,9 +228,13 @@ public:
|
|||
double get_current_animation_position() const;
|
||||
double get_current_animation_length() const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
|
||||
#endif
|
||||
void set_section_with_markers(const StringName &p_start_marker = StringName(), const StringName &p_end_marker = StringName());
|
||||
void set_section(double p_start_time = -1, double p_end_time = -1);
|
||||
void reset_section();
|
||||
|
||||
double get_section_start_time() const;
|
||||
double get_section_end_time() const;
|
||||
bool has_section() const;
|
||||
|
||||
virtual void advance(double p_time) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
#include "animation_tree.compat.inc"
|
||||
|
||||
#include "animation_blend_tree.h"
|
||||
#include "core/config/engine.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
|
||||
void AnimationNode::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
|
|
@ -75,20 +74,34 @@ void AnimationNode::set_parameter(const StringName &p_name, const Variant &p_val
|
|||
if (process_state->is_testing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const AHashMap<StringName, int>::Iterator it = property_cache.find(p_name);
|
||||
if (it) {
|
||||
process_state->tree->property_map.get_by_index(it->value).value.first = p_value;
|
||||
return;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(!process_state->tree->property_parent_map.has(node_state.base_path));
|
||||
ERR_FAIL_COND(!process_state->tree->property_parent_map[node_state.base_path].has(p_name));
|
||||
StringName path = process_state->tree->property_parent_map[node_state.base_path][p_name];
|
||||
|
||||
process_state->tree->property_map[path].first = p_value;
|
||||
int idx = process_state->tree->property_map.get_index(path);
|
||||
property_cache.insert_new(p_name, idx);
|
||||
process_state->tree->property_map.get_by_index(idx).value.first = p_value;
|
||||
}
|
||||
|
||||
Variant AnimationNode::get_parameter(const StringName &p_name) const {
|
||||
ERR_FAIL_NULL_V(process_state, Variant());
|
||||
const AHashMap<StringName, int>::ConstIterator it = property_cache.find(p_name);
|
||||
if (it) {
|
||||
return process_state->tree->property_map.get_by_index(it->value).value.first;
|
||||
}
|
||||
ERR_FAIL_COND_V(!process_state->tree->property_parent_map.has(node_state.base_path), Variant());
|
||||
ERR_FAIL_COND_V(!process_state->tree->property_parent_map[node_state.base_path].has(p_name), Variant());
|
||||
|
||||
StringName path = process_state->tree->property_parent_map[node_state.base_path][p_name];
|
||||
return process_state->tree->property_map[path].first;
|
||||
int idx = process_state->tree->property_map.get_index(path);
|
||||
property_cache.insert_new(p_name, idx);
|
||||
return process_state->tree->property_map.get_by_index(idx).value.first;
|
||||
}
|
||||
|
||||
void AnimationNode::set_node_time_info(const NodeTimeInfo &p_node_time_info) {
|
||||
|
|
@ -147,7 +160,7 @@ AnimationTree *AnimationNode::get_animation_tree() const {
|
|||
}
|
||||
|
||||
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());
|
||||
ERR_FAIL_INDEX_V(p_input, (int64_t)inputs.size(), NodeTimeInfo());
|
||||
|
||||
AnimationNodeBlendTree *blend_tree = Object::cast_to<AnimationNodeBlendTree>(node_state.parent);
|
||||
ERR_FAIL_NULL_V(blend_tree, NodeTimeInfo());
|
||||
|
|
@ -167,12 +180,12 @@ AnimationNode::NodeTimeInfo AnimationNode::blend_input(int p_input, AnimationMix
|
|||
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);
|
||||
LocalVector<AnimationTree::Activity> *activity_ptr = process_state->tree->input_activity_map.getptr(node_state.base_path);
|
||||
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;
|
||||
if (activity_ptr && p_input < (int64_t)activity_ptr->size()) {
|
||||
(*activity_ptr)[p_input].last_pass = process_state->last_pass;
|
||||
(*activity_ptr)[p_input].activity = activity;
|
||||
}
|
||||
return nti;
|
||||
}
|
||||
|
|
@ -188,11 +201,11 @@ AnimationNode::NodeTimeInfo AnimationNode::_blend_node(Ref<AnimationNode> p_node
|
|||
|
||||
int blend_count = node_state.track_weights.size();
|
||||
|
||||
if (p_node->node_state.track_weights.size() != blend_count) {
|
||||
if ((int64_t)p_node->node_state.track_weights.size() != blend_count) {
|
||||
p_node->node_state.track_weights.resize(blend_count);
|
||||
}
|
||||
|
||||
real_t *blendw = p_node->node_state.track_weights.ptrw();
|
||||
real_t *blendw = p_node->node_state.track_weights.ptr();
|
||||
const real_t *blendr = node_state.track_weights.ptr();
|
||||
|
||||
bool any_valid = false;
|
||||
|
|
@ -203,10 +216,11 @@ AnimationNode::NodeTimeInfo AnimationNode::_blend_node(Ref<AnimationNode> p_node
|
|||
}
|
||||
|
||||
for (const KeyValue<NodePath, bool> &E : filter) {
|
||||
if (!process_state->track_map.has(E.key)) {
|
||||
const AHashMap<NodePath, int> &map = *process_state->track_map;
|
||||
if (!map.has(E.key)) {
|
||||
continue;
|
||||
}
|
||||
int idx = process_state->track_map[E.key];
|
||||
int idx = map[E.key];
|
||||
blendw[idx] = 1.0; // Filtered goes to one.
|
||||
}
|
||||
|
||||
|
|
@ -291,7 +305,7 @@ AnimationNode::NodeTimeInfo AnimationNode::_blend_node(Ref<AnimationNode> p_node
|
|||
|
||||
// This process, which depends on p_sync is needed to process sync correctly in the case of
|
||||
// that a synced AnimationNodeSync exists under the un-synced AnimationNodeSync.
|
||||
p_node->node_state.base_path = new_path;
|
||||
p_node->set_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.delta = 0.0;
|
||||
|
|
@ -310,7 +324,7 @@ bool AnimationNode::add_input(const String &p_name) {
|
|||
// Root nodes can't add inputs.
|
||||
ERR_FAIL_COND_V(Object::cast_to<AnimationRootNode>(this) != nullptr, false);
|
||||
Input input;
|
||||
ERR_FAIL_COND_V(p_name.contains(".") || p_name.contains("/"), false);
|
||||
ERR_FAIL_COND_V(p_name.contains_char('.') || p_name.contains_char('/'), false);
|
||||
input.name = p_name;
|
||||
inputs.push_back(input);
|
||||
emit_changed();
|
||||
|
|
@ -318,21 +332,21 @@ bool AnimationNode::add_input(const String &p_name) {
|
|||
}
|
||||
|
||||
void AnimationNode::remove_input(int p_index) {
|
||||
ERR_FAIL_INDEX(p_index, inputs.size());
|
||||
ERR_FAIL_INDEX(p_index, (int64_t)inputs.size());
|
||||
inputs.remove_at(p_index);
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
bool AnimationNode::set_input_name(int p_input, const String &p_name) {
|
||||
ERR_FAIL_INDEX_V(p_input, inputs.size(), false);
|
||||
ERR_FAIL_COND_V(p_name.contains(".") || p_name.contains("/"), false);
|
||||
inputs.write[p_input].name = p_name;
|
||||
ERR_FAIL_INDEX_V(p_input, (int64_t)inputs.size(), false);
|
||||
ERR_FAIL_COND_V(p_name.contains_char('.') || p_name.contains_char('/'), false);
|
||||
inputs[p_input].name = p_name;
|
||||
emit_changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String AnimationNode::get_input_name(int p_input) const {
|
||||
ERR_FAIL_INDEX_V(p_input, inputs.size(), String());
|
||||
ERR_FAIL_INDEX_V(p_input, (int64_t)inputs.size(), String());
|
||||
return inputs[p_input].name;
|
||||
}
|
||||
|
||||
|
|
@ -342,7 +356,7 @@ int AnimationNode::get_input_count() const {
|
|||
|
||||
int AnimationNode::find_input(const String &p_name) const {
|
||||
int idx = -1;
|
||||
for (int i = 0; i < inputs.size(); i++) {
|
||||
for (int i = 0; i < (int64_t)inputs.size(); i++) {
|
||||
if (inputs[i].name == p_name) {
|
||||
idx = i;
|
||||
break;
|
||||
|
|
@ -356,7 +370,9 @@ AnimationNode::NodeTimeInfo AnimationNode::process(const AnimationMixer::Playbac
|
|||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
if (p_playback_info.seeked) {
|
||||
pi.delta = get_node_time_info().position - p_playback_info.time;
|
||||
if (p_playback_info.is_external_seeking) {
|
||||
pi.delta = get_node_time_info().position - p_playback_info.time;
|
||||
}
|
||||
} else {
|
||||
pi.time = get_node_time_info().position + p_playback_info.delta;
|
||||
}
|
||||
|
|
@ -402,6 +418,16 @@ bool AnimationNode::is_deletable() const {
|
|||
return closable;
|
||||
}
|
||||
|
||||
ObjectID AnimationNode::get_processing_animation_tree_instance_id() const {
|
||||
ERR_FAIL_NULL_V(process_state, ObjectID());
|
||||
return process_state->tree->get_instance_id();
|
||||
}
|
||||
|
||||
bool AnimationNode::is_process_testing() const {
|
||||
ERR_FAIL_NULL_V(process_state, false);
|
||||
return process_state->is_testing;
|
||||
}
|
||||
|
||||
bool AnimationNode::is_path_filtered(const NodePath &p_path) const {
|
||||
return filter.has(p_path);
|
||||
}
|
||||
|
|
@ -447,7 +473,7 @@ Ref<AnimationNode> AnimationNode::find_node_by_path(const String &p_name) const
|
|||
Ref<AnimationNode> ret = const_cast<AnimationNode *>(this);
|
||||
for (int i = 0; i < split.size(); i++) {
|
||||
ret = ret->get_child_by_name(split[i]);
|
||||
if (!ret.is_valid()) {
|
||||
if (ret.is_null()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -527,6 +553,10 @@ void AnimationNode::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_filter_enabled", "enable"), &AnimationNode::set_filter_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_filter_enabled"), &AnimationNode::is_filter_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_processing_animation_tree_instance_id"), &AnimationNode::get_processing_animation_tree_instance_id);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_process_testing"), &AnimationNode::is_process_testing);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters);
|
||||
ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters);
|
||||
|
||||
|
|
@ -602,10 +632,10 @@ Ref<AnimationRootNode> AnimationTree::get_root_animation_node() const {
|
|||
return root_animation_node;
|
||||
}
|
||||
|
||||
bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) {
|
||||
bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) {
|
||||
_update_properties(); // If properties need updating, update them.
|
||||
|
||||
if (!root_animation_node.is_valid()) {
|
||||
if (root_animation_node.is_null()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -618,15 +648,15 @@ bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const
|
|||
process_state.valid = true;
|
||||
process_state.invalid_reasons = "";
|
||||
process_state.last_pass = process_pass;
|
||||
process_state.track_map = p_track_map;
|
||||
process_state.track_map = &p_track_map;
|
||||
|
||||
// Init node state for root AnimationNode.
|
||||
root_animation_node->node_state.track_weights.resize(p_track_count);
|
||||
real_t *src_blendsw = root_animation_node->node_state.track_weights.ptrw();
|
||||
real_t *src_blendsw = root_animation_node->node_state.track_weights.ptr();
|
||||
for (int i = 0; i < p_track_count; i++) {
|
||||
src_blendsw[i] = 1.0; // By default all go to 1 for the root input.
|
||||
}
|
||||
root_animation_node->node_state.base_path = SNAME(Animation::PARAMETERS_BASE_PATH.ascii().get_data());
|
||||
root_animation_node->set_node_state_base_path(SNAME(Animation::PARAMETERS_BASE_PATH.ascii().get_data()));
|
||||
root_animation_node->node_state.parent = nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -680,8 +710,8 @@ uint64_t AnimationTree::get_last_process_pass() const {
|
|||
}
|
||||
|
||||
PackedStringArray AnimationTree::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
if (!root_animation_node.is_valid()) {
|
||||
PackedStringArray warnings = AnimationMixer::get_configuration_warnings();
|
||||
if (root_animation_node.is_null()) {
|
||||
warnings.push_back(RTR("No root AnimationNode for the graph is set."));
|
||||
}
|
||||
return warnings;
|
||||
|
|
@ -728,17 +758,17 @@ void AnimationTree::_animation_node_removed(const ObjectID &p_oid, const StringN
|
|||
_update_properties();
|
||||
}
|
||||
|
||||
void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<AnimationNode> p_node) {
|
||||
void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<AnimationNode> p_node) const {
|
||||
ERR_FAIL_COND(p_node.is_null());
|
||||
if (!property_parent_map.has(p_base_path)) {
|
||||
property_parent_map[p_base_path] = HashMap<StringName, StringName>();
|
||||
property_parent_map[p_base_path] = AHashMap<StringName, StringName>();
|
||||
}
|
||||
if (!property_reference_map.has(p_node->get_instance_id())) {
|
||||
property_reference_map[p_node->get_instance_id()] = p_base_path;
|
||||
}
|
||||
|
||||
if (p_node->get_input_count() && !input_activity_map.has(p_base_path)) {
|
||||
Vector<Activity> activity;
|
||||
LocalVector<Activity> activity;
|
||||
for (int i = 0; i < p_node->get_input_count(); i++) {
|
||||
Activity a;
|
||||
a.activity = 0;
|
||||
|
|
@ -766,7 +796,7 @@ void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<A
|
|||
pinfo.name = p_base_path + key;
|
||||
properties.push_back(pinfo);
|
||||
}
|
||||
|
||||
p_node->make_cache_dirty();
|
||||
List<AnimationNode::ChildNode> children;
|
||||
p_node->get_child_nodes(&children);
|
||||
|
||||
|
|
@ -775,7 +805,7 @@ void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<A
|
|||
}
|
||||
}
|
||||
|
||||
void AnimationTree::_update_properties() {
|
||||
void AnimationTree::_update_properties() const {
|
||||
if (!properties_dirty) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -792,7 +822,7 @@ void AnimationTree::_update_properties() {
|
|||
|
||||
properties_dirty = false;
|
||||
|
||||
notify_property_list_changed();
|
||||
const_cast<AnimationTree *>(this)->notify_property_list_changed();
|
||||
}
|
||||
|
||||
void AnimationTree::_notification(int p_what) {
|
||||
|
|
@ -908,7 +938,7 @@ bool AnimationTree::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
}
|
||||
#endif // DISABLE_DEPRECATED
|
||||
if (properties_dirty) {
|
||||
const_cast<AnimationTree *>(this)->_update_properties();
|
||||
_update_properties();
|
||||
}
|
||||
|
||||
if (property_map.has(p_name)) {
|
||||
|
|
@ -921,7 +951,7 @@ bool AnimationTree::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
|
||||
void AnimationTree::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
if (properties_dirty) {
|
||||
const_cast<AnimationTree *>(this)->_update_properties();
|
||||
_update_properties();
|
||||
}
|
||||
|
||||
for (const PropertyInfo &E : properties) {
|
||||
|
|
@ -933,9 +963,9 @@ real_t AnimationTree::get_connection_activity(const StringName &p_path, int p_co
|
|||
if (!input_activity_map_get.has(p_path)) {
|
||||
return 0;
|
||||
}
|
||||
const Vector<Activity> *activity = input_activity_map_get[p_path];
|
||||
const LocalVector<Activity> *activity = input_activity_map_get[p_path];
|
||||
|
||||
if (!activity || p_connection < 0 || p_connection >= activity->size()) {
|
||||
if (!activity || p_connection < 0 || p_connection >= (int64_t)activity->size()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ public:
|
|||
};
|
||||
|
||||
bool closable = false;
|
||||
Vector<Input> inputs;
|
||||
HashMap<NodePath, bool> filter;
|
||||
LocalVector<Input> inputs;
|
||||
AHashMap<NodePath, bool> filter;
|
||||
bool filter_enabled = false;
|
||||
|
||||
// To propagate information from upstream for use in estimation of playback progress.
|
||||
|
|
@ -91,28 +91,63 @@ public:
|
|||
if (Math::is_zero_approx(remain)) {
|
||||
return 0;
|
||||
}
|
||||
return length - position;
|
||||
return remain;
|
||||
}
|
||||
};
|
||||
|
||||
// Temporary state for blending process which needs to be stored in each AnimationNodes.
|
||||
struct NodeState {
|
||||
friend AnimationNode;
|
||||
|
||||
private:
|
||||
StringName base_path;
|
||||
|
||||
public:
|
||||
AnimationNode *parent = nullptr;
|
||||
Vector<StringName> connections;
|
||||
Vector<real_t> track_weights;
|
||||
LocalVector<real_t> track_weights;
|
||||
|
||||
const StringName get_base_path() const {
|
||||
return base_path;
|
||||
}
|
||||
|
||||
} node_state;
|
||||
|
||||
// Temporary state for blending process which needs to be started in the AnimationTree, pass through the AnimationNodes, and then return to the AnimationTree.
|
||||
struct ProcessState {
|
||||
AnimationTree *tree = nullptr;
|
||||
HashMap<NodePath, int> track_map; // TODO: Is there a better way to manage filter/tracks?
|
||||
const AHashMap<NodePath, int> *track_map; // TODO: Is there a better way to manage filter/tracks?
|
||||
bool is_testing = false;
|
||||
bool valid = false;
|
||||
String invalid_reasons;
|
||||
uint64_t last_pass = 0;
|
||||
} *process_state = nullptr;
|
||||
|
||||
private:
|
||||
mutable AHashMap<StringName, int> property_cache;
|
||||
|
||||
public:
|
||||
void set_node_state_base_path(const StringName p_base_path) {
|
||||
if (p_base_path != node_state.base_path) {
|
||||
node_state.base_path = p_base_path;
|
||||
make_cache_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void set_node_state_base_path(const String p_base_path) {
|
||||
if (p_base_path != node_state.base_path) {
|
||||
node_state.base_path = p_base_path;
|
||||
make_cache_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
const StringName get_node_state_base_path() const {
|
||||
return node_state.get_base_path();
|
||||
}
|
||||
|
||||
void make_cache_dirty() {
|
||||
property_cache.clear();
|
||||
}
|
||||
Array _get_filters() const;
|
||||
void _set_filters(const Array &p_filters);
|
||||
friend class AnimationNodeBlendTree;
|
||||
|
|
@ -151,7 +186,7 @@ protected:
|
|||
GDVIRTUAL1RC(Ref<AnimationNode>, _get_child_by_name, StringName)
|
||||
GDVIRTUAL1RC(Variant, _get_parameter_default_value, StringName)
|
||||
GDVIRTUAL1RC(bool, _is_parameter_read_only, StringName)
|
||||
GDVIRTUAL4RC(double, _process, double, bool, bool, bool)
|
||||
GDVIRTUAL4R(double, _process, double, bool, bool, bool)
|
||||
GDVIRTUAL0RC(String, _get_caption)
|
||||
GDVIRTUAL0RC(bool, _has_filter)
|
||||
|
||||
|
|
@ -191,6 +226,10 @@ public:
|
|||
void set_deletable(bool p_closable);
|
||||
bool is_deletable() const;
|
||||
|
||||
ObjectID get_processing_animation_tree_instance_id() const;
|
||||
|
||||
bool is_process_testing() const;
|
||||
|
||||
virtual bool has_filter() const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
|
@ -249,15 +288,15 @@ private:
|
|||
|
||||
friend class AnimationNode;
|
||||
|
||||
List<PropertyInfo> properties;
|
||||
HashMap<StringName, HashMap<StringName, StringName>> property_parent_map;
|
||||
HashMap<ObjectID, StringName> property_reference_map;
|
||||
HashMap<StringName, Pair<Variant, bool>> property_map; // Property value and read-only flag.
|
||||
mutable List<PropertyInfo> properties;
|
||||
mutable AHashMap<StringName, AHashMap<StringName, StringName>> property_parent_map;
|
||||
mutable AHashMap<ObjectID, StringName> property_reference_map;
|
||||
mutable AHashMap<StringName, Pair<Variant, bool>> property_map; // Property value and read-only flag.
|
||||
|
||||
bool properties_dirty = true;
|
||||
mutable bool properties_dirty = true;
|
||||
|
||||
void _update_properties();
|
||||
void _update_properties_for_node(const String &p_base_path, Ref<AnimationNode> p_node);
|
||||
void _update_properties() const;
|
||||
void _update_properties_for_node(const String &p_base_path, Ref<AnimationNode> p_node) const;
|
||||
|
||||
void _tree_changed();
|
||||
void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name);
|
||||
|
|
@ -267,8 +306,8 @@ private:
|
|||
uint64_t last_pass = 0;
|
||||
real_t activity = 0.0;
|
||||
};
|
||||
HashMap<StringName, Vector<Activity>> input_activity_map;
|
||||
HashMap<StringName, Vector<Activity> *> input_activity_map_get;
|
||||
mutable HashMap<StringName, LocalVector<Activity>> input_activity_map;
|
||||
mutable HashMap<StringName, LocalVector<Activity> *> input_activity_map_get;
|
||||
|
||||
NodePath animation_player;
|
||||
|
||||
|
|
@ -286,7 +325,7 @@ private:
|
|||
virtual void _set_active(bool p_active) override;
|
||||
|
||||
// Make animation instances.
|
||||
virtual bool _blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) override;
|
||||
virtual bool _blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) override;
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _set_process_callback_bind_compat_80813(AnimationProcessCallback p_mode);
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
#include "root_motion_view.h"
|
||||
|
||||
#include "scene/animation/animation_tree.h"
|
||||
#include "scene/animation/animation_mixer.h"
|
||||
#include "scene/resources/material.h"
|
||||
|
||||
void RootMotionView::set_animation_mixer(const NodePath &p_path) {
|
||||
|
|
@ -94,7 +94,6 @@ void RootMotionView::_notification(int p_what) {
|
|||
|
||||
if (has_node(path)) {
|
||||
Node *node = get_node(path);
|
||||
|
||||
AnimationMixer *mixer = Object::cast_to<AnimationMixer>(node);
|
||||
if (mixer && mixer->is_active() && mixer->get_root_motion_track() != NodePath()) {
|
||||
if (is_processing_internal() && mixer->get_callback_mode_process() == AnimationMixer::ANIMATION_CALLBACK_MODE_PROCESS_PHYSICS) {
|
||||
|
|
@ -106,12 +105,12 @@ void RootMotionView::_notification(int p_what) {
|
|||
set_process_internal(true);
|
||||
set_physics_process_internal(false);
|
||||
}
|
||||
|
||||
transform.origin = mixer->get_root_motion_position();
|
||||
transform.basis = mixer->get_root_motion_rotation(); // Scale is meaningless.
|
||||
diff = mixer->get_root_motion_rotation_accumulator();
|
||||
diff = mixer->is_root_motion_local() ? Quaternion() : mixer->get_root_motion_rotation_accumulator();
|
||||
}
|
||||
}
|
||||
|
||||
if (!first && transform == Transform3D()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,10 +57,20 @@ void Tweener::set_tween(const Ref<Tween> &p_tween) {
|
|||
tween_id = p_tween->get_instance_id();
|
||||
}
|
||||
|
||||
void Tweener::start() {
|
||||
elapsed_time = 0;
|
||||
finished = false;
|
||||
}
|
||||
|
||||
Ref<Tween> Tweener::_get_tween() {
|
||||
return Ref<Tween>(ObjectDB::get_instance(tween_id));
|
||||
}
|
||||
|
||||
void Tweener::_finish() {
|
||||
finished = true;
|
||||
emit_signal(SceneStringName(finished));
|
||||
}
|
||||
|
||||
void Tweener::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo("finished"));
|
||||
}
|
||||
|
|
@ -116,7 +126,8 @@ Ref<PropertyTweener> Tween::tween_property(const Object *p_target, const NodePat
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<PropertyTweener> tweener = memnew(PropertyTweener(p_target, property_subnames, p_to, p_duration));
|
||||
Ref<PropertyTweener> tweener;
|
||||
tweener.instantiate(p_target, property_subnames, p_to, p_duration);
|
||||
append(tweener);
|
||||
return tweener;
|
||||
}
|
||||
|
|
@ -124,7 +135,8 @@ Ref<PropertyTweener> Tween::tween_property(const Object *p_target, const NodePat
|
|||
Ref<IntervalTweener> Tween::tween_interval(double p_time) {
|
||||
CHECK_VALID();
|
||||
|
||||
Ref<IntervalTweener> tweener = memnew(IntervalTweener(p_time));
|
||||
Ref<IntervalTweener> tweener;
|
||||
tweener.instantiate(p_time);
|
||||
append(tweener);
|
||||
return tweener;
|
||||
}
|
||||
|
|
@ -132,7 +144,8 @@ Ref<IntervalTweener> Tween::tween_interval(double p_time) {
|
|||
Ref<CallbackTweener> Tween::tween_callback(const Callable &p_callback) {
|
||||
CHECK_VALID();
|
||||
|
||||
Ref<CallbackTweener> tweener = memnew(CallbackTweener(p_callback));
|
||||
Ref<CallbackTweener> tweener;
|
||||
tweener.instantiate(p_callback);
|
||||
append(tweener);
|
||||
return tweener;
|
||||
}
|
||||
|
|
@ -144,7 +157,27 @@ Ref<MethodTweener> Tween::tween_method(const Callable &p_callback, const Variant
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<MethodTweener> tweener = memnew(MethodTweener(p_callback, p_from, p_to, p_duration));
|
||||
Ref<MethodTweener> tweener;
|
||||
tweener.instantiate(p_callback, p_from, p_to, p_duration);
|
||||
append(tweener);
|
||||
return tweener;
|
||||
}
|
||||
|
||||
Ref<SubtweenTweener> Tween::tween_subtween(const Ref<Tween> &p_subtween) {
|
||||
CHECK_VALID();
|
||||
|
||||
// Ensure that the subtween being added is not null.
|
||||
ERR_FAIL_COND_V(p_subtween.is_null(), nullptr);
|
||||
|
||||
Ref<SubtweenTweener> tweener;
|
||||
tweener.instantiate(p_subtween);
|
||||
|
||||
// Remove the tween from its parent tree, if it has one.
|
||||
// If the user created this tween without a parent tree attached,
|
||||
// then this step isn't necessary.
|
||||
if (tweener->subtween->parent_tree != nullptr) {
|
||||
tweener->subtween->parent_tree->remove_tween(tweener->subtween);
|
||||
}
|
||||
append(tweener);
|
||||
return tweener;
|
||||
}
|
||||
|
|
@ -208,7 +241,7 @@ Ref<Tween> Tween::set_process_mode(TweenProcessMode p_mode) {
|
|||
return this;
|
||||
}
|
||||
|
||||
Tween::TweenProcessMode Tween::get_process_mode() {
|
||||
Tween::TweenProcessMode Tween::get_process_mode() const {
|
||||
return process_mode;
|
||||
}
|
||||
|
||||
|
|
@ -217,10 +250,19 @@ Ref<Tween> Tween::set_pause_mode(TweenPauseMode p_mode) {
|
|||
return this;
|
||||
}
|
||||
|
||||
Tween::TweenPauseMode Tween::get_pause_mode() {
|
||||
Tween::TweenPauseMode Tween::get_pause_mode() const {
|
||||
return pause_mode;
|
||||
}
|
||||
|
||||
Ref<Tween> Tween::set_ignore_time_scale(bool p_ignore) {
|
||||
ignore_time_scale = p_ignore;
|
||||
return this;
|
||||
}
|
||||
|
||||
bool Tween::is_ignoring_time_scale() const {
|
||||
return ignore_time_scale;
|
||||
}
|
||||
|
||||
Ref<Tween> Tween::set_parallel(bool p_parallel) {
|
||||
default_parallel = p_parallel;
|
||||
parallel_enabled = p_parallel;
|
||||
|
|
@ -250,7 +292,7 @@ Ref<Tween> Tween::set_trans(TransitionType p_trans) {
|
|||
return this;
|
||||
}
|
||||
|
||||
Tween::TransitionType Tween::get_trans() {
|
||||
Tween::TransitionType Tween::get_trans() const {
|
||||
return default_transition;
|
||||
}
|
||||
|
||||
|
|
@ -259,7 +301,7 @@ Ref<Tween> Tween::set_ease(EaseType p_ease) {
|
|||
return this;
|
||||
}
|
||||
|
||||
Tween::EaseType Tween::get_ease() {
|
||||
Tween::EaseType Tween::get_ease() const {
|
||||
return default_ease;
|
||||
}
|
||||
|
||||
|
|
@ -433,6 +475,7 @@ void Tween::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("tween_interval", "time"), &Tween::tween_interval);
|
||||
ClassDB::bind_method(D_METHOD("tween_callback", "callback"), &Tween::tween_callback);
|
||||
ClassDB::bind_method(D_METHOD("tween_method", "method", "from", "to", "duration"), &Tween::tween_method);
|
||||
ClassDB::bind_method(D_METHOD("tween_subtween", "subtween"), &Tween::tween_subtween);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("custom_step", "delta"), &Tween::custom_step);
|
||||
ClassDB::bind_method(D_METHOD("stop"), &Tween::stop);
|
||||
|
|
@ -446,6 +489,7 @@ void Tween::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("bind_node", "node"), &Tween::bind_node);
|
||||
ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Tween::set_process_mode);
|
||||
ClassDB::bind_method(D_METHOD("set_pause_mode", "mode"), &Tween::set_pause_mode);
|
||||
ClassDB::bind_method(D_METHOD("set_ignore_time_scale", "ignore"), &Tween::set_ignore_time_scale, DEFVAL(true));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_parallel", "parallel"), &Tween::set_parallel, DEFVAL(true));
|
||||
ClassDB::bind_method(D_METHOD("set_loops", "loops"), &Tween::set_loops, DEFVAL(0));
|
||||
|
|
@ -493,8 +537,9 @@ Tween::Tween() {
|
|||
ERR_FAIL_MSG("Tween can't be created directly. Use create_tween() method.");
|
||||
}
|
||||
|
||||
Tween::Tween(bool p_valid) {
|
||||
valid = p_valid;
|
||||
Tween::Tween(SceneTree *p_parent_tree) {
|
||||
parent_tree = p_parent_tree;
|
||||
valid = true;
|
||||
}
|
||||
|
||||
Ref<PropertyTweener> PropertyTweener::from(const Variant &p_value) {
|
||||
|
|
@ -542,8 +587,7 @@ Ref<PropertyTweener> PropertyTweener::set_delay(double p_delay) {
|
|||
}
|
||||
|
||||
void PropertyTweener::start() {
|
||||
elapsed_time = 0;
|
||||
finished = false;
|
||||
Tweener::start();
|
||||
|
||||
Object *target_instance = ObjectDB::get_instance(target);
|
||||
if (!target_instance) {
|
||||
|
|
@ -574,6 +618,7 @@ bool PropertyTweener::step(double &r_delta) {
|
|||
|
||||
Object *target_instance = ObjectDB::get_instance(target);
|
||||
if (!target_instance) {
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
elapsed_time += r_delta;
|
||||
|
|
@ -612,9 +657,8 @@ bool PropertyTweener::step(double &r_delta) {
|
|||
return true;
|
||||
} else {
|
||||
target_instance->set_indexed(property, final_val);
|
||||
finished = true;
|
||||
r_delta = elapsed_time - delay - duration;
|
||||
emit_signal(SceneStringName(finished));
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -656,11 +700,6 @@ PropertyTweener::PropertyTweener() {
|
|||
ERR_FAIL_MSG("PropertyTweener can't be created directly. Use the tween_property() method in Tween.");
|
||||
}
|
||||
|
||||
void IntervalTweener::start() {
|
||||
elapsed_time = 0;
|
||||
finished = false;
|
||||
}
|
||||
|
||||
bool IntervalTweener::step(double &r_delta) {
|
||||
if (finished) {
|
||||
return false;
|
||||
|
|
@ -672,9 +711,8 @@ bool IntervalTweener::step(double &r_delta) {
|
|||
r_delta = 0;
|
||||
return true;
|
||||
} else {
|
||||
finished = true;
|
||||
r_delta = elapsed_time - duration;
|
||||
emit_signal(SceneStringName(finished));
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -692,17 +730,13 @@ Ref<CallbackTweener> CallbackTweener::set_delay(double p_delay) {
|
|||
return this;
|
||||
}
|
||||
|
||||
void CallbackTweener::start() {
|
||||
elapsed_time = 0;
|
||||
finished = false;
|
||||
}
|
||||
|
||||
bool CallbackTweener::step(double &r_delta) {
|
||||
if (finished) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!callback.is_valid()) {
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -715,9 +749,8 @@ bool CallbackTweener::step(double &r_delta) {
|
|||
ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce) + ".");
|
||||
}
|
||||
|
||||
finished = true;
|
||||
r_delta = elapsed_time - delay;
|
||||
emit_signal(SceneStringName(finished));
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -757,17 +790,13 @@ Ref<MethodTweener> MethodTweener::set_ease(Tween::EaseType p_ease) {
|
|||
return this;
|
||||
}
|
||||
|
||||
void MethodTweener::start() {
|
||||
elapsed_time = 0;
|
||||
finished = false;
|
||||
}
|
||||
|
||||
bool MethodTweener::step(double &r_delta) {
|
||||
if (finished) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!callback.is_valid()) {
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -801,9 +830,8 @@ bool MethodTweener::step(double &r_delta) {
|
|||
r_delta = 0;
|
||||
return true;
|
||||
} else {
|
||||
finished = true;
|
||||
r_delta = elapsed_time - delay - duration;
|
||||
emit_signal(SceneStringName(finished));
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -840,3 +868,50 @@ MethodTweener::MethodTweener(const Callable &p_callback, const Variant &p_from,
|
|||
MethodTweener::MethodTweener() {
|
||||
ERR_FAIL_MSG("MethodTweener can't be created directly. Use the tween_method() method in Tween.");
|
||||
}
|
||||
|
||||
void SubtweenTweener::start() {
|
||||
Tweener::start();
|
||||
|
||||
// Reset the subtween.
|
||||
subtween->stop();
|
||||
subtween->play();
|
||||
}
|
||||
|
||||
bool SubtweenTweener::step(double &r_delta) {
|
||||
if (finished) {
|
||||
return false;
|
||||
}
|
||||
|
||||
elapsed_time += r_delta;
|
||||
|
||||
if (elapsed_time < delay) {
|
||||
r_delta = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!subtween->step(r_delta)) {
|
||||
r_delta = elapsed_time - delay - subtween->get_total_time();
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
r_delta = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<SubtweenTweener> SubtweenTweener::set_delay(double p_delay) {
|
||||
delay = p_delay;
|
||||
return this;
|
||||
}
|
||||
|
||||
void SubtweenTweener::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_delay", "delay"), &SubtweenTweener::set_delay);
|
||||
}
|
||||
|
||||
SubtweenTweener::SubtweenTweener(const Ref<Tween> &p_subtween) {
|
||||
subtween = p_subtween;
|
||||
}
|
||||
|
||||
SubtweenTweener::SubtweenTweener() {
|
||||
ERR_FAIL_MSG("SubtweenTweener can't be created directly. Use the tween_subtween() method in Tween.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
class Tween;
|
||||
class Node;
|
||||
class SceneTree;
|
||||
|
||||
class Tweener : public RefCounted {
|
||||
GDCLASS(Tweener, RefCounted);
|
||||
|
|
@ -43,13 +44,14 @@ class Tweener : public RefCounted {
|
|||
|
||||
public:
|
||||
virtual void set_tween(const Ref<Tween> &p_tween);
|
||||
virtual void start() = 0;
|
||||
virtual void start();
|
||||
virtual bool step(double &r_delta) = 0;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
Ref<Tween> _get_tween();
|
||||
void _finish();
|
||||
|
||||
double elapsed_time = 0;
|
||||
bool finished = false;
|
||||
|
|
@ -59,6 +61,7 @@ class PropertyTweener;
|
|||
class IntervalTweener;
|
||||
class CallbackTweener;
|
||||
class MethodTweener;
|
||||
class SubtweenTweener;
|
||||
|
||||
class Tween : public RefCounted {
|
||||
GDCLASS(Tween, RefCounted);
|
||||
|
|
@ -108,12 +111,14 @@ private:
|
|||
EaseType default_ease = EaseType::EASE_IN_OUT;
|
||||
ObjectID bound_node;
|
||||
|
||||
SceneTree *parent_tree = nullptr;
|
||||
Vector<List<Ref<Tweener>>> tweeners;
|
||||
double total_time = 0;
|
||||
int current_step = -1;
|
||||
int loops = 1;
|
||||
int loops_done = 0;
|
||||
float speed_scale = 1;
|
||||
bool ignore_time_scale = false;
|
||||
|
||||
bool is_bound = false;
|
||||
bool started = false;
|
||||
|
|
@ -143,6 +148,7 @@ public:
|
|||
Ref<IntervalTweener> tween_interval(double p_time);
|
||||
Ref<CallbackTweener> tween_callback(const Callable &p_callback);
|
||||
Ref<MethodTweener> tween_method(const Callable &p_callback, const Variant p_from, Variant p_to, double p_duration);
|
||||
Ref<SubtweenTweener> tween_subtween(const Ref<Tween> &p_subtween);
|
||||
void append(Ref<Tweener> p_tweener);
|
||||
|
||||
bool custom_step(double p_delta);
|
||||
|
|
@ -157,18 +163,20 @@ public:
|
|||
|
||||
Ref<Tween> bind_node(const Node *p_node);
|
||||
Ref<Tween> set_process_mode(TweenProcessMode p_mode);
|
||||
TweenProcessMode get_process_mode();
|
||||
TweenProcessMode get_process_mode() const;
|
||||
Ref<Tween> set_pause_mode(TweenPauseMode p_mode);
|
||||
TweenPauseMode get_pause_mode();
|
||||
TweenPauseMode get_pause_mode() const;
|
||||
Ref<Tween> set_ignore_time_scale(bool p_ignore = true);
|
||||
bool is_ignoring_time_scale() const;
|
||||
|
||||
Ref<Tween> set_parallel(bool p_parallel);
|
||||
Ref<Tween> set_loops(int p_loops);
|
||||
int get_loops_left() const;
|
||||
Ref<Tween> set_speed_scale(float p_speed);
|
||||
Ref<Tween> set_trans(TransitionType p_trans);
|
||||
TransitionType get_trans();
|
||||
TransitionType get_trans() const;
|
||||
Ref<Tween> set_ease(EaseType p_ease);
|
||||
EaseType get_ease();
|
||||
EaseType get_ease() const;
|
||||
|
||||
Ref<Tween> parallel();
|
||||
Ref<Tween> chain();
|
||||
|
|
@ -182,7 +190,7 @@ public:
|
|||
double get_total_time() const;
|
||||
|
||||
Tween();
|
||||
Tween(bool p_valid);
|
||||
Tween(SceneTree *p_parent_tree);
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(Tween::TweenPauseMode);
|
||||
|
|
@ -237,7 +245,6 @@ class IntervalTweener : public Tweener {
|
|||
GDCLASS(IntervalTweener, Tweener);
|
||||
|
||||
public:
|
||||
void start() override;
|
||||
bool step(double &r_delta) override;
|
||||
|
||||
IntervalTweener(double p_time);
|
||||
|
|
@ -253,7 +260,6 @@ class CallbackTweener : public Tweener {
|
|||
public:
|
||||
Ref<CallbackTweener> set_delay(double p_delay);
|
||||
|
||||
void start() override;
|
||||
bool step(double &r_delta) override;
|
||||
|
||||
CallbackTweener(const Callable &p_callback);
|
||||
|
|
@ -278,7 +284,6 @@ public:
|
|||
Ref<MethodTweener> set_delay(double p_delay);
|
||||
|
||||
void set_tween(const Ref<Tween> &p_tween) override;
|
||||
void start() override;
|
||||
bool step(double &r_delta) override;
|
||||
|
||||
MethodTweener(const Callable &p_callback, const Variant &p_from, const Variant &p_to, double p_duration);
|
||||
|
|
@ -301,4 +306,24 @@ private:
|
|||
Ref<RefCounted> ref_copy;
|
||||
};
|
||||
|
||||
class SubtweenTweener : public Tweener {
|
||||
GDCLASS(SubtweenTweener, Tweener);
|
||||
|
||||
public:
|
||||
Ref<Tween> subtween;
|
||||
void start() override;
|
||||
bool step(double &r_delta) override;
|
||||
|
||||
Ref<SubtweenTweener> set_delay(double p_delay);
|
||||
|
||||
SubtweenTweener(const Ref<Tween> &p_subtween);
|
||||
SubtweenTweener();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
double delay = 0;
|
||||
};
|
||||
|
||||
#endif // TWEEN_H
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue