1/**************************************************************************/
2/* animation_blend_tree.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "animation_blend_tree.h"
32
33#include "scene/resources/animation.h"
34#include "scene/scene_string_names.h"
35
36void AnimationNodeAnimation::set_animation(const StringName &p_name) {
37 animation = p_name;
38}
39
40StringName AnimationNodeAnimation::get_animation() const {
41 return animation;
42}
43
44Vector<String> (*AnimationNodeAnimation::get_editable_animation_list)() = nullptr;
45
46void AnimationNodeAnimation::get_parameter_list(List<PropertyInfo> *r_list) const {
47 r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
48}
49
50void AnimationNodeAnimation::_validate_property(PropertyInfo &p_property) const {
51 if (p_property.name == "animation" && get_editable_animation_list) {
52 Vector<String> names = get_editable_animation_list();
53 String anims;
54 for (int i = 0; i < names.size(); i++) {
55 if (i > 0) {
56 anims += ",";
57 }
58 anims += String(names[i]);
59 }
60 if (!anims.is_empty()) {
61 p_property.hint = PROPERTY_HINT_ENUM;
62 p_property.hint_string = anims;
63 }
64 }
65}
66
67double AnimationNodeAnimation::_process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only) {
68 AnimationPlayer *ap = state->player;
69 ERR_FAIL_NULL_V(ap, 0);
70
71 double cur_time = get_parameter(time);
72
73 if (!ap->has_animation(animation)) {
74 AnimationNodeBlendTree *tree = Object::cast_to<AnimationNodeBlendTree>(parent);
75 if (tree) {
76 String node_name = tree->get_node_name(Ref<AnimationNodeAnimation>(this));
77 make_invalid(vformat(RTR("On BlendTree node '%s', animation not found: '%s'"), node_name, animation));
78
79 } else {
80 make_invalid(vformat(RTR("Animation not found: '%s'"), animation));
81 }
82
83 return 0;
84 }
85
86 Ref<Animation> anim = ap->get_animation(animation);
87 double anim_size = (double)anim->get_length();
88 double step = 0.0;
89 double prev_time = cur_time;
90 Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
91 bool node_backward = play_mode == PLAY_MODE_BACKWARD;
92
93 if (p_seek) {
94 step = p_time - cur_time;
95 cur_time = p_time;
96 } else {
97 p_time *= backward ? -1.0 : 1.0;
98 cur_time = cur_time + p_time;
99 step = p_time;
100 }
101
102 bool is_looping = false;
103 if (anim->get_loop_mode() == Animation::LOOP_PINGPONG) {
104 if (!Math::is_zero_approx(anim_size)) {
105 if (prev_time >= 0 && cur_time < 0) {
106 backward = !backward;
107 looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
108 }
109 if (prev_time <= anim_size && cur_time > anim_size) {
110 backward = !backward;
111 looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
112 }
113 cur_time = Math::pingpong(cur_time, anim_size);
114 }
115 is_looping = true;
116 } else if (anim->get_loop_mode() == Animation::LOOP_LINEAR) {
117 if (!Math::is_zero_approx(anim_size)) {
118 if (prev_time >= 0 && cur_time < 0) {
119 looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
120 }
121 if (prev_time <= anim_size && cur_time > anim_size) {
122 looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
123 }
124 cur_time = Math::fposmod(cur_time, anim_size);
125 }
126 backward = false;
127 is_looping = true;
128 } else {
129 if (cur_time < 0) {
130 step += cur_time;
131 cur_time = 0;
132 } else if (cur_time > anim_size) {
133 step += anim_size - cur_time;
134 cur_time = anim_size;
135 }
136 backward = false;
137
138 // If ended, don't progress animation. So set delta to 0.
139 if (p_time > 0) {
140 if (play_mode == PLAY_MODE_FORWARD) {
141 if (prev_time >= anim_size) {
142 step = 0;
143 }
144 } else {
145 if (prev_time <= 0) {
146 step = 0;
147 }
148 }
149 }
150
151 // Emit start & finish signal. Internally, the detections are the same for backward.
152 // We should use call_deferred since the track keys are still being prosessed.
153 if (state->tree && !p_test_only) {
154 // AnimationTree uses seek to 0 "internally" to process the first key of the animation, which is used as the start detection.
155 if (p_seek && !p_is_external_seeking && cur_time == 0) {
156 state->tree->call_deferred(SNAME("emit_signal"), "animation_started", animation);
157 }
158 // Finished.
159 if (prev_time < anim_size && cur_time >= anim_size) {
160 state->tree->call_deferred(SNAME("emit_signal"), "animation_finished", animation);
161 }
162 }
163 }
164
165 if (!p_test_only) {
166 if (play_mode == PLAY_MODE_FORWARD) {
167 blend_animation(animation, cur_time, step, p_seek, p_is_external_seeking, 1.0, looped_flag);
168 } else {
169 blend_animation(animation, anim_size - cur_time, -step, p_seek, p_is_external_seeking, 1.0, looped_flag);
170 }
171 }
172 set_parameter(time, cur_time);
173
174 return is_looping ? HUGE_LENGTH : anim_size - cur_time;
175}
176
177String AnimationNodeAnimation::get_caption() const {
178 return "Animation";
179}
180
181void AnimationNodeAnimation::set_play_mode(PlayMode p_play_mode) {
182 play_mode = p_play_mode;
183}
184
185AnimationNodeAnimation::PlayMode AnimationNodeAnimation::get_play_mode() const {
186 return play_mode;
187}
188
189void AnimationNodeAnimation::set_backward(bool p_backward) {
190 backward = p_backward;
191}
192
193bool AnimationNodeAnimation::is_backward() const {
194 return backward;
195}
196
197void AnimationNodeAnimation::_bind_methods() {
198 ClassDB::bind_method(D_METHOD("set_animation", "name"), &AnimationNodeAnimation::set_animation);
199 ClassDB::bind_method(D_METHOD("get_animation"), &AnimationNodeAnimation::get_animation);
200
201 ClassDB::bind_method(D_METHOD("set_play_mode", "mode"), &AnimationNodeAnimation::set_play_mode);
202 ClassDB::bind_method(D_METHOD("get_play_mode"), &AnimationNodeAnimation::get_play_mode);
203
204 ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation");
205 ADD_PROPERTY(PropertyInfo(Variant::INT, "play_mode", PROPERTY_HINT_ENUM, "Forward,Backward"), "set_play_mode", "get_play_mode");
206
207 BIND_ENUM_CONSTANT(PLAY_MODE_FORWARD);
208 BIND_ENUM_CONSTANT(PLAY_MODE_BACKWARD);
209}
210
211AnimationNodeAnimation::AnimationNodeAnimation() {
212}
213
214////////////////////////////////////////////////////////
215
216void AnimationNodeSync::_bind_methods() {
217 ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeSync::set_use_sync);
218 ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeSync::is_using_sync);
219
220 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
221}
222
223void AnimationNodeSync::set_use_sync(bool p_sync) {
224 sync = p_sync;
225}
226
227bool AnimationNodeSync::is_using_sync() const {
228 return sync;
229}
230
231AnimationNodeSync::AnimationNodeSync() {
232}
233
234////////////////////////////////////////////////////////
235void AnimationNodeOneShot::get_parameter_list(List<PropertyInfo> *r_list) const {
236 r_list->push_back(PropertyInfo(Variant::BOOL, active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY));
237 r_list->push_back(PropertyInfo(Variant::BOOL, internal_active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY));
238 r_list->push_back(PropertyInfo(Variant::INT, request, PROPERTY_HINT_ENUM, ",Fire,Abort,Fade Out"));
239 r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
240 r_list->push_back(PropertyInfo(Variant::FLOAT, remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
241 r_list->push_back(PropertyInfo(Variant::FLOAT, fade_out_remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
242 r_list->push_back(PropertyInfo(Variant::FLOAT, time_to_restart, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
243}
244
245Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_parameter) const {
246 if (p_parameter == request) {
247 return ONE_SHOT_REQUEST_NONE;
248 } else if (p_parameter == active || p_parameter == internal_active) {
249 return false;
250 } else if (p_parameter == time_to_restart) {
251 return -1;
252 } else {
253 return 0.0;
254 }
255}
256
257bool AnimationNodeOneShot::is_parameter_read_only(const StringName &p_parameter) const {
258 if (p_parameter == active || p_parameter == internal_active) {
259 return true;
260 }
261 return false;
262}
263
264void AnimationNodeOneShot::set_fade_in_time(double p_time) {
265 fade_in = p_time;
266}
267
268double AnimationNodeOneShot::get_fade_in_time() const {
269 return fade_in;
270}
271
272void AnimationNodeOneShot::set_fade_out_time(double p_time) {
273 fade_out = p_time;
274}
275
276double AnimationNodeOneShot::get_fade_out_time() const {
277 return fade_out;
278}
279
280void AnimationNodeOneShot::set_fade_in_curve(const Ref<Curve> &p_curve) {
281 fade_in_curve = p_curve;
282}
283
284Ref<Curve> AnimationNodeOneShot::get_fade_in_curve() const {
285 return fade_in_curve;
286}
287
288void AnimationNodeOneShot::set_fade_out_curve(const Ref<Curve> &p_curve) {
289 fade_out_curve = p_curve;
290}
291
292Ref<Curve> AnimationNodeOneShot::get_fade_out_curve() const {
293 return fade_out_curve;
294}
295
296void AnimationNodeOneShot::set_auto_restart_enabled(bool p_enabled) {
297 auto_restart = p_enabled;
298}
299
300void AnimationNodeOneShot::set_auto_restart_delay(double p_time) {
301 auto_restart_delay = p_time;
302}
303
304void AnimationNodeOneShot::set_auto_restart_random_delay(double p_time) {
305 auto_restart_random_delay = p_time;
306}
307
308bool AnimationNodeOneShot::is_auto_restart_enabled() const {
309 return auto_restart;
310}
311
312double AnimationNodeOneShot::get_auto_restart_delay() const {
313 return auto_restart_delay;
314}
315
316double AnimationNodeOneShot::get_auto_restart_random_delay() const {
317 return auto_restart_random_delay;
318}
319
320void AnimationNodeOneShot::set_mix_mode(MixMode p_mix) {
321 mix = p_mix;
322}
323
324AnimationNodeOneShot::MixMode AnimationNodeOneShot::get_mix_mode() const {
325 return mix;
326}
327
328String AnimationNodeOneShot::get_caption() const {
329 return "OneShot";
330}
331
332bool AnimationNodeOneShot::has_filter() const {
333 return true;
334}
335
336double AnimationNodeOneShot::_process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only) {
337 OneShotRequest cur_request = static_cast<OneShotRequest>((int)get_parameter(request));
338 bool cur_active = get_parameter(active);
339 bool cur_internal_active = get_parameter(internal_active);
340 double cur_time = get_parameter(time);
341 double cur_remaining = get_parameter(remaining);
342 double cur_fade_out_remaining = get_parameter(fade_out_remaining);
343 double cur_time_to_restart = get_parameter(time_to_restart);
344
345 set_parameter(request, ONE_SHOT_REQUEST_NONE);
346
347 bool is_shooting = true;
348 bool clear_remaining_fade = false;
349 bool is_fading_out = cur_active == true && cur_internal_active == false;
350
351 if (p_time == 0 && p_seek && !p_is_external_seeking) {
352 clear_remaining_fade = true; // Reset occurs.
353 }
354
355 bool do_start = cur_request == ONE_SHOT_REQUEST_FIRE;
356 if (cur_request == ONE_SHOT_REQUEST_ABORT) {
357 set_parameter(internal_active, false);
358 set_parameter(active, false);
359 set_parameter(time_to_restart, -1);
360 is_shooting = false;
361 } else if (cur_request == ONE_SHOT_REQUEST_FADE_OUT && !is_fading_out) { // If fading, keep current fade.
362 if (cur_active) {
363 // Request fading.
364 is_fading_out = true;
365 cur_fade_out_remaining = fade_out;
366 } else {
367 // Shot is ended, do nothing.
368 is_shooting = false;
369 }
370 set_parameter(internal_active, false);
371 set_parameter(time_to_restart, -1);
372 } else if (!do_start && !cur_active) {
373 if (cur_time_to_restart >= 0.0 && !p_seek) {
374 cur_time_to_restart -= p_time;
375 if (cur_time_to_restart < 0) {
376 do_start = true; // Restart.
377 }
378 set_parameter(time_to_restart, cur_time_to_restart);
379 }
380 if (!do_start) {
381 is_shooting = false;
382 }
383 }
384
385 bool os_seek = p_seek;
386
387 if (clear_remaining_fade) {
388 os_seek = false;
389 cur_fade_out_remaining = 0;
390 set_parameter(fade_out_remaining, 0);
391 if (is_fading_out) {
392 is_fading_out = false;
393 set_parameter(internal_active, false);
394 set_parameter(active, false);
395 }
396 }
397
398 if (!is_shooting) {
399 return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync, p_test_only);
400 }
401
402 if (do_start) {
403 cur_time = 0;
404 os_seek = true;
405 set_parameter(request, ONE_SHOT_REQUEST_NONE);
406 set_parameter(internal_active, true);
407 set_parameter(active, true);
408 }
409
410 real_t blend = 1.0;
411 bool use_blend = sync;
412 if (cur_time < fade_in) {
413 if (fade_in > 0) {
414 use_blend = true;
415 blend = cur_time / fade_in;
416 if (fade_in_curve.is_valid()) {
417 blend = fade_in_curve->sample(blend);
418 }
419 } else {
420 blend = 0; // Should not happen.
421 }
422 } else if (!do_start && !is_fading_out && cur_remaining <= fade_out) {
423 is_fading_out = true;
424 cur_fade_out_remaining = cur_remaining;
425 set_parameter(internal_active, false);
426 }
427
428 if (is_fading_out) {
429 use_blend = true;
430 if (fade_out > 0) {
431 blend = cur_fade_out_remaining / fade_out;
432 if (fade_out_curve.is_valid()) {
433 blend = 1.0 - fade_out_curve->sample(1.0 - blend);
434 }
435 } else {
436 blend = 0;
437 }
438 }
439
440 double main_rem = 0.0;
441 if (mix == MIX_MODE_ADD) {
442 main_rem = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync, p_test_only);
443 } else {
444 main_rem = blend_input(0, p_time, use_blend && p_seek, p_is_external_seeking, 1.0 - blend, FILTER_BLEND, sync, p_test_only); // Unlike below, processing this edge is a corner case.
445 }
446 double os_rem = blend_input(1, os_seek ? cur_time : p_time, os_seek, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_PASS, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
447
448 if (do_start) {
449 cur_remaining = os_rem;
450 }
451
452 if (p_seek) {
453 cur_time = p_time;
454 } else {
455 cur_time += p_time;
456 cur_remaining = os_rem;
457 cur_fade_out_remaining -= p_time;
458 if (cur_remaining <= 0 || (is_fading_out && cur_fade_out_remaining <= 0)) {
459 set_parameter(internal_active, false);
460 set_parameter(active, false);
461 if (auto_restart) {
462 double restart_sec = auto_restart_delay + Math::randd() * auto_restart_random_delay;
463 set_parameter(time_to_restart, restart_sec);
464 }
465 }
466 }
467
468 set_parameter(time, cur_time);
469 set_parameter(remaining, cur_remaining);
470 set_parameter(fade_out_remaining, cur_fade_out_remaining);
471
472 return MAX(main_rem, cur_remaining);
473}
474
475void AnimationNodeOneShot::_bind_methods() {
476 ClassDB::bind_method(D_METHOD("set_fadein_time", "time"), &AnimationNodeOneShot::set_fade_in_time);
477 ClassDB::bind_method(D_METHOD("get_fadein_time"), &AnimationNodeOneShot::get_fade_in_time);
478
479 ClassDB::bind_method(D_METHOD("set_fadein_curve", "curve"), &AnimationNodeOneShot::set_fade_in_curve);
480 ClassDB::bind_method(D_METHOD("get_fadein_curve"), &AnimationNodeOneShot::get_fade_in_curve);
481
482 ClassDB::bind_method(D_METHOD("set_fadeout_time", "time"), &AnimationNodeOneShot::set_fade_out_time);
483 ClassDB::bind_method(D_METHOD("get_fadeout_time"), &AnimationNodeOneShot::get_fade_out_time);
484
485 ClassDB::bind_method(D_METHOD("set_fadeout_curve", "curve"), &AnimationNodeOneShot::set_fade_out_curve);
486 ClassDB::bind_method(D_METHOD("get_fadeout_curve"), &AnimationNodeOneShot::get_fade_out_curve);
487
488 ClassDB::bind_method(D_METHOD("set_autorestart", "active"), &AnimationNodeOneShot::set_auto_restart_enabled);
489 ClassDB::bind_method(D_METHOD("has_autorestart"), &AnimationNodeOneShot::is_auto_restart_enabled);
490
491 ClassDB::bind_method(D_METHOD("set_autorestart_delay", "time"), &AnimationNodeOneShot::set_auto_restart_delay);
492 ClassDB::bind_method(D_METHOD("get_autorestart_delay"), &AnimationNodeOneShot::get_auto_restart_delay);
493
494 ClassDB::bind_method(D_METHOD("set_autorestart_random_delay", "time"), &AnimationNodeOneShot::set_auto_restart_random_delay);
495 ClassDB::bind_method(D_METHOD("get_autorestart_random_delay"), &AnimationNodeOneShot::get_auto_restart_random_delay);
496
497 ClassDB::bind_method(D_METHOD("set_mix_mode", "mode"), &AnimationNodeOneShot::set_mix_mode);
498 ClassDB::bind_method(D_METHOD("get_mix_mode"), &AnimationNodeOneShot::get_mix_mode);
499
500 ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_mode", PROPERTY_HINT_ENUM, "Blend,Add"), "set_mix_mode", "get_mix_mode");
501
502 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadein_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_fadein_time", "get_fadein_time");
503 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fadein_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_fadein_curve", "get_fadein_curve");
504 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadeout_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_fadeout_time", "get_fadeout_time");
505 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fadeout_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_fadeout_curve", "get_fadeout_curve");
506
507 ADD_GROUP("Auto Restart", "autorestart_");
508 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autorestart"), "set_autorestart", "has_autorestart");
509
510 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_autorestart_delay", "get_autorestart_delay");
511 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_random_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_autorestart_random_delay", "get_autorestart_random_delay");
512
513 BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_NONE);
514 BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_FIRE);
515 BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_ABORT);
516 BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_FADE_OUT);
517
518 BIND_ENUM_CONSTANT(MIX_MODE_BLEND);
519 BIND_ENUM_CONSTANT(MIX_MODE_ADD);
520}
521
522AnimationNodeOneShot::AnimationNodeOneShot() {
523 add_input("in");
524 add_input("shot");
525}
526
527////////////////////////////////////////////////
528
529void AnimationNodeAdd2::get_parameter_list(List<PropertyInfo> *r_list) const {
530 r_list->push_back(PropertyInfo(Variant::FLOAT, add_amount, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater"));
531}
532
533Variant AnimationNodeAdd2::get_parameter_default_value(const StringName &p_parameter) const {
534 return 0;
535}
536
537String AnimationNodeAdd2::get_caption() const {
538 return "Add2";
539}
540
541bool AnimationNodeAdd2::has_filter() const {
542 return true;
543}
544
545double AnimationNodeAdd2::_process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only) {
546 double amount = get_parameter(add_amount);
547 double rem0 = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync, p_test_only);
548 blend_input(1, p_time, p_seek, p_is_external_seeking, amount, FILTER_PASS, sync, p_test_only);
549
550 return rem0;
551}
552
553void AnimationNodeAdd2::_bind_methods() {
554}
555
556AnimationNodeAdd2::AnimationNodeAdd2() {
557 add_input("in");
558 add_input("add");
559}
560
561////////////////////////////////////////////////
562
563void AnimationNodeAdd3::get_parameter_list(List<PropertyInfo> *r_list) const {
564 r_list->push_back(PropertyInfo(Variant::FLOAT, add_amount, PROPERTY_HINT_RANGE, "-1,1,0.01,or_less,or_greater"));
565}
566
567Variant AnimationNodeAdd3::get_parameter_default_value(const StringName &p_parameter) const {
568 return 0;
569}
570
571String AnimationNodeAdd3::get_caption() const {
572 return "Add3";
573}
574
575bool AnimationNodeAdd3::has_filter() const {
576 return true;
577}
578
579double AnimationNodeAdd3::_process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only) {
580 double amount = get_parameter(add_amount);
581 blend_input(0, p_time, p_seek, p_is_external_seeking, MAX(0, -amount), FILTER_PASS, sync, p_test_only);
582 double rem0 = blend_input(1, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync, p_test_only);
583 blend_input(2, p_time, p_seek, p_is_external_seeking, MAX(0, amount), FILTER_PASS, sync, p_test_only);
584
585 return rem0;
586}
587
588void AnimationNodeAdd3::_bind_methods() {
589}
590
591AnimationNodeAdd3::AnimationNodeAdd3() {
592 add_input("-add");
593 add_input("in");
594 add_input("+add");
595}
596
597/////////////////////////////////////////////
598
599void AnimationNodeBlend2::get_parameter_list(List<PropertyInfo> *r_list) const {
600 r_list->push_back(PropertyInfo(Variant::FLOAT, blend_amount, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater"));
601}
602
603Variant AnimationNodeBlend2::get_parameter_default_value(const StringName &p_parameter) const {
604 return 0; // For blend amount.
605}
606
607String AnimationNodeBlend2::get_caption() const {
608 return "Blend2";
609}
610
611double AnimationNodeBlend2::_process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only) {
612 double amount = get_parameter(blend_amount);
613
614 double rem0 = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0 - amount, FILTER_BLEND, sync, p_test_only);
615 double rem1 = blend_input(1, p_time, p_seek, p_is_external_seeking, amount, FILTER_PASS, sync, p_test_only);
616
617 return amount > 0.5 ? rem1 : rem0; // Hacky but good enough.
618}
619
620bool AnimationNodeBlend2::has_filter() const {
621 return true;
622}
623
624void AnimationNodeBlend2::_bind_methods() {
625}
626
627AnimationNodeBlend2::AnimationNodeBlend2() {
628 add_input("in");
629 add_input("blend");
630}
631
632//////////////////////////////////////
633
634void AnimationNodeBlend3::get_parameter_list(List<PropertyInfo> *r_list) const {
635 r_list->push_back(PropertyInfo(Variant::FLOAT, blend_amount, PROPERTY_HINT_RANGE, "-1,1,0.01,or_less,or_greater"));
636}
637
638Variant AnimationNodeBlend3::get_parameter_default_value(const StringName &p_parameter) const {
639 return 0; // For blend amount.
640}
641
642String AnimationNodeBlend3::get_caption() const {
643 return "Blend3";
644}
645
646double AnimationNodeBlend3::_process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only) {
647 double amount = get_parameter(blend_amount);
648 double rem0 = blend_input(0, p_time, p_seek, p_is_external_seeking, MAX(0, -amount), FILTER_IGNORE, sync, p_test_only);
649 double rem1 = blend_input(1, p_time, p_seek, p_is_external_seeking, 1.0 - ABS(amount), FILTER_IGNORE, sync, p_test_only);
650 double rem2 = blend_input(2, p_time, p_seek, p_is_external_seeking, MAX(0, amount), FILTER_IGNORE, sync, p_test_only);
651
652 return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); // Hacky but good enough.
653}
654
655void AnimationNodeBlend3::_bind_methods() {
656}
657
658AnimationNodeBlend3::AnimationNodeBlend3() {
659 add_input("-blend");
660 add_input("in");
661 add_input("+blend");
662}
663
664////////////////////////////////////////////////
665
666void AnimationNodeSub2::get_parameter_list(List<PropertyInfo> *r_list) const {
667 r_list->push_back(PropertyInfo(Variant::FLOAT, sub_amount, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater"));
668}
669
670Variant AnimationNodeSub2::get_parameter_default_value(const StringName &p_parameter) const {
671 return 0;
672}
673
674String AnimationNodeSub2::get_caption() const {
675 return "Sub2";
676}
677
678bool AnimationNodeSub2::has_filter() const {
679 return true;
680}
681
682double AnimationNodeSub2::_process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only) {
683 double amount = get_parameter(sub_amount);
684 // Out = Sub.Transform3D^(-1) * In.Transform3D
685 blend_input(1, p_time, p_seek, p_is_external_seeking, -amount, FILTER_PASS, sync, p_test_only);
686 return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync, p_test_only);
687}
688
689void AnimationNodeSub2::_bind_methods() {
690}
691
692AnimationNodeSub2::AnimationNodeSub2() {
693 add_input("in");
694 add_input("sub");
695}
696
697/////////////////////////////////
698
699void AnimationNodeTimeScale::get_parameter_list(List<PropertyInfo> *r_list) const {
700 r_list->push_back(PropertyInfo(Variant::FLOAT, scale, PROPERTY_HINT_RANGE, "-32,32,0.01,or_less,or_greater"));
701}
702
703Variant AnimationNodeTimeScale::get_parameter_default_value(const StringName &p_parameter) const {
704 return 1.0; // Initial timescale.
705}
706
707String AnimationNodeTimeScale::get_caption() const {
708 return "TimeScale";
709}
710
711double AnimationNodeTimeScale::_process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only) {
712 double cur_scale = get_parameter(scale);
713 if (p_seek) {
714 return blend_input(0, p_time, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true, p_test_only);
715 } else {
716 return blend_input(0, p_time * cur_scale, false, p_is_external_seeking, 1.0, FILTER_IGNORE, true, p_test_only);
717 }
718}
719
720void AnimationNodeTimeScale::_bind_methods() {
721}
722
723AnimationNodeTimeScale::AnimationNodeTimeScale() {
724 add_input("in");
725}
726
727////////////////////////////////////
728
729void AnimationNodeTimeSeek::get_parameter_list(List<PropertyInfo> *r_list) const {
730 r_list->push_back(PropertyInfo(Variant::FLOAT, seek_pos_request, PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater")); // It will be reset to -1 after seeking the position immediately.
731}
732
733Variant AnimationNodeTimeSeek::get_parameter_default_value(const StringName &p_parameter) const {
734 return -1.0; // Initial seek request.
735}
736
737String AnimationNodeTimeSeek::get_caption() const {
738 return "TimeSeek";
739}
740
741double AnimationNodeTimeSeek::_process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only) {
742 double cur_seek_pos = get_parameter(seek_pos_request);
743 if (p_seek) {
744 return blend_input(0, p_time, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true, p_test_only);
745 } else if (cur_seek_pos >= 0) {
746 double ret = blend_input(0, cur_seek_pos, true, true, 1.0, FILTER_IGNORE, true, p_test_only);
747 set_parameter(seek_pos_request, -1.0); // Reset.
748 return ret;
749 } else {
750 return blend_input(0, p_time, false, p_is_external_seeking, 1.0, FILTER_IGNORE, true, p_test_only);
751 }
752}
753
754void AnimationNodeTimeSeek::_bind_methods() {
755}
756
757AnimationNodeTimeSeek::AnimationNodeTimeSeek() {
758 add_input("in");
759}
760
761/////////////////////////////////////////////////
762
763bool AnimationNodeTransition::_set(const StringName &p_path, const Variant &p_value) {
764 String path = p_path;
765
766 if (!path.begins_with("input_")) {
767 return false;
768 }
769
770 int which = path.get_slicec('/', 0).get_slicec('_', 1).to_int();
771 String what = path.get_slicec('/', 1);
772
773 if (which == get_input_count() && what == "name") {
774 if (add_input(p_value)) {
775 return true;
776 }
777 return false;
778 }
779
780 ERR_FAIL_INDEX_V(which, get_input_count(), false);
781
782 if (what == "name") {
783 set_input_name(which, p_value);
784 } else if (what == "auto_advance") {
785 set_input_as_auto_advance(which, p_value);
786 } else if (what == "reset") {
787 set_input_reset(which, p_value);
788 } else {
789 return false;
790 }
791
792 return true;
793}
794
795bool AnimationNodeTransition::_get(const StringName &p_path, Variant &r_ret) const {
796 String path = p_path;
797
798 if (!path.begins_with("input_")) {
799 return false;
800 }
801
802 int which = path.get_slicec('/', 0).get_slicec('_', 1).to_int();
803 String what = path.get_slicec('/', 1);
804
805 ERR_FAIL_INDEX_V(which, get_input_count(), false);
806
807 if (what == "name") {
808 r_ret = get_input_name(which);
809 } else if (what == "auto_advance") {
810 r_ret = is_input_set_as_auto_advance(which);
811 } else if (what == "reset") {
812 r_ret = is_input_reset(which);
813 } else {
814 return false;
815 }
816
817 return true;
818}
819
820void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) const {
821 String anims;
822 for (int i = 0; i < get_input_count(); i++) {
823 if (i > 0) {
824 anims += ",";
825 }
826 anims += inputs[i].name;
827 }
828
829 r_list->push_back(PropertyInfo(Variant::STRING, current_state, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); // For interface.
830 r_list->push_back(PropertyInfo(Variant::STRING, transition_request, PROPERTY_HINT_ENUM, anims)); // For transition request. It will be cleared after setting the value immediately.
831 r_list->push_back(PropertyInfo(Variant::INT, current_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY)); // To avoid finding the index every frame, use this internally.
832 r_list->push_back(PropertyInfo(Variant::INT, prev_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
833 r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
834 r_list->push_back(PropertyInfo(Variant::FLOAT, prev_xfading, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
835}
836
837Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p_parameter) const {
838 if (p_parameter == time || p_parameter == prev_xfading) {
839 return 0.0;
840 } else if (p_parameter == prev_index || p_parameter == current_index) {
841 return -1;
842 } else {
843 return String();
844 }
845}
846
847bool AnimationNodeTransition::is_parameter_read_only(const StringName &p_parameter) const {
848 if (p_parameter == current_state || p_parameter == current_index) {
849 return true;
850 }
851 return false;
852}
853
854String AnimationNodeTransition::get_caption() const {
855 return "Transition";
856}
857
858void AnimationNodeTransition::set_input_count(int p_inputs) {
859 for (int i = get_input_count(); i < p_inputs; i++) {
860 add_input("state_" + itos(i));
861 }
862 while (get_input_count() > p_inputs) {
863 remove_input(get_input_count() - 1);
864 }
865
866 pending_update = true;
867
868 emit_signal(SNAME("tree_changed")); // For updating connect activity map.
869 notify_property_list_changed();
870}
871
872bool AnimationNodeTransition::add_input(const String &p_name) {
873 if (AnimationNode::add_input(p_name)) {
874 input_data.push_back(InputData());
875 return true;
876 }
877 return false;
878}
879
880void AnimationNodeTransition::remove_input(int p_index) {
881 input_data.remove_at(p_index);
882 AnimationNode::remove_input(p_index);
883}
884
885bool AnimationNodeTransition::set_input_name(int p_input, const String &p_name) {
886 pending_update = true;
887 return AnimationNode::set_input_name(p_input, p_name);
888}
889
890void AnimationNodeTransition::set_input_as_auto_advance(int p_input, bool p_enable) {
891 ERR_FAIL_INDEX(p_input, get_input_count());
892 input_data.write[p_input].auto_advance = p_enable;
893}
894
895bool AnimationNodeTransition::is_input_set_as_auto_advance(int p_input) const {
896 ERR_FAIL_INDEX_V(p_input, get_input_count(), false);
897 return input_data[p_input].auto_advance;
898}
899
900void AnimationNodeTransition::set_input_reset(int p_input, bool p_enable) {
901 ERR_FAIL_INDEX(p_input, get_input_count());
902 input_data.write[p_input].reset = p_enable;
903}
904
905bool AnimationNodeTransition::is_input_reset(int p_input) const {
906 ERR_FAIL_INDEX_V(p_input, get_input_count(), true);
907 return input_data[p_input].reset;
908}
909
910void AnimationNodeTransition::set_xfade_time(double p_fade) {
911 xfade_time = p_fade;
912}
913
914double AnimationNodeTransition::get_xfade_time() const {
915 return xfade_time;
916}
917
918void AnimationNodeTransition::set_xfade_curve(const Ref<Curve> &p_curve) {
919 xfade_curve = p_curve;
920}
921
922Ref<Curve> AnimationNodeTransition::get_xfade_curve() const {
923 return xfade_curve;
924}
925
926void AnimationNodeTransition::set_allow_transition_to_self(bool p_enable) {
927 allow_transition_to_self = p_enable;
928}
929
930bool AnimationNodeTransition::is_allow_transition_to_self() const {
931 return allow_transition_to_self;
932}
933
934double AnimationNodeTransition::_process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only) {
935 String cur_transition_request = get_parameter(transition_request);
936 int cur_current_index = get_parameter(current_index);
937 int cur_prev_index = get_parameter(prev_index);
938
939 double cur_time = get_parameter(time);
940 double cur_prev_xfading = get_parameter(prev_xfading);
941
942 bool switched = false;
943 bool restart = false;
944 bool clear_remaining_fade = false;
945
946 if (pending_update) {
947 if (cur_current_index < 0 || cur_current_index >= get_input_count()) {
948 set_parameter(prev_index, -1);
949 if (get_input_count() > 0) {
950 set_parameter(current_index, 0);
951 set_parameter(current_state, get_input_name(0));
952 } else {
953 set_parameter(current_index, -1);
954 set_parameter(current_state, StringName());
955 }
956 } else {
957 set_parameter(current_state, get_input_name(cur_current_index));
958 }
959 pending_update = false;
960 }
961
962 if (p_time == 0 && p_seek && !p_is_external_seeking) {
963 clear_remaining_fade = true; // Reset occurs.
964 }
965
966 if (!cur_transition_request.is_empty()) {
967 int new_idx = find_input(cur_transition_request);
968 if (new_idx >= 0) {
969 if (cur_current_index == new_idx) {
970 if (allow_transition_to_self) {
971 // Transition to same state.
972 restart = input_data[cur_current_index].reset;
973 clear_remaining_fade = true;
974 }
975 } else {
976 switched = true;
977 cur_prev_index = cur_current_index;
978 set_parameter(prev_index, cur_current_index);
979 cur_current_index = new_idx;
980 set_parameter(current_index, cur_current_index);
981 set_parameter(current_state, cur_transition_request);
982 }
983 } else {
984 ERR_PRINT("No such input: '" + cur_transition_request + "'");
985 }
986 cur_transition_request = String();
987 set_parameter(transition_request, cur_transition_request);
988 }
989
990 if (clear_remaining_fade) {
991 cur_prev_xfading = 0;
992 set_parameter(prev_xfading, 0);
993 cur_prev_index = -1;
994 set_parameter(prev_index, -1);
995 }
996
997 // Special case for restart.
998 if (restart) {
999 set_parameter(time, 0);
1000 return blend_input(cur_current_index, 0, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true, p_test_only);
1001 }
1002
1003 if (switched) {
1004 cur_prev_xfading = xfade_time;
1005 cur_time = 0;
1006 }
1007
1008 if (cur_current_index < 0 || cur_current_index >= get_input_count() || cur_prev_index >= get_input_count()) {
1009 return 0;
1010 }
1011
1012 double rem = 0.0;
1013 double abs_time = Math::abs(p_time);
1014
1015 if (sync) {
1016 for (int i = 0; i < get_input_count(); i++) {
1017 if (i != cur_current_index && i != cur_prev_index) {
1018 blend_input(i, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true, p_test_only);
1019 }
1020 }
1021 }
1022
1023 if (cur_prev_index < 0) { // Process current animation, check for transition.
1024
1025 rem = blend_input(cur_current_index, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true, p_test_only);
1026
1027 if (p_seek) {
1028 cur_time = abs_time;
1029 } else {
1030 cur_time += abs_time;
1031 }
1032
1033 if (input_data[cur_current_index].auto_advance && rem <= xfade_time) {
1034 set_parameter(transition_request, get_input_name((cur_current_index + 1) % get_input_count()));
1035 }
1036
1037 } else { // Cross-fading from prev to current.
1038
1039 real_t blend = 0.0;
1040 real_t blend_inv = 1.0;
1041 bool use_blend = sync;
1042 if (xfade_time > 0) {
1043 use_blend = true;
1044 blend = cur_prev_xfading / xfade_time;
1045 if (xfade_curve.is_valid()) {
1046 blend = xfade_curve->sample(blend);
1047 }
1048 blend_inv = 1.0 - blend;
1049 blend = Math::is_zero_approx(blend) ? CMP_EPSILON : blend;
1050 blend_inv = Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv;
1051 }
1052
1053 // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
1054 if (input_data[cur_current_index].reset && !p_seek && switched) { // Just switched, seek to start of current.
1055 rem = blend_input(cur_current_index, 0, true, p_is_external_seeking, blend_inv, FILTER_IGNORE, true, p_test_only);
1056 } else {
1057 rem = blend_input(cur_current_index, p_time, p_seek, p_is_external_seeking, blend_inv, FILTER_IGNORE, true, p_test_only);
1058 }
1059
1060 blend_input(cur_prev_index, p_time, use_blend && p_seek, p_is_external_seeking, blend, FILTER_IGNORE, true, p_test_only);
1061 if (p_seek) {
1062 cur_time = abs_time;
1063 } else {
1064 cur_time += abs_time;
1065 cur_prev_xfading -= abs_time;
1066 if (cur_prev_xfading < 0) {
1067 set_parameter(prev_index, -1);
1068 }
1069 }
1070 }
1071
1072 set_parameter(time, cur_time);
1073 set_parameter(prev_xfading, cur_prev_xfading);
1074
1075 return rem;
1076}
1077
1078void AnimationNodeTransition::_get_property_list(List<PropertyInfo> *p_list) const {
1079 for (int i = 0; i < get_input_count(); i++) {
1080 p_list->push_back(PropertyInfo(Variant::STRING, "input_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
1081 p_list->push_back(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/auto_advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
1082 p_list->push_back(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/reset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
1083 }
1084}
1085
1086void AnimationNodeTransition::_bind_methods() {
1087 ClassDB::bind_method(D_METHOD("set_input_count", "input_count"), &AnimationNodeTransition::set_input_count);
1088
1089 ClassDB::bind_method(D_METHOD("set_input_as_auto_advance", "input", "enable"), &AnimationNodeTransition::set_input_as_auto_advance);
1090 ClassDB::bind_method(D_METHOD("is_input_set_as_auto_advance", "input"), &AnimationNodeTransition::is_input_set_as_auto_advance);
1091
1092 ClassDB::bind_method(D_METHOD("set_input_reset", "input", "enable"), &AnimationNodeTransition::set_input_reset);
1093 ClassDB::bind_method(D_METHOD("is_input_reset", "input"), &AnimationNodeTransition::is_input_reset);
1094
1095 ClassDB::bind_method(D_METHOD("set_xfade_time", "time"), &AnimationNodeTransition::set_xfade_time);
1096 ClassDB::bind_method(D_METHOD("get_xfade_time"), &AnimationNodeTransition::get_xfade_time);
1097
1098 ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeTransition::set_xfade_curve);
1099 ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeTransition::get_xfade_curve);
1100
1101 ClassDB::bind_method(D_METHOD("set_allow_transition_to_self", "enable"), &AnimationNodeTransition::set_allow_transition_to_self);
1102 ClassDB::bind_method(D_METHOD("is_allow_transition_to_self"), &AnimationNodeTransition::is_allow_transition_to_self);
1103
1104 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01,suffix:s"), "set_xfade_time", "get_xfade_time");
1105 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve");
1106 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_transition_to_self"), "set_allow_transition_to_self", "is_allow_transition_to_self");
1107 ADD_PROPERTY(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED, "Inputs,input_"), "set_input_count", "get_input_count");
1108}
1109
1110AnimationNodeTransition::AnimationNodeTransition() {
1111}
1112
1113/////////////////////
1114
1115String AnimationNodeOutput::get_caption() const {
1116 return "Output";
1117}
1118
1119double AnimationNodeOutput::_process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only) {
1120 return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true, p_test_only);
1121}
1122
1123AnimationNodeOutput::AnimationNodeOutput() {
1124 add_input("output");
1125}
1126
1127///////////////////////////////////////////////////////
1128void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position) {
1129 ERR_FAIL_COND(nodes.has(p_name));
1130 ERR_FAIL_COND(p_node.is_null());
1131 ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output);
1132 ERR_FAIL_COND(String(p_name).contains("/"));
1133
1134 Node n;
1135 n.node = p_node;
1136 n.position = p_position;
1137 n.connections.resize(n.node->get_input_count());
1138 nodes[p_name] = n;
1139
1140 emit_changed();
1141 emit_signal(SNAME("tree_changed"));
1142
1143 p_node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed), CONNECT_REFERENCE_COUNTED);
1144 p_node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
1145 p_node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
1146 p_node->connect_changed(callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_name), CONNECT_REFERENCE_COUNTED);
1147}
1148
1149Ref<AnimationNode> AnimationNodeBlendTree::get_node(const StringName &p_name) const {
1150 ERR_FAIL_COND_V(!nodes.has(p_name), Ref<AnimationNode>());
1151
1152 return nodes[p_name].node;
1153}
1154
1155StringName AnimationNodeBlendTree::get_node_name(const Ref<AnimationNode> &p_node) const {
1156 for (const KeyValue<StringName, Node> &E : nodes) {
1157 if (E.value.node == p_node) {
1158 return E.key;
1159 }
1160 }
1161
1162 ERR_FAIL_V(StringName());
1163}
1164
1165void AnimationNodeBlendTree::set_node_position(const StringName &p_node, const Vector2 &p_position) {
1166 ERR_FAIL_COND(!nodes.has(p_node));
1167 nodes[p_node].position = p_position;
1168}
1169
1170Vector2 AnimationNodeBlendTree::get_node_position(const StringName &p_node) const {
1171 ERR_FAIL_COND_V(!nodes.has(p_node), Vector2());
1172 return nodes[p_node].position;
1173}
1174
1175void AnimationNodeBlendTree::get_child_nodes(List<ChildNode> *r_child_nodes) {
1176 Vector<StringName> ns;
1177
1178 for (const KeyValue<StringName, Node> &E : nodes) {
1179 ns.push_back(E.key);
1180 }
1181
1182 for (int i = 0; i < ns.size(); i++) {
1183 ChildNode cn;
1184 cn.name = ns[i];
1185 cn.node = nodes[cn.name].node;
1186 r_child_nodes->push_back(cn);
1187 }
1188}
1189
1190bool AnimationNodeBlendTree::has_node(const StringName &p_name) const {
1191 return nodes.has(p_name);
1192}
1193
1194Vector<StringName> AnimationNodeBlendTree::get_node_connection_array(const StringName &p_name) const {
1195 ERR_FAIL_COND_V(!nodes.has(p_name), Vector<StringName>());
1196 return nodes[p_name].connections;
1197}
1198
1199void AnimationNodeBlendTree::remove_node(const StringName &p_name) {
1200 ERR_FAIL_COND(!nodes.has(p_name));
1201 ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output); //can't delete output
1202
1203 {
1204 Ref<AnimationNode> node = nodes[p_name].node;
1205 node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed));
1206 node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_renamed));
1207 node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_removed));
1208 node->disconnect_changed(callable_mp(this, &AnimationNodeBlendTree::_node_changed));
1209 }
1210
1211 nodes.erase(p_name);
1212
1213 // Erase connections to name.
1214 for (KeyValue<StringName, Node> &E : nodes) {
1215 for (int i = 0; i < E.value.connections.size(); i++) {
1216 if (E.value.connections[i] == p_name) {
1217 E.value.connections.write[i] = StringName();
1218 }
1219 }
1220 }
1221
1222 emit_signal(SNAME("animation_node_removed"), get_instance_id(), p_name);
1223 emit_changed();
1224 emit_signal(SNAME("tree_changed"));
1225}
1226
1227void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringName &p_new_name) {
1228 ERR_FAIL_COND(!nodes.has(p_name));
1229 ERR_FAIL_COND(nodes.has(p_new_name));
1230 ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output);
1231 ERR_FAIL_COND(p_new_name == SceneStringNames::get_singleton()->output);
1232
1233 nodes[p_name].node->disconnect_changed(callable_mp(this, &AnimationNodeBlendTree::_node_changed));
1234
1235 nodes[p_new_name] = nodes[p_name];
1236 nodes.erase(p_name);
1237
1238 // Rename connections.
1239 for (KeyValue<StringName, Node> &E : nodes) {
1240 for (int i = 0; i < E.value.connections.size(); i++) {
1241 if (E.value.connections[i] == p_name) {
1242 E.value.connections.write[i] = p_new_name;
1243 }
1244 }
1245 }
1246 // Connection must be done with new name.
1247 nodes[p_new_name].node->connect_changed(callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_new_name), CONNECT_REFERENCE_COUNTED);
1248
1249 emit_signal(SNAME("animation_node_renamed"), get_instance_id(), p_name, p_new_name);
1250 emit_signal(SNAME("tree_changed"));
1251}
1252
1253void AnimationNodeBlendTree::connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node) {
1254 ERR_FAIL_COND(!nodes.has(p_output_node));
1255 ERR_FAIL_COND(!nodes.has(p_input_node));
1256 ERR_FAIL_COND(p_output_node == SceneStringNames::get_singleton()->output);
1257 ERR_FAIL_COND(p_input_node == p_output_node);
1258
1259 Ref<AnimationNode> input = nodes[p_input_node].node;
1260 ERR_FAIL_INDEX(p_input_index, nodes[p_input_node].connections.size());
1261
1262 for (KeyValue<StringName, Node> &E : nodes) {
1263 for (int i = 0; i < E.value.connections.size(); i++) {
1264 StringName output = E.value.connections[i];
1265 ERR_FAIL_COND(output == p_output_node);
1266 }
1267 }
1268
1269 nodes[p_input_node].connections.write[p_input_index] = p_output_node;
1270
1271 emit_changed();
1272}
1273
1274void AnimationNodeBlendTree::disconnect_node(const StringName &p_node, int p_input_index) {
1275 ERR_FAIL_COND(!nodes.has(p_node));
1276
1277 Ref<AnimationNode> input = nodes[p_node].node;
1278 ERR_FAIL_INDEX(p_input_index, nodes[p_node].connections.size());
1279
1280 nodes[p_node].connections.write[p_input_index] = StringName();
1281}
1282
1283AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node) const {
1284 if (!nodes.has(p_output_node) || p_output_node == SceneStringNames::get_singleton()->output) {
1285 return CONNECTION_ERROR_NO_OUTPUT;
1286 }
1287
1288 if (!nodes.has(p_input_node)) {
1289 return CONNECTION_ERROR_NO_INPUT;
1290 }
1291
1292 if (p_input_node == p_output_node) {
1293 return CONNECTION_ERROR_SAME_NODE;
1294 }
1295
1296 Ref<AnimationNode> input = nodes[p_input_node].node;
1297
1298 if (p_input_index < 0 || p_input_index >= nodes[p_input_node].connections.size()) {
1299 return CONNECTION_ERROR_NO_INPUT_INDEX;
1300 }
1301
1302 if (nodes[p_input_node].connections[p_input_index] != StringName()) {
1303 return CONNECTION_ERROR_CONNECTION_EXISTS;
1304 }
1305
1306 for (const KeyValue<StringName, Node> &E : nodes) {
1307 for (int i = 0; i < E.value.connections.size(); i++) {
1308 const StringName output = E.value.connections[i];
1309 if (output == p_output_node) {
1310 return CONNECTION_ERROR_CONNECTION_EXISTS;
1311 }
1312 }
1313 }
1314 return CONNECTION_OK;
1315}
1316
1317void AnimationNodeBlendTree::get_node_connections(List<NodeConnection> *r_connections) const {
1318 for (const KeyValue<StringName, Node> &E : nodes) {
1319 for (int i = 0; i < E.value.connections.size(); i++) {
1320 const StringName output = E.value.connections[i];
1321 if (output != StringName()) {
1322 NodeConnection nc;
1323 nc.input_node = E.key;
1324 nc.input_index = i;
1325 nc.output_node = output;
1326 r_connections->push_back(nc);
1327 }
1328 }
1329 }
1330}
1331
1332String AnimationNodeBlendTree::get_caption() const {
1333 return "BlendTree";
1334}
1335
1336double AnimationNodeBlendTree::_process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only) {
1337 Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output].node;
1338 return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true, nullptr, p_test_only);
1339}
1340
1341void AnimationNodeBlendTree::get_node_list(List<StringName> *r_list) {
1342 for (const KeyValue<StringName, Node> &E : nodes) {
1343 r_list->push_back(E.key);
1344 }
1345}
1346
1347void AnimationNodeBlendTree::set_graph_offset(const Vector2 &p_graph_offset) {
1348 graph_offset = p_graph_offset;
1349}
1350
1351Vector2 AnimationNodeBlendTree::get_graph_offset() const {
1352 return graph_offset;
1353}
1354
1355Ref<AnimationNode> AnimationNodeBlendTree::get_child_by_name(const StringName &p_name) const {
1356 return get_node(p_name);
1357}
1358
1359bool AnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_value) {
1360 String prop_name = p_name;
1361 if (prop_name.begins_with("nodes/")) {
1362 String node_name = prop_name.get_slicec('/', 1);
1363 String what = prop_name.get_slicec('/', 2);
1364
1365 if (what == "node") {
1366 Ref<AnimationNode> anode = p_value;
1367 if (anode.is_valid()) {
1368 add_node(node_name, p_value);
1369 }
1370 return true;
1371 }
1372
1373 if (what == "position") {
1374 if (nodes.has(node_name)) {
1375 nodes[node_name].position = p_value;
1376 }
1377 return true;
1378 }
1379 } else if (prop_name == "node_connections") {
1380 Array conns = p_value;
1381 ERR_FAIL_COND_V(conns.size() % 3 != 0, false);
1382
1383 for (int i = 0; i < conns.size(); i += 3) {
1384 connect_node(conns[i], conns[i + 1], conns[i + 2]);
1385 }
1386 return true;
1387 }
1388
1389 return false;
1390}
1391
1392bool AnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_ret) const {
1393 String prop_name = p_name;
1394 if (prop_name.begins_with("nodes/")) {
1395 String node_name = prop_name.get_slicec('/', 1);
1396 String what = prop_name.get_slicec('/', 2);
1397
1398 if (what == "node") {
1399 if (nodes.has(node_name)) {
1400 r_ret = nodes[node_name].node;
1401 return true;
1402 }
1403 }
1404
1405 if (what == "position") {
1406 if (nodes.has(node_name)) {
1407 r_ret = nodes[node_name].position;
1408 return true;
1409 }
1410 }
1411 } else if (prop_name == "node_connections") {
1412 List<NodeConnection> nc;
1413 get_node_connections(&nc);
1414 Array conns;
1415 conns.resize(nc.size() * 3);
1416
1417 int idx = 0;
1418 for (const NodeConnection &E : nc) {
1419 conns[idx * 3 + 0] = E.input_node;
1420 conns[idx * 3 + 1] = E.input_index;
1421 conns[idx * 3 + 2] = E.output_node;
1422 idx++;
1423 }
1424
1425 r_ret = conns;
1426 return true;
1427 }
1428
1429 return false;
1430}
1431
1432void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) const {
1433 List<StringName> names;
1434 for (const KeyValue<StringName, Node> &E : nodes) {
1435 names.push_back(E.key);
1436 }
1437
1438 for (const StringName &E : names) {
1439 String prop_name = E;
1440 if (prop_name != "output") {
1441 p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NO_EDITOR));
1442 }
1443 p_list->push_back(PropertyInfo(Variant::VECTOR2, "nodes/" + prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
1444 }
1445
1446 p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
1447}
1448
1449void AnimationNodeBlendTree::_tree_changed() {
1450 AnimationRootNode::_tree_changed();
1451}
1452
1453void AnimationNodeBlendTree::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
1454 AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
1455}
1456
1457void AnimationNodeBlendTree::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
1458 AnimationRootNode::_animation_node_removed(p_oid, p_node);
1459}
1460
1461void AnimationNodeBlendTree::reset_state() {
1462 graph_offset = Vector2();
1463 nodes.clear();
1464 _initialize_node_tree();
1465 emit_changed();
1466 emit_signal(SNAME("tree_changed"));
1467}
1468
1469void AnimationNodeBlendTree::_node_changed(const StringName &p_node) {
1470 ERR_FAIL_COND(!nodes.has(p_node));
1471 nodes[p_node].connections.resize(nodes[p_node].node->get_input_count());
1472 emit_signal(SNAME("node_changed"), p_node);
1473}
1474
1475void AnimationNodeBlendTree::_bind_methods() {
1476 ClassDB::bind_method(D_METHOD("add_node", "name", "node", "position"), &AnimationNodeBlendTree::add_node, DEFVAL(Vector2()));
1477 ClassDB::bind_method(D_METHOD("get_node", "name"), &AnimationNodeBlendTree::get_node);
1478 ClassDB::bind_method(D_METHOD("remove_node", "name"), &AnimationNodeBlendTree::remove_node);
1479 ClassDB::bind_method(D_METHOD("rename_node", "name", "new_name"), &AnimationNodeBlendTree::rename_node);
1480 ClassDB::bind_method(D_METHOD("has_node", "name"), &AnimationNodeBlendTree::has_node);
1481 ClassDB::bind_method(D_METHOD("connect_node", "input_node", "input_index", "output_node"), &AnimationNodeBlendTree::connect_node);
1482 ClassDB::bind_method(D_METHOD("disconnect_node", "input_node", "input_index"), &AnimationNodeBlendTree::disconnect_node);
1483
1484 ClassDB::bind_method(D_METHOD("set_node_position", "name", "position"), &AnimationNodeBlendTree::set_node_position);
1485 ClassDB::bind_method(D_METHOD("get_node_position", "name"), &AnimationNodeBlendTree::get_node_position);
1486
1487 ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeBlendTree::set_graph_offset);
1488 ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeBlendTree::get_graph_offset);
1489
1490 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_graph_offset", "get_graph_offset");
1491
1492 BIND_CONSTANT(CONNECTION_OK);
1493 BIND_CONSTANT(CONNECTION_ERROR_NO_INPUT);
1494 BIND_CONSTANT(CONNECTION_ERROR_NO_INPUT_INDEX);
1495 BIND_CONSTANT(CONNECTION_ERROR_NO_OUTPUT);
1496 BIND_CONSTANT(CONNECTION_ERROR_SAME_NODE);
1497 BIND_CONSTANT(CONNECTION_ERROR_CONNECTION_EXISTS);
1498
1499 ADD_SIGNAL(MethodInfo("node_changed", PropertyInfo(Variant::STRING_NAME, "node_name")));
1500}
1501
1502void AnimationNodeBlendTree::_initialize_node_tree() {
1503 Ref<AnimationNodeOutput> output;
1504 output.instantiate();
1505 Node n;
1506 n.node = output;
1507 n.position = Vector2(300, 150);
1508 n.connections.resize(1);
1509 nodes["output"] = n;
1510}
1511
1512AnimationNodeBlendTree::AnimationNodeBlendTree() {
1513 _initialize_node_tree();
1514}
1515
1516AnimationNodeBlendTree::~AnimationNodeBlendTree() {
1517}
1518