| 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 | |