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 | |
36 | void AnimationNodeAnimation::set_animation(const StringName &p_name) { |
37 | animation = p_name; |
38 | } |
39 | |
40 | StringName AnimationNodeAnimation::get_animation() const { |
41 | return animation; |
42 | } |
43 | |
44 | Vector<String> (*AnimationNodeAnimation::get_editable_animation_list)() = nullptr; |
45 | |
46 | void 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 | |
50 | void 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 | |
67 | double 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 | |
177 | String AnimationNodeAnimation::get_caption() const { |
178 | return "Animation" ; |
179 | } |
180 | |
181 | void AnimationNodeAnimation::set_play_mode(PlayMode p_play_mode) { |
182 | play_mode = p_play_mode; |
183 | } |
184 | |
185 | AnimationNodeAnimation::PlayMode AnimationNodeAnimation::get_play_mode() const { |
186 | return play_mode; |
187 | } |
188 | |
189 | void AnimationNodeAnimation::set_backward(bool p_backward) { |
190 | backward = p_backward; |
191 | } |
192 | |
193 | bool AnimationNodeAnimation::is_backward() const { |
194 | return backward; |
195 | } |
196 | |
197 | void 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 | |
211 | AnimationNodeAnimation::AnimationNodeAnimation() { |
212 | } |
213 | |
214 | //////////////////////////////////////////////////////// |
215 | |
216 | void 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 | |
223 | void AnimationNodeSync::set_use_sync(bool p_sync) { |
224 | sync = p_sync; |
225 | } |
226 | |
227 | bool AnimationNodeSync::is_using_sync() const { |
228 | return sync; |
229 | } |
230 | |
231 | AnimationNodeSync::AnimationNodeSync() { |
232 | } |
233 | |
234 | //////////////////////////////////////////////////////// |
235 | void 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 | |
245 | Variant 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 | |
257 | bool 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 | |
264 | void AnimationNodeOneShot::set_fade_in_time(double p_time) { |
265 | fade_in = p_time; |
266 | } |
267 | |
268 | double AnimationNodeOneShot::get_fade_in_time() const { |
269 | return fade_in; |
270 | } |
271 | |
272 | void AnimationNodeOneShot::set_fade_out_time(double p_time) { |
273 | fade_out = p_time; |
274 | } |
275 | |
276 | double AnimationNodeOneShot::get_fade_out_time() const { |
277 | return fade_out; |
278 | } |
279 | |
280 | void AnimationNodeOneShot::set_fade_in_curve(const Ref<Curve> &p_curve) { |
281 | fade_in_curve = p_curve; |
282 | } |
283 | |
284 | Ref<Curve> AnimationNodeOneShot::get_fade_in_curve() const { |
285 | return fade_in_curve; |
286 | } |
287 | |
288 | void AnimationNodeOneShot::set_fade_out_curve(const Ref<Curve> &p_curve) { |
289 | fade_out_curve = p_curve; |
290 | } |
291 | |
292 | Ref<Curve> AnimationNodeOneShot::get_fade_out_curve() const { |
293 | return fade_out_curve; |
294 | } |
295 | |
296 | void AnimationNodeOneShot::set_auto_restart_enabled(bool p_enabled) { |
297 | auto_restart = p_enabled; |
298 | } |
299 | |
300 | void AnimationNodeOneShot::set_auto_restart_delay(double p_time) { |
301 | auto_restart_delay = p_time; |
302 | } |
303 | |
304 | void AnimationNodeOneShot::set_auto_restart_random_delay(double p_time) { |
305 | auto_restart_random_delay = p_time; |
306 | } |
307 | |
308 | bool AnimationNodeOneShot::is_auto_restart_enabled() const { |
309 | return auto_restart; |
310 | } |
311 | |
312 | double AnimationNodeOneShot::get_auto_restart_delay() const { |
313 | return auto_restart_delay; |
314 | } |
315 | |
316 | double AnimationNodeOneShot::get_auto_restart_random_delay() const { |
317 | return auto_restart_random_delay; |
318 | } |
319 | |
320 | void AnimationNodeOneShot::set_mix_mode(MixMode p_mix) { |
321 | mix = p_mix; |
322 | } |
323 | |
324 | AnimationNodeOneShot::MixMode AnimationNodeOneShot::get_mix_mode() const { |
325 | return mix; |
326 | } |
327 | |
328 | String AnimationNodeOneShot::get_caption() const { |
329 | return "OneShot" ; |
330 | } |
331 | |
332 | bool AnimationNodeOneShot::has_filter() const { |
333 | return true; |
334 | } |
335 | |
336 | double 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 | |
475 | void 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 | |
522 | AnimationNodeOneShot::AnimationNodeOneShot() { |
523 | add_input("in" ); |
524 | add_input("shot" ); |
525 | } |
526 | |
527 | //////////////////////////////////////////////// |
528 | |
529 | void 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 | |
533 | Variant AnimationNodeAdd2::get_parameter_default_value(const StringName &p_parameter) const { |
534 | return 0; |
535 | } |
536 | |
537 | String AnimationNodeAdd2::get_caption() const { |
538 | return "Add2" ; |
539 | } |
540 | |
541 | bool AnimationNodeAdd2::has_filter() const { |
542 | return true; |
543 | } |
544 | |
545 | double 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 | |
553 | void AnimationNodeAdd2::_bind_methods() { |
554 | } |
555 | |
556 | AnimationNodeAdd2::AnimationNodeAdd2() { |
557 | add_input("in" ); |
558 | add_input("add" ); |
559 | } |
560 | |
561 | //////////////////////////////////////////////// |
562 | |
563 | void 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 | |
567 | Variant AnimationNodeAdd3::get_parameter_default_value(const StringName &p_parameter) const { |
568 | return 0; |
569 | } |
570 | |
571 | String AnimationNodeAdd3::get_caption() const { |
572 | return "Add3" ; |
573 | } |
574 | |
575 | bool AnimationNodeAdd3::has_filter() const { |
576 | return true; |
577 | } |
578 | |
579 | double 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 | |
588 | void AnimationNodeAdd3::_bind_methods() { |
589 | } |
590 | |
591 | AnimationNodeAdd3::AnimationNodeAdd3() { |
592 | add_input("-add" ); |
593 | add_input("in" ); |
594 | add_input("+add" ); |
595 | } |
596 | |
597 | ///////////////////////////////////////////// |
598 | |
599 | void 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 | |
603 | Variant AnimationNodeBlend2::get_parameter_default_value(const StringName &p_parameter) const { |
604 | return 0; // For blend amount. |
605 | } |
606 | |
607 | String AnimationNodeBlend2::get_caption() const { |
608 | return "Blend2" ; |
609 | } |
610 | |
611 | double 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 | |
620 | bool AnimationNodeBlend2::has_filter() const { |
621 | return true; |
622 | } |
623 | |
624 | void AnimationNodeBlend2::_bind_methods() { |
625 | } |
626 | |
627 | AnimationNodeBlend2::AnimationNodeBlend2() { |
628 | add_input("in" ); |
629 | add_input("blend" ); |
630 | } |
631 | |
632 | ////////////////////////////////////// |
633 | |
634 | void 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 | |
638 | Variant AnimationNodeBlend3::get_parameter_default_value(const StringName &p_parameter) const { |
639 | return 0; // For blend amount. |
640 | } |
641 | |
642 | String AnimationNodeBlend3::get_caption() const { |
643 | return "Blend3" ; |
644 | } |
645 | |
646 | double 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 | |
655 | void AnimationNodeBlend3::_bind_methods() { |
656 | } |
657 | |
658 | AnimationNodeBlend3::AnimationNodeBlend3() { |
659 | add_input("-blend" ); |
660 | add_input("in" ); |
661 | add_input("+blend" ); |
662 | } |
663 | |
664 | //////////////////////////////////////////////// |
665 | |
666 | void 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 | |
670 | Variant AnimationNodeSub2::get_parameter_default_value(const StringName &p_parameter) const { |
671 | return 0; |
672 | } |
673 | |
674 | String AnimationNodeSub2::get_caption() const { |
675 | return "Sub2" ; |
676 | } |
677 | |
678 | bool AnimationNodeSub2::has_filter() const { |
679 | return true; |
680 | } |
681 | |
682 | double 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 | |
689 | void AnimationNodeSub2::_bind_methods() { |
690 | } |
691 | |
692 | AnimationNodeSub2::AnimationNodeSub2() { |
693 | add_input("in" ); |
694 | add_input("sub" ); |
695 | } |
696 | |
697 | ///////////////////////////////// |
698 | |
699 | void 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 | |
703 | Variant AnimationNodeTimeScale::get_parameter_default_value(const StringName &p_parameter) const { |
704 | return 1.0; // Initial timescale. |
705 | } |
706 | |
707 | String AnimationNodeTimeScale::get_caption() const { |
708 | return "TimeScale" ; |
709 | } |
710 | |
711 | double 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 | |
720 | void AnimationNodeTimeScale::_bind_methods() { |
721 | } |
722 | |
723 | AnimationNodeTimeScale::AnimationNodeTimeScale() { |
724 | add_input("in" ); |
725 | } |
726 | |
727 | //////////////////////////////////// |
728 | |
729 | void 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 | |
733 | Variant AnimationNodeTimeSeek::get_parameter_default_value(const StringName &p_parameter) const { |
734 | return -1.0; // Initial seek request. |
735 | } |
736 | |
737 | String AnimationNodeTimeSeek::get_caption() const { |
738 | return "TimeSeek" ; |
739 | } |
740 | |
741 | double 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 | |
754 | void AnimationNodeTimeSeek::_bind_methods() { |
755 | } |
756 | |
757 | AnimationNodeTimeSeek::AnimationNodeTimeSeek() { |
758 | add_input("in" ); |
759 | } |
760 | |
761 | ///////////////////////////////////////////////// |
762 | |
763 | bool 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 | |
795 | bool 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 | |
820 | void 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 | |
837 | Variant 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 | |
847 | bool 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 | |
854 | String AnimationNodeTransition::get_caption() const { |
855 | return "Transition" ; |
856 | } |
857 | |
858 | void 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 | |
872 | bool 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 | |
880 | void AnimationNodeTransition::remove_input(int p_index) { |
881 | input_data.remove_at(p_index); |
882 | AnimationNode::remove_input(p_index); |
883 | } |
884 | |
885 | bool 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 | |
890 | void 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 | |
895 | bool 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 | |
900 | void 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 | |
905 | bool 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 | |
910 | void AnimationNodeTransition::set_xfade_time(double p_fade) { |
911 | xfade_time = p_fade; |
912 | } |
913 | |
914 | double AnimationNodeTransition::get_xfade_time() const { |
915 | return xfade_time; |
916 | } |
917 | |
918 | void AnimationNodeTransition::set_xfade_curve(const Ref<Curve> &p_curve) { |
919 | xfade_curve = p_curve; |
920 | } |
921 | |
922 | Ref<Curve> AnimationNodeTransition::get_xfade_curve() const { |
923 | return xfade_curve; |
924 | } |
925 | |
926 | void AnimationNodeTransition::set_allow_transition_to_self(bool p_enable) { |
927 | allow_transition_to_self = p_enable; |
928 | } |
929 | |
930 | bool AnimationNodeTransition::is_allow_transition_to_self() const { |
931 | return allow_transition_to_self; |
932 | } |
933 | |
934 | double 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 | |
1078 | void 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 | |
1086 | void 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 | |
1110 | AnimationNodeTransition::AnimationNodeTransition() { |
1111 | } |
1112 | |
1113 | ///////////////////// |
1114 | |
1115 | String AnimationNodeOutput::get_caption() const { |
1116 | return "Output" ; |
1117 | } |
1118 | |
1119 | double 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 | |
1123 | AnimationNodeOutput::AnimationNodeOutput() { |
1124 | add_input("output" ); |
1125 | } |
1126 | |
1127 | /////////////////////////////////////////////////////// |
1128 | void 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 | |
1149 | Ref<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 | |
1155 | StringName 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 | |
1165 | void 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 | |
1170 | Vector2 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 | |
1175 | void 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 | |
1190 | bool AnimationNodeBlendTree::has_node(const StringName &p_name) const { |
1191 | return nodes.has(p_name); |
1192 | } |
1193 | |
1194 | Vector<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 | |
1199 | void 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 | |
1227 | void 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 | |
1253 | void 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 | |
1274 | void 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 | |
1283 | AnimationNodeBlendTree::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 | |
1317 | void 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 | |
1332 | String AnimationNodeBlendTree::get_caption() const { |
1333 | return "BlendTree" ; |
1334 | } |
1335 | |
1336 | double 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 | |
1341 | void 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 | |
1347 | void AnimationNodeBlendTree::set_graph_offset(const Vector2 &p_graph_offset) { |
1348 | graph_offset = p_graph_offset; |
1349 | } |
1350 | |
1351 | Vector2 AnimationNodeBlendTree::get_graph_offset() const { |
1352 | return graph_offset; |
1353 | } |
1354 | |
1355 | Ref<AnimationNode> AnimationNodeBlendTree::get_child_by_name(const StringName &p_name) const { |
1356 | return get_node(p_name); |
1357 | } |
1358 | |
1359 | bool 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 | |
1392 | bool 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 | |
1432 | void 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 | |
1449 | void AnimationNodeBlendTree::_tree_changed() { |
1450 | AnimationRootNode::_tree_changed(); |
1451 | } |
1452 | |
1453 | void 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 | |
1457 | void AnimationNodeBlendTree::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) { |
1458 | AnimationRootNode::_animation_node_removed(p_oid, p_node); |
1459 | } |
1460 | |
1461 | void 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 | |
1469 | void 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 | |
1475 | void 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 | |
1502 | void 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 | |
1512 | AnimationNodeBlendTree::AnimationNodeBlendTree() { |
1513 | _initialize_node_tree(); |
1514 | } |
1515 | |
1516 | AnimationNodeBlendTree::~AnimationNodeBlendTree() { |
1517 | } |
1518 | |