Path: blob/master/scene/animation/animation_tree.cpp
20931 views
/**************************************************************************/1/* animation_tree.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "animation_tree.h"31#include "animation_tree.compat.inc"3233#include "animation_blend_tree.h"34#include "scene/animation/animation_player.h"3536void AnimationNode::get_parameter_list(List<PropertyInfo> *r_list) const {37Array parameters;3839if (GDVIRTUAL_CALL(_get_parameter_list, parameters)) {40for (int i = 0; i < parameters.size(); i++) {41Dictionary d = parameters[i];42ERR_CONTINUE(d.is_empty());43r_list->push_back(PropertyInfo::from_dict(d));44}45}4647r_list->push_back(PropertyInfo(Variant::FLOAT, current_length, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_READ_ONLY));48r_list->push_back(PropertyInfo(Variant::FLOAT, current_position, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_READ_ONLY));49r_list->push_back(PropertyInfo(Variant::FLOAT, current_delta, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_READ_ONLY));50}5152Variant AnimationNode::get_parameter_default_value(const StringName &p_parameter) const {53Variant ret;54if (p_parameter == current_length || p_parameter == current_position || p_parameter == current_delta) {55return 0.0;56}57GDVIRTUAL_CALL(_get_parameter_default_value, p_parameter, ret);58return ret;59}6061bool AnimationNode::is_parameter_read_only(const StringName &p_parameter) const {62bool ret = false;63if (GDVIRTUAL_CALL(_is_parameter_read_only, p_parameter, ret) && ret) {64return true;65}6667if (p_parameter == current_length || p_parameter == current_position || p_parameter == current_delta) {68return true;69}7071return false;72}7374void AnimationNode::set_parameter(const StringName &p_name, const Variant &p_value) {75ERR_FAIL_NULL(process_state);76if (process_state->is_testing) {77return;78}7980const AHashMap<StringName, int>::Iterator it = property_cache.find(p_name);81if (it) {82Pair<Variant, bool> &prop = process_state->tree->property_map.get_by_index(it->value).value;83Variant value = p_value;84if (Animation::validate_type_match(prop.first, value)) {85prop.first = value;86}87return;88}8990ERR_FAIL_COND(!process_state->tree->property_parent_map.has(node_state.base_path));91ERR_FAIL_COND(!process_state->tree->property_parent_map[node_state.base_path].has(p_name));92StringName path = process_state->tree->property_parent_map[node_state.base_path][p_name];93int idx = process_state->tree->property_map.get_index(path);94property_cache.insert_new(p_name, idx);95process_state->tree->property_map.get_by_index(idx).value.first = p_value;96}9798Variant AnimationNode::get_parameter(const StringName &p_name) const {99ERR_FAIL_NULL_V(process_state, Variant());100const AHashMap<StringName, int>::ConstIterator it = property_cache.find(p_name);101if (it) {102return process_state->tree->property_map.get_by_index(it->value).value.first;103}104ERR_FAIL_COND_V(!process_state->tree->property_parent_map.has(node_state.base_path), Variant());105ERR_FAIL_COND_V(!process_state->tree->property_parent_map[node_state.base_path].has(p_name), Variant());106107StringName path = process_state->tree->property_parent_map[node_state.base_path][p_name];108int idx = process_state->tree->property_map.get_index(path);109property_cache.insert_new(p_name, idx);110return process_state->tree->property_map.get_by_index(idx).value.first;111}112113void AnimationNode::set_node_time_info(const NodeTimeInfo &p_node_time_info) {114set_parameter(current_length, p_node_time_info.length);115set_parameter(current_position, p_node_time_info.position);116set_parameter(current_delta, p_node_time_info.delta);117}118119AnimationNode::NodeTimeInfo AnimationNode::get_node_time_info() const {120NodeTimeInfo nti;121nti.length = get_parameter(current_length);122nti.position = get_parameter(current_position);123nti.delta = get_parameter(current_delta);124return nti;125}126127void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) {128Dictionary cn;129if (GDVIRTUAL_CALL(_get_child_nodes, cn)) {130for (const KeyValue<Variant, Variant> &kv : cn) {131ChildNode child;132child.name = kv.key;133child.node = kv.value;134r_child_nodes->push_back(child);135}136}137}138139void AnimationNode::blend_animation(const StringName &p_animation, AnimationMixer::PlaybackInfo p_playback_info) {140ERR_FAIL_NULL(process_state);141p_playback_info.track_weights = Vector<real_t>(node_state.track_weights);142process_state->tree->make_animation_instance(p_animation, p_playback_info);143}144145AnimationNode::NodeTimeInfo AnimationNode::_pre_process(ProcessState *p_process_state, AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {146process_state = p_process_state;147NodeTimeInfo nti = process(p_playback_info, p_test_only);148process_state = nullptr;149return nti;150}151152void AnimationNode::make_invalid(const String &p_reason) {153ERR_FAIL_NULL(process_state);154process_state->valid = false;155if (!process_state->invalid_reasons.is_empty()) {156process_state->invalid_reasons += "\n";157}158process_state->invalid_reasons += String::utf8("• ") + p_reason;159}160161AnimationTree *AnimationNode::get_animation_tree() const {162ERR_FAIL_NULL_V(process_state, nullptr);163return process_state->tree;164}165166AnimationNode::NodeTimeInfo AnimationNode::blend_input(int p_input, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only) {167ERR_FAIL_INDEX_V(p_input, (int64_t)inputs.size(), NodeTimeInfo());168169AnimationNodeBlendTree *blend_tree = Object::cast_to<AnimationNodeBlendTree>(node_state.parent);170ERR_FAIL_NULL_V(blend_tree, NodeTimeInfo());171172// Update connections.173StringName current_name = blend_tree->get_node_name(Ref<AnimationNode>(this));174node_state.connections = blend_tree->get_node_connection_array(current_name);175176// Get node which is connected input port.177StringName node_name = node_state.connections[p_input];178if (!blend_tree->has_node(node_name)) {179make_invalid(vformat(RTR("Nothing connected to input '%s' of node '%s'."), get_input_name(p_input), current_name));180return NodeTimeInfo();181}182183Ref<AnimationNode> node = blend_tree->get_node(node_name);184ERR_FAIL_COND_V(node.is_null(), NodeTimeInfo());185186real_t activity = 0.0;187LocalVector<AnimationTree::Activity> *activity_ptr = process_state->tree->input_activity_map.getptr(node_state.base_path);188NodeTimeInfo nti = _blend_node(node, node_name, nullptr, p_playback_info, p_filter, p_sync, p_test_only, &activity);189190if (activity_ptr && p_input < (int64_t)activity_ptr->size()) {191(*activity_ptr)[p_input].last_pass = process_state->last_pass;192(*activity_ptr)[p_input].activity = activity;193}194return nti;195}196197AnimationNode::NodeTimeInfo AnimationNode::blend_node(Ref<AnimationNode> p_node, const StringName &p_subpath, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only) {198ERR_FAIL_COND_V(p_node.is_null(), NodeTimeInfo());199p_node->node_state.connections.clear();200return _blend_node(p_node, p_subpath, this, p_playback_info, p_filter, p_sync, p_test_only, nullptr);201}202203AnimationNode::NodeTimeInfo AnimationNode::_blend_node(Ref<AnimationNode> p_node, const StringName &p_subpath, AnimationNode *p_new_parent, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only, real_t *r_activity) {204ERR_FAIL_NULL_V(process_state, NodeTimeInfo());205206int blend_count = node_state.track_weights.size();207208if ((int64_t)p_node->node_state.track_weights.size() != blend_count) {209p_node->node_state.track_weights.resize(blend_count);210}211212real_t *blendw = p_node->node_state.track_weights.ptr();213const real_t *blendr = node_state.track_weights.ptr();214215bool any_valid = false;216217if (has_filter() && is_filter_enabled() && p_filter != FILTER_IGNORE) {218for (int i = 0; i < blend_count; i++) {219blendw[i] = 0.0; // All to zero by default.220}221222for (const KeyValue<NodePath, bool> &E : filter) {223const AHashMap<NodePath, int> &map = *process_state->track_map;224if (!map.has(E.key)) {225continue;226}227int idx = map[E.key];228blendw[idx] = 1.0; // Filtered goes to one.229}230231switch (p_filter) {232case FILTER_IGNORE:233break; // Will not happen anyway.234case FILTER_PASS: {235// Values filtered pass, the rest don't.236for (int i = 0; i < blend_count; i++) {237if (blendw[i] == 0) { // Not filtered, does not pass.238continue;239}240241blendw[i] = blendr[i] * p_playback_info.weight;242if (!Math::is_zero_approx(blendw[i])) {243any_valid = true;244}245}246247} break;248case FILTER_STOP: {249// Values filtered don't pass, the rest are blended.250251for (int i = 0; i < blend_count; i++) {252if (blendw[i] > 0) { // Filtered, does not pass.253continue;254}255256blendw[i] = blendr[i] * p_playback_info.weight;257if (!Math::is_zero_approx(blendw[i])) {258any_valid = true;259}260}261262} break;263case FILTER_BLEND: {264// Filtered values are blended, the rest are passed without blending.265266for (int i = 0; i < blend_count; i++) {267if (blendw[i] == 1.0) {268blendw[i] = blendr[i] * p_playback_info.weight; // Filtered, blend.269} else {270blendw[i] = blendr[i]; // Not filtered, do not blend.271}272273if (!Math::is_zero_approx(blendw[i])) {274any_valid = true;275}276}277278} break;279}280} else {281for (int i = 0; i < blend_count; i++) {282// Regular blend.283blendw[i] = blendr[i] * p_playback_info.weight;284if (!Math::is_zero_approx(blendw[i])) {285any_valid = true;286}287}288}289290if (r_activity) {291*r_activity = 0;292for (int i = 0; i < blend_count; i++) {293*r_activity = MAX(*r_activity, Math::abs(blendw[i]));294}295}296297String new_path;298AnimationNode *new_parent;299300// This is the slowest part of processing, but as strings process in powers of 2, and the paths always exist, it will not result in that many allocations.301if (p_new_parent) {302new_parent = p_new_parent;303new_path = String(node_state.base_path) + String(p_subpath) + "/";304} else {305ERR_FAIL_NULL_V(node_state.parent, NodeTimeInfo());306new_parent = node_state.parent;307new_path = String(new_parent->node_state.base_path) + String(p_subpath) + "/";308}309310// This process, which depends on p_sync is needed to process sync correctly in the case of311// that a synced AnimationNodeSync exists under the un-synced AnimationNodeSync.312p_node->set_node_state_base_path(new_path);313p_node->node_state.parent = new_parent;314if (!p_playback_info.seeked && !p_sync && !any_valid) {315p_playback_info.delta = 0.0;316return p_node->_pre_process(process_state, p_playback_info, p_test_only);317}318return p_node->_pre_process(process_state, p_playback_info, p_test_only);319}320321String AnimationNode::get_caption() const {322String ret = "Node";323GDVIRTUAL_CALL(_get_caption, ret);324return ret;325}326327bool AnimationNode::add_input(const String &p_name) {328// Root nodes can't add inputs.329ERR_FAIL_COND_V(Object::cast_to<AnimationRootNode>(this) != nullptr, false);330Input input;331ERR_FAIL_COND_V(p_name.contains_char('.') || p_name.contains_char('/'), false);332input.name = p_name;333inputs.push_back(input);334emit_changed();335return true;336}337338void AnimationNode::remove_input(int p_index) {339ERR_FAIL_INDEX(p_index, (int64_t)inputs.size());340inputs.remove_at(p_index);341emit_changed();342}343344bool AnimationNode::set_input_name(int p_input, const String &p_name) {345ERR_FAIL_INDEX_V(p_input, (int64_t)inputs.size(), false);346ERR_FAIL_COND_V(p_name.contains_char('.') || p_name.contains_char('/'), false);347inputs[p_input].name = p_name;348emit_changed();349return true;350}351352String AnimationNode::get_input_name(int p_input) const {353ERR_FAIL_INDEX_V(p_input, (int64_t)inputs.size(), String());354return inputs[p_input].name;355}356357int AnimationNode::get_input_count() const {358return inputs.size();359}360361int AnimationNode::find_input(const String &p_name) const {362int idx = -1;363for (int i = 0; i < (int64_t)inputs.size(); i++) {364if (inputs[i].name == p_name) {365idx = i;366break;367}368}369return idx;370}371372AnimationNode::NodeTimeInfo AnimationNode::process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {373process_state->is_testing = p_test_only;374375AnimationMixer::PlaybackInfo pi = p_playback_info;376if (p_playback_info.seeked) {377if (p_playback_info.is_external_seeking) {378pi.delta = get_node_time_info().position - p_playback_info.time;379}380} else {381pi.time = get_node_time_info().position + p_playback_info.delta;382}383384NodeTimeInfo nti = _process(pi, p_test_only);385386if (!p_test_only) {387set_node_time_info(nti);388}389390return nti;391}392393AnimationNode::NodeTimeInfo AnimationNode::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {394double r_ret = 0.0;395GDVIRTUAL_CALL(_process, p_playback_info.time, p_playback_info.seeked, p_playback_info.is_external_seeking, p_test_only, r_ret);396NodeTimeInfo nti;397nti.delta = r_ret;398return nti;399}400401void AnimationNode::set_filter_path(const NodePath &p_path, bool p_enable) {402if (p_enable) {403filter[p_path] = true;404} else {405filter.erase(p_path);406}407}408409void AnimationNode::set_filter_enabled(bool p_enable) {410filter_enabled = p_enable;411}412413bool AnimationNode::is_filter_enabled() const {414return filter_enabled;415}416417void AnimationNode::set_deletable(bool p_closable) {418closable = p_closable;419}420421bool AnimationNode::is_deletable() const {422return closable;423}424425ObjectID AnimationNode::get_processing_animation_tree_instance_id() const {426ERR_FAIL_NULL_V(process_state, ObjectID());427return process_state->tree->get_instance_id();428}429430bool AnimationNode::is_process_testing() const {431ERR_FAIL_NULL_V(process_state, false);432return process_state->is_testing;433}434435bool AnimationNode::is_path_filtered(const NodePath &p_path) const {436return filter.has(p_path);437}438439bool AnimationNode::has_filter() const {440bool ret = false;441GDVIRTUAL_CALL(_has_filter, ret);442return ret;443}444445Array AnimationNode::_get_filters() const {446Array paths;447448for (const KeyValue<NodePath, bool> &E : filter) {449paths.push_back(String(E.key)); // Use strings, so sorting is possible.450}451paths.sort(); // Done so every time the scene is saved, it does not change.452453return paths;454}455456void AnimationNode::_set_filters(const Array &p_filters) {457filter.clear();458for (int i = 0; i < p_filters.size(); i++) {459set_filter_path(p_filters[i], true);460}461}462463void AnimationNode::_validate_property(PropertyInfo &p_property) const {464if (!has_filter() && (p_property.name == "filter_enabled" || p_property.name == "filters")) {465p_property.usage = PROPERTY_USAGE_NONE;466}467}468469Ref<AnimationNode> AnimationNode::get_child_by_name(const StringName &p_name) const {470Ref<AnimationNode> ret;471GDVIRTUAL_CALL(_get_child_by_name, p_name, ret);472return ret;473}474475Ref<AnimationNode> AnimationNode::find_node_by_path(const String &p_name) const {476Vector<String> split = p_name.split("/");477Ref<AnimationNode> ret = const_cast<AnimationNode *>(this);478for (int i = 0; i < split.size(); i++) {479ret = ret->get_child_by_name(split[i]);480if (ret.is_null()) {481break;482}483}484return ret;485}486487void AnimationNode::blend_animation_ex(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_is_external_seeking, real_t p_blend, Animation::LoopedFlag p_looped_flag) {488AnimationMixer::PlaybackInfo info;489info.time = p_time;490info.delta = p_delta;491info.seeked = p_seeked;492info.is_external_seeking = p_is_external_seeking;493info.weight = p_blend;494info.looped_flag = p_looped_flag;495blend_animation(p_animation, info);496}497498double AnimationNode::blend_node_ex(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter, bool p_sync, bool p_test_only) {499AnimationMixer::PlaybackInfo info;500info.time = p_time;501info.seeked = p_seek;502info.is_external_seeking = p_is_external_seeking;503info.weight = p_blend;504NodeTimeInfo nti = blend_node(p_node, p_sub_path, info, p_filter, p_sync, p_test_only);505return nti.length - nti.position;506}507508double AnimationNode::blend_input_ex(int p_input, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter, bool p_sync, bool p_test_only) {509AnimationMixer::PlaybackInfo info;510info.time = p_time;511info.seeked = p_seek;512info.is_external_seeking = p_is_external_seeking;513info.weight = p_blend;514NodeTimeInfo nti = blend_input(p_input, info, p_filter, p_sync, p_test_only);515return nti.length - nti.position;516}517518#ifdef TOOLS_ENABLED519void AnimationNode::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {520const String pf = p_function;521if (p_idx == 0) {522if (pf == "find_input") {523for (const AnimationNode::Input &E : inputs) {524r_options->push_back(E.name.quote());525}526} else if (pf == "get_parameter" || pf == "set_parameter") {527bool is_setter = pf == "set_parameter";528List<PropertyInfo> parameters;529get_parameter_list(¶meters);530for (const PropertyInfo &E : parameters) {531if (is_setter && is_parameter_read_only(E.name)) {532continue;533}534r_options->push_back(E.name.quote());535}536} else if (pf == "set_filter_path" || pf == "is_path_filtered") {537for (const KeyValue<NodePath, bool> &E : filter) {538r_options->push_back(String(E.key).quote());539}540}541}542Resource::get_argument_options(p_function, p_idx, r_options);543}544#endif545546void AnimationNode::_bind_methods() {547ClassDB::bind_method(D_METHOD("add_input", "name"), &AnimationNode::add_input);548ClassDB::bind_method(D_METHOD("remove_input", "index"), &AnimationNode::remove_input);549ClassDB::bind_method(D_METHOD("set_input_name", "input", "name"), &AnimationNode::set_input_name);550ClassDB::bind_method(D_METHOD("get_input_name", "input"), &AnimationNode::get_input_name);551ClassDB::bind_method(D_METHOD("get_input_count"), &AnimationNode::get_input_count);552ClassDB::bind_method(D_METHOD("find_input", "name"), &AnimationNode::find_input);553554ClassDB::bind_method(D_METHOD("set_filter_path", "path", "enable"), &AnimationNode::set_filter_path);555ClassDB::bind_method(D_METHOD("is_path_filtered", "path"), &AnimationNode::is_path_filtered);556557ClassDB::bind_method(D_METHOD("set_filter_enabled", "enable"), &AnimationNode::set_filter_enabled);558ClassDB::bind_method(D_METHOD("is_filter_enabled"), &AnimationNode::is_filter_enabled);559560ClassDB::bind_method(D_METHOD("get_processing_animation_tree_instance_id"), &AnimationNode::get_processing_animation_tree_instance_id);561562ClassDB::bind_method(D_METHOD("is_process_testing"), &AnimationNode::is_process_testing);563564ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters);565ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters);566567ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "is_external_seeking", "blend", "looped_flag"), &AnimationNode::blend_animation_ex, DEFVAL(Animation::LOOPED_FLAG_NONE));568ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "is_external_seeking", "blend", "filter", "sync", "test_only"), &AnimationNode::blend_node_ex, DEFVAL(FILTER_IGNORE), DEFVAL(true), DEFVAL(false));569ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "is_external_seeking", "blend", "filter", "sync", "test_only"), &AnimationNode::blend_input_ex, DEFVAL(FILTER_IGNORE), DEFVAL(true), DEFVAL(false));570571ClassDB::bind_method(D_METHOD("set_parameter", "name", "value"), &AnimationNode::set_parameter);572ClassDB::bind_method(D_METHOD("get_parameter", "name"), &AnimationNode::get_parameter);573574ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_filter_enabled", "is_filter_enabled");575ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "filters", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_filters", "_get_filters");576577GDVIRTUAL_BIND(_get_child_nodes);578GDVIRTUAL_BIND(_get_parameter_list);579GDVIRTUAL_BIND(_get_child_by_name, "name");580GDVIRTUAL_BIND(_get_parameter_default_value, "parameter");581GDVIRTUAL_BIND(_is_parameter_read_only, "parameter");582GDVIRTUAL_BIND(_process, "time", "seek", "is_external_seeking", "test_only");583GDVIRTUAL_BIND(_get_caption);584GDVIRTUAL_BIND(_has_filter);585586ADD_SIGNAL(MethodInfo("tree_changed"));587ADD_SIGNAL(MethodInfo("animation_node_renamed", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "old_name"), PropertyInfo(Variant::STRING, "new_name")));588ADD_SIGNAL(MethodInfo("animation_node_removed", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "name")));589590BIND_ENUM_CONSTANT(FILTER_IGNORE);591BIND_ENUM_CONSTANT(FILTER_PASS);592BIND_ENUM_CONSTANT(FILTER_STOP);593BIND_ENUM_CONSTANT(FILTER_BLEND);594}595596AnimationNode::AnimationNode() {597}598599////////////////////600601void AnimationRootNode::_tree_changed() {602emit_signal(SNAME("tree_changed"));603}604605void AnimationRootNode::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {606emit_signal(SNAME("animation_node_renamed"), p_oid, p_old_name, p_new_name);607}608609void AnimationRootNode::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {610emit_signal(SNAME("animation_node_removed"), p_oid, p_node);611}612613////////////////////614615void AnimationTree::set_root_animation_node(const Ref<AnimationRootNode> &p_animation_node) {616if (root_animation_node.is_valid()) {617root_animation_node->disconnect(SNAME("tree_changed"), callable_mp(this, &AnimationTree::_tree_changed));618root_animation_node->disconnect(SNAME("animation_node_renamed"), callable_mp(this, &AnimationTree::_animation_node_renamed));619root_animation_node->disconnect(SNAME("animation_node_removed"), callable_mp(this, &AnimationTree::_animation_node_removed));620}621622root_animation_node = p_animation_node;623624if (root_animation_node.is_valid()) {625root_animation_node->connect(SNAME("tree_changed"), callable_mp(this, &AnimationTree::_tree_changed));626root_animation_node->connect(SNAME("animation_node_renamed"), callable_mp(this, &AnimationTree::_animation_node_renamed));627root_animation_node->connect(SNAME("animation_node_removed"), callable_mp(this, &AnimationTree::_animation_node_removed));628}629630properties_dirty = true;631632update_configuration_warnings();633}634635Ref<AnimationRootNode> AnimationTree::get_root_animation_node() const {636return root_animation_node;637}638639bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) {640_update_properties(); // If properties need updating, update them.641642if (root_animation_node.is_null()) {643return false;644}645646{ // Setup.647process_pass++;648649// Init process state.650process_state = AnimationNode::ProcessState();651process_state.tree = this;652process_state.valid = true;653process_state.invalid_reasons = "";654process_state.last_pass = process_pass;655process_state.track_map = &p_track_map;656657// Init node state for root AnimationNode.658root_animation_node->node_state.track_weights.resize(p_track_count);659real_t *src_blendsw = root_animation_node->node_state.track_weights.ptr();660for (int i = 0; i < p_track_count; i++) {661src_blendsw[i] = 1.0; // By default all go to 1 for the root input.662}663root_animation_node->set_node_state_base_path(SNAME(Animation::PARAMETERS_BASE_PATH.ascii().get_data()));664root_animation_node->node_state.parent = nullptr;665}666667// Process.668{669PlaybackInfo pi;670671if (started) {672// If started, seek.673pi.seeked = true;674pi.delta = p_delta;675root_animation_node->_pre_process(&process_state, pi, false);676started = false;677} else {678pi.seeked = false;679pi.delta = p_delta;680root_animation_node->_pre_process(&process_state, pi, false);681}682}683684if (!process_state.valid) {685return false; // State is not valid, abort process.686}687688return true;689}690691void AnimationTree::_set_active(bool p_active) {692_set_process(p_active);693started = p_active;694}695696void AnimationTree::set_advance_expression_base_node(const NodePath &p_path) {697advance_expression_base_node = p_path;698}699700NodePath AnimationTree::get_advance_expression_base_node() const {701return advance_expression_base_node;702}703704bool AnimationTree::is_state_invalid() const {705return !process_state.valid;706}707708String AnimationTree::get_invalid_state_reason() const {709return process_state.invalid_reasons;710}711712uint64_t AnimationTree::get_last_process_pass() const {713return process_pass;714}715716PackedStringArray AnimationTree::get_configuration_warnings() const {717PackedStringArray warnings = AnimationMixer::get_configuration_warnings();718if (root_animation_node.is_null()) {719warnings.push_back(RTR("No root AnimationNode for the graph is set."));720}721return warnings;722}723724void AnimationTree::_tree_changed() {725if (properties_dirty) {726return;727}728729callable_mp(this, &AnimationTree::_update_properties).call_deferred();730properties_dirty = true;731}732733void AnimationTree::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {734ERR_FAIL_COND(!property_reference_map.has(p_oid));735String base_path = property_reference_map[p_oid];736String old_base = base_path + p_old_name;737String new_base = base_path + p_new_name;738for (const PropertyInfo &E : properties) {739if (E.name.begins_with(old_base)) {740String new_name = E.name.replace_first(old_base, new_base);741const Pair<Variant, bool> temp_copy = property_map[E.name];742property_map[new_name] = temp_copy;743property_map.erase(E.name);744}745}746747// Update tree second.748properties_dirty = true;749_update_properties();750}751752void AnimationTree::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {753ERR_FAIL_COND(!property_reference_map.has(p_oid));754String base_path = String(property_reference_map[p_oid]) + String(p_node);755for (const PropertyInfo &E : properties) {756if (E.name.begins_with(base_path)) {757property_map.erase(E.name);758}759}760761// Update tree second.762properties_dirty = true;763_update_properties();764}765766void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<AnimationNode> p_node) const {767ERR_FAIL_COND(p_node.is_null());768if (!property_parent_map.has(p_base_path)) {769property_parent_map[p_base_path] = AHashMap<StringName, StringName>();770}771if (!property_reference_map.has(p_node->get_instance_id())) {772property_reference_map[p_node->get_instance_id()] = p_base_path;773}774775if (p_node->get_input_count() && !input_activity_map.has(p_base_path)) {776LocalVector<Activity> activity;777for (int i = 0; i < p_node->get_input_count(); i++) {778Activity a;779a.activity = 0;780a.last_pass = 0;781activity.push_back(a);782}783input_activity_map[p_base_path] = activity;784input_activity_map_get[String(p_base_path).substr(0, String(p_base_path).length() - 1)] = input_activity_map.get_index(p_base_path);785}786787List<PropertyInfo> plist;788p_node->get_parameter_list(&plist);789for (PropertyInfo &pinfo : plist) {790StringName key = pinfo.name;791792if (!property_map.has(p_base_path + key)) {793Pair<Variant, bool> param;794param.first = p_node->get_parameter_default_value(key);795param.second = p_node->is_parameter_read_only(key);796property_map[p_base_path + key] = param;797}798799property_parent_map[p_base_path][key] = p_base_path + key;800801pinfo.name = p_base_path + key;802properties.push_back(pinfo);803}804p_node->make_cache_dirty();805List<AnimationNode::ChildNode> children;806p_node->get_child_nodes(&children);807808for (const AnimationNode::ChildNode &E : children) {809_update_properties_for_node(p_base_path + E.name + "/", E.node);810}811}812813void AnimationTree::_update_properties() const {814if (!properties_dirty) {815return;816}817818properties.clear();819property_reference_map.clear();820property_parent_map.clear();821input_activity_map.clear();822input_activity_map_get.clear();823824if (root_animation_node.is_valid()) {825_update_properties_for_node(Animation::PARAMETERS_BASE_PATH, root_animation_node);826}827828properties_dirty = false;829830const_cast<AnimationTree *>(this)->notify_property_list_changed();831}832833void AnimationTree::_notification(int p_what) {834switch (p_what) {835case NOTIFICATION_ENTER_TREE: {836_setup_animation_player();837if (active) {838_set_process(true);839}840} break;841}842}843844void AnimationTree::set_animation_player(const NodePath &p_path) {845animation_player = p_path;846if (p_path.is_empty()) {847set_root_node(SceneStringName(path_pp));848while (animation_libraries.size()) {849remove_animation_library(animation_libraries[0].name);850}851}852emit_signal(SNAME("animation_player_changed")); // Needs to unpin AnimationPlayerEditor.853_setup_animation_player();854notify_property_list_changed();855}856857NodePath AnimationTree::get_animation_player() const {858return animation_player;859}860861void AnimationTree::_setup_animation_player() {862if (!is_inside_tree()) {863return;864}865866cache_valid = false;867868if (animation_player.is_empty()) {869clear_caches();870return;871}872873// Using AnimationPlayer here is for compatibility. Changing to AnimationMixer needs extra work like error handling.874AnimationPlayer *player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player));875if (player) {876if (!player->is_connected(SNAME("caches_cleared"), callable_mp(this, &AnimationTree::_setup_animation_player))) {877player->connect(SNAME("caches_cleared"), callable_mp(this, &AnimationTree::_setup_animation_player), CONNECT_DEFERRED);878}879if (!player->is_connected(SNAME("animation_list_changed"), callable_mp(this, &AnimationTree::_setup_animation_player))) {880player->connect(SNAME("animation_list_changed"), callable_mp(this, &AnimationTree::_setup_animation_player), CONNECT_DEFERRED);881}882Node *root = player->get_node_or_null(player->get_root_node());883if (root) {884set_root_node(get_path_to(root, true));885}886while (animation_libraries.size()) {887remove_animation_library(animation_libraries[0].name);888}889List<StringName> list;890player->get_animation_library_list(&list);891for (const StringName &E : list) {892Ref<AnimationLibrary> lib = player->get_animation_library(E);893if (lib.is_valid()) {894add_animation_library(E, lib);895}896}897}898899clear_caches();900}901902// `libraries` is a dynamic property, so we can't use `_validate_property` to change it.903uint32_t AnimationTree::_get_libraries_property_usage() const {904if (!animation_player.is_empty()) {905return PROPERTY_USAGE_READ_ONLY;906}907return PROPERTY_USAGE_STORAGE;908}909910void AnimationTree::_validate_property(PropertyInfo &p_property) const {911if (!Engine::get_singleton()->is_editor_hint()) {912return;913}914915if (!animation_player.is_empty()) {916if (p_property.name == "root_node") {917p_property.usage |= PROPERTY_USAGE_READ_ONLY;918}919}920}921922bool AnimationTree::_set(const StringName &p_name, const Variant &p_value) {923#ifndef DISABLE_DEPRECATED924String name = p_name;925if (name == "process_callback") {926set_callback_mode_process(static_cast<AnimationCallbackModeProcess>((int)p_value));927return true;928}929#endif // DISABLE_DEPRECATED930if (properties_dirty) {931_update_properties();932}933934if (property_map.has(p_name)) {935if (is_inside_tree() && property_map[p_name].second) {936return false; // Prevent to set property by user.937}938Pair<Variant, bool> &prop = property_map[p_name];939Variant value = p_value;940if (Animation::validate_type_match(prop.first, value)) {941prop.first = value;942}943return true;944}945946return false;947}948949bool AnimationTree::_get(const StringName &p_name, Variant &r_ret) const {950#ifndef DISABLE_DEPRECATED951if (p_name == "process_callback") {952r_ret = get_callback_mode_process();953return true;954}955#endif // DISABLE_DEPRECATED956if (properties_dirty) {957_update_properties();958}959960if (property_map.has(p_name)) {961r_ret = property_map[p_name].first;962return true;963}964965return false;966}967968void AnimationTree::_get_property_list(List<PropertyInfo> *p_list) const {969if (properties_dirty) {970_update_properties();971}972973for (const PropertyInfo &E : properties) {974p_list->push_back(E);975}976}977978real_t AnimationTree::get_connection_activity(const StringName &p_path, int p_connection) const {979if (!input_activity_map_get.has(p_path)) {980return 0;981}982983int index = input_activity_map_get[p_path];984const LocalVector<Activity> &activity = input_activity_map.get_by_index(index).value;985986if (p_connection < 0 || p_connection >= (int64_t)activity.size() || activity[p_connection].last_pass != process_pass) {987return 0;988}989990return activity[p_connection].activity;991}992993#ifdef TOOLS_ENABLED994String AnimationTree::get_editor_error_message() const {995if (!is_active()) {996return TTR("The AnimationTree is inactive.\nActivate it in the inspector to enable playback; check node warnings if activation fails.");997} else if (!is_enabled()) {998return TTR("The AnimationTree node (or one of its parents) has its process mode set to Disabled.\nChange the process mode in the inspector to allow playback.");999} else if (is_state_invalid()) {1000return get_invalid_state_reason();1001}10021003return "";1004}1005#endif10061007void AnimationTree::_bind_methods() {1008ClassDB::bind_method(D_METHOD("set_tree_root", "animation_node"), &AnimationTree::set_root_animation_node);1009ClassDB::bind_method(D_METHOD("get_tree_root"), &AnimationTree::get_root_animation_node);10101011ClassDB::bind_method(D_METHOD("set_advance_expression_base_node", "path"), &AnimationTree::set_advance_expression_base_node);1012ClassDB::bind_method(D_METHOD("get_advance_expression_base_node"), &AnimationTree::get_advance_expression_base_node);10131014ClassDB::bind_method(D_METHOD("set_animation_player", "path"), &AnimationTree::set_animation_player);1015ClassDB::bind_method(D_METHOD("get_animation_player"), &AnimationTree::get_animation_player);10161017ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, AnimationRootNode::get_class_static()), "set_tree_root", "get_tree_root");1018ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "advance_expression_base_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node"), "set_advance_expression_base_node", "get_advance_expression_base_node");1019ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player");10201021ADD_SIGNAL(MethodInfo(SNAME("animation_player_changed")));1022}10231024AnimationTree::AnimationTree() {1025deterministic = true;1026callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS;1027}10281029AnimationTree::~AnimationTree() {1030}103110321033