initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
22
scene/animation/SCsub
Normal file
22
scene/animation/SCsub
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
# Thirdparty code
|
||||
|
||||
thirdparty_obj = []
|
||||
|
||||
env_thirdparty = env.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
env.scene_sources += thirdparty_obj
|
||||
|
||||
# Godot source files
|
||||
|
||||
scene_obj = []
|
||||
|
||||
env.add_source_files(scene_obj, "*.cpp")
|
||||
env.scene_sources += scene_obj
|
||||
|
||||
# Needed to force rebuilding the scene files when the thirdparty code is updated.
|
||||
env.Depends(scene_obj, thirdparty_obj)
|
432
scene/animation/animation_blend_space_1d.cpp
Normal file
432
scene/animation/animation_blend_space_1d.cpp
Normal file
@@ -0,0 +1,432 @@
|
||||
/**************************************************************************/
|
||||
/* animation_blend_space_1d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "animation_blend_space_1d.h"
|
||||
|
||||
#include "animation_blend_tree.h"
|
||||
|
||||
void AnimationNodeBlendSpace1D::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, blend_position));
|
||||
r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
}
|
||||
|
||||
Variant AnimationNodeBlendSpace1D::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
|
||||
if (ret != Variant()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (p_parameter == closest) {
|
||||
return (int)-1;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<AnimationNode> AnimationNodeBlendSpace1D::get_child_by_name(const StringName &p_name) const {
|
||||
return get_blend_point_node(p_name.operator String().to_int());
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (p_property.name.begins_with("blend_point_")) {
|
||||
String left = p_property.name.get_slicec('/', 0);
|
||||
int idx = left.get_slicec('_', 2).to_int();
|
||||
if (idx >= blend_points_used) {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::_tree_changed() {
|
||||
AnimationRootNode::_tree_changed();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
|
||||
AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
|
||||
AnimationRootNode::_animation_node_removed(p_oid, p_node);
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace1D::add_blend_point, DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace1D::set_blend_point_position);
|
||||
ClassDB::bind_method(D_METHOD("get_blend_point_position", "point"), &AnimationNodeBlendSpace1D::get_blend_point_position);
|
||||
ClassDB::bind_method(D_METHOD("set_blend_point_node", "point", "node"), &AnimationNodeBlendSpace1D::set_blend_point_node);
|
||||
ClassDB::bind_method(D_METHOD("get_blend_point_node", "point"), &AnimationNodeBlendSpace1D::get_blend_point_node);
|
||||
ClassDB::bind_method(D_METHOD("remove_blend_point", "point"), &AnimationNodeBlendSpace1D::remove_blend_point);
|
||||
ClassDB::bind_method(D_METHOD("get_blend_point_count"), &AnimationNodeBlendSpace1D::get_blend_point_count);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_min_space", "min_space"), &AnimationNodeBlendSpace1D::set_min_space);
|
||||
ClassDB::bind_method(D_METHOD("get_min_space"), &AnimationNodeBlendSpace1D::get_min_space);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_max_space", "max_space"), &AnimationNodeBlendSpace1D::set_max_space);
|
||||
ClassDB::bind_method(D_METHOD("get_max_space"), &AnimationNodeBlendSpace1D::get_max_space);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_snap", "snap"), &AnimationNodeBlendSpace1D::set_snap);
|
||||
ClassDB::bind_method(D_METHOD("get_snap"), &AnimationNodeBlendSpace1D::get_snap);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_value_label", "text"), &AnimationNodeBlendSpace1D::set_value_label);
|
||||
ClassDB::bind_method(D_METHOD("get_value_label"), &AnimationNodeBlendSpace1D::get_value_label);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_blend_mode", "mode"), &AnimationNodeBlendSpace1D::set_blend_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_blend_mode"), &AnimationNodeBlendSpace1D::get_blend_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlendSpace1D::set_use_sync);
|
||||
ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlendSpace1D::is_using_sync);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace1D::_add_blend_point);
|
||||
|
||||
for (int i = 0; i < MAX_BLEND_POINTS; i++) {
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i);
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i);
|
||||
}
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_min_space", "get_min_space");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_space", "get_max_space");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_snap", "get_snap");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_value_label", "get_value_label");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Interpolated,Discrete,Carry", PROPERTY_USAGE_NO_EDITOR), "set_blend_mode", "get_blend_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_use_sync", "is_using_sync");
|
||||
|
||||
BIND_ENUM_CONSTANT(BLEND_MODE_INTERPOLATED);
|
||||
BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE);
|
||||
BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE_CARRY);
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::get_child_nodes(List<ChildNode> *r_child_nodes) {
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
ChildNode cn;
|
||||
cn.name = itos(i);
|
||||
cn.node = blend_points[i].node;
|
||||
r_child_nodes->push_back(cn);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index) {
|
||||
ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS);
|
||||
ERR_FAIL_COND(p_node.is_null());
|
||||
|
||||
ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used);
|
||||
|
||||
if (p_at_index == -1 || p_at_index == blend_points_used) {
|
||||
p_at_index = blend_points_used;
|
||||
} else {
|
||||
for (int i = blend_points_used - 1; i > p_at_index; i--) {
|
||||
blend_points[i] = blend_points[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
blend_points[p_at_index].node = p_node;
|
||||
blend_points[p_at_index].position = p_position;
|
||||
|
||||
blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
||||
blend_points[p_at_index].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
|
||||
blend_points[p_at_index].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
|
||||
|
||||
blend_points_used++;
|
||||
emit_signal(SNAME("tree_changed"));
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::set_blend_point_position(int p_point, float p_position) {
|
||||
ERR_FAIL_INDEX(p_point, blend_points_used);
|
||||
|
||||
blend_points[p_point].position = p_position;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::set_blend_point_node(int p_point, const Ref<AnimationRootNode> &p_node) {
|
||||
ERR_FAIL_INDEX(p_point, blend_points_used);
|
||||
ERR_FAIL_COND(p_node.is_null());
|
||||
|
||||
if (blend_points[p_point].node.is_valid()) {
|
||||
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed));
|
||||
blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed));
|
||||
blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed));
|
||||
}
|
||||
|
||||
blend_points[p_point].node = p_node;
|
||||
blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
||||
blend_points[p_point].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
|
||||
blend_points[p_point].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
|
||||
|
||||
emit_signal(SNAME("tree_changed"));
|
||||
}
|
||||
|
||||
float AnimationNodeBlendSpace1D::get_blend_point_position(int p_point) const {
|
||||
ERR_FAIL_INDEX_V(p_point, MAX_BLEND_POINTS, 0);
|
||||
return blend_points[p_point].position;
|
||||
}
|
||||
|
||||
Ref<AnimationRootNode> AnimationNodeBlendSpace1D::get_blend_point_node(int p_point) const {
|
||||
ERR_FAIL_INDEX_V(p_point, MAX_BLEND_POINTS, Ref<AnimationRootNode>());
|
||||
return blend_points[p_point].node;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::remove_blend_point(int p_point) {
|
||||
ERR_FAIL_INDEX(p_point, blend_points_used);
|
||||
|
||||
ERR_FAIL_COND(blend_points[p_point].node.is_null());
|
||||
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed));
|
||||
blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed));
|
||||
blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed));
|
||||
|
||||
for (int i = p_point; i < blend_points_used - 1; i++) {
|
||||
blend_points[i] = blend_points[i + 1];
|
||||
}
|
||||
|
||||
blend_points_used--;
|
||||
|
||||
emit_signal(SNAME("animation_node_removed"), get_instance_id(), itos(p_point));
|
||||
emit_signal(SNAME("tree_changed"));
|
||||
}
|
||||
|
||||
int AnimationNodeBlendSpace1D::get_blend_point_count() const {
|
||||
return blend_points_used;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::set_min_space(float p_min) {
|
||||
min_space = p_min;
|
||||
|
||||
if (min_space >= max_space) {
|
||||
min_space = max_space - 1;
|
||||
}
|
||||
}
|
||||
|
||||
float AnimationNodeBlendSpace1D::get_min_space() const {
|
||||
return min_space;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::set_max_space(float p_max) {
|
||||
max_space = p_max;
|
||||
|
||||
if (max_space <= min_space) {
|
||||
max_space = min_space + 1;
|
||||
}
|
||||
}
|
||||
|
||||
float AnimationNodeBlendSpace1D::get_max_space() const {
|
||||
return max_space;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::set_snap(float p_snap) {
|
||||
snap = p_snap;
|
||||
}
|
||||
|
||||
float AnimationNodeBlendSpace1D::get_snap() const {
|
||||
return snap;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::set_value_label(const String &p_label) {
|
||||
value_label = p_label;
|
||||
}
|
||||
|
||||
String AnimationNodeBlendSpace1D::get_value_label() const {
|
||||
return value_label;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::set_blend_mode(BlendMode p_blend_mode) {
|
||||
blend_mode = p_blend_mode;
|
||||
}
|
||||
|
||||
AnimationNodeBlendSpace1D::BlendMode AnimationNodeBlendSpace1D::get_blend_mode() const {
|
||||
return blend_mode;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::set_use_sync(bool p_sync) {
|
||||
sync = p_sync;
|
||||
}
|
||||
|
||||
bool AnimationNodeBlendSpace1D::is_using_sync() const {
|
||||
return sync;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node) {
|
||||
if (p_index == blend_points_used) {
|
||||
add_blend_point(p_node, 0);
|
||||
} else {
|
||||
set_blend_point_node(p_index, p_node);
|
||||
}
|
||||
}
|
||||
|
||||
AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
if (!blend_points_used) {
|
||||
return NodeTimeInfo();
|
||||
}
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
|
||||
if (blend_points_used == 1) {
|
||||
// only one point available, just play that animation
|
||||
pi.weight = 1.0;
|
||||
return blend_node(blend_points[0].node, blend_points[0].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
}
|
||||
|
||||
double blend_pos = get_parameter(blend_position);
|
||||
int cur_closest = get_parameter(closest);
|
||||
NodeTimeInfo mind;
|
||||
|
||||
if (blend_mode == BLEND_MODE_INTERPOLATED) {
|
||||
int point_lower = -1;
|
||||
float pos_lower = 0.0;
|
||||
int point_higher = -1;
|
||||
float pos_higher = 0.0;
|
||||
|
||||
// find the closest two points to blend between
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
float pos = blend_points[i].position;
|
||||
|
||||
if (pos <= blend_pos) {
|
||||
if (point_lower == -1 || pos > pos_lower) {
|
||||
point_lower = i;
|
||||
pos_lower = pos;
|
||||
}
|
||||
} else if (point_higher == -1 || pos < pos_higher) {
|
||||
point_higher = i;
|
||||
pos_higher = pos;
|
||||
}
|
||||
}
|
||||
|
||||
// fill in weights
|
||||
float weights[MAX_BLEND_POINTS] = {};
|
||||
if (point_lower == -1 && point_higher != -1) {
|
||||
// we are on the left side, no other point to the left
|
||||
// we just play the next point.
|
||||
|
||||
weights[point_higher] = 1.0;
|
||||
} else if (point_higher == -1) {
|
||||
// we are on the right side, no other point to the right
|
||||
// we just play the previous point
|
||||
|
||||
weights[point_lower] = 1.0;
|
||||
} else {
|
||||
// we are between two points.
|
||||
// figure out weights, then blend the animations
|
||||
|
||||
float distance_between_points = pos_higher - pos_lower;
|
||||
|
||||
float current_pos_inbetween = blend_pos - pos_lower;
|
||||
|
||||
float blend_percentage = current_pos_inbetween / distance_between_points;
|
||||
|
||||
float blend_lower = 1.0 - blend_percentage;
|
||||
float blend_higher = blend_percentage;
|
||||
|
||||
weights[point_lower] = blend_lower;
|
||||
weights[point_higher] = blend_higher;
|
||||
}
|
||||
|
||||
// actually blend the animations now
|
||||
bool first = true;
|
||||
double max_weight = 0.0;
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
if (i == point_lower || i == point_higher) {
|
||||
pi.weight = weights[i];
|
||||
NodeTimeInfo t = blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
if (first || pi.weight > max_weight) {
|
||||
max_weight = pi.weight;
|
||||
mind = t;
|
||||
first = false;
|
||||
}
|
||||
} else if (sync) {
|
||||
pi.weight = 0;
|
||||
blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int new_closest = -1;
|
||||
double new_closest_dist = 1e20;
|
||||
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
double d = std::abs(blend_points[i].position - blend_pos);
|
||||
if (d < new_closest_dist) {
|
||||
new_closest = i;
|
||||
new_closest_dist = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_closest != cur_closest && new_closest != -1) {
|
||||
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) {
|
||||
NodeTimeInfo from;
|
||||
// For ping-pong loop.
|
||||
Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node);
|
||||
Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node);
|
||||
if (na_c.is_valid() && na_n.is_valid()) {
|
||||
na_n->process_state = process_state;
|
||||
na_c->process_state = process_state;
|
||||
|
||||
na_n->set_backward(na_c->is_backward());
|
||||
|
||||
na_n = nullptr;
|
||||
na_c = nullptr;
|
||||
}
|
||||
// See how much animation remains.
|
||||
pi.seeked = false;
|
||||
pi.weight = 0;
|
||||
from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, true);
|
||||
pi.time = from.position;
|
||||
}
|
||||
pi.seeked = true;
|
||||
pi.weight = 1.0;
|
||||
mind = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
cur_closest = new_closest;
|
||||
} else {
|
||||
pi.weight = 1.0;
|
||||
mind = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
pi = p_playback_info;
|
||||
pi.weight = 0;
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
if (i != cur_closest) {
|
||||
blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_parameter(closest, cur_closest);
|
||||
return mind;
|
||||
}
|
||||
|
||||
String AnimationNodeBlendSpace1D::get_caption() const {
|
||||
return "BlendSpace1D";
|
||||
}
|
||||
|
||||
AnimationNodeBlendSpace1D::AnimationNodeBlendSpace1D() {
|
||||
for (int i = 0; i < MAX_BLEND_POINTS; i++) {
|
||||
blend_points[i].name = itos(i);
|
||||
}
|
||||
}
|
||||
|
||||
AnimationNodeBlendSpace1D::~AnimationNodeBlendSpace1D() {
|
||||
}
|
124
scene/animation/animation_blend_space_1d.h
Normal file
124
scene/animation/animation_blend_space_1d.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/**************************************************************************/
|
||||
/* animation_blend_space_1d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/animation/animation_tree.h"
|
||||
|
||||
class AnimationNodeBlendSpace1D : public AnimationRootNode {
|
||||
GDCLASS(AnimationNodeBlendSpace1D, AnimationRootNode);
|
||||
|
||||
public:
|
||||
enum BlendMode {
|
||||
BLEND_MODE_INTERPOLATED,
|
||||
BLEND_MODE_DISCRETE,
|
||||
BLEND_MODE_DISCRETE_CARRY,
|
||||
};
|
||||
|
||||
protected:
|
||||
enum {
|
||||
MAX_BLEND_POINTS = 64
|
||||
};
|
||||
|
||||
struct BlendPoint {
|
||||
StringName name;
|
||||
Ref<AnimationRootNode> node;
|
||||
float position = 0.0;
|
||||
};
|
||||
|
||||
BlendPoint blend_points[MAX_BLEND_POINTS];
|
||||
int blend_points_used = 0;
|
||||
|
||||
float max_space = 1.0;
|
||||
float min_space = -1.0;
|
||||
|
||||
float snap = 0.1;
|
||||
|
||||
String value_label = "value";
|
||||
|
||||
void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node);
|
||||
|
||||
StringName blend_position = "blend_position";
|
||||
StringName closest = "closest";
|
||||
|
||||
BlendMode blend_mode = BLEND_MODE_INTERPOLATED;
|
||||
|
||||
bool sync = false;
|
||||
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
static void _bind_methods();
|
||||
|
||||
virtual void _tree_changed() override;
|
||||
virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
|
||||
virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
|
||||
|
||||
public:
|
||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
||||
virtual void get_child_nodes(List<ChildNode> *r_child_nodes) override;
|
||||
|
||||
void add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index = -1);
|
||||
void set_blend_point_position(int p_point, float p_position);
|
||||
void set_blend_point_node(int p_point, const Ref<AnimationRootNode> &p_node);
|
||||
|
||||
float get_blend_point_position(int p_point) const;
|
||||
Ref<AnimationRootNode> get_blend_point_node(int p_point) const;
|
||||
void remove_blend_point(int p_point);
|
||||
int get_blend_point_count() const;
|
||||
|
||||
void set_min_space(float p_min);
|
||||
float get_min_space() const;
|
||||
|
||||
void set_max_space(float p_max);
|
||||
float get_max_space() const;
|
||||
|
||||
void set_snap(float p_snap);
|
||||
float get_snap() const;
|
||||
|
||||
void set_value_label(const String &p_label);
|
||||
String get_value_label() const;
|
||||
|
||||
void set_blend_mode(BlendMode p_blend_mode);
|
||||
BlendMode get_blend_mode() const;
|
||||
|
||||
void set_use_sync(bool p_sync);
|
||||
bool is_using_sync() const;
|
||||
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
String get_caption() const override;
|
||||
|
||||
Ref<AnimationNode> get_child_by_name(const StringName &p_name) const override;
|
||||
|
||||
AnimationNodeBlendSpace1D();
|
||||
~AnimationNodeBlendSpace1D();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationNodeBlendSpace1D::BlendMode)
|
729
scene/animation/animation_blend_space_2d.cpp
Normal file
729
scene/animation/animation_blend_space_2d.cpp
Normal file
@@ -0,0 +1,729 @@
|
||||
/**************************************************************************/
|
||||
/* animation_blend_space_2d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "animation_blend_space_2d.h"
|
||||
|
||||
#include "animation_blend_tree.h"
|
||||
#include "core/math/geometry_2d.h"
|
||||
|
||||
void AnimationNodeBlendSpace2D::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
AnimationNode::get_parameter_list(r_list);
|
||||
r_list->push_back(PropertyInfo(Variant::VECTOR2, blend_position));
|
||||
r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
}
|
||||
|
||||
Variant AnimationNodeBlendSpace2D::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
Variant ret = AnimationNode::get_parameter_default_value(p_parameter);
|
||||
if (ret != Variant()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (p_parameter == closest) {
|
||||
return (int)-1;
|
||||
} else {
|
||||
return Vector2();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::get_child_nodes(List<ChildNode> *r_child_nodes) {
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
ChildNode cn;
|
||||
cn.name = itos(i);
|
||||
cn.node = blend_points[i].node;
|
||||
r_child_nodes->push_back(cn);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index) {
|
||||
ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS);
|
||||
ERR_FAIL_COND(p_node.is_null());
|
||||
ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used);
|
||||
|
||||
if (p_at_index == -1 || p_at_index == blend_points_used) {
|
||||
p_at_index = blend_points_used;
|
||||
} else {
|
||||
for (int i = blend_points_used - 1; i > p_at_index; i--) {
|
||||
blend_points[i] = blend_points[i - 1];
|
||||
}
|
||||
for (int i = 0; i < triangles.size(); i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
if (triangles[i].points[j] >= p_at_index) {
|
||||
triangles.write[i].points[j]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
blend_points[p_at_index].node = p_node;
|
||||
blend_points[p_at_index].position = p_position;
|
||||
|
||||
blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
||||
blend_points[p_at_index].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
|
||||
blend_points[p_at_index].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
|
||||
blend_points_used++;
|
||||
|
||||
_queue_auto_triangles();
|
||||
|
||||
emit_signal(SNAME("tree_changed"));
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::set_blend_point_position(int p_point, const Vector2 &p_position) {
|
||||
ERR_FAIL_INDEX(p_point, blend_points_used);
|
||||
blend_points[p_point].position = p_position;
|
||||
_queue_auto_triangles();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::set_blend_point_node(int p_point, const Ref<AnimationRootNode> &p_node) {
|
||||
ERR_FAIL_INDEX(p_point, blend_points_used);
|
||||
ERR_FAIL_COND(p_node.is_null());
|
||||
|
||||
if (blend_points[p_point].node.is_valid()) {
|
||||
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed));
|
||||
blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed));
|
||||
blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed));
|
||||
}
|
||||
blend_points[p_point].node = p_node;
|
||||
blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED);
|
||||
blend_points[p_point].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
|
||||
blend_points[p_point].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
|
||||
|
||||
emit_signal(SNAME("tree_changed"));
|
||||
}
|
||||
|
||||
Vector2 AnimationNodeBlendSpace2D::get_blend_point_position(int p_point) const {
|
||||
ERR_FAIL_INDEX_V(p_point, MAX_BLEND_POINTS, Vector2());
|
||||
return blend_points[p_point].position;
|
||||
}
|
||||
|
||||
Ref<AnimationRootNode> AnimationNodeBlendSpace2D::get_blend_point_node(int p_point) const {
|
||||
ERR_FAIL_INDEX_V(p_point, MAX_BLEND_POINTS, Ref<AnimationRootNode>());
|
||||
return blend_points[p_point].node;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) {
|
||||
ERR_FAIL_INDEX(p_point, blend_points_used);
|
||||
|
||||
ERR_FAIL_COND(blend_points[p_point].node.is_null());
|
||||
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed));
|
||||
blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed));
|
||||
blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed));
|
||||
|
||||
for (int i = 0; i < triangles.size(); i++) {
|
||||
bool erase = false;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
if (triangles[i].points[j] == p_point) {
|
||||
erase = true;
|
||||
break;
|
||||
} else if (triangles[i].points[j] > p_point) {
|
||||
triangles.write[i].points[j]--;
|
||||
}
|
||||
}
|
||||
if (erase) {
|
||||
triangles.remove_at(i);
|
||||
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = p_point; i < blend_points_used - 1; i++) {
|
||||
blend_points[i] = blend_points[i + 1];
|
||||
}
|
||||
blend_points_used--;
|
||||
|
||||
emit_signal(SNAME("animation_node_removed"), get_instance_id(), itos(p_point));
|
||||
emit_signal(SNAME("tree_changed"));
|
||||
}
|
||||
|
||||
int AnimationNodeBlendSpace2D::get_blend_point_count() const {
|
||||
return blend_points_used;
|
||||
}
|
||||
|
||||
bool AnimationNodeBlendSpace2D::has_triangle(int p_x, int p_y, int p_z) const {
|
||||
ERR_FAIL_INDEX_V(p_x, blend_points_used, false);
|
||||
ERR_FAIL_INDEX_V(p_y, blend_points_used, false);
|
||||
ERR_FAIL_INDEX_V(p_z, blend_points_used, false);
|
||||
|
||||
BlendTriangle t;
|
||||
t.points[0] = p_x;
|
||||
t.points[1] = p_y;
|
||||
t.points[2] = p_z;
|
||||
|
||||
SortArray<int> sort;
|
||||
sort.sort(t.points, 3);
|
||||
|
||||
for (int i = 0; i < triangles.size(); i++) {
|
||||
bool all_equal = true;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
if (triangles[i].points[j] != t.points[j]) {
|
||||
all_equal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_equal) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::add_triangle(int p_x, int p_y, int p_z, int p_at_index) {
|
||||
ERR_FAIL_INDEX(p_x, blend_points_used);
|
||||
ERR_FAIL_INDEX(p_y, blend_points_used);
|
||||
ERR_FAIL_INDEX(p_z, blend_points_used);
|
||||
|
||||
_update_triangles();
|
||||
|
||||
BlendTriangle t;
|
||||
t.points[0] = p_x;
|
||||
t.points[1] = p_y;
|
||||
t.points[2] = p_z;
|
||||
|
||||
SortArray<int> sort;
|
||||
sort.sort(t.points, 3);
|
||||
|
||||
for (int i = 0; i < triangles.size(); i++) {
|
||||
bool all_equal = true;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
if (triangles[i].points[j] != t.points[j]) {
|
||||
all_equal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ERR_FAIL_COND(all_equal);
|
||||
}
|
||||
|
||||
if (p_at_index == -1 || p_at_index == triangles.size()) {
|
||||
triangles.push_back(t);
|
||||
} else {
|
||||
triangles.insert(p_at_index, t);
|
||||
}
|
||||
}
|
||||
|
||||
int AnimationNodeBlendSpace2D::get_triangle_point(int p_triangle, int p_point) {
|
||||
_update_triangles();
|
||||
|
||||
ERR_FAIL_INDEX_V(p_point, 3, -1);
|
||||
ERR_FAIL_INDEX_V(p_triangle, triangles.size(), -1);
|
||||
return triangles[p_triangle].points[p_point];
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::remove_triangle(int p_triangle) {
|
||||
ERR_FAIL_INDEX(p_triangle, triangles.size());
|
||||
|
||||
triangles.remove_at(p_triangle);
|
||||
}
|
||||
|
||||
int AnimationNodeBlendSpace2D::get_triangle_count() const {
|
||||
return triangles.size();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::set_min_space(const Vector2 &p_min) {
|
||||
min_space = p_min;
|
||||
if (min_space.x >= max_space.x) {
|
||||
min_space.x = max_space.x - 1;
|
||||
}
|
||||
if (min_space.y >= max_space.y) {
|
||||
min_space.y = max_space.y - 1;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 AnimationNodeBlendSpace2D::get_min_space() const {
|
||||
return min_space;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::set_max_space(const Vector2 &p_max) {
|
||||
max_space = p_max;
|
||||
if (max_space.x <= min_space.x) {
|
||||
max_space.x = min_space.x + 1;
|
||||
}
|
||||
if (max_space.y <= min_space.y) {
|
||||
max_space.y = min_space.y + 1;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 AnimationNodeBlendSpace2D::get_max_space() const {
|
||||
return max_space;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::set_snap(const Vector2 &p_snap) {
|
||||
snap = p_snap;
|
||||
}
|
||||
|
||||
Vector2 AnimationNodeBlendSpace2D::get_snap() const {
|
||||
return snap;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::set_x_label(const String &p_label) {
|
||||
x_label = p_label;
|
||||
}
|
||||
|
||||
String AnimationNodeBlendSpace2D::get_x_label() const {
|
||||
return x_label;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::set_y_label(const String &p_label) {
|
||||
y_label = p_label;
|
||||
}
|
||||
|
||||
String AnimationNodeBlendSpace2D::get_y_label() const {
|
||||
return y_label;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::_add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node) {
|
||||
if (p_index == blend_points_used) {
|
||||
add_blend_point(p_node, Vector2());
|
||||
} else {
|
||||
set_blend_point_node(p_index, p_node);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::_set_triangles(const Vector<int> &p_triangles) {
|
||||
if (auto_triangles) {
|
||||
return;
|
||||
}
|
||||
ERR_FAIL_COND(p_triangles.size() % 3 != 0);
|
||||
for (int i = 0; i < p_triangles.size(); i += 3) {
|
||||
add_triangle(p_triangles[i + 0], p_triangles[i + 1], p_triangles[i + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
Vector<int> AnimationNodeBlendSpace2D::_get_triangles() const {
|
||||
Vector<int> t;
|
||||
if (auto_triangles && triangles_dirty) {
|
||||
return t;
|
||||
}
|
||||
|
||||
t.resize(triangles.size() * 3);
|
||||
for (int i = 0; i < triangles.size(); i++) {
|
||||
t.write[i * 3 + 0] = triangles[i].points[0];
|
||||
t.write[i * 3 + 1] = triangles[i].points[1];
|
||||
t.write[i * 3 + 2] = triangles[i].points[2];
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::_queue_auto_triangles() {
|
||||
if (!auto_triangles || triangles_dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
triangles_dirty = true;
|
||||
callable_mp(this, &AnimationNodeBlendSpace2D::_update_triangles).call_deferred();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::_update_triangles() {
|
||||
if (!auto_triangles || !triangles_dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
triangles_dirty = false;
|
||||
triangles.clear();
|
||||
if (blend_points_used < 3) {
|
||||
emit_signal(SNAME("triangles_updated"));
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<Vector2> points;
|
||||
points.resize(blend_points_used);
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
points.write[i] = blend_points[i].position;
|
||||
}
|
||||
|
||||
Vector<Delaunay2D::Triangle> tr = Delaunay2D::triangulate(points);
|
||||
|
||||
for (int i = 0; i < tr.size(); i++) {
|
||||
add_triangle(tr[i].points[0], tr[i].points[1], tr[i].points[2]);
|
||||
}
|
||||
emit_signal(SNAME("triangles_updated"));
|
||||
}
|
||||
|
||||
Vector2 AnimationNodeBlendSpace2D::get_closest_point(const Vector2 &p_point) {
|
||||
_update_triangles();
|
||||
|
||||
if (triangles.is_empty()) {
|
||||
return Vector2();
|
||||
}
|
||||
|
||||
Vector2 best_point;
|
||||
bool first = true;
|
||||
|
||||
for (int i = 0; i < triangles.size(); i++) {
|
||||
Vector2 points[3];
|
||||
for (int j = 0; j < 3; j++) {
|
||||
points[j] = get_blend_point_position(get_triangle_point(i, j));
|
||||
}
|
||||
|
||||
if (Geometry2D::is_point_in_triangle(p_point, points[0], points[1], points[2])) {
|
||||
return p_point;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
const Vector2 segment_a = points[j];
|
||||
const Vector2 segment_b = points[(j + 1) % 3];
|
||||
Vector2 closest_point = Geometry2D::get_closest_point_to_segment(p_point, segment_a, segment_b);
|
||||
if (first || closest_point.distance_to(p_point) < best_point.distance_to(p_point)) {
|
||||
best_point = closest_point;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best_point;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::_blend_triangle(const Vector2 &p_pos, const Vector2 *p_points, float *r_weights) {
|
||||
if (p_pos.is_equal_approx(p_points[0])) {
|
||||
r_weights[0] = 1;
|
||||
r_weights[1] = 0;
|
||||
r_weights[2] = 0;
|
||||
return;
|
||||
}
|
||||
if (p_pos.is_equal_approx(p_points[1])) {
|
||||
r_weights[0] = 0;
|
||||
r_weights[1] = 1;
|
||||
r_weights[2] = 0;
|
||||
return;
|
||||
}
|
||||
if (p_pos.is_equal_approx(p_points[2])) {
|
||||
r_weights[0] = 0;
|
||||
r_weights[1] = 0;
|
||||
r_weights[2] = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 v0 = p_points[1] - p_points[0];
|
||||
Vector2 v1 = p_points[2] - p_points[0];
|
||||
Vector2 v2 = p_pos - p_points[0];
|
||||
|
||||
float d00 = v0.dot(v0);
|
||||
float d01 = v0.dot(v1);
|
||||
float d11 = v1.dot(v1);
|
||||
float d20 = v2.dot(v0);
|
||||
float d21 = v2.dot(v1);
|
||||
float denom = (d00 * d11 - d01 * d01);
|
||||
if (denom == 0) {
|
||||
r_weights[0] = 1;
|
||||
r_weights[1] = 0;
|
||||
r_weights[2] = 0;
|
||||
return;
|
||||
}
|
||||
float v = (d11 * d20 - d01 * d21) / denom;
|
||||
float w = (d00 * d21 - d01 * d20) / denom;
|
||||
float u = 1.0f - v - w;
|
||||
|
||||
r_weights[0] = u;
|
||||
r_weights[1] = v;
|
||||
r_weights[2] = w;
|
||||
}
|
||||
|
||||
AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
_update_triangles();
|
||||
|
||||
if (!blend_points_used) {
|
||||
return NodeTimeInfo();
|
||||
}
|
||||
|
||||
Vector2 blend_pos = get_parameter(blend_position);
|
||||
int cur_closest = get_parameter(closest);
|
||||
NodeTimeInfo mind; //time of min distance point
|
||||
|
||||
AnimationMixer::PlaybackInfo pi = p_playback_info;
|
||||
|
||||
if (blend_mode == BLEND_MODE_INTERPOLATED) {
|
||||
if (triangles.is_empty()) {
|
||||
return NodeTimeInfo();
|
||||
}
|
||||
|
||||
Vector2 best_point;
|
||||
bool first = true;
|
||||
int blend_triangle = -1;
|
||||
float blend_weights[3] = { 0, 0, 0 };
|
||||
|
||||
for (int i = 0; i < triangles.size(); i++) {
|
||||
Vector2 points[3];
|
||||
for (int j = 0; j < 3; j++) {
|
||||
points[j] = get_blend_point_position(get_triangle_point(i, j));
|
||||
}
|
||||
|
||||
if (Geometry2D::is_point_in_triangle(blend_pos, points[0], points[1], points[2])) {
|
||||
blend_triangle = i;
|
||||
_blend_triangle(blend_pos, points, blend_weights);
|
||||
break;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
const Vector2 segment_a = points[j];
|
||||
const Vector2 segment_b = points[(j + 1) % 3];
|
||||
Vector2 closest2 = Geometry2D::get_closest_point_to_segment(blend_pos, segment_a, segment_b);
|
||||
if (first || closest2.distance_to(blend_pos) < best_point.distance_to(blend_pos)) {
|
||||
best_point = closest2;
|
||||
blend_triangle = i;
|
||||
first = false;
|
||||
const real_t d = segment_a.distance_to(segment_b);
|
||||
if (d == 0.0) {
|
||||
blend_weights[j] = 1.0;
|
||||
blend_weights[(j + 1) % 3] = 0.0;
|
||||
blend_weights[(j + 2) % 3] = 0.0;
|
||||
} else {
|
||||
const real_t c = segment_a.distance_to(closest2) / d;
|
||||
|
||||
blend_weights[j] = 1.0 - c;
|
||||
blend_weights[(j + 1) % 3] = c;
|
||||
blend_weights[(j + 2) % 3] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(blend_triangle == -1, NodeTimeInfo()); //should never reach here
|
||||
|
||||
int triangle_points[3];
|
||||
for (int j = 0; j < 3; j++) {
|
||||
triangle_points[j] = get_triangle_point(blend_triangle, j);
|
||||
}
|
||||
|
||||
first = true;
|
||||
|
||||
double max_weight = 0.0;
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
bool found = false;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
if (i == triangle_points[j]) {
|
||||
//blend with the given weight
|
||||
pi.weight = blend_weights[j];
|
||||
NodeTimeInfo t = blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
if (first || pi.weight > max_weight) {
|
||||
mind = t;
|
||||
max_weight = pi.weight;
|
||||
first = false;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sync && !found) {
|
||||
pi.weight = 0;
|
||||
blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int new_closest = -1;
|
||||
float new_closest_dist = 1e20;
|
||||
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
float d = blend_points[i].position.distance_squared_to(blend_pos);
|
||||
if (d < new_closest_dist) {
|
||||
new_closest = i;
|
||||
new_closest_dist = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_closest != cur_closest && new_closest != -1) {
|
||||
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) {
|
||||
NodeTimeInfo from;
|
||||
// For ping-pong loop.
|
||||
Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node);
|
||||
Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node);
|
||||
if (na_c.is_valid() && na_n.is_valid()) {
|
||||
na_n->process_state = process_state;
|
||||
na_c->process_state = process_state;
|
||||
|
||||
na_n->set_backward(na_c->is_backward());
|
||||
|
||||
na_n = nullptr;
|
||||
na_c = nullptr;
|
||||
}
|
||||
// See how much animation remains.
|
||||
pi.seeked = false;
|
||||
pi.weight = 0;
|
||||
from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, true);
|
||||
pi.time = from.position;
|
||||
}
|
||||
pi.seeked = true;
|
||||
pi.weight = 1.0;
|
||||
mind = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
cur_closest = new_closest;
|
||||
} else {
|
||||
pi.weight = 1.0;
|
||||
mind = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
pi = p_playback_info;
|
||||
pi.weight = 0;
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
if (i != cur_closest) {
|
||||
blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_parameter(closest, cur_closest);
|
||||
return mind;
|
||||
}
|
||||
|
||||
String AnimationNodeBlendSpace2D::get_caption() const {
|
||||
return "BlendSpace2D";
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (auto_triangles && p_property.name == "triangles") {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
if (p_property.name.begins_with("blend_point_")) {
|
||||
String left = p_property.name.get_slicec('/', 0);
|
||||
int idx = left.get_slicec('_', 2).to_int();
|
||||
if (idx >= blend_points_used) {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::set_auto_triangles(bool p_enable) {
|
||||
if (auto_triangles == p_enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto_triangles = p_enable;
|
||||
_queue_auto_triangles();
|
||||
}
|
||||
|
||||
bool AnimationNodeBlendSpace2D::get_auto_triangles() const {
|
||||
return auto_triangles;
|
||||
}
|
||||
|
||||
Ref<AnimationNode> AnimationNodeBlendSpace2D::get_child_by_name(const StringName &p_name) const {
|
||||
return get_blend_point_node(p_name.operator String().to_int());
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::set_blend_mode(BlendMode p_blend_mode) {
|
||||
blend_mode = p_blend_mode;
|
||||
}
|
||||
|
||||
AnimationNodeBlendSpace2D::BlendMode AnimationNodeBlendSpace2D::get_blend_mode() const {
|
||||
return blend_mode;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::set_use_sync(bool p_sync) {
|
||||
sync = p_sync;
|
||||
}
|
||||
|
||||
bool AnimationNodeBlendSpace2D::is_using_sync() const {
|
||||
return sync;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::_tree_changed() {
|
||||
AnimationRootNode::_tree_changed();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
|
||||
AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
|
||||
AnimationRootNode::_animation_node_removed(p_oid, p_node);
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace2D::set_blend_point_position);
|
||||
ClassDB::bind_method(D_METHOD("get_blend_point_position", "point"), &AnimationNodeBlendSpace2D::get_blend_point_position);
|
||||
ClassDB::bind_method(D_METHOD("set_blend_point_node", "point", "node"), &AnimationNodeBlendSpace2D::set_blend_point_node);
|
||||
ClassDB::bind_method(D_METHOD("get_blend_point_node", "point"), &AnimationNodeBlendSpace2D::get_blend_point_node);
|
||||
ClassDB::bind_method(D_METHOD("remove_blend_point", "point"), &AnimationNodeBlendSpace2D::remove_blend_point);
|
||||
ClassDB::bind_method(D_METHOD("get_blend_point_count"), &AnimationNodeBlendSpace2D::get_blend_point_count);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_triangle", "x", "y", "z", "at_index"), &AnimationNodeBlendSpace2D::add_triangle, DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("get_triangle_point", "triangle", "point"), &AnimationNodeBlendSpace2D::get_triangle_point);
|
||||
ClassDB::bind_method(D_METHOD("remove_triangle", "triangle"), &AnimationNodeBlendSpace2D::remove_triangle);
|
||||
ClassDB::bind_method(D_METHOD("get_triangle_count"), &AnimationNodeBlendSpace2D::get_triangle_count);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_min_space", "min_space"), &AnimationNodeBlendSpace2D::set_min_space);
|
||||
ClassDB::bind_method(D_METHOD("get_min_space"), &AnimationNodeBlendSpace2D::get_min_space);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_max_space", "max_space"), &AnimationNodeBlendSpace2D::set_max_space);
|
||||
ClassDB::bind_method(D_METHOD("get_max_space"), &AnimationNodeBlendSpace2D::get_max_space);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_snap", "snap"), &AnimationNodeBlendSpace2D::set_snap);
|
||||
ClassDB::bind_method(D_METHOD("get_snap"), &AnimationNodeBlendSpace2D::get_snap);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_x_label", "text"), &AnimationNodeBlendSpace2D::set_x_label);
|
||||
ClassDB::bind_method(D_METHOD("get_x_label"), &AnimationNodeBlendSpace2D::get_x_label);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_y_label", "text"), &AnimationNodeBlendSpace2D::set_y_label);
|
||||
ClassDB::bind_method(D_METHOD("get_y_label"), &AnimationNodeBlendSpace2D::get_y_label);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace2D::_add_blend_point);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_set_triangles", "triangles"), &AnimationNodeBlendSpace2D::_set_triangles);
|
||||
ClassDB::bind_method(D_METHOD("_get_triangles"), &AnimationNodeBlendSpace2D::_get_triangles);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_auto_triangles", "enable"), &AnimationNodeBlendSpace2D::set_auto_triangles);
|
||||
ClassDB::bind_method(D_METHOD("get_auto_triangles"), &AnimationNodeBlendSpace2D::get_auto_triangles);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_blend_mode", "mode"), &AnimationNodeBlendSpace2D::set_blend_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_blend_mode"), &AnimationNodeBlendSpace2D::get_blend_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlendSpace2D::set_use_sync);
|
||||
ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlendSpace2D::is_using_sync);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_auto_triangles", "get_auto_triangles");
|
||||
|
||||
for (int i = 0; i < MAX_BLEND_POINTS; i++) {
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i);
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::VECTOR2, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i);
|
||||
}
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_triangles", "_get_triangles");
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_min_space", "get_min_space");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_space", "get_max_space");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_snap", "get_snap");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "x_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_x_label", "get_x_label");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "y_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_y_label", "get_y_label");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Interpolated,Discrete,Carry", PROPERTY_USAGE_NO_EDITOR), "set_blend_mode", "get_blend_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_use_sync", "is_using_sync");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("triangles_updated"));
|
||||
BIND_ENUM_CONSTANT(BLEND_MODE_INTERPOLATED);
|
||||
BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE);
|
||||
BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE_CARRY);
|
||||
}
|
||||
|
||||
AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() {
|
||||
for (int i = 0; i < MAX_BLEND_POINTS; i++) {
|
||||
blend_points[i].name = itos(i);
|
||||
}
|
||||
}
|
||||
|
||||
AnimationNodeBlendSpace2D::~AnimationNodeBlendSpace2D() {
|
||||
}
|
150
scene/animation/animation_blend_space_2d.h
Normal file
150
scene/animation/animation_blend_space_2d.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/**************************************************************************/
|
||||
/* animation_blend_space_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/animation/animation_tree.h"
|
||||
|
||||
class AnimationNodeBlendSpace2D : public AnimationRootNode {
|
||||
GDCLASS(AnimationNodeBlendSpace2D, AnimationRootNode);
|
||||
|
||||
public:
|
||||
enum BlendMode {
|
||||
BLEND_MODE_INTERPOLATED,
|
||||
BLEND_MODE_DISCRETE,
|
||||
BLEND_MODE_DISCRETE_CARRY,
|
||||
};
|
||||
|
||||
protected:
|
||||
enum {
|
||||
MAX_BLEND_POINTS = 64
|
||||
};
|
||||
|
||||
struct BlendPoint {
|
||||
StringName name;
|
||||
Ref<AnimationRootNode> node;
|
||||
Vector2 position;
|
||||
};
|
||||
|
||||
BlendPoint blend_points[MAX_BLEND_POINTS];
|
||||
int blend_points_used = 0;
|
||||
|
||||
struct BlendTriangle {
|
||||
int points[3] = {};
|
||||
};
|
||||
|
||||
Vector<BlendTriangle> triangles;
|
||||
|
||||
StringName blend_position = "blend_position";
|
||||
StringName closest = "closest";
|
||||
Vector2 max_space = Vector2(1, 1);
|
||||
Vector2 min_space = Vector2(-1, -1);
|
||||
Vector2 snap = Vector2(0.1, 0.1);
|
||||
String x_label = "x";
|
||||
String y_label = "y";
|
||||
BlendMode blend_mode = BLEND_MODE_INTERPOLATED;
|
||||
|
||||
void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node);
|
||||
void _set_triangles(const Vector<int> &p_triangles);
|
||||
Vector<int> _get_triangles() const;
|
||||
|
||||
void _blend_triangle(const Vector2 &p_pos, const Vector2 *p_points, float *r_weights);
|
||||
|
||||
bool auto_triangles = true;
|
||||
bool triangles_dirty = false;
|
||||
|
||||
void _update_triangles();
|
||||
void _queue_auto_triangles();
|
||||
|
||||
bool sync = false;
|
||||
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
static void _bind_methods();
|
||||
|
||||
virtual void _tree_changed() override;
|
||||
virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
|
||||
virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
|
||||
|
||||
public:
|
||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
||||
virtual void get_child_nodes(List<ChildNode> *r_child_nodes) override;
|
||||
|
||||
void add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index = -1);
|
||||
void set_blend_point_position(int p_point, const Vector2 &p_position);
|
||||
void set_blend_point_node(int p_point, const Ref<AnimationRootNode> &p_node);
|
||||
Vector2 get_blend_point_position(int p_point) const;
|
||||
Ref<AnimationRootNode> get_blend_point_node(int p_point) const;
|
||||
void remove_blend_point(int p_point);
|
||||
int get_blend_point_count() const;
|
||||
|
||||
bool has_triangle(int p_x, int p_y, int p_z) const;
|
||||
void add_triangle(int p_x, int p_y, int p_z, int p_at_index = -1);
|
||||
int get_triangle_point(int p_triangle, int p_point);
|
||||
void remove_triangle(int p_triangle);
|
||||
int get_triangle_count() const;
|
||||
|
||||
void set_min_space(const Vector2 &p_min);
|
||||
Vector2 get_min_space() const;
|
||||
|
||||
void set_max_space(const Vector2 &p_max);
|
||||
Vector2 get_max_space() const;
|
||||
|
||||
void set_snap(const Vector2 &p_snap);
|
||||
Vector2 get_snap() const;
|
||||
|
||||
void set_x_label(const String &p_label);
|
||||
String get_x_label() const;
|
||||
|
||||
void set_y_label(const String &p_label);
|
||||
String get_y_label() const;
|
||||
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual String get_caption() const override;
|
||||
|
||||
Vector2 get_closest_point(const Vector2 &p_point);
|
||||
|
||||
void set_auto_triangles(bool p_enable);
|
||||
bool get_auto_triangles() const;
|
||||
|
||||
void set_blend_mode(BlendMode p_blend_mode);
|
||||
BlendMode get_blend_mode() const;
|
||||
|
||||
void set_use_sync(bool p_sync);
|
||||
bool is_using_sync() const;
|
||||
|
||||
virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name) const override;
|
||||
|
||||
AnimationNodeBlendSpace2D();
|
||||
~AnimationNodeBlendSpace2D();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationNodeBlendSpace2D::BlendMode)
|
1885
scene/animation/animation_blend_tree.cpp
Normal file
1885
scene/animation/animation_blend_tree.cpp
Normal file
File diff suppressed because it is too large
Load Diff
487
scene/animation/animation_blend_tree.h
Normal file
487
scene/animation/animation_blend_tree.h
Normal file
@@ -0,0 +1,487 @@
|
||||
/**************************************************************************/
|
||||
/* animation_blend_tree.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/animation/animation_tree.h"
|
||||
|
||||
class AnimationNodeAnimation : public AnimationRootNode {
|
||||
GDCLASS(AnimationNodeAnimation, AnimationRootNode);
|
||||
|
||||
StringName backward = "backward"; // Only used by pingpong animation.
|
||||
|
||||
StringName animation;
|
||||
|
||||
bool advance_on_start = false;
|
||||
|
||||
bool use_custom_timeline = false;
|
||||
double timeline_length = 1.0;
|
||||
Animation::LoopMode loop_mode = Animation::LOOP_NONE;
|
||||
bool stretch_time_scale = true;
|
||||
double start_offset = 0.0;
|
||||
|
||||
uint64_t last_version = 0;
|
||||
bool skip = false;
|
||||
|
||||
public:
|
||||
enum PlayMode {
|
||||
PLAY_MODE_FORWARD,
|
||||
PLAY_MODE_BACKWARD
|
||||
};
|
||||
|
||||
void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
||||
virtual NodeTimeInfo get_node_time_info() const override; // Wrapper of get_parameter().
|
||||
|
||||
static Vector<String> (*get_editable_animation_list)();
|
||||
|
||||
virtual String get_caption() const override;
|
||||
virtual NodeTimeInfo process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
void set_animation(const StringName &p_name);
|
||||
StringName get_animation() const;
|
||||
|
||||
void set_play_mode(PlayMode p_play_mode);
|
||||
PlayMode get_play_mode() const;
|
||||
|
||||
void set_backward(bool p_backward);
|
||||
bool is_backward() const;
|
||||
|
||||
void set_advance_on_start(bool p_advance_on_start);
|
||||
bool is_advance_on_start() const;
|
||||
|
||||
void set_use_custom_timeline(bool p_use_custom_timeline);
|
||||
bool is_using_custom_timeline() const;
|
||||
|
||||
void set_timeline_length(double p_length);
|
||||
double get_timeline_length() const;
|
||||
|
||||
void set_stretch_time_scale(bool p_stretch_time_scale);
|
||||
bool is_stretching_time_scale() const;
|
||||
|
||||
void set_start_offset(double p_offset);
|
||||
double get_start_offset() const;
|
||||
|
||||
void set_loop_mode(Animation::LoopMode p_loop_mode);
|
||||
Animation::LoopMode get_loop_mode() const;
|
||||
|
||||
AnimationNodeAnimation();
|
||||
|
||||
protected:
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
PlayMode play_mode = PLAY_MODE_FORWARD;
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationNodeAnimation::PlayMode)
|
||||
|
||||
class AnimationNodeSync : public AnimationNode {
|
||||
GDCLASS(AnimationNodeSync, AnimationNode);
|
||||
|
||||
protected:
|
||||
bool sync = false;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_use_sync(bool p_sync);
|
||||
bool is_using_sync() const;
|
||||
|
||||
AnimationNodeSync();
|
||||
};
|
||||
|
||||
class AnimationNodeOneShot : public AnimationNodeSync {
|
||||
GDCLASS(AnimationNodeOneShot, AnimationNodeSync);
|
||||
|
||||
public:
|
||||
enum OneShotRequest {
|
||||
ONE_SHOT_REQUEST_NONE,
|
||||
ONE_SHOT_REQUEST_FIRE,
|
||||
ONE_SHOT_REQUEST_ABORT,
|
||||
ONE_SHOT_REQUEST_FADE_OUT,
|
||||
};
|
||||
|
||||
enum MixMode {
|
||||
MIX_MODE_BLEND,
|
||||
MIX_MODE_ADD
|
||||
};
|
||||
|
||||
private:
|
||||
double fade_in = 0.0;
|
||||
Ref<Curve> fade_in_curve;
|
||||
double fade_out = 0.0;
|
||||
Ref<Curve> fade_out_curve;
|
||||
|
||||
bool auto_restart = false;
|
||||
double auto_restart_delay = 1.0;
|
||||
double auto_restart_random_delay = 0.0;
|
||||
MixMode mix = MIX_MODE_BLEND;
|
||||
bool break_loop_at_end = false;
|
||||
|
||||
StringName request = PNAME("request");
|
||||
StringName active = PNAME("active");
|
||||
StringName internal_active = PNAME("internal_active");
|
||||
StringName fade_in_remaining = "fade_in_remaining";
|
||||
StringName fade_out_remaining = "fade_out_remaining";
|
||||
StringName time_to_restart = "time_to_restart";
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
virtual bool is_parameter_read_only(const StringName &p_parameter) const override;
|
||||
|
||||
virtual String get_caption() const override;
|
||||
|
||||
void set_fade_in_time(double p_time);
|
||||
double get_fade_in_time() const;
|
||||
|
||||
void set_fade_in_curve(const Ref<Curve> &p_curve);
|
||||
Ref<Curve> get_fade_in_curve() const;
|
||||
|
||||
void set_fade_out_time(double p_time);
|
||||
double get_fade_out_time() const;
|
||||
|
||||
void set_fade_out_curve(const Ref<Curve> &p_curve);
|
||||
Ref<Curve> get_fade_out_curve() const;
|
||||
|
||||
void set_auto_restart_enabled(bool p_enabled);
|
||||
void set_auto_restart_delay(double p_time);
|
||||
void set_auto_restart_random_delay(double p_time);
|
||||
|
||||
bool is_auto_restart_enabled() const;
|
||||
double get_auto_restart_delay() const;
|
||||
double get_auto_restart_random_delay() const;
|
||||
|
||||
void set_mix_mode(MixMode p_mix);
|
||||
MixMode get_mix_mode() const;
|
||||
|
||||
void set_break_loop_at_end(bool p_enable);
|
||||
bool is_loop_broken_at_end() const;
|
||||
|
||||
virtual bool has_filter() const override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
AnimationNodeOneShot();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationNodeOneShot::OneShotRequest)
|
||||
VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode)
|
||||
|
||||
class AnimationNodeAdd2 : public AnimationNodeSync {
|
||||
GDCLASS(AnimationNodeAdd2, AnimationNodeSync);
|
||||
|
||||
StringName add_amount = PNAME("add_amount");
|
||||
|
||||
public:
|
||||
void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual bool has_filter() const override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
AnimationNodeAdd2();
|
||||
};
|
||||
|
||||
class AnimationNodeAdd3 : public AnimationNodeSync {
|
||||
GDCLASS(AnimationNodeAdd3, AnimationNodeSync);
|
||||
|
||||
StringName add_amount = PNAME("add_amount");
|
||||
|
||||
public:
|
||||
void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual bool has_filter() const override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
AnimationNodeAdd3();
|
||||
};
|
||||
|
||||
class AnimationNodeBlend2 : public AnimationNodeSync {
|
||||
GDCLASS(AnimationNodeBlend2, AnimationNodeSync);
|
||||
|
||||
StringName blend_amount = PNAME("blend_amount");
|
||||
|
||||
public:
|
||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
||||
virtual String get_caption() const override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
virtual bool has_filter() const override;
|
||||
AnimationNodeBlend2();
|
||||
};
|
||||
|
||||
class AnimationNodeBlend3 : public AnimationNodeSync {
|
||||
GDCLASS(AnimationNodeBlend3, AnimationNodeSync);
|
||||
|
||||
StringName blend_amount = PNAME("blend_amount");
|
||||
|
||||
public:
|
||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
AnimationNodeBlend3();
|
||||
};
|
||||
|
||||
class AnimationNodeSub2 : public AnimationNodeSync {
|
||||
GDCLASS(AnimationNodeSub2, AnimationNodeSync);
|
||||
|
||||
StringName sub_amount = PNAME("sub_amount");
|
||||
|
||||
public:
|
||||
void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual bool has_filter() const override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
AnimationNodeSub2();
|
||||
};
|
||||
|
||||
class AnimationNodeTimeScale : public AnimationNode {
|
||||
GDCLASS(AnimationNodeTimeScale, AnimationNode);
|
||||
|
||||
StringName scale = PNAME("scale");
|
||||
|
||||
public:
|
||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
AnimationNodeTimeScale();
|
||||
};
|
||||
|
||||
class AnimationNodeTimeSeek : public AnimationNode {
|
||||
GDCLASS(AnimationNodeTimeSeek, AnimationNode);
|
||||
|
||||
StringName seek_pos_request = PNAME("seek_request");
|
||||
bool explicit_elapse = true;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
void set_explicit_elapse(bool p_enable);
|
||||
bool is_explicit_elapse() const;
|
||||
|
||||
AnimationNodeTimeSeek();
|
||||
};
|
||||
|
||||
class AnimationNodeTransition : public AnimationNodeSync {
|
||||
GDCLASS(AnimationNodeTransition, AnimationNodeSync);
|
||||
|
||||
struct InputData {
|
||||
bool auto_advance = false;
|
||||
bool break_loop_at_end = false;
|
||||
bool reset = true;
|
||||
};
|
||||
LocalVector<InputData> input_data;
|
||||
|
||||
StringName prev_xfading = "prev_xfading";
|
||||
StringName prev_index = "prev_index";
|
||||
StringName current_index = PNAME("current_index");
|
||||
StringName current_state = PNAME("current_state");
|
||||
StringName transition_request = PNAME("transition_request");
|
||||
|
||||
StringName prev_frame_current = "pf_current";
|
||||
StringName prev_frame_current_idx = "pf_current_idx";
|
||||
|
||||
double xfade_time = 0.0;
|
||||
Ref<Curve> xfade_curve;
|
||||
bool allow_transition_to_self = false;
|
||||
|
||||
bool pending_update = false;
|
||||
|
||||
protected:
|
||||
bool _get(const StringName &p_path, Variant &r_ret) const;
|
||||
bool _set(const StringName &p_path, const Variant &p_value);
|
||||
static void _bind_methods();
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
public:
|
||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
virtual bool is_parameter_read_only(const StringName &p_parameter) const override;
|
||||
|
||||
virtual String get_caption() const override;
|
||||
|
||||
void set_input_count(int p_inputs);
|
||||
|
||||
virtual bool add_input(const String &p_name) override;
|
||||
virtual void remove_input(int p_index) override;
|
||||
virtual bool set_input_name(int p_input, const String &p_name) override;
|
||||
|
||||
void set_input_as_auto_advance(int p_input, bool p_enable);
|
||||
bool is_input_set_as_auto_advance(int p_input) const;
|
||||
|
||||
void set_input_break_loop_at_end(int p_input, bool p_enable);
|
||||
bool is_input_loop_broken_at_end(int p_input) const;
|
||||
|
||||
void set_input_reset(int p_input, bool p_enable);
|
||||
bool is_input_reset(int p_input) const;
|
||||
|
||||
void set_xfade_time(double p_fade);
|
||||
double get_xfade_time() const;
|
||||
|
||||
void set_xfade_curve(const Ref<Curve> &p_curve);
|
||||
Ref<Curve> get_xfade_curve() const;
|
||||
|
||||
void set_allow_transition_to_self(bool p_enable);
|
||||
bool is_allow_transition_to_self() const;
|
||||
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
AnimationNodeTransition();
|
||||
};
|
||||
|
||||
class AnimationNodeOutput : public AnimationNode {
|
||||
GDCLASS(AnimationNodeOutput, AnimationNode);
|
||||
|
||||
public:
|
||||
virtual String get_caption() const override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
AnimationNodeOutput();
|
||||
};
|
||||
|
||||
/////
|
||||
|
||||
class AnimationNodeBlendTree : public AnimationRootNode {
|
||||
GDCLASS(AnimationNodeBlendTree, AnimationRootNode);
|
||||
|
||||
struct Node {
|
||||
Ref<AnimationNode> node;
|
||||
Vector2 position;
|
||||
Vector<StringName> connections;
|
||||
};
|
||||
|
||||
AHashMap<StringName, Node> nodes;
|
||||
|
||||
Vector2 graph_offset;
|
||||
|
||||
void _node_changed(const StringName &p_node);
|
||||
|
||||
void _initialize_node_tree();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
virtual void _tree_changed() override;
|
||||
virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
|
||||
virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
|
||||
|
||||
virtual void reset_state() override;
|
||||
|
||||
public:
|
||||
enum ConnectionError {
|
||||
CONNECTION_OK,
|
||||
CONNECTION_ERROR_NO_INPUT,
|
||||
CONNECTION_ERROR_NO_INPUT_INDEX,
|
||||
CONNECTION_ERROR_NO_OUTPUT,
|
||||
CONNECTION_ERROR_SAME_NODE,
|
||||
CONNECTION_ERROR_CONNECTION_EXISTS,
|
||||
//no need to check for cycles due to tree topology
|
||||
};
|
||||
|
||||
void add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position = Vector2());
|
||||
Ref<AnimationNode> get_node(const StringName &p_name) const;
|
||||
void remove_node(const StringName &p_name);
|
||||
void rename_node(const StringName &p_name, const StringName &p_new_name);
|
||||
bool has_node(const StringName &p_name) const;
|
||||
StringName get_node_name(const Ref<AnimationNode> &p_node) const;
|
||||
Vector<StringName> get_node_connection_array(const StringName &p_name) const;
|
||||
|
||||
void set_node_position(const StringName &p_node, const Vector2 &p_position);
|
||||
Vector2 get_node_position(const StringName &p_node) const;
|
||||
|
||||
virtual void get_child_nodes(List<ChildNode> *r_child_nodes) override;
|
||||
|
||||
void connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node);
|
||||
void disconnect_node(const StringName &p_node, int p_input_index);
|
||||
|
||||
struct NodeConnection {
|
||||
StringName input_node;
|
||||
int input_index = 0;
|
||||
StringName output_node;
|
||||
};
|
||||
|
||||
ConnectionError can_connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node) const;
|
||||
void get_node_connections(List<NodeConnection> *r_connections) const;
|
||||
|
||||
virtual String get_caption() const override;
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
LocalVector<StringName> get_node_list() const;
|
||||
TypedArray<StringName> get_node_list_as_typed_array() const;
|
||||
|
||||
void set_graph_offset(const Vector2 &p_graph_offset);
|
||||
Vector2 get_graph_offset() const;
|
||||
|
||||
virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name) const override;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
|
||||
#endif
|
||||
|
||||
AnimationNodeBlendTree();
|
||||
~AnimationNodeBlendTree();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationNodeBlendTree::ConnectionError)
|
44
scene/animation/animation_mixer.compat.inc
Normal file
44
scene/animation/animation_mixer.compat.inc
Normal file
@@ -0,0 +1,44 @@
|
||||
/**************************************************************************/
|
||||
/* animation_mixer.compat.inc */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
Variant AnimationMixer::_post_process_key_value_bind_compat_86687(const Ref<Animation> &p_anim, int p_track, Variant p_value, Object *p_object, int p_object_idx) {
|
||||
if (!p_object) {
|
||||
return Variant();
|
||||
}
|
||||
return _post_process_key_value(p_anim, p_track, p_value, p_object->get_instance_id(), p_object_idx);
|
||||
}
|
||||
|
||||
void AnimationMixer::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_method(D_METHOD("_post_process_key_value", "animation", "track", "value", "object", "object_idx"), &AnimationMixer::_post_process_key_value_bind_compat_86687);
|
||||
}
|
||||
|
||||
#endif // DISABLE_DEPRECATED
|
2563
scene/animation/animation_mixer.cpp
Normal file
2563
scene/animation/animation_mixer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
505
scene/animation/animation_mixer.h
Normal file
505
scene/animation/animation_mixer.h
Normal file
@@ -0,0 +1,505 @@
|
||||
/**************************************************************************/
|
||||
/* animation_mixer.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/templates/a_hash_map.h"
|
||||
#include "scene/animation/tween.h"
|
||||
#include "scene/main/node.h"
|
||||
#include "scene/resources/animation.h"
|
||||
#include "scene/resources/animation_library.h"
|
||||
#include "scene/resources/audio_stream_polyphonic.h"
|
||||
|
||||
class AnimatedValuesBackup;
|
||||
|
||||
class AnimationMixer : public Node {
|
||||
GDCLASS(AnimationMixer, Node);
|
||||
friend AnimatedValuesBackup;
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool editing = false;
|
||||
bool dummy = false;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
bool reset_on_save = true;
|
||||
bool is_GDVIRTUAL_CALL_post_process_key_value = true;
|
||||
|
||||
public:
|
||||
enum AnimationCallbackModeProcess {
|
||||
ANIMATION_CALLBACK_MODE_PROCESS_PHYSICS,
|
||||
ANIMATION_CALLBACK_MODE_PROCESS_IDLE,
|
||||
ANIMATION_CALLBACK_MODE_PROCESS_MANUAL,
|
||||
};
|
||||
|
||||
enum AnimationCallbackModeMethod {
|
||||
ANIMATION_CALLBACK_MODE_METHOD_DEFERRED,
|
||||
ANIMATION_CALLBACK_MODE_METHOD_IMMEDIATE,
|
||||
};
|
||||
|
||||
enum AnimationCallbackModeDiscrete {
|
||||
ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT,
|
||||
ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE,
|
||||
ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS,
|
||||
};
|
||||
|
||||
/* ---- Data ---- */
|
||||
struct AnimationLibraryData {
|
||||
StringName name;
|
||||
Ref<AnimationLibrary> library;
|
||||
bool operator<(const AnimationLibraryData &p_data) const { return name.operator String() < p_data.name.operator String(); }
|
||||
};
|
||||
|
||||
struct AnimationData {
|
||||
String name;
|
||||
Ref<Animation> animation;
|
||||
StringName animation_library;
|
||||
uint64_t last_update = 0;
|
||||
};
|
||||
|
||||
struct PlaybackInfo {
|
||||
double time = 0.0;
|
||||
double delta = 0.0;
|
||||
double start = 0.0;
|
||||
double end = 0.0;
|
||||
bool seeked = false;
|
||||
bool is_external_seeking = false;
|
||||
Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
|
||||
real_t weight = 0.0;
|
||||
Vector<real_t> track_weights;
|
||||
};
|
||||
|
||||
struct AnimationInstance {
|
||||
AnimationData animation_data;
|
||||
PlaybackInfo playback_info;
|
||||
};
|
||||
|
||||
protected:
|
||||
/* ---- Data lists ---- */
|
||||
LocalVector<AnimationLibraryData> animation_libraries;
|
||||
AHashMap<StringName, AnimationData> animation_set; // HashMap<Library name + Animation name, AnimationData>
|
||||
|
||||
TypedArray<StringName> _get_animation_library_list() const;
|
||||
Vector<String> _get_animation_list() const {
|
||||
List<StringName> animations;
|
||||
get_animation_list(&animations);
|
||||
Vector<String> ret;
|
||||
while (animations.size()) {
|
||||
ret.push_back(animations.front()->get());
|
||||
animations.pop_front();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// For caches.
|
||||
uint64_t animation_set_update_pass = 1;
|
||||
void _animation_set_cache_update();
|
||||
|
||||
// Signals.
|
||||
virtual void _animation_added(const StringName &p_name, const StringName &p_library);
|
||||
virtual void _animation_removed(const StringName &p_name, const StringName &p_library);
|
||||
virtual void _animation_renamed(const StringName &p_name, const StringName &p_to_name, const StringName &p_library);
|
||||
virtual void _animation_changed(const StringName &p_name);
|
||||
|
||||
/* ---- General settings for animation ---- */
|
||||
AnimationCallbackModeProcess callback_mode_process = ANIMATION_CALLBACK_MODE_PROCESS_IDLE;
|
||||
AnimationCallbackModeMethod callback_mode_method = ANIMATION_CALLBACK_MODE_METHOD_DEFERRED;
|
||||
AnimationCallbackModeDiscrete callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE;
|
||||
int audio_max_polyphony = 32;
|
||||
NodePath root_node;
|
||||
|
||||
bool processing = false;
|
||||
bool active = true;
|
||||
|
||||
void _set_process(bool p_process, bool p_force = false);
|
||||
|
||||
/* ---- Caches for blending ---- */
|
||||
bool cache_valid = false;
|
||||
uint64_t setup_pass = 1;
|
||||
uint64_t process_pass = 1;
|
||||
|
||||
struct TrackCache {
|
||||
bool root_motion = false;
|
||||
uint64_t setup_pass = 0;
|
||||
Animation::TrackType type = Animation::TrackType::TYPE_ANIMATION;
|
||||
NodePath path;
|
||||
int blend_idx = -1;
|
||||
ObjectID object_id;
|
||||
real_t total_weight = 0.0;
|
||||
|
||||
TrackCache() = default;
|
||||
TrackCache(const TrackCache &p_other) :
|
||||
root_motion(p_other.root_motion),
|
||||
setup_pass(p_other.setup_pass),
|
||||
type(p_other.type),
|
||||
object_id(p_other.object_id),
|
||||
total_weight(p_other.total_weight) {}
|
||||
|
||||
virtual ~TrackCache() {}
|
||||
};
|
||||
|
||||
struct TrackCacheTransform : public TrackCache {
|
||||
#ifndef _3D_DISABLED
|
||||
ObjectID skeleton_id;
|
||||
#endif // _3D_DISABLED
|
||||
int bone_idx = -1;
|
||||
bool loc_used = false;
|
||||
bool rot_used = false;
|
||||
bool scale_used = false;
|
||||
Vector3 init_loc = Vector3(0, 0, 0);
|
||||
Quaternion init_rot = Quaternion(0, 0, 0, 1);
|
||||
Vector3 init_scale = Vector3(1, 1, 1);
|
||||
Vector3 loc;
|
||||
Quaternion rot;
|
||||
Vector3 scale;
|
||||
|
||||
TrackCacheTransform(const TrackCacheTransform &p_other) :
|
||||
TrackCache(p_other),
|
||||
#ifndef _3D_DISABLED
|
||||
skeleton_id(p_other.skeleton_id),
|
||||
#endif
|
||||
bone_idx(p_other.bone_idx),
|
||||
loc_used(p_other.loc_used),
|
||||
rot_used(p_other.rot_used),
|
||||
scale_used(p_other.scale_used),
|
||||
init_loc(p_other.init_loc),
|
||||
init_rot(p_other.init_rot),
|
||||
init_scale(p_other.init_scale),
|
||||
loc(p_other.loc),
|
||||
rot(p_other.rot),
|
||||
scale(p_other.scale) {
|
||||
}
|
||||
|
||||
TrackCacheTransform() {
|
||||
type = Animation::TYPE_POSITION_3D;
|
||||
}
|
||||
};
|
||||
|
||||
struct RootMotionCache {
|
||||
Vector3 loc = Vector3(0, 0, 0);
|
||||
Quaternion rot = Quaternion(0, 0, 0, 1);
|
||||
Vector3 scale = Vector3(1, 1, 1);
|
||||
};
|
||||
|
||||
struct TrackCacheBlendShape : public TrackCache {
|
||||
float init_value = 0;
|
||||
float value = 0;
|
||||
int shape_index = -1;
|
||||
|
||||
TrackCacheBlendShape(const TrackCacheBlendShape &p_other) :
|
||||
TrackCache(p_other),
|
||||
init_value(p_other.init_value),
|
||||
value(p_other.value),
|
||||
shape_index(p_other.shape_index) {}
|
||||
|
||||
TrackCacheBlendShape() { type = Animation::TYPE_BLEND_SHAPE; }
|
||||
};
|
||||
|
||||
struct TrackCacheValue : public TrackCache {
|
||||
Variant init_value;
|
||||
Variant value;
|
||||
Vector<StringName> subpath;
|
||||
|
||||
// TODO: There are many boolean, can be packed into one integer.
|
||||
bool is_init = false;
|
||||
bool use_continuous = false;
|
||||
bool use_discrete = false;
|
||||
bool is_using_angle = false;
|
||||
bool is_variant_interpolatable = true;
|
||||
|
||||
Variant element_size;
|
||||
|
||||
TrackCacheValue(const TrackCacheValue &p_other) :
|
||||
TrackCache(p_other),
|
||||
init_value(p_other.init_value),
|
||||
value(p_other.value),
|
||||
subpath(p_other.subpath),
|
||||
is_init(p_other.is_init),
|
||||
use_continuous(p_other.use_continuous),
|
||||
use_discrete(p_other.use_discrete),
|
||||
is_using_angle(p_other.is_using_angle),
|
||||
is_variant_interpolatable(p_other.is_variant_interpolatable),
|
||||
element_size(p_other.element_size) {}
|
||||
|
||||
TrackCacheValue() { type = Animation::TYPE_VALUE; }
|
||||
~TrackCacheValue() {
|
||||
// Clear ref to avoid leaking.
|
||||
init_value = Variant();
|
||||
value = Variant();
|
||||
}
|
||||
};
|
||||
|
||||
struct TrackCacheMethod : public TrackCache {
|
||||
TrackCacheMethod() { type = Animation::TYPE_METHOD; }
|
||||
};
|
||||
|
||||
// Audio stream information for each audio stream placed on the track.
|
||||
struct PlayingAudioStreamInfo {
|
||||
AudioStreamPlaybackPolyphonic::ID index = -1; // ID retrieved from AudioStreamPlaybackPolyphonic.
|
||||
double start = 0.0;
|
||||
double len = 0.0;
|
||||
};
|
||||
|
||||
// Audio track information for mixng and ending.
|
||||
struct PlayingAudioTrackInfo {
|
||||
AHashMap<int, PlayingAudioStreamInfo> stream_info;
|
||||
double length = 0.0;
|
||||
double time = 0.0;
|
||||
real_t volume = 0.0;
|
||||
bool loop = false;
|
||||
bool backward = false;
|
||||
bool use_blend = false;
|
||||
};
|
||||
|
||||
struct TrackCacheAudio : public TrackCache {
|
||||
Ref<AudioStreamPolyphonic> audio_stream;
|
||||
Ref<AudioStreamPlaybackPolyphonic> audio_stream_playback;
|
||||
HashMap<ObjectID, PlayingAudioTrackInfo> playing_streams; // Key is Animation resource ObjectID.
|
||||
AudioServer::PlaybackType playback_type;
|
||||
StringName bus;
|
||||
|
||||
TrackCacheAudio(const TrackCacheAudio &p_other) :
|
||||
TrackCache(p_other),
|
||||
audio_stream(p_other.audio_stream),
|
||||
audio_stream_playback(p_other.audio_stream_playback),
|
||||
playing_streams(p_other.playing_streams),
|
||||
playback_type(p_other.playback_type) {}
|
||||
|
||||
TrackCacheAudio() {
|
||||
type = Animation::TYPE_AUDIO;
|
||||
}
|
||||
};
|
||||
|
||||
struct TrackCacheAnimation : public TrackCache {
|
||||
bool playing = false;
|
||||
|
||||
TrackCacheAnimation() {
|
||||
type = Animation::TYPE_ANIMATION;
|
||||
}
|
||||
};
|
||||
|
||||
RootMotionCache root_motion_cache;
|
||||
AHashMap<Animation::TypeHash, TrackCache *, HashHasher> track_cache;
|
||||
AHashMap<Ref<Animation>, LocalVector<TrackCache *>> animation_track_num_to_track_cache;
|
||||
HashSet<TrackCache *> playing_caches;
|
||||
Vector<Node *> playing_audio_stream_players;
|
||||
|
||||
// Helpers.
|
||||
void _clear_caches();
|
||||
void _clear_audio_streams();
|
||||
void _clear_playing_caches();
|
||||
void _init_root_motion_cache();
|
||||
bool _update_caches();
|
||||
void _create_track_num_to_track_cache_for_animation(Ref<Animation> &p_animation);
|
||||
|
||||
/* ---- Audio ---- */
|
||||
AudioServer::PlaybackType playback_type;
|
||||
|
||||
/* ---- Blending processor ---- */
|
||||
LocalVector<AnimationInstance> animation_instances;
|
||||
AHashMap<NodePath, int> track_map;
|
||||
int track_count = 0;
|
||||
bool deterministic = false;
|
||||
|
||||
/* ---- Root motion accumulator for Skeleton3D ---- */
|
||||
NodePath root_motion_track;
|
||||
bool root_motion_local = false;
|
||||
Vector3 root_motion_position = Vector3(0, 0, 0);
|
||||
Quaternion root_motion_rotation = Quaternion(0, 0, 0, 1);
|
||||
Vector3 root_motion_scale = Vector3(0, 0, 0);
|
||||
Vector3 root_motion_position_accumulator = Vector3(0, 0, 0);
|
||||
Quaternion root_motion_rotation_accumulator = Quaternion(0, 0, 0, 1);
|
||||
Vector3 root_motion_scale_accumulator = Vector3(1, 1, 1);
|
||||
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
virtual uint32_t _get_libraries_property_usage() const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
void _notification(int p_what);
|
||||
virtual void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
|
||||
#endif
|
||||
|
||||
static void _bind_methods();
|
||||
void _node_removed(Node *p_node);
|
||||
|
||||
// Helper for extended class.
|
||||
virtual void _set_active(bool p_active);
|
||||
virtual void _remove_animation(const StringName &p_name);
|
||||
virtual void _rename_animation(const StringName &p_from_name, const StringName &p_to_name);
|
||||
|
||||
/* ---- Blending processor ---- */
|
||||
virtual void _process_animation(double p_delta, bool p_update_only = false);
|
||||
|
||||
// For post process with retrieved key value during blending.
|
||||
virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant &p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
|
||||
Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
|
||||
GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, ObjectID, int);
|
||||
|
||||
void _blend_init();
|
||||
virtual bool _blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map);
|
||||
virtual void _blend_capture(double p_delta);
|
||||
void _blend_calc_total_weight(); // For indeterministic blending.
|
||||
void _blend_process(double p_delta, bool p_update_only = false);
|
||||
void _blend_apply();
|
||||
virtual void _blend_post_process();
|
||||
void _call_object(ObjectID p_object_id, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred);
|
||||
|
||||
/* ---- Capture feature ---- */
|
||||
struct CaptureCache {
|
||||
Ref<Animation> animation;
|
||||
double remain = 0.0;
|
||||
double step = 0.0;
|
||||
Tween::TransitionType trans_type = Tween::TRANS_LINEAR;
|
||||
Tween::EaseType ease_type = Tween::EASE_IN;
|
||||
|
||||
void clear() {
|
||||
animation.unref();
|
||||
remain = 0.0;
|
||||
step = 0.0;
|
||||
}
|
||||
|
||||
~CaptureCache() {
|
||||
clear();
|
||||
}
|
||||
} capture_cache;
|
||||
void blend_capture(double p_delta); // To blend capture track with all other animations.
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
virtual Variant _post_process_key_value_bind_compat_86687(const Ref<Animation> &p_anim, int p_track, Variant p_value, Object *p_object, int p_object_idx = -1);
|
||||
static void _bind_compatibility_methods();
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
public:
|
||||
/* ---- Data lists ---- */
|
||||
Dictionary *get_animation_libraries();
|
||||
|
||||
void get_animation_library_list(List<StringName> *p_animations) const;
|
||||
Ref<AnimationLibrary> get_animation_library(const StringName &p_name) const;
|
||||
bool has_animation_library(const StringName &p_name) const;
|
||||
StringName get_animation_library_name(const Ref<AnimationLibrary> &p_animation_library) const;
|
||||
StringName find_animation_library(const Ref<Animation> &p_animation) const;
|
||||
Error add_animation_library(const StringName &p_name, const Ref<AnimationLibrary> &p_animation_library);
|
||||
void remove_animation_library(const StringName &p_name);
|
||||
void rename_animation_library(const StringName &p_name, const StringName &p_new_name);
|
||||
|
||||
void get_animation_list(List<StringName> *p_animations) const;
|
||||
Ref<Animation> get_animation(const StringName &p_name) const;
|
||||
bool has_animation(const StringName &p_name) const;
|
||||
StringName find_animation(const Ref<Animation> &p_animation) const;
|
||||
|
||||
/* ---- General settings for animation ---- */
|
||||
void set_active(bool p_active);
|
||||
bool is_active() const;
|
||||
|
||||
void set_deterministic(bool p_deterministic);
|
||||
bool is_deterministic() const;
|
||||
|
||||
void set_root_node(const NodePath &p_path);
|
||||
NodePath get_root_node() const;
|
||||
|
||||
void set_callback_mode_process(AnimationCallbackModeProcess p_mode);
|
||||
AnimationCallbackModeProcess get_callback_mode_process() const;
|
||||
|
||||
void set_callback_mode_method(AnimationCallbackModeMethod p_mode);
|
||||
AnimationCallbackModeMethod get_callback_mode_method() const;
|
||||
|
||||
void set_callback_mode_discrete(AnimationCallbackModeDiscrete p_mode);
|
||||
AnimationCallbackModeDiscrete get_callback_mode_discrete() const;
|
||||
|
||||
/* ---- Audio ---- */
|
||||
void set_audio_max_polyphony(int p_audio_max_polyphony);
|
||||
int get_audio_max_polyphony() const;
|
||||
|
||||
/* ---- Root motion accumulator for Skeleton3D ---- */
|
||||
void set_root_motion_track(const NodePath &p_track);
|
||||
NodePath get_root_motion_track() const;
|
||||
|
||||
void set_root_motion_local(bool p_enabled);
|
||||
bool is_root_motion_local() const;
|
||||
|
||||
Vector3 get_root_motion_position() const;
|
||||
Quaternion get_root_motion_rotation() const;
|
||||
Vector3 get_root_motion_scale() const;
|
||||
|
||||
Vector3 get_root_motion_position_accumulator() const;
|
||||
Quaternion get_root_motion_rotation_accumulator() const;
|
||||
Vector3 get_root_motion_scale_accumulator() const;
|
||||
|
||||
/* ---- Blending processor ---- */
|
||||
void make_animation_instance(const StringName &p_name, const PlaybackInfo p_playback_info);
|
||||
void clear_animation_instances();
|
||||
virtual void advance(double p_time);
|
||||
virtual void clear_caches(); // Must be called by hand if an animation was modified after added.
|
||||
|
||||
/* ---- Capture feature ---- */
|
||||
void capture(const StringName &p_name, double p_duration, Tween::TransitionType p_trans_type = Tween::TRANS_LINEAR, Tween::EaseType p_ease_type = Tween::EASE_IN);
|
||||
|
||||
/* ---- Reset on save ---- */
|
||||
void set_reset_on_save_enabled(bool p_enabled);
|
||||
bool is_reset_on_save_enabled() const;
|
||||
bool can_apply_reset() const;
|
||||
void _build_backup_track_cache();
|
||||
Ref<AnimatedValuesBackup> make_backup();
|
||||
void restore(const Ref<AnimatedValuesBackup> &p_backup);
|
||||
void reset();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Ref<AnimatedValuesBackup> apply_reset(bool p_user_initiated = false);
|
||||
|
||||
void set_editing(bool p_editing);
|
||||
bool is_editing() const;
|
||||
|
||||
void set_dummy(bool p_dummy);
|
||||
bool is_dummy() const;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
AnimationMixer();
|
||||
~AnimationMixer();
|
||||
};
|
||||
|
||||
class AnimatedValuesBackup : public RefCounted {
|
||||
GDCLASS(AnimatedValuesBackup, RefCounted);
|
||||
|
||||
AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> data;
|
||||
|
||||
public:
|
||||
void set_data(const AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> p_data);
|
||||
AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> get_data() const;
|
||||
void clear_data();
|
||||
|
||||
AnimationMixer::TrackCache *get_cache_copy(AnimationMixer::TrackCache *p_cache) const;
|
||||
|
||||
~AnimatedValuesBackup() { clear_data(); }
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationMixer::AnimationCallbackModeProcess);
|
||||
VARIANT_ENUM_CAST(AnimationMixer::AnimationCallbackModeMethod);
|
||||
VARIANT_ENUM_CAST(AnimationMixer::AnimationCallbackModeDiscrete);
|
83
scene/animation/animation_node_extension.cpp
Normal file
83
scene/animation/animation_node_extension.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/**************************************************************************/
|
||||
/* animation_node_extension.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "animation_node_extension.h"
|
||||
|
||||
AnimationNode::NodeTimeInfo AnimationNodeExtension::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
|
||||
PackedFloat32Array r_ret;
|
||||
|
||||
GDVIRTUAL_CALL(
|
||||
_process_animation_node,
|
||||
_playback_info_to_array(p_playback_info),
|
||||
p_test_only,
|
||||
r_ret);
|
||||
|
||||
return _array_to_node_time_info(r_ret);
|
||||
}
|
||||
|
||||
bool AnimationNodeExtension::is_looping(const PackedFloat32Array &p_node_info) {
|
||||
return _array_to_node_time_info(p_node_info).is_looping();
|
||||
}
|
||||
|
||||
double AnimationNodeExtension::get_remaining_time(const PackedFloat32Array &p_node_info, bool p_break_loop) {
|
||||
return _array_to_node_time_info(p_node_info).get_remain(p_break_loop);
|
||||
}
|
||||
|
||||
void AnimationNodeExtension::_bind_methods() {
|
||||
ClassDB::bind_static_method("AnimationNodeExtension", D_METHOD("is_looping", "node_info"), &AnimationNodeExtension::is_looping);
|
||||
ClassDB::bind_static_method("AnimationNodeExtension", D_METHOD("get_remaining_time", "node_info", "break_loop"), &AnimationNodeExtension::get_remaining_time);
|
||||
GDVIRTUAL_BIND(_process_animation_node, "playback_info", "test_only");
|
||||
}
|
||||
|
||||
AnimationNode::NodeTimeInfo AnimationNodeExtension::_array_to_node_time_info(const PackedFloat32Array &p_node_info) {
|
||||
ERR_FAIL_COND_V_MSG(p_node_info.size() != 6, AnimationNode::NodeTimeInfo(), "Invalid node info.");
|
||||
AnimationNode::NodeTimeInfo ret_val;
|
||||
ret_val.length = p_node_info[0];
|
||||
ret_val.position = p_node_info[1];
|
||||
ret_val.delta = p_node_info[2];
|
||||
ret_val.loop_mode = static_cast<Animation::LoopMode>(p_node_info[3]);
|
||||
ret_val.will_end = p_node_info[4] > 0.0;
|
||||
ret_val.is_infinity = p_node_info[5] > 0.0;
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
PackedFloat64Array AnimationNodeExtension::_playback_info_to_array(const AnimationMixer::PlaybackInfo &p_playback_info) {
|
||||
PackedFloat64Array playback_info_array;
|
||||
playback_info_array.push_back(p_playback_info.time);
|
||||
playback_info_array.push_back(p_playback_info.delta);
|
||||
playback_info_array.push_back(p_playback_info.start);
|
||||
playback_info_array.push_back(p_playback_info.end);
|
||||
playback_info_array.push_back(p_playback_info.seeked);
|
||||
playback_info_array.push_back(p_playback_info.is_external_seeking);
|
||||
playback_info_array.push_back(p_playback_info.looped_flag);
|
||||
playback_info_array.push_back(p_playback_info.weight);
|
||||
|
||||
return playback_info_array;
|
||||
}
|
52
scene/animation/animation_node_extension.h
Normal file
52
scene/animation/animation_node_extension.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/**************************************************************************/
|
||||
/* animation_node_extension.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/animation/animation_tree.h"
|
||||
|
||||
class AnimationNodeExtension : public AnimationNode {
|
||||
GDCLASS(AnimationNodeExtension, AnimationNode);
|
||||
|
||||
public:
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
|
||||
static bool is_looping(const PackedFloat32Array &p_node_info);
|
||||
static double get_remaining_time(const PackedFloat32Array &p_node_info, bool p_break_loop = false);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
GDVIRTUAL2R_REQUIRED(PackedFloat32Array, _process_animation_node, PackedFloat64Array, bool);
|
||||
|
||||
private:
|
||||
static AnimationNode::NodeTimeInfo _array_to_node_time_info(const PackedFloat32Array &p_array);
|
||||
static PackedFloat64Array _playback_info_to_array(const AnimationMixer::PlaybackInfo &p_playback_info);
|
||||
};
|
1899
scene/animation/animation_node_state_machine.cpp
Normal file
1899
scene/animation/animation_node_state_machine.cpp
Normal file
File diff suppressed because it is too large
Load Diff
352
scene/animation/animation_node_state_machine.h
Normal file
352
scene/animation/animation_node_state_machine.h
Normal file
@@ -0,0 +1,352 @@
|
||||
/**************************************************************************/
|
||||
/* animation_node_state_machine.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/math/expression.h"
|
||||
#include "scene/animation/animation_tree.h"
|
||||
|
||||
class AnimationNodeStateMachineTransition : public Resource {
|
||||
GDCLASS(AnimationNodeStateMachineTransition, Resource);
|
||||
|
||||
public:
|
||||
enum SwitchMode {
|
||||
SWITCH_MODE_IMMEDIATE,
|
||||
SWITCH_MODE_SYNC,
|
||||
SWITCH_MODE_AT_END,
|
||||
};
|
||||
|
||||
enum AdvanceMode {
|
||||
ADVANCE_MODE_DISABLED,
|
||||
ADVANCE_MODE_ENABLED,
|
||||
ADVANCE_MODE_AUTO,
|
||||
};
|
||||
|
||||
private:
|
||||
SwitchMode switch_mode = SWITCH_MODE_IMMEDIATE;
|
||||
AdvanceMode advance_mode = ADVANCE_MODE_ENABLED;
|
||||
StringName advance_condition;
|
||||
StringName advance_condition_name;
|
||||
float xfade_time = 0.0;
|
||||
Ref<Curve> xfade_curve;
|
||||
bool break_loop_at_end = false;
|
||||
bool reset = true;
|
||||
int priority = 1;
|
||||
String advance_expression;
|
||||
|
||||
friend class AnimationNodeStateMachinePlayback;
|
||||
Ref<Expression> expression;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_switch_mode(SwitchMode p_mode);
|
||||
SwitchMode get_switch_mode() const;
|
||||
|
||||
void set_advance_mode(AdvanceMode p_mode);
|
||||
AdvanceMode get_advance_mode() const;
|
||||
|
||||
void set_advance_condition(const StringName &p_condition);
|
||||
StringName get_advance_condition() const;
|
||||
|
||||
StringName get_advance_condition_name() const;
|
||||
|
||||
void set_advance_expression(const String &p_expression);
|
||||
String get_advance_expression() const;
|
||||
|
||||
void set_xfade_time(float p_xfade);
|
||||
float get_xfade_time() const;
|
||||
|
||||
void set_break_loop_at_end(bool p_enable);
|
||||
bool is_loop_broken_at_end() const;
|
||||
|
||||
void set_reset(bool p_reset);
|
||||
bool is_reset() const;
|
||||
|
||||
void set_xfade_curve(const Ref<Curve> &p_curve);
|
||||
Ref<Curve> get_xfade_curve() const;
|
||||
|
||||
void set_priority(int p_priority);
|
||||
int get_priority() const;
|
||||
|
||||
AnimationNodeStateMachineTransition();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationNodeStateMachineTransition::SwitchMode)
|
||||
VARIANT_ENUM_CAST(AnimationNodeStateMachineTransition::AdvanceMode)
|
||||
|
||||
class AnimationNodeStateMachinePlayback;
|
||||
|
||||
class AnimationNodeStateMachine : public AnimationRootNode {
|
||||
GDCLASS(AnimationNodeStateMachine, AnimationRootNode);
|
||||
|
||||
public:
|
||||
enum StateMachineType {
|
||||
STATE_MACHINE_TYPE_ROOT,
|
||||
STATE_MACHINE_TYPE_NESTED,
|
||||
STATE_MACHINE_TYPE_GROUPED,
|
||||
};
|
||||
|
||||
private:
|
||||
friend class AnimationNodeStateMachinePlayback;
|
||||
|
||||
StateMachineType state_machine_type = STATE_MACHINE_TYPE_ROOT;
|
||||
|
||||
struct State {
|
||||
Ref<AnimationRootNode> node;
|
||||
Vector2 position;
|
||||
};
|
||||
|
||||
HashMap<StringName, State> states;
|
||||
bool allow_transition_to_self = false;
|
||||
bool reset_ends = false;
|
||||
|
||||
struct Transition {
|
||||
StringName from;
|
||||
StringName to;
|
||||
Ref<AnimationNodeStateMachineTransition> transition;
|
||||
};
|
||||
|
||||
Vector<Transition> transitions;
|
||||
|
||||
StringName playback = "playback";
|
||||
bool updating_transitions = false;
|
||||
|
||||
Vector2 graph_offset;
|
||||
|
||||
void _remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition);
|
||||
void _rename_transitions(const StringName &p_name, const StringName &p_new_name);
|
||||
bool _can_connect(const StringName &p_name);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
|
||||
|
||||
virtual void _tree_changed() override;
|
||||
virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
|
||||
virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
|
||||
|
||||
virtual void reset_state() override;
|
||||
|
||||
public:
|
||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
|
||||
virtual bool is_parameter_read_only(const StringName &p_parameter) const override;
|
||||
|
||||
void add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position = Vector2());
|
||||
void replace_node(const StringName &p_name, Ref<AnimationNode> p_node);
|
||||
Ref<AnimationNode> get_node(const StringName &p_name) const;
|
||||
void remove_node(const StringName &p_name);
|
||||
void rename_node(const StringName &p_name, const StringName &p_new_name);
|
||||
bool has_node(const StringName &p_name) const;
|
||||
StringName get_node_name(const Ref<AnimationNode> &p_node) const;
|
||||
LocalVector<StringName> get_node_list() const;
|
||||
TypedArray<StringName> get_node_list_as_typed_array() const;
|
||||
|
||||
void set_node_position(const StringName &p_name, const Vector2 &p_position);
|
||||
Vector2 get_node_position(const StringName &p_name) const;
|
||||
|
||||
virtual void get_child_nodes(List<ChildNode> *r_child_nodes) override;
|
||||
|
||||
bool has_transition(const StringName &p_from, const StringName &p_to) const;
|
||||
bool has_transition_from(const StringName &p_from) const;
|
||||
bool has_transition_to(const StringName &p_to) const;
|
||||
int find_transition(const StringName &p_from, const StringName &p_to) const;
|
||||
Vector<int> find_transition_from(const StringName &p_from) const;
|
||||
Vector<int> find_transition_to(const StringName &p_to) const;
|
||||
void add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition);
|
||||
Ref<AnimationNodeStateMachineTransition> get_transition(int p_transition) const;
|
||||
StringName get_transition_from(int p_transition) const;
|
||||
StringName get_transition_to(int p_transition) const;
|
||||
int get_transition_count() const;
|
||||
bool is_transition_across_group(int p_transition) const;
|
||||
void remove_transition_by_index(const int p_transition);
|
||||
void remove_transition(const StringName &p_from, const StringName &p_to);
|
||||
|
||||
void set_state_machine_type(StateMachineType p_state_machine_type);
|
||||
StateMachineType get_state_machine_type() const;
|
||||
|
||||
void set_allow_transition_to_self(bool p_enable);
|
||||
bool is_allow_transition_to_self() const;
|
||||
|
||||
void set_reset_ends(bool p_enable);
|
||||
bool are_ends_reset() const;
|
||||
|
||||
bool can_edit_node(const StringName &p_name) const;
|
||||
|
||||
void set_graph_offset(const Vector2 &p_offset);
|
||||
Vector2 get_graph_offset() const;
|
||||
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override;
|
||||
virtual String get_caption() const override;
|
||||
|
||||
virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name) const override;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
|
||||
#endif
|
||||
|
||||
Vector<StringName> get_nodes_with_transitions_from(const StringName &p_node) const;
|
||||
Vector<StringName> get_nodes_with_transitions_to(const StringName &p_node) const;
|
||||
|
||||
AnimationNodeStateMachine();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationNodeStateMachine::StateMachineType);
|
||||
|
||||
class AnimationNodeStateMachinePlayback : public Resource {
|
||||
GDCLASS(AnimationNodeStateMachinePlayback, Resource);
|
||||
|
||||
friend class AnimationNodeStateMachine;
|
||||
|
||||
struct AStarCost {
|
||||
float distance = 0.0;
|
||||
StringName prev;
|
||||
};
|
||||
|
||||
struct TransitionInfo {
|
||||
StringName from;
|
||||
StringName to;
|
||||
StringName next;
|
||||
};
|
||||
|
||||
struct NextInfo {
|
||||
StringName node;
|
||||
double xfade;
|
||||
Ref<Curve> curve;
|
||||
AnimationNodeStateMachineTransition::SwitchMode switch_mode;
|
||||
bool is_reset;
|
||||
bool break_loop_at_end;
|
||||
};
|
||||
|
||||
struct ChildStateMachineInfo {
|
||||
Ref<AnimationNodeStateMachinePlayback> playback;
|
||||
Vector<StringName> path;
|
||||
bool is_reset = false;
|
||||
};
|
||||
|
||||
Ref<AnimationNodeStateMachineTransition> default_transition;
|
||||
String base_path;
|
||||
|
||||
AnimationNode::NodeTimeInfo current_nti;
|
||||
StringName current;
|
||||
Ref<Curve> current_curve;
|
||||
|
||||
Ref<AnimationNodeStateMachineTransition> group_start_transition;
|
||||
Ref<AnimationNodeStateMachineTransition> group_end_transition;
|
||||
|
||||
AnimationNode::NodeTimeInfo fadeing_from_nti;
|
||||
StringName fading_from;
|
||||
float fading_time = 0.0;
|
||||
float fading_pos = 0.0;
|
||||
|
||||
Vector<StringName> path;
|
||||
bool playing = false;
|
||||
|
||||
StringName start_request;
|
||||
StringName travel_request;
|
||||
bool reset_request = false;
|
||||
bool reset_request_on_teleport = false;
|
||||
bool _reset_request_for_fading_from = false;
|
||||
bool next_request = false;
|
||||
bool stop_request = false;
|
||||
bool teleport_request = false;
|
||||
|
||||
bool is_grouped = false;
|
||||
|
||||
void _clear_fading(AnimationNodeStateMachine *p_state_machine, const StringName &p_state);
|
||||
void _signal_state_change(AnimationTree *p_animation_tree, const StringName &p_state, bool p_started);
|
||||
void _travel_main(const StringName &p_state, bool p_reset_on_teleport = true);
|
||||
void _start_main(const StringName &p_state, bool p_reset = true);
|
||||
void _next_main();
|
||||
void _stop_main();
|
||||
|
||||
bool _make_travel_path(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, Vector<StringName> &r_path, bool p_test_only);
|
||||
String _validate_path(AnimationNodeStateMachine *p_state_machine, const String &p_path);
|
||||
bool _travel(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, bool p_test_only);
|
||||
void _start(AnimationNodeStateMachine *p_state_machine);
|
||||
|
||||
void _clear_path_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_test_only);
|
||||
bool _travel_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const String &p_path, bool p_is_allow_transition_to_self, bool p_is_parent_same_state, bool p_test_only);
|
||||
void _start_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const String &p_path, bool p_test_only);
|
||||
|
||||
AnimationNode::NodeTimeInfo process(AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only);
|
||||
AnimationNode::NodeTimeInfo _process(AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only);
|
||||
|
||||
bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
|
||||
bool _transition_to_next_recursive(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, double p_delta, bool p_test_only);
|
||||
NextInfo _find_next(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine) const;
|
||||
Ref<AnimationNodeStateMachineTransition> _check_group_transition(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const AnimationNodeStateMachine::Transition &p_transition, Ref<AnimationNodeStateMachine> &r_state_machine, bool &r_bypass) const;
|
||||
bool _can_transition_to_next(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, NextInfo p_next, bool p_test_only);
|
||||
|
||||
void _set_current(AnimationNodeStateMachine *p_state_machine, const StringName &p_state);
|
||||
void _set_grouped(bool p_is_grouped);
|
||||
void _set_base_path(const String &p_base_path);
|
||||
Ref<AnimationNodeStateMachinePlayback> _get_parent_playback(AnimationTree *p_tree) const;
|
||||
Ref<AnimationNodeStateMachine> _get_parent_state_machine(AnimationTree *p_tree) const;
|
||||
Ref<AnimationNodeStateMachineTransition> _get_group_start_transition() const;
|
||||
Ref<AnimationNodeStateMachineTransition> _get_group_end_transition() const;
|
||||
|
||||
TypedArray<StringName> _get_travel_path() const;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void travel(const StringName &p_state, bool p_reset_on_teleport = true);
|
||||
void start(const StringName &p_state, bool p_reset = true);
|
||||
void next();
|
||||
void stop();
|
||||
bool is_playing() const;
|
||||
bool is_end() const;
|
||||
StringName get_current_node() const;
|
||||
StringName get_fading_from_node() const;
|
||||
Vector<StringName> get_travel_path() const;
|
||||
float get_current_play_pos() const;
|
||||
float get_current_length() const;
|
||||
|
||||
float get_fade_from_play_pos() const;
|
||||
float get_fade_from_length() const;
|
||||
|
||||
float get_fading_time() const;
|
||||
float get_fading_pos() const;
|
||||
|
||||
void clear_path();
|
||||
void push_path(const StringName &p_state);
|
||||
|
||||
AnimationNodeStateMachinePlayback();
|
||||
};
|
76
scene/animation/animation_player.compat.inc
Normal file
76
scene/animation/animation_player.compat.inc
Normal file
@@ -0,0 +1,76 @@
|
||||
/**************************************************************************/
|
||||
/* animation_player.compat.inc */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
void AnimationPlayer::_set_process_callback_bind_compat_80813(AnimationPlayer::AnimationProcessCallback p_mode) {
|
||||
set_callback_mode_process(static_cast<AnimationMixer::AnimationCallbackModeProcess>(static_cast<int>(p_mode)));
|
||||
}
|
||||
|
||||
AnimationPlayer::AnimationProcessCallback AnimationPlayer::_get_process_callback_bind_compat_80813() const {
|
||||
return static_cast<AnimationProcessCallback>(static_cast<int>(get_callback_mode_process()));
|
||||
}
|
||||
|
||||
void AnimationPlayer::_set_method_call_mode_bind_compat_80813(AnimationPlayer::AnimationMethodCallMode p_mode) {
|
||||
set_callback_mode_method(static_cast<AnimationMixer::AnimationCallbackModeMethod>(static_cast<int>(p_mode)));
|
||||
}
|
||||
|
||||
AnimationPlayer::AnimationMethodCallMode AnimationPlayer::_get_method_call_mode_bind_compat_80813() const {
|
||||
return static_cast<AnimationMethodCallMode>(static_cast<int>(get_callback_mode_method()));
|
||||
}
|
||||
|
||||
void AnimationPlayer::_set_root_bind_compat_80813(const NodePath &p_root) {
|
||||
set_root_node(p_root);
|
||||
}
|
||||
|
||||
NodePath AnimationPlayer::_get_root_bind_compat_80813() const {
|
||||
return get_root_node();
|
||||
}
|
||||
|
||||
void AnimationPlayer::_seek_bind_compat_80813(double p_time, bool p_update) {
|
||||
seek(p_time, p_update, false);
|
||||
}
|
||||
|
||||
void AnimationPlayer::_bind_compatibility_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_process_callback", "mode"), &AnimationPlayer::_set_process_callback_bind_compat_80813);
|
||||
ClassDB::bind_method(D_METHOD("get_process_callback"), &AnimationPlayer::_get_process_callback_bind_compat_80813);
|
||||
ClassDB::bind_method(D_METHOD("set_method_call_mode", "mode"), &AnimationPlayer::_set_method_call_mode_bind_compat_80813);
|
||||
ClassDB::bind_method(D_METHOD("get_method_call_mode"), &AnimationPlayer::_get_method_call_mode_bind_compat_80813);
|
||||
ClassDB::bind_method(D_METHOD("set_root", "path"), &AnimationPlayer::_set_root_bind_compat_80813);
|
||||
ClassDB::bind_method(D_METHOD("get_root"), &AnimationPlayer::_get_root_bind_compat_80813);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("seek", "seconds", "update"), &AnimationPlayer::_seek_bind_compat_80813, DEFVAL(false));
|
||||
BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS);
|
||||
BIND_ENUM_CONSTANT(ANIMATION_PROCESS_IDLE);
|
||||
BIND_ENUM_CONSTANT(ANIMATION_PROCESS_MANUAL);
|
||||
BIND_ENUM_CONSTANT(ANIMATION_METHOD_CALL_DEFERRED);
|
||||
BIND_ENUM_CONSTANT(ANIMATION_METHOD_CALL_IMMEDIATE);
|
||||
}
|
||||
|
||||
#endif // DISABLE_DEPRECATED
|
1039
scene/animation/animation_player.cpp
Normal file
1039
scene/animation/animation_player.cpp
Normal file
File diff suppressed because it is too large
Load Diff
247
scene/animation/animation_player.h
Normal file
247
scene/animation/animation_player.h
Normal file
@@ -0,0 +1,247 @@
|
||||
/**************************************************************************/
|
||||
/* animation_player.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "animation_mixer.h"
|
||||
#include "scene/resources/animation.h"
|
||||
|
||||
class AnimationPlayer : public AnimationMixer {
|
||||
GDCLASS(AnimationPlayer, AnimationMixer);
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
public:
|
||||
enum AnimationProcessCallback {
|
||||
ANIMATION_PROCESS_PHYSICS,
|
||||
ANIMATION_PROCESS_IDLE,
|
||||
ANIMATION_PROCESS_MANUAL,
|
||||
};
|
||||
enum AnimationMethodCallMode {
|
||||
ANIMATION_METHOD_CALL_DEFERRED,
|
||||
ANIMATION_METHOD_CALL_IMMEDIATE,
|
||||
};
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
private:
|
||||
AHashMap<StringName, StringName> animation_next_set; // For auto advance.
|
||||
|
||||
float speed_scale = 1.0;
|
||||
double default_blend_time = 0.0;
|
||||
|
||||
bool auto_capture = true;
|
||||
double auto_capture_duration = -1.0;
|
||||
Tween::TransitionType auto_capture_transition_type = Tween::TRANS_LINEAR;
|
||||
Tween::EaseType auto_capture_ease_type = Tween::EASE_IN;
|
||||
|
||||
bool is_stopping = false;
|
||||
|
||||
struct PlaybackData {
|
||||
AnimationData *from = nullptr;
|
||||
double pos = 0.0;
|
||||
float speed_scale = 1.0;
|
||||
double start_time = 0.0;
|
||||
double end_time = 0.0;
|
||||
double get_start_time() const {
|
||||
if (from && (Animation::is_less_approx(start_time, 0) || Animation::is_greater_approx(start_time, from->animation->get_length()))) {
|
||||
return 0;
|
||||
}
|
||||
return start_time;
|
||||
}
|
||||
double get_end_time() const {
|
||||
if (from && (Animation::is_less_approx(end_time, 0) || Animation::is_greater_approx(end_time, from->animation->get_length()))) {
|
||||
return from->animation->get_length();
|
||||
}
|
||||
return end_time;
|
||||
}
|
||||
};
|
||||
|
||||
struct Blend {
|
||||
PlaybackData data;
|
||||
double blend_time = 0.0;
|
||||
double blend_left = 0.0;
|
||||
};
|
||||
|
||||
struct Playback {
|
||||
PlaybackData current;
|
||||
StringName assigned;
|
||||
bool seeked = false;
|
||||
bool internal_seeked = false;
|
||||
bool started = false;
|
||||
List<Blend> blend;
|
||||
} playback;
|
||||
|
||||
struct BlendKey {
|
||||
StringName from;
|
||||
StringName to;
|
||||
static uint32_t hash(const BlendKey &p_key) {
|
||||
return hash_one_uint64((uint64_t(p_key.from.hash()) << 32) | uint32_t(p_key.to.hash()));
|
||||
}
|
||||
bool operator==(const BlendKey &bk) const {
|
||||
return from == bk.from && to == bk.to;
|
||||
}
|
||||
bool operator<(const BlendKey &bk) const {
|
||||
if (from == bk.from) {
|
||||
return StringName::AlphCompare()(to, bk.to);
|
||||
} else {
|
||||
return StringName::AlphCompare()(from, bk.from);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
HashMap<BlendKey, double, BlendKey> blend_times;
|
||||
|
||||
List<StringName> playback_queue;
|
||||
ObjectID tmp_from;
|
||||
bool end_reached = false;
|
||||
bool end_notify = false;
|
||||
|
||||
StringName autoplay;
|
||||
|
||||
bool reset_on_save = true;
|
||||
bool movie_quit_on_finish = false;
|
||||
|
||||
void _play(const StringName &p_name, double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
|
||||
void _capture(const StringName &p_name, bool p_from_end = false, double p_duration = -1.0, Tween::TransitionType p_trans_type = Tween::TRANS_LINEAR, Tween::EaseType p_ease_type = Tween::EASE_IN);
|
||||
void _process_playback_data(PlaybackData &cd, double p_delta, float p_blend, bool p_seeked, bool p_internal_seeked, bool p_started, bool p_is_current = false);
|
||||
void _blend_playback_data(double p_delta, bool p_started);
|
||||
void _stop_internal(bool p_reset, bool p_keep_state);
|
||||
void _check_immediately_after_start();
|
||||
|
||||
float get_current_blend_amount();
|
||||
|
||||
bool playing = false;
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
virtual void _validate_property(PropertyInfo &p_property) const override;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
void _notification(int p_what);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
// Make animation instances.
|
||||
virtual bool _blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) override;
|
||||
virtual void _blend_capture(double p_delta) override;
|
||||
virtual void _blend_post_process() override;
|
||||
|
||||
virtual void _animation_removed(const StringName &p_name, const StringName &p_library) override;
|
||||
virtual void _rename_animation(const StringName &p_from_name, const StringName &p_to_name) override;
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _set_process_callback_bind_compat_80813(AnimationProcessCallback p_mode);
|
||||
AnimationProcessCallback _get_process_callback_bind_compat_80813() const;
|
||||
void _set_method_call_mode_bind_compat_80813(AnimationMethodCallMode p_mode);
|
||||
AnimationMethodCallMode _get_method_call_mode_bind_compat_80813() const;
|
||||
void _set_root_bind_compat_80813(const NodePath &p_root);
|
||||
NodePath _get_root_bind_compat_80813() const;
|
||||
void _seek_bind_compat_80813(double p_time, bool p_update = false);
|
||||
void _play_compat_84906(const StringName &p_name = StringName(), double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
|
||||
void _play_backwards_compat_84906(const StringName &p_name = StringName(), double p_custom_blend = -1);
|
||||
|
||||
static void _bind_compatibility_methods();
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
public:
|
||||
void animation_set_next(const StringName &p_animation, const StringName &p_next);
|
||||
StringName animation_get_next(const StringName &p_animation) const;
|
||||
|
||||
void set_blend_time(const StringName &p_animation1, const StringName &p_animation2, double p_time);
|
||||
double get_blend_time(const StringName &p_animation1, const StringName &p_animation2) const;
|
||||
|
||||
void set_default_blend_time(double p_default);
|
||||
double get_default_blend_time() const;
|
||||
|
||||
void set_auto_capture(bool p_auto_capture);
|
||||
bool is_auto_capture() const;
|
||||
void set_auto_capture_duration(double p_auto_capture_duration);
|
||||
double get_auto_capture_duration() const;
|
||||
void set_auto_capture_transition_type(Tween::TransitionType p_auto_capture_transition_type);
|
||||
Tween::TransitionType get_auto_capture_transition_type() const;
|
||||
void set_auto_capture_ease_type(Tween::EaseType p_auto_capture_ease_type);
|
||||
Tween::EaseType get_auto_capture_ease_type() const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
|
||||
#endif
|
||||
|
||||
void play(const StringName &p_name = StringName(), double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
|
||||
void play_section_with_markers(const StringName &p_name = StringName(), const StringName &p_start_marker = StringName(), const StringName &p_end_marker = StringName(), double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
|
||||
void play_section(const StringName &p_name = StringName(), double p_start_time = -1, double p_end_time = -1, double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
|
||||
void play_backwards(const StringName &p_name = StringName(), double p_custom_blend = -1);
|
||||
void play_section_with_markers_backwards(const StringName &p_name = StringName(), const StringName &p_start_marker = StringName(), const StringName &p_end_marker = StringName(), double p_custom_blend = -1);
|
||||
void play_section_backwards(const StringName &p_name = StringName(), double p_start_time = -1, double p_end_time = -1, double p_custom_blend = -1);
|
||||
void play_with_capture(const StringName &p_name = StringName(), double p_duration = -1.0, double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false, Tween::TransitionType p_trans_type = Tween::TRANS_LINEAR, Tween::EaseType p_ease_type = Tween::EASE_IN);
|
||||
void queue(const StringName &p_name);
|
||||
Vector<String> get_queue();
|
||||
void clear_queue();
|
||||
void pause();
|
||||
void stop(bool p_keep_state = false);
|
||||
bool is_playing() const;
|
||||
String get_current_animation() const;
|
||||
void set_current_animation(const String &p_animation);
|
||||
String get_assigned_animation() const;
|
||||
void set_assigned_animation(const String &p_animation);
|
||||
bool is_valid() const;
|
||||
|
||||
void set_speed_scale(float p_speed);
|
||||
float get_speed_scale() const;
|
||||
float get_playing_speed() const;
|
||||
|
||||
void set_autoplay(const String &p_name);
|
||||
String get_autoplay() const;
|
||||
|
||||
void set_movie_quit_on_finish_enabled(bool p_enabled);
|
||||
bool is_movie_quit_on_finish_enabled() const;
|
||||
|
||||
void seek_internal(double p_time, bool p_update = false, bool p_update_only = false, bool p_is_internal_seek = false);
|
||||
void seek(double p_time, bool p_update = false, bool p_update_only = false);
|
||||
|
||||
double get_current_animation_position() const;
|
||||
double get_current_animation_length() const;
|
||||
|
||||
void set_section_with_markers(const StringName &p_start_marker = StringName(), const StringName &p_end_marker = StringName());
|
||||
void set_section(double p_start_time = -1, double p_end_time = -1);
|
||||
void reset_section();
|
||||
|
||||
double get_section_start_time() const;
|
||||
double get_section_end_time() const;
|
||||
bool has_section() const;
|
||||
|
||||
virtual void advance(double p_time) override;
|
||||
|
||||
AnimationPlayer();
|
||||
~AnimationPlayer();
|
||||
};
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
VARIANT_ENUM_CAST(AnimationPlayer::AnimationProcessCallback);
|
||||
VARIANT_ENUM_CAST(AnimationPlayer::AnimationMethodCallMode);
|
||||
#endif // DISABLE_DEPRECATED
|
64
scene/animation/animation_tree.compat.inc
Normal file
64
scene/animation/animation_tree.compat.inc
Normal file
@@ -0,0 +1,64 @@
|
||||
/**************************************************************************/
|
||||
/* animation_tree.compat.inc */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
void AnimationTree::_set_process_callback_bind_compat_80813(AnimationTree::AnimationProcessCallback p_mode) {
|
||||
set_callback_mode_process(static_cast<AnimationMixer::AnimationCallbackModeProcess>(static_cast<int>(p_mode)));
|
||||
}
|
||||
|
||||
AnimationTree::AnimationProcessCallback AnimationTree::_get_process_callback_bind_compat_80813() const {
|
||||
return static_cast<AnimationProcessCallback>(static_cast<int>(get_callback_mode_process()));
|
||||
}
|
||||
|
||||
void AnimationTree::_set_tree_root_bind_compat_80813(const Ref<AnimationNode> &p_root) {
|
||||
const Ref<AnimationRootNode> rn = Ref<AnimationRootNode>(p_root.ptr());
|
||||
if (rn.is_null()) {
|
||||
return;
|
||||
}
|
||||
return (set_root_animation_node(rn));
|
||||
}
|
||||
|
||||
Ref<AnimationNode> AnimationTree::_get_tree_root_bind_compat_80813() const {
|
||||
const Ref<AnimationRootNode> rn = Ref<AnimationNode>(get_root_animation_node().ptr());
|
||||
return rn;
|
||||
}
|
||||
|
||||
void AnimationTree::_bind_compatibility_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_process_callback", "mode"), &AnimationTree::_set_process_callback_bind_compat_80813);
|
||||
ClassDB::bind_method(D_METHOD("get_process_callback"), &AnimationTree::_get_process_callback_bind_compat_80813);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("set_tree_root", "root"), &AnimationTree::_set_tree_root_bind_compat_80813);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("get_tree_root"), &AnimationTree::_get_tree_root_bind_compat_80813);
|
||||
BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS);
|
||||
BIND_ENUM_CONSTANT(ANIMATION_PROCESS_IDLE);
|
||||
BIND_ENUM_CONSTANT(ANIMATION_PROCESS_MANUAL);
|
||||
}
|
||||
|
||||
#endif // DISABLE_DEPRECATED
|
1016
scene/animation/animation_tree.cpp
Normal file
1016
scene/animation/animation_tree.cpp
Normal file
File diff suppressed because it is too large
Load Diff
361
scene/animation/animation_tree.h
Normal file
361
scene/animation/animation_tree.h
Normal file
@@ -0,0 +1,361 @@
|
||||
/**************************************************************************/
|
||||
/* animation_tree.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "animation_mixer.h"
|
||||
#include "scene/resources/animation.h"
|
||||
|
||||
#define HUGE_LENGTH 31540000 // 31540000 seconds mean 1 year... is it too long? It must be longer than any Animation length and Transition xfade time to prevent time inversion for AnimationNodeStateMachine.
|
||||
|
||||
class AnimationNodeBlendTree;
|
||||
class AnimationNodeStartState;
|
||||
class AnimationNodeEndState;
|
||||
class AnimationTree;
|
||||
|
||||
class AnimationNode : public Resource {
|
||||
GDCLASS(AnimationNode, Resource);
|
||||
|
||||
public:
|
||||
friend class AnimationTree;
|
||||
|
||||
enum FilterAction {
|
||||
FILTER_IGNORE,
|
||||
FILTER_PASS,
|
||||
FILTER_STOP,
|
||||
FILTER_BLEND
|
||||
};
|
||||
|
||||
struct Input {
|
||||
String name;
|
||||
};
|
||||
|
||||
bool closable = false;
|
||||
LocalVector<Input> inputs;
|
||||
AHashMap<NodePath, bool> filter;
|
||||
bool filter_enabled = false;
|
||||
|
||||
// To propagate information from upstream for use in estimation of playback progress.
|
||||
// These values must be taken from the result of blend_node() or blend_input() and must be essentially read-only.
|
||||
// For example, if you want to change the position, you need to change the pi.time value of PlaybackInfo passed to blend_input(pi) and get the result.
|
||||
struct NodeTimeInfo {
|
||||
// Retain the previous frame values. These are stored into the AnimationTree's Map and exposing them as read-only values.
|
||||
double length = 0.0;
|
||||
double position = 0.0;
|
||||
double delta = 0.0;
|
||||
|
||||
// Needs internally to estimate remain time, the previous frame values are not retained.
|
||||
Animation::LoopMode loop_mode = Animation::LOOP_NONE;
|
||||
bool will_end = false; // For breaking loop, it is true when just looped.
|
||||
bool is_infinity = false; // For unpredictable state machine's end.
|
||||
|
||||
bool is_looping() {
|
||||
return loop_mode != Animation::LOOP_NONE;
|
||||
}
|
||||
double get_remain(bool p_break_loop = false) {
|
||||
if ((is_looping() && !p_break_loop) || is_infinity) {
|
||||
return HUGE_LENGTH;
|
||||
}
|
||||
if (is_looping() && p_break_loop && will_end) {
|
||||
return 0;
|
||||
}
|
||||
double remain = length - position;
|
||||
if (Math::is_zero_approx(remain)) {
|
||||
return 0;
|
||||
}
|
||||
return remain;
|
||||
}
|
||||
};
|
||||
|
||||
// Temporary state for blending process which needs to be stored in each AnimationNodes.
|
||||
struct NodeState {
|
||||
friend AnimationNode;
|
||||
|
||||
private:
|
||||
StringName base_path;
|
||||
|
||||
public:
|
||||
AnimationNode *parent = nullptr;
|
||||
Vector<StringName> connections;
|
||||
LocalVector<real_t> track_weights;
|
||||
|
||||
const StringName get_base_path() const {
|
||||
return base_path;
|
||||
}
|
||||
|
||||
} node_state;
|
||||
|
||||
// Temporary state for blending process which needs to be started in the AnimationTree, pass through the AnimationNodes, and then return to the AnimationTree.
|
||||
struct ProcessState {
|
||||
AnimationTree *tree = nullptr;
|
||||
const AHashMap<NodePath, int> *track_map; // TODO: Is there a better way to manage filter/tracks?
|
||||
bool is_testing = false;
|
||||
bool valid = false;
|
||||
String invalid_reasons;
|
||||
uint64_t last_pass = 0;
|
||||
} *process_state = nullptr;
|
||||
|
||||
private:
|
||||
mutable AHashMap<StringName, int> property_cache;
|
||||
|
||||
public:
|
||||
void set_node_state_base_path(const StringName p_base_path) {
|
||||
if (p_base_path != node_state.base_path) {
|
||||
node_state.base_path = p_base_path;
|
||||
make_cache_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void set_node_state_base_path(const String p_base_path) {
|
||||
if (p_base_path != node_state.base_path) {
|
||||
node_state.base_path = p_base_path;
|
||||
make_cache_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
const StringName get_node_state_base_path() const {
|
||||
return node_state.get_base_path();
|
||||
}
|
||||
|
||||
void make_cache_dirty() {
|
||||
property_cache.clear();
|
||||
}
|
||||
Array _get_filters() const;
|
||||
void _set_filters(const Array &p_filters);
|
||||
friend class AnimationNodeBlendTree;
|
||||
|
||||
// The time information is passed from upstream to downstream by AnimationMixer::PlaybackInfo::p_playback_info until AnimationNodeAnimation processes it.
|
||||
// Conversely, AnimationNodeAnimation returns the processed result as NodeTimeInfo from downstream to upstream.
|
||||
NodeTimeInfo _blend_node(Ref<AnimationNode> p_node, const StringName &p_subpath, AnimationNode *p_new_parent, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false, real_t *r_activity = nullptr);
|
||||
NodeTimeInfo _pre_process(ProcessState *p_process_state, AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false);
|
||||
|
||||
protected:
|
||||
StringName current_length = "current_length";
|
||||
StringName current_position = "current_position";
|
||||
StringName current_delta = "current_delta";
|
||||
|
||||
virtual NodeTimeInfo process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false); // To organize time information. Virtualizing for especially AnimationNodeAnimation needs to take "backward" into account.
|
||||
virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false); // Main process.
|
||||
|
||||
void blend_animation(const StringName &p_animation, AnimationMixer::PlaybackInfo p_playback_info);
|
||||
NodeTimeInfo blend_node(Ref<AnimationNode> p_node, const StringName &p_subpath, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false);
|
||||
NodeTimeInfo blend_input(int p_input, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false);
|
||||
|
||||
// Bind-able methods to expose for compatibility, moreover AnimationMixer::PlaybackInfo is not exposed.
|
||||
void 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 = Animation::LOOPED_FLAG_NONE);
|
||||
double 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 = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false);
|
||||
double blend_input_ex(int p_input, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false);
|
||||
|
||||
void make_invalid(const String &p_reason);
|
||||
AnimationTree *get_animation_tree() const;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
GDVIRTUAL0RC(Dictionary, _get_child_nodes)
|
||||
GDVIRTUAL0RC(Array, _get_parameter_list)
|
||||
GDVIRTUAL1RC(Ref<AnimationNode>, _get_child_by_name, StringName)
|
||||
GDVIRTUAL1RC(Variant, _get_parameter_default_value, StringName)
|
||||
GDVIRTUAL1RC(bool, _is_parameter_read_only, StringName)
|
||||
GDVIRTUAL4R(double, _process, double, bool, bool, bool)
|
||||
GDVIRTUAL0RC(String, _get_caption)
|
||||
GDVIRTUAL0RC(bool, _has_filter)
|
||||
|
||||
public:
|
||||
virtual void get_parameter_list(List<PropertyInfo> *r_list) const;
|
||||
virtual Variant get_parameter_default_value(const StringName &p_parameter) const;
|
||||
virtual bool is_parameter_read_only(const StringName &p_parameter) const;
|
||||
|
||||
void set_parameter(const StringName &p_name, const Variant &p_value);
|
||||
Variant get_parameter(const StringName &p_name) const;
|
||||
|
||||
void set_node_time_info(const NodeTimeInfo &p_node_time_info); // Wrapper of set_parameter().
|
||||
virtual NodeTimeInfo get_node_time_info() const; // Wrapper of get_parameter().
|
||||
|
||||
struct ChildNode {
|
||||
StringName name;
|
||||
Ref<AnimationNode> node;
|
||||
};
|
||||
|
||||
virtual void get_child_nodes(List<ChildNode> *r_child_nodes);
|
||||
|
||||
virtual String get_caption() const;
|
||||
|
||||
virtual bool add_input(const String &p_name);
|
||||
virtual void remove_input(int p_index);
|
||||
virtual bool set_input_name(int p_input, const String &p_name);
|
||||
virtual String get_input_name(int p_input) const;
|
||||
int get_input_count() const;
|
||||
int find_input(const String &p_name) const;
|
||||
|
||||
void set_filter_path(const NodePath &p_path, bool p_enable);
|
||||
bool is_path_filtered(const NodePath &p_path) const;
|
||||
|
||||
void set_filter_enabled(bool p_enable);
|
||||
bool is_filter_enabled() const;
|
||||
|
||||
void set_deletable(bool p_closable);
|
||||
bool is_deletable() const;
|
||||
|
||||
ObjectID get_processing_animation_tree_instance_id() const;
|
||||
|
||||
bool is_process_testing() const;
|
||||
|
||||
virtual bool has_filter() const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
|
||||
#endif
|
||||
|
||||
virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name) const;
|
||||
Ref<AnimationNode> find_node_by_path(const String &p_name) const;
|
||||
|
||||
AnimationNode();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationNode::FilterAction)
|
||||
|
||||
// Root node does not allow inputs.
|
||||
class AnimationRootNode : public AnimationNode {
|
||||
GDCLASS(AnimationRootNode, AnimationNode);
|
||||
|
||||
protected:
|
||||
virtual void _tree_changed();
|
||||
virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name);
|
||||
virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node);
|
||||
};
|
||||
|
||||
class AnimationNodeStartState : public AnimationRootNode {
|
||||
GDCLASS(AnimationNodeStartState, AnimationRootNode);
|
||||
};
|
||||
|
||||
class AnimationNodeEndState : public AnimationRootNode {
|
||||
GDCLASS(AnimationNodeEndState, AnimationRootNode);
|
||||
};
|
||||
|
||||
class AnimationTree : public AnimationMixer {
|
||||
GDCLASS(AnimationTree, AnimationMixer);
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
public:
|
||||
enum AnimationProcessCallback {
|
||||
ANIMATION_PROCESS_PHYSICS,
|
||||
ANIMATION_PROCESS_IDLE,
|
||||
ANIMATION_PROCESS_MANUAL,
|
||||
};
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
private:
|
||||
Ref<AnimationRootNode> root_animation_node;
|
||||
NodePath advance_expression_base_node = NodePath(String("."));
|
||||
|
||||
AnimationNode::ProcessState process_state;
|
||||
uint64_t process_pass = 1;
|
||||
|
||||
bool started = true;
|
||||
|
||||
friend class AnimationNode;
|
||||
|
||||
mutable List<PropertyInfo> properties;
|
||||
mutable AHashMap<StringName, AHashMap<StringName, StringName>> property_parent_map;
|
||||
mutable AHashMap<ObjectID, StringName> property_reference_map;
|
||||
mutable AHashMap<StringName, Pair<Variant, bool>> property_map; // Property value and read-only flag.
|
||||
|
||||
mutable bool properties_dirty = true;
|
||||
|
||||
void _update_properties() const;
|
||||
void _update_properties_for_node(const String &p_base_path, Ref<AnimationNode> p_node) const;
|
||||
|
||||
void _tree_changed();
|
||||
void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name);
|
||||
void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node);
|
||||
|
||||
struct Activity {
|
||||
uint64_t last_pass = 0;
|
||||
real_t activity = 0.0;
|
||||
};
|
||||
mutable AHashMap<StringName, LocalVector<Activity>> input_activity_map;
|
||||
mutable AHashMap<StringName, int> input_activity_map_get;
|
||||
|
||||
NodePath animation_player;
|
||||
|
||||
void _setup_animation_player();
|
||||
void _animation_player_changed();
|
||||
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
virtual uint32_t _get_libraries_property_usage() const override;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
virtual void _validate_property(PropertyInfo &p_property) const override;
|
||||
void _notification(int p_what);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
virtual void _set_active(bool p_active) override;
|
||||
|
||||
// Make animation instances.
|
||||
virtual bool _blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) override;
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _set_process_callback_bind_compat_80813(AnimationProcessCallback p_mode);
|
||||
AnimationProcessCallback _get_process_callback_bind_compat_80813() const;
|
||||
void _set_tree_root_bind_compat_80813(const Ref<AnimationNode> &p_root);
|
||||
Ref<AnimationNode> _get_tree_root_bind_compat_80813() const;
|
||||
|
||||
static void _bind_compatibility_methods();
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
public:
|
||||
void set_animation_player(const NodePath &p_path);
|
||||
NodePath get_animation_player() const;
|
||||
|
||||
void set_root_animation_node(const Ref<AnimationRootNode> &p_animation_node);
|
||||
Ref<AnimationRootNode> get_root_animation_node() const;
|
||||
|
||||
void set_advance_expression_base_node(const NodePath &p_path);
|
||||
NodePath get_advance_expression_base_node() const;
|
||||
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
|
||||
bool is_state_invalid() const;
|
||||
String get_invalid_state_reason() const;
|
||||
|
||||
real_t get_connection_activity(const StringName &p_path, int p_connection) const;
|
||||
|
||||
uint64_t get_last_process_pass() const;
|
||||
|
||||
AnimationTree();
|
||||
~AnimationTree();
|
||||
};
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
VARIANT_ENUM_CAST(AnimationTree::AnimationProcessCallback);
|
||||
#endif // DISABLE_DEPRECATED
|
444
scene/animation/easing_equations.h
Normal file
444
scene/animation/easing_equations.h
Normal file
@@ -0,0 +1,444 @@
|
||||
/**************************************************************************/
|
||||
/* easing_equations.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/math/math_funcs.h"
|
||||
|
||||
/*
|
||||
* Derived from Robert Penner's easing equations: http://robertpenner.com/easing/
|
||||
*
|
||||
* Copyright (c) 2001 Robert Penner
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Linear {
|
||||
static real_t in(real_t t, real_t b, real_t c, real_t d) {
|
||||
return c * t / d + b;
|
||||
}
|
||||
}; // namespace Linear
|
||||
|
||||
namespace Sine {
|
||||
static real_t in(real_t t, real_t b, real_t c, real_t d) {
|
||||
return -c * std::cos(t / d * (Math::PI / 2)) + c + b;
|
||||
}
|
||||
|
||||
static real_t out(real_t t, real_t b, real_t c, real_t d) {
|
||||
return c * std::sin(t / d * (Math::PI / 2)) + b;
|
||||
}
|
||||
|
||||
static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
|
||||
return -c / 2 * (std::cos(Math::PI * t / d) - 1) + b;
|
||||
}
|
||||
|
||||
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t < d / 2) {
|
||||
return out(t * 2, b, c / 2, d);
|
||||
}
|
||||
real_t h = c / 2;
|
||||
return in(t * 2 - d, b + h, h, d);
|
||||
}
|
||||
}; // namespace Sine
|
||||
|
||||
namespace Quint {
|
||||
static real_t in(real_t t, real_t b, real_t c, real_t d) {
|
||||
return c * std::pow(t / d, 5) + b;
|
||||
}
|
||||
|
||||
static real_t out(real_t t, real_t b, real_t c, real_t d) {
|
||||
return c * (std::pow(t / d - 1, 5) + 1) + b;
|
||||
}
|
||||
|
||||
static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
|
||||
t = t / d * 2;
|
||||
|
||||
if (t < 1) {
|
||||
return c / 2 * std::pow(t, 5) + b;
|
||||
}
|
||||
return c / 2 * (std::pow(t - 2, 5) + 2) + b;
|
||||
}
|
||||
|
||||
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t < d / 2) {
|
||||
return out(t * 2, b, c / 2, d);
|
||||
}
|
||||
real_t h = c / 2;
|
||||
return in(t * 2 - d, b + h, h, d);
|
||||
}
|
||||
}; // namespace Quint
|
||||
|
||||
namespace Quart {
|
||||
static real_t in(real_t t, real_t b, real_t c, real_t d) {
|
||||
return c * std::pow(t / d, 4) + b;
|
||||
}
|
||||
|
||||
static real_t out(real_t t, real_t b, real_t c, real_t d) {
|
||||
return -c * (std::pow(t / d - 1, 4) - 1) + b;
|
||||
}
|
||||
|
||||
static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
|
||||
t = t / d * 2;
|
||||
|
||||
if (t < 1) {
|
||||
return c / 2 * std::pow(t, 4) + b;
|
||||
}
|
||||
return -c / 2 * (std::pow(t - 2, 4) - 2) + b;
|
||||
}
|
||||
|
||||
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t < d / 2) {
|
||||
return out(t * 2, b, c / 2, d);
|
||||
}
|
||||
real_t h = c / 2;
|
||||
return in(t * 2 - d, b + h, h, d);
|
||||
}
|
||||
}; // namespace Quart
|
||||
|
||||
namespace Quad {
|
||||
static real_t in(real_t t, real_t b, real_t c, real_t d) {
|
||||
return c * std::pow(t / d, 2) + b;
|
||||
}
|
||||
|
||||
static real_t out(real_t t, real_t b, real_t c, real_t d) {
|
||||
t /= d;
|
||||
return -c * t * (t - 2) + b;
|
||||
}
|
||||
|
||||
static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
|
||||
t = t / d * 2;
|
||||
|
||||
if (t < 1) {
|
||||
return c / 2 * std::pow(t, 2) + b;
|
||||
}
|
||||
return -c / 2 * ((t - 1) * (t - 3) - 1) + b;
|
||||
}
|
||||
|
||||
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t < d / 2) {
|
||||
return out(t * 2, b, c / 2, d);
|
||||
}
|
||||
real_t h = c / 2;
|
||||
return in(t * 2 - d, b + h, h, d);
|
||||
}
|
||||
}; // namespace Quad
|
||||
|
||||
namespace Expo {
|
||||
static real_t in(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t == 0) {
|
||||
return b;
|
||||
}
|
||||
return c * std::pow(2, 10 * (t / d - 1)) + b - c * 0.001;
|
||||
}
|
||||
|
||||
static real_t out(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t == d) {
|
||||
return b + c;
|
||||
}
|
||||
return c * 1.001 * (-std::pow(2, -10 * t / d) + 1) + b;
|
||||
}
|
||||
|
||||
static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t == 0) {
|
||||
return b;
|
||||
}
|
||||
|
||||
if (t == d) {
|
||||
return b + c;
|
||||
}
|
||||
|
||||
t = t / d * 2;
|
||||
|
||||
if (t < 1) {
|
||||
return c / 2 * std::pow(2, 10 * (t - 1)) + b - c * 0.0005;
|
||||
}
|
||||
return c / 2 * 1.0005 * (-std::pow(2, -10 * (t - 1)) + 2) + b;
|
||||
}
|
||||
|
||||
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t < d / 2) {
|
||||
return out(t * 2, b, c / 2, d);
|
||||
}
|
||||
real_t h = c / 2;
|
||||
return in(t * 2 - d, b + h, h, d);
|
||||
}
|
||||
}; // namespace Expo
|
||||
|
||||
namespace Elastic {
|
||||
static real_t in(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t == 0) {
|
||||
return b;
|
||||
}
|
||||
|
||||
t /= d;
|
||||
if (t == 1) {
|
||||
return b + c;
|
||||
}
|
||||
|
||||
t -= 1;
|
||||
float p = d * 0.3f;
|
||||
float a = c * std::pow(2, 10 * t);
|
||||
float s = p / 4;
|
||||
|
||||
return -(a * std::sin((t * d - s) * (2 * Math::PI) / p)) + b;
|
||||
}
|
||||
|
||||
static real_t out(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t == 0) {
|
||||
return b;
|
||||
}
|
||||
|
||||
t /= d;
|
||||
if (t == 1) {
|
||||
return b + c;
|
||||
}
|
||||
|
||||
float p = d * 0.3f;
|
||||
float s = p / 4;
|
||||
|
||||
return (c * std::pow(2, -10 * t) * std::sin((t * d - s) * (2 * Math::PI) / p) + c + b);
|
||||
}
|
||||
|
||||
static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t == 0) {
|
||||
return b;
|
||||
}
|
||||
|
||||
if ((t /= d / 2) == 2) {
|
||||
return b + c;
|
||||
}
|
||||
|
||||
float p = d * (0.3f * 1.5f);
|
||||
float a = c;
|
||||
float s = p / 4;
|
||||
|
||||
if (t < 1) {
|
||||
t -= 1;
|
||||
a *= std::pow(2, 10 * t);
|
||||
return -0.5f * (a * std::sin((t * d - s) * (2 * Math::PI) / p)) + b;
|
||||
}
|
||||
|
||||
t -= 1;
|
||||
a *= std::pow(2, -10 * t);
|
||||
return a * std::sin((t * d - s) * (2 * Math::PI) / p) * 0.5f + c + b;
|
||||
}
|
||||
|
||||
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t < d / 2) {
|
||||
return out(t * 2, b, c / 2, d);
|
||||
}
|
||||
real_t h = c / 2;
|
||||
return in(t * 2 - d, b + h, h, d);
|
||||
}
|
||||
}; // namespace Elastic
|
||||
|
||||
namespace Cubic {
|
||||
static real_t in(real_t t, real_t b, real_t c, real_t d) {
|
||||
t /= d;
|
||||
return c * t * t * t + b;
|
||||
}
|
||||
|
||||
static real_t out(real_t t, real_t b, real_t c, real_t d) {
|
||||
t = t / d - 1;
|
||||
return c * (t * t * t + 1) + b;
|
||||
}
|
||||
|
||||
static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
|
||||
t /= d / 2;
|
||||
if (t < 1) {
|
||||
return c / 2 * t * t * t + b;
|
||||
}
|
||||
|
||||
t -= 2;
|
||||
return c / 2 * (t * t * t + 2) + b;
|
||||
}
|
||||
|
||||
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t < d / 2) {
|
||||
return out(t * 2, b, c / 2, d);
|
||||
}
|
||||
real_t h = c / 2;
|
||||
return in(t * 2 - d, b + h, h, d);
|
||||
}
|
||||
}; // namespace Cubic
|
||||
|
||||
namespace Circ {
|
||||
static real_t in(real_t t, real_t b, real_t c, real_t d) {
|
||||
t /= d;
|
||||
return -c * (std::sqrt(1 - t * t) - 1) + b;
|
||||
}
|
||||
|
||||
static real_t out(real_t t, real_t b, real_t c, real_t d) {
|
||||
t = t / d - 1;
|
||||
return c * std::sqrt(1 - t * t) + b;
|
||||
}
|
||||
|
||||
static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
|
||||
t /= d / 2;
|
||||
if (t < 1) {
|
||||
return -c / 2 * (std::sqrt(1 - t * t) - 1) + b;
|
||||
}
|
||||
|
||||
t -= 2;
|
||||
return c / 2 * (std::sqrt(1 - t * t) + 1) + b;
|
||||
}
|
||||
|
||||
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t < d / 2) {
|
||||
return out(t * 2, b, c / 2, d);
|
||||
}
|
||||
real_t h = c / 2;
|
||||
return in(t * 2 - d, b + h, h, d);
|
||||
}
|
||||
}; // namespace Circ
|
||||
|
||||
namespace Bounce {
|
||||
static real_t out(real_t t, real_t b, real_t c, real_t d) {
|
||||
t /= d;
|
||||
|
||||
if (t < (1 / 2.75f)) {
|
||||
return c * (7.5625f * t * t) + b;
|
||||
}
|
||||
|
||||
if (t < (2 / 2.75f)) {
|
||||
t -= 1.5f / 2.75f;
|
||||
return c * (7.5625f * t * t + 0.75f) + b;
|
||||
}
|
||||
|
||||
if (t < (2.5 / 2.75)) {
|
||||
t -= 2.25f / 2.75f;
|
||||
return c * (7.5625f * t * t + 0.9375f) + b;
|
||||
}
|
||||
|
||||
t -= 2.625f / 2.75f;
|
||||
return c * (7.5625f * t * t + 0.984375f) + b;
|
||||
}
|
||||
|
||||
static real_t in(real_t t, real_t b, real_t c, real_t d) {
|
||||
return c - out(d - t, 0, c, d) + b;
|
||||
}
|
||||
|
||||
static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t < d / 2) {
|
||||
return in(t * 2, b, c / 2, d);
|
||||
}
|
||||
real_t h = c / 2;
|
||||
return out(t * 2 - d, b + h, h, d);
|
||||
}
|
||||
|
||||
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t < d / 2) {
|
||||
return out(t * 2, b, c / 2, d);
|
||||
}
|
||||
real_t h = c / 2;
|
||||
return in(t * 2 - d, b + h, h, d);
|
||||
}
|
||||
}; // namespace Bounce
|
||||
|
||||
namespace Back {
|
||||
static real_t in(real_t t, real_t b, real_t c, real_t d) {
|
||||
float s = 1.70158f;
|
||||
t /= d;
|
||||
|
||||
return c * t * t * ((s + 1) * t - s) + b;
|
||||
}
|
||||
|
||||
static real_t out(real_t t, real_t b, real_t c, real_t d) {
|
||||
float s = 1.70158f;
|
||||
t = t / d - 1;
|
||||
|
||||
return c * (t * t * ((s + 1) * t + s) + 1) + b;
|
||||
}
|
||||
|
||||
static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
|
||||
float s = 1.70158f * 1.525f;
|
||||
t /= d / 2;
|
||||
|
||||
if (t < 1) {
|
||||
return c / 2 * (t * t * ((s + 1) * t - s)) + b;
|
||||
}
|
||||
|
||||
t -= 2;
|
||||
return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b;
|
||||
}
|
||||
|
||||
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t < d / 2) {
|
||||
return out(t * 2, b, c / 2, d);
|
||||
}
|
||||
real_t h = c / 2;
|
||||
return in(t * 2 - d, b + h, h, d);
|
||||
}
|
||||
}; // namespace Back
|
||||
|
||||
namespace Spring {
|
||||
static real_t out(real_t t, real_t b, real_t c, real_t d) {
|
||||
t /= d;
|
||||
real_t s = 1.0 - t;
|
||||
t = (std::sin(t * Math::PI * (0.2 + 2.5 * t * t * t)) * std::pow(s, 2.2) + t) * (1.0 + (1.2 * s));
|
||||
return c * t + b;
|
||||
}
|
||||
|
||||
static real_t in(real_t t, real_t b, real_t c, real_t d) {
|
||||
return c - out(d - t, 0, c, d) + b;
|
||||
}
|
||||
|
||||
static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t < d / 2) {
|
||||
return in(t * 2, b, c / 2, d);
|
||||
}
|
||||
real_t h = c / 2;
|
||||
return out(t * 2 - d, b + h, h, d);
|
||||
}
|
||||
|
||||
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
|
||||
if (t < d / 2) {
|
||||
return out(t * 2, b, c / 2, d);
|
||||
}
|
||||
real_t h = c / 2;
|
||||
return in(t * 2 - d, b + h, h, d);
|
||||
}
|
||||
}; // namespace Spring
|
208
scene/animation/root_motion_view.cpp
Normal file
208
scene/animation/root_motion_view.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
/**************************************************************************/
|
||||
/* root_motion_view.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
|
||||
#include "root_motion_view.h"
|
||||
|
||||
#include "scene/animation/animation_mixer.h"
|
||||
#include "scene/resources/material.h"
|
||||
|
||||
void RootMotionView::set_animation_mixer(const NodePath &p_path) {
|
||||
path = p_path;
|
||||
first = true;
|
||||
}
|
||||
|
||||
NodePath RootMotionView::get_animation_mixer() const {
|
||||
return path;
|
||||
}
|
||||
|
||||
void RootMotionView::set_color(const Color &p_color) {
|
||||
color = p_color;
|
||||
first = true;
|
||||
}
|
||||
|
||||
Color RootMotionView::get_color() const {
|
||||
return color;
|
||||
}
|
||||
|
||||
void RootMotionView::set_cell_size(float p_size) {
|
||||
cell_size = p_size;
|
||||
first = true;
|
||||
}
|
||||
|
||||
float RootMotionView::get_cell_size() const {
|
||||
return cell_size;
|
||||
}
|
||||
|
||||
void RootMotionView::set_radius(float p_radius) {
|
||||
radius = p_radius;
|
||||
first = true;
|
||||
}
|
||||
|
||||
float RootMotionView::get_radius() const {
|
||||
return radius;
|
||||
}
|
||||
|
||||
void RootMotionView::set_zero_y(bool p_zero_y) {
|
||||
zero_y = p_zero_y;
|
||||
}
|
||||
|
||||
bool RootMotionView::get_zero_y() const {
|
||||
return zero_y;
|
||||
}
|
||||
|
||||
void RootMotionView::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
immediate_material = StandardMaterial3D::get_material_for_2d(false, BaseMaterial3D::TRANSPARENCY_ALPHA, false);
|
||||
|
||||
first = true;
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PROCESS:
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
Transform3D transform;
|
||||
Basis diff;
|
||||
|
||||
if (has_node(path)) {
|
||||
Node *node = get_node(path);
|
||||
AnimationMixer *mixer = Object::cast_to<AnimationMixer>(node);
|
||||
if (mixer && mixer->is_active() && mixer->get_root_motion_track() != NodePath()) {
|
||||
if (is_processing_internal() && mixer->get_callback_mode_process() == AnimationMixer::ANIMATION_CALLBACK_MODE_PROCESS_PHYSICS) {
|
||||
set_process_internal(false);
|
||||
set_physics_process_internal(true);
|
||||
}
|
||||
|
||||
if (is_physics_processing_internal() && mixer->get_callback_mode_process() == AnimationMixer::ANIMATION_CALLBACK_MODE_PROCESS_IDLE) {
|
||||
set_process_internal(true);
|
||||
set_physics_process_internal(false);
|
||||
}
|
||||
|
||||
transform.origin = mixer->get_root_motion_position();
|
||||
transform.basis = mixer->get_root_motion_rotation(); // Scale is meaningless.
|
||||
diff = mixer->is_root_motion_local() ? Quaternion() : mixer->get_root_motion_rotation_accumulator();
|
||||
}
|
||||
}
|
||||
if (!first && transform == Transform3D()) {
|
||||
return;
|
||||
}
|
||||
|
||||
first = false;
|
||||
|
||||
accumulated.basis *= transform.basis;
|
||||
transform.origin = (diff.inverse() * accumulated.basis).xform(transform.origin);
|
||||
accumulated.origin += transform.origin;
|
||||
|
||||
accumulated.origin.x = Math::fposmod(accumulated.origin.x, cell_size);
|
||||
if (zero_y) {
|
||||
accumulated.origin.y = 0;
|
||||
}
|
||||
accumulated.origin.z = Math::fposmod(accumulated.origin.z, cell_size);
|
||||
|
||||
immediate->clear_surfaces();
|
||||
|
||||
int cells_in_radius = int((radius / cell_size) + 1.0);
|
||||
|
||||
immediate->surface_begin(Mesh::PRIMITIVE_LINES, immediate_material);
|
||||
|
||||
for (int i = -cells_in_radius; i < cells_in_radius; i++) {
|
||||
for (int j = -cells_in_radius; j < cells_in_radius; j++) {
|
||||
Vector3 from(i * cell_size, 0, j * cell_size);
|
||||
Vector3 from_i((i + 1) * cell_size, 0, j * cell_size);
|
||||
Vector3 from_j(i * cell_size, 0, (j + 1) * cell_size);
|
||||
from = accumulated.xform_inv(from);
|
||||
from_i = accumulated.xform_inv(from_i);
|
||||
from_j = accumulated.xform_inv(from_j);
|
||||
|
||||
Color c = color, c_i = color, c_j = color;
|
||||
c.a *= MAX(0, 1.0 - from.length() / radius);
|
||||
c_i.a *= MAX(0, 1.0 - from_i.length() / radius);
|
||||
c_j.a *= MAX(0, 1.0 - from_j.length() / radius);
|
||||
|
||||
immediate->surface_set_color(c);
|
||||
immediate->surface_add_vertex(from);
|
||||
|
||||
immediate->surface_set_color(c_i);
|
||||
immediate->surface_add_vertex(from_i);
|
||||
|
||||
immediate->surface_set_color(c);
|
||||
immediate->surface_add_vertex(from);
|
||||
|
||||
immediate->surface_set_color(c_j);
|
||||
immediate->surface_add_vertex(from_j);
|
||||
}
|
||||
}
|
||||
|
||||
immediate->surface_end();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
AABB RootMotionView::get_aabb() const {
|
||||
return AABB(Vector3(-radius, 0, -radius), Vector3(radius * 2, 0.001, radius * 2));
|
||||
}
|
||||
|
||||
void RootMotionView::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_animation_path", "path"), &RootMotionView::set_animation_mixer);
|
||||
ClassDB::bind_method(D_METHOD("get_animation_path"), &RootMotionView::get_animation_mixer);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_color", "color"), &RootMotionView::set_color);
|
||||
ClassDB::bind_method(D_METHOD("get_color"), &RootMotionView::get_color);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_cell_size", "size"), &RootMotionView::set_cell_size);
|
||||
ClassDB::bind_method(D_METHOD("get_cell_size"), &RootMotionView::get_cell_size);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_radius", "size"), &RootMotionView::set_radius);
|
||||
ClassDB::bind_method(D_METHOD("get_radius"), &RootMotionView::get_radius);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_zero_y", "enable"), &RootMotionView::set_zero_y);
|
||||
ClassDB::bind_method(D_METHOD("get_zero_y"), &RootMotionView::get_zero_y);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "animation_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationMixer"), "set_animation_path", "get_animation_path");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size", PROPERTY_HINT_RANGE, "0.1,16,0.01,or_greater,suffix:m"), "set_cell_size", "get_cell_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,16,0.01,or_greater,suffix:m"), "set_radius", "get_radius");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "zero_y"), "set_zero_y", "get_zero_y");
|
||||
}
|
||||
|
||||
RootMotionView::RootMotionView() {
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
set_process_internal(true);
|
||||
}
|
||||
immediate.instantiate();
|
||||
set_base(immediate->get_rid());
|
||||
}
|
||||
|
||||
RootMotionView::~RootMotionView() {
|
||||
set_base(RID());
|
||||
}
|
||||
|
||||
#endif // _3D_DISABLED
|
76
scene/animation/root_motion_view.h
Normal file
76
scene/animation/root_motion_view.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/**************************************************************************/
|
||||
/* root_motion_view.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/3d/visual_instance_3d.h"
|
||||
#include "scene/resources/immediate_mesh.h"
|
||||
class RootMotionView : public VisualInstance3D {
|
||||
GDCLASS(RootMotionView, VisualInstance3D);
|
||||
|
||||
public:
|
||||
Ref<ImmediateMesh> immediate;
|
||||
NodePath path;
|
||||
real_t cell_size = 1.0;
|
||||
real_t radius = 10.0;
|
||||
bool use_in_game = false;
|
||||
Color color = Color(0.5, 0.5, 1.0);
|
||||
bool first = true;
|
||||
bool zero_y = true;
|
||||
|
||||
Ref<Material> immediate_material;
|
||||
|
||||
Transform3D accumulated;
|
||||
|
||||
private:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_animation_mixer(const NodePath &p_path);
|
||||
NodePath get_animation_mixer() const;
|
||||
|
||||
void set_color(const Color &p_color);
|
||||
Color get_color() const;
|
||||
|
||||
void set_cell_size(float p_size);
|
||||
float get_cell_size() const;
|
||||
|
||||
void set_radius(float p_radius);
|
||||
float get_radius() const;
|
||||
|
||||
void set_zero_y(bool p_zero_y);
|
||||
bool get_zero_y() const;
|
||||
|
||||
virtual AABB get_aabb() const override;
|
||||
|
||||
RootMotionView();
|
||||
~RootMotionView();
|
||||
};
|
924
scene/animation/tween.cpp
Normal file
924
scene/animation/tween.cpp
Normal file
@@ -0,0 +1,924 @@
|
||||
/**************************************************************************/
|
||||
/* tween.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "tween.h"
|
||||
|
||||
#include "scene/animation/easing_equations.h"
|
||||
#include "scene/main/node.h"
|
||||
#include "scene/resources/animation.h"
|
||||
|
||||
#define CHECK_VALID() \
|
||||
ERR_FAIL_COND_V_MSG(!valid, nullptr, "Tween invalid. Either finished or created outside scene tree."); \
|
||||
ERR_FAIL_COND_V_MSG(started, nullptr, "Can't append to a Tween that has started. Use stop() first.");
|
||||
|
||||
Tween::interpolater Tween::interpolaters[Tween::TRANS_MAX][Tween::EASE_MAX] = {
|
||||
{ &Linear::in, &Linear::in, &Linear::in, &Linear::in }, // Linear is the same for each easing.
|
||||
{ &Sine::in, &Sine::out, &Sine::in_out, &Sine::out_in },
|
||||
{ &Quint::in, &Quint::out, &Quint::in_out, &Quint::out_in },
|
||||
{ &Quart::in, &Quart::out, &Quart::in_out, &Quart::out_in },
|
||||
{ &Quad::in, &Quad::out, &Quad::in_out, &Quad::out_in },
|
||||
{ &Expo::in, &Expo::out, &Expo::in_out, &Expo::out_in },
|
||||
{ &Elastic::in, &Elastic::out, &Elastic::in_out, &Elastic::out_in },
|
||||
{ &Cubic::in, &Cubic::out, &Cubic::in_out, &Cubic::out_in },
|
||||
{ &Circ::in, &Circ::out, &Circ::in_out, &Circ::out_in },
|
||||
{ &Bounce::in, &Bounce::out, &Bounce::in_out, &Bounce::out_in },
|
||||
{ &Back::in, &Back::out, &Back::in_out, &Back::out_in },
|
||||
{ &Spring::in, &Spring::out, &Spring::in_out, &Spring::out_in },
|
||||
};
|
||||
|
||||
void Tweener::set_tween(const Ref<Tween> &p_tween) {
|
||||
tween_id = p_tween->get_instance_id();
|
||||
}
|
||||
|
||||
void Tweener::start() {
|
||||
elapsed_time = 0;
|
||||
finished = false;
|
||||
}
|
||||
|
||||
Ref<Tween> Tweener::_get_tween() {
|
||||
return ObjectDB::get_ref<Tween>(tween_id);
|
||||
}
|
||||
|
||||
void Tweener::_finish() {
|
||||
finished = true;
|
||||
emit_signal(SceneStringName(finished));
|
||||
}
|
||||
|
||||
void Tweener::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo("finished"));
|
||||
}
|
||||
|
||||
void Tween::_start_tweeners() {
|
||||
if (tweeners.is_empty()) {
|
||||
dead = true;
|
||||
ERR_FAIL_MSG("Tween without commands, aborting.");
|
||||
}
|
||||
|
||||
for (Ref<Tweener> &tweener : tweeners.write[current_step]) {
|
||||
tweener->start();
|
||||
}
|
||||
}
|
||||
|
||||
void Tween::_stop_internal(bool p_reset) {
|
||||
running = false;
|
||||
if (p_reset) {
|
||||
started = false;
|
||||
dead = false;
|
||||
total_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<PropertyTweener> Tween::tween_property(const Object *p_target, const NodePath &p_property, Variant p_to, double p_duration) {
|
||||
ERR_FAIL_NULL_V(p_target, nullptr);
|
||||
CHECK_VALID();
|
||||
|
||||
Vector<StringName> property_subnames = p_property.get_as_property_path().get_subnames();
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool prop_valid;
|
||||
const Variant &prop_value = p_target->get_indexed(property_subnames, &prop_valid);
|
||||
ERR_FAIL_COND_V_MSG(!prop_valid, nullptr, vformat("The tweened property \"%s\" does not exist in object \"%s\".", p_property, p_target));
|
||||
#else
|
||||
const Variant &prop_value = p_target->get_indexed(property_subnames);
|
||||
#endif
|
||||
|
||||
if (!Animation::validate_type_match(prop_value, p_to)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<PropertyTweener> tweener;
|
||||
tweener.instantiate(p_target, property_subnames, p_to, p_duration);
|
||||
append(tweener);
|
||||
return tweener;
|
||||
}
|
||||
|
||||
Ref<IntervalTweener> Tween::tween_interval(double p_time) {
|
||||
CHECK_VALID();
|
||||
|
||||
Ref<IntervalTweener> tweener;
|
||||
tweener.instantiate(p_time);
|
||||
append(tweener);
|
||||
return tweener;
|
||||
}
|
||||
|
||||
Ref<CallbackTweener> Tween::tween_callback(const Callable &p_callback) {
|
||||
CHECK_VALID();
|
||||
|
||||
Ref<CallbackTweener> tweener;
|
||||
tweener.instantiate(p_callback);
|
||||
append(tweener);
|
||||
return tweener;
|
||||
}
|
||||
|
||||
Ref<MethodTweener> Tween::tween_method(const Callable &p_callback, const Variant p_from, Variant p_to, double p_duration) {
|
||||
CHECK_VALID();
|
||||
|
||||
if (!Animation::validate_type_match(p_from, p_to)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<MethodTweener> tweener;
|
||||
tweener.instantiate(p_callback, p_from, p_to, p_duration);
|
||||
append(tweener);
|
||||
return tweener;
|
||||
}
|
||||
|
||||
Ref<SubtweenTweener> Tween::tween_subtween(const Ref<Tween> &p_subtween) {
|
||||
CHECK_VALID();
|
||||
|
||||
// Ensure that the subtween being added is not null.
|
||||
ERR_FAIL_COND_V(p_subtween.is_null(), nullptr);
|
||||
|
||||
Ref<SubtweenTweener> tweener;
|
||||
tweener.instantiate(p_subtween);
|
||||
|
||||
// Remove the tween from its parent tree, if it has one.
|
||||
// If the user created this tween without a parent tree attached,
|
||||
// then this step isn't necessary.
|
||||
if (tweener->subtween->parent_tree != nullptr) {
|
||||
tweener->subtween->parent_tree->remove_tween(tweener->subtween);
|
||||
}
|
||||
append(tweener);
|
||||
return tweener;
|
||||
}
|
||||
|
||||
void Tween::append(Ref<Tweener> p_tweener) {
|
||||
p_tweener->set_tween(this);
|
||||
|
||||
if (parallel_enabled) {
|
||||
current_step = MAX(current_step, 0);
|
||||
} else {
|
||||
current_step++;
|
||||
}
|
||||
parallel_enabled = default_parallel;
|
||||
|
||||
tweeners.resize(current_step + 1);
|
||||
tweeners.write[current_step].push_back(p_tweener);
|
||||
}
|
||||
|
||||
void Tween::stop() {
|
||||
_stop_internal(true);
|
||||
}
|
||||
|
||||
void Tween::pause() {
|
||||
_stop_internal(false);
|
||||
}
|
||||
|
||||
void Tween::play() {
|
||||
ERR_FAIL_COND_MSG(!valid, "Tween invalid. Either finished or created outside scene tree.");
|
||||
ERR_FAIL_COND_MSG(dead, "Can't play finished Tween, use stop() first to reset its state.");
|
||||
running = true;
|
||||
}
|
||||
|
||||
void Tween::kill() {
|
||||
running = false; // For the sake of is_running().
|
||||
valid = false;
|
||||
dead = true;
|
||||
}
|
||||
|
||||
bool Tween::is_running() {
|
||||
return running;
|
||||
}
|
||||
|
||||
bool Tween::is_valid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
void Tween::clear() {
|
||||
valid = false;
|
||||
tweeners.clear();
|
||||
}
|
||||
|
||||
Ref<Tween> Tween::bind_node(const Node *p_node) {
|
||||
ERR_FAIL_NULL_V(p_node, this);
|
||||
|
||||
bound_node = p_node->get_instance_id();
|
||||
is_bound = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Ref<Tween> Tween::set_process_mode(TweenProcessMode p_mode) {
|
||||
process_mode = p_mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
Tween::TweenProcessMode Tween::get_process_mode() const {
|
||||
return process_mode;
|
||||
}
|
||||
|
||||
Ref<Tween> Tween::set_pause_mode(TweenPauseMode p_mode) {
|
||||
pause_mode = p_mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
Tween::TweenPauseMode Tween::get_pause_mode() const {
|
||||
return pause_mode;
|
||||
}
|
||||
|
||||
Ref<Tween> Tween::set_ignore_time_scale(bool p_ignore) {
|
||||
ignore_time_scale = p_ignore;
|
||||
return this;
|
||||
}
|
||||
|
||||
bool Tween::is_ignoring_time_scale() const {
|
||||
return ignore_time_scale;
|
||||
}
|
||||
|
||||
Ref<Tween> Tween::set_parallel(bool p_parallel) {
|
||||
default_parallel = p_parallel;
|
||||
parallel_enabled = p_parallel;
|
||||
return this;
|
||||
}
|
||||
|
||||
Ref<Tween> Tween::set_loops(int p_loops) {
|
||||
loops = p_loops;
|
||||
return this;
|
||||
}
|
||||
|
||||
int Tween::get_loops_left() const {
|
||||
if (loops <= 0) {
|
||||
return -1; // Infinite loop.
|
||||
} else {
|
||||
return loops - loops_done;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Tween> Tween::set_speed_scale(float p_speed) {
|
||||
speed_scale = p_speed;
|
||||
return this;
|
||||
}
|
||||
|
||||
Ref<Tween> Tween::set_trans(TransitionType p_trans) {
|
||||
default_transition = p_trans;
|
||||
return this;
|
||||
}
|
||||
|
||||
Tween::TransitionType Tween::get_trans() const {
|
||||
return default_transition;
|
||||
}
|
||||
|
||||
Ref<Tween> Tween::set_ease(EaseType p_ease) {
|
||||
default_ease = p_ease;
|
||||
return this;
|
||||
}
|
||||
|
||||
Tween::EaseType Tween::get_ease() const {
|
||||
return default_ease;
|
||||
}
|
||||
|
||||
Ref<Tween> Tween::parallel() {
|
||||
parallel_enabled = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Ref<Tween> Tween::chain() {
|
||||
parallel_enabled = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
bool Tween::custom_step(double p_delta) {
|
||||
ERR_FAIL_COND_V_MSG(in_step, true, "Can't call custom_step() during another Tween step.");
|
||||
|
||||
bool r = running;
|
||||
running = true;
|
||||
bool ret = step(p_delta);
|
||||
running = running && r; // Running might turn false when Tween finished.
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Tween::step(double p_delta) {
|
||||
if (dead) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_bound) {
|
||||
Node *node = get_bound_node();
|
||||
if (node) {
|
||||
if (!node->is_inside_tree()) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!running) {
|
||||
return true;
|
||||
}
|
||||
in_step = true;
|
||||
|
||||
if (!started) {
|
||||
if (tweeners.is_empty()) {
|
||||
String tween_id;
|
||||
Node *node = get_bound_node();
|
||||
if (node) {
|
||||
tween_id = vformat("Tween (bound to %s)", node->is_inside_tree() ? (String)node->get_path() : (String)node->get_name());
|
||||
} else {
|
||||
tween_id = to_string();
|
||||
}
|
||||
in_step = false;
|
||||
ERR_FAIL_V_MSG(false, tween_id + ": started with no Tweeners.");
|
||||
}
|
||||
current_step = 0;
|
||||
loops_done = 0;
|
||||
total_time = 0;
|
||||
_start_tweeners();
|
||||
started = true;
|
||||
}
|
||||
|
||||
double rem_delta = p_delta * speed_scale;
|
||||
bool step_active = false;
|
||||
total_time += rem_delta;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
double initial_delta = rem_delta;
|
||||
bool potential_infinite = false;
|
||||
#endif
|
||||
|
||||
while (running && rem_delta > 0) {
|
||||
double step_delta = rem_delta;
|
||||
step_active = false;
|
||||
|
||||
for (Ref<Tweener> &tweener : tweeners.write[current_step]) {
|
||||
// Modified inside Tweener.step().
|
||||
double temp_delta = rem_delta;
|
||||
// Turns to true if any Tweener returns true (i.e. is still not finished).
|
||||
step_active = tweener->step(temp_delta) || step_active;
|
||||
step_delta = MIN(temp_delta, step_delta);
|
||||
}
|
||||
|
||||
rem_delta = step_delta;
|
||||
|
||||
if (!step_active) {
|
||||
emit_signal(SNAME("step_finished"), current_step);
|
||||
current_step++;
|
||||
|
||||
if (current_step == tweeners.size()) {
|
||||
loops_done++;
|
||||
if (loops_done == loops) {
|
||||
running = false;
|
||||
dead = true;
|
||||
emit_signal(SceneStringName(finished));
|
||||
break;
|
||||
} else {
|
||||
emit_signal(SNAME("loop_finished"), loops_done);
|
||||
current_step = 0;
|
||||
_start_tweeners();
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (loops <= 0 && Math::is_equal_approx(rem_delta, initial_delta)) {
|
||||
if (!potential_infinite) {
|
||||
potential_infinite = true;
|
||||
} else {
|
||||
// Looped twice without using any time, this is 100% certain infinite loop.
|
||||
in_step = false;
|
||||
ERR_FAIL_V_MSG(false, "Infinite loop detected. Check set_loops() description for more info.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
_start_tweeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
in_step = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Tween::can_process(bool p_tree_paused) const {
|
||||
if (is_bound && pause_mode == TWEEN_PAUSE_BOUND) {
|
||||
Node *node = get_bound_node();
|
||||
if (node) {
|
||||
return node->is_inside_tree() && node->can_process();
|
||||
}
|
||||
}
|
||||
|
||||
return !p_tree_paused || pause_mode == TWEEN_PAUSE_PROCESS;
|
||||
}
|
||||
|
||||
Node *Tween::get_bound_node() const {
|
||||
if (is_bound) {
|
||||
return ObjectDB::get_instance<Node>(bound_node);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
double Tween::get_total_time() const {
|
||||
return total_time;
|
||||
}
|
||||
|
||||
real_t Tween::run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t p_time, real_t p_initial, real_t p_delta, real_t p_duration) {
|
||||
if (p_duration == 0) {
|
||||
// Special case to avoid dividing by 0 in equations.
|
||||
return p_initial + p_delta;
|
||||
}
|
||||
|
||||
interpolater func = interpolaters[p_trans_type][p_ease_type];
|
||||
return func(p_time, p_initial, p_delta, p_duration);
|
||||
}
|
||||
|
||||
Variant Tween::interpolate_variant(const Variant &p_initial_val, const Variant &p_delta_val, double p_time, double p_duration, TransitionType p_trans, EaseType p_ease) {
|
||||
ERR_FAIL_INDEX_V(p_trans, TransitionType::TRANS_MAX, Variant());
|
||||
ERR_FAIL_INDEX_V(p_ease, EaseType::EASE_MAX, Variant());
|
||||
|
||||
Variant ret = Animation::add_variant(p_initial_val, p_delta_val);
|
||||
ret = Animation::interpolate_variant(p_initial_val, ret, run_equation(p_trans, p_ease, p_time, 0.0, 1.0, p_duration), p_initial_val.is_string());
|
||||
return ret;
|
||||
}
|
||||
|
||||
String Tween::to_string() {
|
||||
String ret = Object::to_string();
|
||||
Node *node = get_bound_node();
|
||||
if (node) {
|
||||
ret += vformat(" (bound to %s)", node->get_name());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Tween::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("tween_property", "object", "property", "final_val", "duration"), &Tween::tween_property);
|
||||
ClassDB::bind_method(D_METHOD("tween_interval", "time"), &Tween::tween_interval);
|
||||
ClassDB::bind_method(D_METHOD("tween_callback", "callback"), &Tween::tween_callback);
|
||||
ClassDB::bind_method(D_METHOD("tween_method", "method", "from", "to", "duration"), &Tween::tween_method);
|
||||
ClassDB::bind_method(D_METHOD("tween_subtween", "subtween"), &Tween::tween_subtween);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("custom_step", "delta"), &Tween::custom_step);
|
||||
ClassDB::bind_method(D_METHOD("stop"), &Tween::stop);
|
||||
ClassDB::bind_method(D_METHOD("pause"), &Tween::pause);
|
||||
ClassDB::bind_method(D_METHOD("play"), &Tween::play);
|
||||
ClassDB::bind_method(D_METHOD("kill"), &Tween::kill);
|
||||
ClassDB::bind_method(D_METHOD("get_total_elapsed_time"), &Tween::get_total_time);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_running"), &Tween::is_running);
|
||||
ClassDB::bind_method(D_METHOD("is_valid"), &Tween::is_valid);
|
||||
ClassDB::bind_method(D_METHOD("bind_node", "node"), &Tween::bind_node);
|
||||
ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Tween::set_process_mode);
|
||||
ClassDB::bind_method(D_METHOD("set_pause_mode", "mode"), &Tween::set_pause_mode);
|
||||
ClassDB::bind_method(D_METHOD("set_ignore_time_scale", "ignore"), &Tween::set_ignore_time_scale, DEFVAL(true));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_parallel", "parallel"), &Tween::set_parallel, DEFVAL(true));
|
||||
ClassDB::bind_method(D_METHOD("set_loops", "loops"), &Tween::set_loops, DEFVAL(0));
|
||||
ClassDB::bind_method(D_METHOD("get_loops_left"), &Tween::get_loops_left);
|
||||
ClassDB::bind_method(D_METHOD("set_speed_scale", "speed"), &Tween::set_speed_scale);
|
||||
ClassDB::bind_method(D_METHOD("set_trans", "trans"), &Tween::set_trans);
|
||||
ClassDB::bind_method(D_METHOD("set_ease", "ease"), &Tween::set_ease);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("parallel"), &Tween::parallel);
|
||||
ClassDB::bind_method(D_METHOD("chain"), &Tween::chain);
|
||||
|
||||
ClassDB::bind_static_method("Tween", D_METHOD("interpolate_value", "initial_value", "delta_value", "elapsed_time", "duration", "trans_type", "ease_type"), &Tween::interpolate_variant);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("step_finished", PropertyInfo(Variant::INT, "idx")));
|
||||
ADD_SIGNAL(MethodInfo("loop_finished", PropertyInfo(Variant::INT, "loop_count")));
|
||||
ADD_SIGNAL(MethodInfo("finished"));
|
||||
|
||||
BIND_ENUM_CONSTANT(TWEEN_PROCESS_PHYSICS);
|
||||
BIND_ENUM_CONSTANT(TWEEN_PROCESS_IDLE);
|
||||
|
||||
BIND_ENUM_CONSTANT(TWEEN_PAUSE_BOUND);
|
||||
BIND_ENUM_CONSTANT(TWEEN_PAUSE_STOP);
|
||||
BIND_ENUM_CONSTANT(TWEEN_PAUSE_PROCESS);
|
||||
|
||||
BIND_ENUM_CONSTANT(TRANS_LINEAR);
|
||||
BIND_ENUM_CONSTANT(TRANS_SINE);
|
||||
BIND_ENUM_CONSTANT(TRANS_QUINT);
|
||||
BIND_ENUM_CONSTANT(TRANS_QUART);
|
||||
BIND_ENUM_CONSTANT(TRANS_QUAD);
|
||||
BIND_ENUM_CONSTANT(TRANS_EXPO);
|
||||
BIND_ENUM_CONSTANT(TRANS_ELASTIC);
|
||||
BIND_ENUM_CONSTANT(TRANS_CUBIC);
|
||||
BIND_ENUM_CONSTANT(TRANS_CIRC);
|
||||
BIND_ENUM_CONSTANT(TRANS_BOUNCE);
|
||||
BIND_ENUM_CONSTANT(TRANS_BACK);
|
||||
BIND_ENUM_CONSTANT(TRANS_SPRING);
|
||||
|
||||
BIND_ENUM_CONSTANT(EASE_IN);
|
||||
BIND_ENUM_CONSTANT(EASE_OUT);
|
||||
BIND_ENUM_CONSTANT(EASE_IN_OUT);
|
||||
BIND_ENUM_CONSTANT(EASE_OUT_IN);
|
||||
}
|
||||
|
||||
Tween::Tween() {
|
||||
ERR_FAIL_MSG("Tween can't be created directly. Use create_tween() method.");
|
||||
}
|
||||
|
||||
Tween::Tween(SceneTree *p_parent_tree) {
|
||||
parent_tree = p_parent_tree;
|
||||
valid = true;
|
||||
}
|
||||
|
||||
double PropertyTweener::_get_custom_interpolated_value(const Variant &p_value) {
|
||||
const Variant *argptr = &p_value;
|
||||
|
||||
Variant result;
|
||||
Callable::CallError ce;
|
||||
custom_method.callp(&argptr, 1, result, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_FAIL_V_MSG(false, "Error calling custom method from PropertyTweener: " + Variant::get_callable_error_text(custom_method, &argptr, 1, ce) + ".");
|
||||
} else if (result.get_type() != Variant::FLOAT) {
|
||||
ERR_FAIL_V_MSG(false, vformat("Wrong return type in PropertyTweener custom method. Expected float, got %s.", Variant::get_type_name(result.get_type())));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Ref<PropertyTweener> PropertyTweener::from(const Variant &p_value) {
|
||||
Ref<Tween> tween = _get_tween();
|
||||
ERR_FAIL_COND_V(tween.is_null(), nullptr);
|
||||
|
||||
Variant from_value = p_value;
|
||||
if (!Animation::validate_type_match(final_val, from_value)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
initial_val = from_value;
|
||||
do_continue = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
Ref<PropertyTweener> PropertyTweener::from_current() {
|
||||
do_continue = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
Ref<PropertyTweener> PropertyTweener::as_relative() {
|
||||
relative = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Ref<PropertyTweener> PropertyTweener::set_trans(Tween::TransitionType p_trans) {
|
||||
trans_type = p_trans;
|
||||
return this;
|
||||
}
|
||||
|
||||
Ref<PropertyTweener> PropertyTweener::set_ease(Tween::EaseType p_ease) {
|
||||
ease_type = p_ease;
|
||||
return this;
|
||||
}
|
||||
|
||||
Ref<PropertyTweener> PropertyTweener::set_custom_interpolator(const Callable &p_method) {
|
||||
custom_method = p_method;
|
||||
return this;
|
||||
}
|
||||
|
||||
Ref<PropertyTweener> PropertyTweener::set_delay(double p_delay) {
|
||||
delay = p_delay;
|
||||
return this;
|
||||
}
|
||||
|
||||
void PropertyTweener::start() {
|
||||
Tweener::start();
|
||||
|
||||
Object *target_instance = ObjectDB::get_instance(target);
|
||||
if (!target_instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (do_continue) {
|
||||
if (Math::is_zero_approx(delay)) {
|
||||
initial_val = target_instance->get_indexed(property);
|
||||
} else {
|
||||
do_continue_delayed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (relative) {
|
||||
final_val = Animation::add_variant(initial_val, base_final_val);
|
||||
}
|
||||
|
||||
delta_val = Animation::subtract_variant(final_val, initial_val);
|
||||
}
|
||||
|
||||
bool PropertyTweener::step(double &r_delta) {
|
||||
if (finished) {
|
||||
// This is needed in case there's a parallel Tweener with longer duration.
|
||||
return false;
|
||||
}
|
||||
|
||||
Object *target_instance = ObjectDB::get_instance(target);
|
||||
if (!target_instance) {
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
elapsed_time += r_delta;
|
||||
|
||||
if (elapsed_time < delay) {
|
||||
r_delta = 0;
|
||||
return true;
|
||||
} else if (do_continue_delayed && !Math::is_zero_approx(delay)) {
|
||||
initial_val = target_instance->get_indexed(property);
|
||||
delta_val = Animation::subtract_variant(final_val, initial_val);
|
||||
do_continue_delayed = false;
|
||||
}
|
||||
|
||||
Ref<Tween> tween = _get_tween();
|
||||
|
||||
double time = MIN(elapsed_time - delay, duration);
|
||||
if (time < duration) {
|
||||
if (custom_method.is_valid()) {
|
||||
const Variant t = tween->interpolate_variant(0.0, 1.0, time, duration, trans_type, ease_type);
|
||||
double result = _get_custom_interpolated_value(t);
|
||||
target_instance->set_indexed(property, Animation::interpolate_variant(initial_val, final_val, result));
|
||||
} else {
|
||||
target_instance->set_indexed(property, tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type));
|
||||
}
|
||||
r_delta = 0;
|
||||
return true;
|
||||
} else {
|
||||
if (custom_method.is_valid()) {
|
||||
double final_t = _get_custom_interpolated_value(1.0);
|
||||
target_instance->set_indexed(property, Animation::interpolate_variant(initial_val, final_val, final_t));
|
||||
} else {
|
||||
target_instance->set_indexed(property, final_val);
|
||||
}
|
||||
r_delta = elapsed_time - delay - duration;
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyTweener::set_tween(const Ref<Tween> &p_tween) {
|
||||
Tweener::set_tween(p_tween);
|
||||
if (trans_type == Tween::TRANS_MAX) {
|
||||
trans_type = p_tween->get_trans();
|
||||
}
|
||||
if (ease_type == Tween::EASE_MAX) {
|
||||
ease_type = p_tween->get_ease();
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyTweener::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("from", "value"), &PropertyTweener::from);
|
||||
ClassDB::bind_method(D_METHOD("from_current"), &PropertyTweener::from_current);
|
||||
ClassDB::bind_method(D_METHOD("as_relative"), &PropertyTweener::as_relative);
|
||||
ClassDB::bind_method(D_METHOD("set_trans", "trans"), &PropertyTweener::set_trans);
|
||||
ClassDB::bind_method(D_METHOD("set_ease", "ease"), &PropertyTweener::set_ease);
|
||||
ClassDB::bind_method(D_METHOD("set_custom_interpolator", "interpolator_method"), &PropertyTweener::set_custom_interpolator);
|
||||
ClassDB::bind_method(D_METHOD("set_delay", "delay"), &PropertyTweener::set_delay);
|
||||
}
|
||||
|
||||
PropertyTweener::PropertyTweener(const Object *p_target, const Vector<StringName> &p_property, const Variant &p_to, double p_duration) {
|
||||
target = p_target->get_instance_id();
|
||||
property = p_property;
|
||||
initial_val = p_target->get_indexed(property);
|
||||
base_final_val = p_to;
|
||||
final_val = base_final_val;
|
||||
duration = p_duration;
|
||||
|
||||
if (p_target->is_ref_counted()) {
|
||||
ref_copy = p_target;
|
||||
}
|
||||
}
|
||||
|
||||
PropertyTweener::PropertyTweener() {
|
||||
ERR_FAIL_MSG("PropertyTweener can't be created directly. Use the tween_property() method in Tween.");
|
||||
}
|
||||
|
||||
bool IntervalTweener::step(double &r_delta) {
|
||||
if (finished) {
|
||||
return false;
|
||||
}
|
||||
|
||||
elapsed_time += r_delta;
|
||||
|
||||
if (elapsed_time < duration) {
|
||||
r_delta = 0;
|
||||
return true;
|
||||
} else {
|
||||
r_delta = elapsed_time - duration;
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
IntervalTweener::IntervalTweener(double p_time) {
|
||||
duration = p_time;
|
||||
}
|
||||
|
||||
IntervalTweener::IntervalTweener() {
|
||||
ERR_FAIL_MSG("IntervalTweener can't be created directly. Use the tween_interval() method in Tween.");
|
||||
}
|
||||
|
||||
Ref<CallbackTweener> CallbackTweener::set_delay(double p_delay) {
|
||||
delay = p_delay;
|
||||
return this;
|
||||
}
|
||||
|
||||
bool CallbackTweener::step(double &r_delta) {
|
||||
if (finished) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!callback.is_valid()) {
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
elapsed_time += r_delta;
|
||||
if (elapsed_time >= delay) {
|
||||
Variant result;
|
||||
Callable::CallError ce;
|
||||
callback.callp(nullptr, 0, result, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce) + ".");
|
||||
}
|
||||
|
||||
r_delta = elapsed_time - delay;
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
r_delta = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CallbackTweener::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_delay", "delay"), &CallbackTweener::set_delay);
|
||||
}
|
||||
|
||||
CallbackTweener::CallbackTweener(const Callable &p_callback) {
|
||||
callback = p_callback;
|
||||
|
||||
Object *callback_instance = p_callback.get_object();
|
||||
if (callback_instance && callback_instance->is_ref_counted()) {
|
||||
ref_copy = callback_instance;
|
||||
}
|
||||
}
|
||||
|
||||
CallbackTweener::CallbackTweener() {
|
||||
ERR_FAIL_MSG("CallbackTweener can't be created directly. Use the tween_callback() method in Tween.");
|
||||
}
|
||||
|
||||
Ref<MethodTweener> MethodTweener::set_delay(double p_delay) {
|
||||
delay = p_delay;
|
||||
return this;
|
||||
}
|
||||
|
||||
Ref<MethodTweener> MethodTweener::set_trans(Tween::TransitionType p_trans) {
|
||||
trans_type = p_trans;
|
||||
return this;
|
||||
}
|
||||
|
||||
Ref<MethodTweener> MethodTweener::set_ease(Tween::EaseType p_ease) {
|
||||
ease_type = p_ease;
|
||||
return this;
|
||||
}
|
||||
|
||||
bool MethodTweener::step(double &r_delta) {
|
||||
if (finished) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!callback.is_valid()) {
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
elapsed_time += r_delta;
|
||||
|
||||
if (elapsed_time < delay) {
|
||||
r_delta = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<Tween> tween = _get_tween();
|
||||
|
||||
Variant current_val;
|
||||
double time = MIN(elapsed_time - delay, duration);
|
||||
if (time < duration) {
|
||||
current_val = tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type);
|
||||
} else {
|
||||
current_val = final_val;
|
||||
}
|
||||
const Variant **argptr = (const Variant **)alloca(sizeof(Variant *));
|
||||
argptr[0] = ¤t_val;
|
||||
|
||||
Variant result;
|
||||
Callable::CallError ce;
|
||||
callback.callp(argptr, 1, result, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_callable_error_text(callback, argptr, 1, ce) + ".");
|
||||
}
|
||||
|
||||
if (time < duration) {
|
||||
r_delta = 0;
|
||||
return true;
|
||||
} else {
|
||||
r_delta = elapsed_time - delay - duration;
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void MethodTweener::set_tween(const Ref<Tween> &p_tween) {
|
||||
Tweener::set_tween(p_tween);
|
||||
if (trans_type == Tween::TRANS_MAX) {
|
||||
trans_type = p_tween->get_trans();
|
||||
}
|
||||
if (ease_type == Tween::EASE_MAX) {
|
||||
ease_type = p_tween->get_ease();
|
||||
}
|
||||
}
|
||||
|
||||
void MethodTweener::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_delay", "delay"), &MethodTweener::set_delay);
|
||||
ClassDB::bind_method(D_METHOD("set_trans", "trans"), &MethodTweener::set_trans);
|
||||
ClassDB::bind_method(D_METHOD("set_ease", "ease"), &MethodTweener::set_ease);
|
||||
}
|
||||
|
||||
MethodTweener::MethodTweener(const Callable &p_callback, const Variant &p_from, const Variant &p_to, double p_duration) {
|
||||
callback = p_callback;
|
||||
initial_val = p_from;
|
||||
delta_val = Animation::subtract_variant(p_to, p_from);
|
||||
final_val = p_to;
|
||||
duration = p_duration;
|
||||
|
||||
Object *callback_instance = p_callback.get_object();
|
||||
if (callback_instance && callback_instance->is_ref_counted()) {
|
||||
ref_copy = callback_instance;
|
||||
}
|
||||
}
|
||||
|
||||
MethodTweener::MethodTweener() {
|
||||
ERR_FAIL_MSG("MethodTweener can't be created directly. Use the tween_method() method in Tween.");
|
||||
}
|
||||
|
||||
void SubtweenTweener::start() {
|
||||
Tweener::start();
|
||||
|
||||
// Reset the subtween.
|
||||
subtween->stop();
|
||||
|
||||
// It's possible that a subtween could be killed before it is started;
|
||||
// if so, we just want to skip it entirely.
|
||||
if (subtween->is_valid()) {
|
||||
subtween->play();
|
||||
} else {
|
||||
_finish();
|
||||
}
|
||||
}
|
||||
|
||||
bool SubtweenTweener::step(double &r_delta) {
|
||||
if (finished) {
|
||||
return false;
|
||||
}
|
||||
|
||||
elapsed_time += r_delta;
|
||||
|
||||
if (elapsed_time < delay) {
|
||||
r_delta = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!subtween->step(r_delta)) {
|
||||
r_delta = elapsed_time - delay - subtween->get_total_time();
|
||||
_finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
r_delta = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<SubtweenTweener> SubtweenTweener::set_delay(double p_delay) {
|
||||
delay = p_delay;
|
||||
return this;
|
||||
}
|
||||
|
||||
void SubtweenTweener::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_delay", "delay"), &SubtweenTweener::set_delay);
|
||||
}
|
||||
|
||||
SubtweenTweener::SubtweenTweener(const Ref<Tween> &p_subtween) {
|
||||
subtween = p_subtween;
|
||||
}
|
||||
|
||||
SubtweenTweener::SubtweenTweener() {
|
||||
ERR_FAIL_MSG("SubtweenTweener can't be created directly. Use the tween_subtween() method in Tween.");
|
||||
}
|
328
scene/animation/tween.h
Normal file
328
scene/animation/tween.h
Normal file
@@ -0,0 +1,328 @@
|
||||
/**************************************************************************/
|
||||
/* tween.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/object/ref_counted.h"
|
||||
|
||||
class Tween;
|
||||
class Node;
|
||||
class SceneTree;
|
||||
|
||||
class Tweener : public RefCounted {
|
||||
GDCLASS(Tweener, RefCounted);
|
||||
|
||||
ObjectID tween_id;
|
||||
|
||||
public:
|
||||
virtual void set_tween(const Ref<Tween> &p_tween);
|
||||
virtual void start();
|
||||
virtual bool step(double &r_delta) = 0;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
Ref<Tween> _get_tween();
|
||||
void _finish();
|
||||
|
||||
double elapsed_time = 0;
|
||||
bool finished = false;
|
||||
};
|
||||
|
||||
class PropertyTweener;
|
||||
class IntervalTweener;
|
||||
class CallbackTweener;
|
||||
class MethodTweener;
|
||||
class SubtweenTweener;
|
||||
|
||||
class Tween : public RefCounted {
|
||||
GDCLASS(Tween, RefCounted);
|
||||
|
||||
friend class PropertyTweener;
|
||||
|
||||
public:
|
||||
enum TweenProcessMode {
|
||||
TWEEN_PROCESS_PHYSICS,
|
||||
TWEEN_PROCESS_IDLE,
|
||||
};
|
||||
|
||||
enum TweenPauseMode {
|
||||
TWEEN_PAUSE_BOUND,
|
||||
TWEEN_PAUSE_STOP,
|
||||
TWEEN_PAUSE_PROCESS,
|
||||
};
|
||||
|
||||
enum TransitionType {
|
||||
TRANS_LINEAR,
|
||||
TRANS_SINE,
|
||||
TRANS_QUINT,
|
||||
TRANS_QUART,
|
||||
TRANS_QUAD,
|
||||
TRANS_EXPO,
|
||||
TRANS_ELASTIC,
|
||||
TRANS_CUBIC,
|
||||
TRANS_CIRC,
|
||||
TRANS_BOUNCE,
|
||||
TRANS_BACK,
|
||||
TRANS_SPRING,
|
||||
TRANS_MAX
|
||||
};
|
||||
|
||||
enum EaseType {
|
||||
EASE_IN,
|
||||
EASE_OUT,
|
||||
EASE_IN_OUT,
|
||||
EASE_OUT_IN,
|
||||
EASE_MAX
|
||||
};
|
||||
|
||||
private:
|
||||
TweenProcessMode process_mode = TweenProcessMode::TWEEN_PROCESS_IDLE;
|
||||
TweenPauseMode pause_mode = TweenPauseMode::TWEEN_PAUSE_BOUND;
|
||||
TransitionType default_transition = TransitionType::TRANS_LINEAR;
|
||||
EaseType default_ease = EaseType::EASE_IN_OUT;
|
||||
ObjectID bound_node;
|
||||
|
||||
SceneTree *parent_tree = nullptr;
|
||||
Vector<List<Ref<Tweener>>> tweeners;
|
||||
double total_time = 0;
|
||||
int current_step = -1;
|
||||
int loops = 1;
|
||||
int loops_done = 0;
|
||||
float speed_scale = 1;
|
||||
bool ignore_time_scale = false;
|
||||
|
||||
bool is_bound = false;
|
||||
bool started = false;
|
||||
bool running = true;
|
||||
bool in_step = false;
|
||||
bool dead = false;
|
||||
bool valid = false;
|
||||
bool default_parallel = false;
|
||||
bool parallel_enabled = false;
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool is_infinite = false;
|
||||
#endif
|
||||
|
||||
typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d);
|
||||
static interpolater interpolaters[TRANS_MAX][EASE_MAX];
|
||||
|
||||
void _start_tweeners();
|
||||
void _stop_internal(bool p_reset);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual String to_string() override;
|
||||
|
||||
Ref<PropertyTweener> tween_property(const Object *p_target, const NodePath &p_property, Variant p_to, double p_duration);
|
||||
Ref<IntervalTweener> tween_interval(double p_time);
|
||||
Ref<CallbackTweener> tween_callback(const Callable &p_callback);
|
||||
Ref<MethodTweener> tween_method(const Callable &p_callback, const Variant p_from, Variant p_to, double p_duration);
|
||||
Ref<SubtweenTweener> tween_subtween(const Ref<Tween> &p_subtween);
|
||||
void append(Ref<Tweener> p_tweener);
|
||||
|
||||
bool custom_step(double p_delta);
|
||||
void stop();
|
||||
void pause();
|
||||
void play();
|
||||
void kill();
|
||||
|
||||
bool is_running();
|
||||
bool is_valid();
|
||||
void clear();
|
||||
|
||||
Ref<Tween> bind_node(const Node *p_node);
|
||||
Ref<Tween> set_process_mode(TweenProcessMode p_mode);
|
||||
TweenProcessMode get_process_mode() const;
|
||||
Ref<Tween> set_pause_mode(TweenPauseMode p_mode);
|
||||
TweenPauseMode get_pause_mode() const;
|
||||
Ref<Tween> set_ignore_time_scale(bool p_ignore = true);
|
||||
bool is_ignoring_time_scale() const;
|
||||
|
||||
Ref<Tween> set_parallel(bool p_parallel);
|
||||
Ref<Tween> set_loops(int p_loops);
|
||||
int get_loops_left() const;
|
||||
Ref<Tween> set_speed_scale(float p_speed);
|
||||
Ref<Tween> set_trans(TransitionType p_trans);
|
||||
TransitionType get_trans() const;
|
||||
Ref<Tween> set_ease(EaseType p_ease);
|
||||
EaseType get_ease() const;
|
||||
|
||||
Ref<Tween> parallel();
|
||||
Ref<Tween> chain();
|
||||
|
||||
static real_t run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d);
|
||||
static Variant interpolate_variant(const Variant &p_initial_val, const Variant &p_delta_val, double p_time, double p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease);
|
||||
|
||||
bool step(double p_delta);
|
||||
bool can_process(bool p_tree_paused) const;
|
||||
Node *get_bound_node() const;
|
||||
double get_total_time() const;
|
||||
|
||||
Tween();
|
||||
Tween(SceneTree *p_parent_tree);
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(Tween::TweenPauseMode);
|
||||
VARIANT_ENUM_CAST(Tween::TweenProcessMode);
|
||||
VARIANT_ENUM_CAST(Tween::TransitionType);
|
||||
VARIANT_ENUM_CAST(Tween::EaseType);
|
||||
|
||||
class PropertyTweener : public Tweener {
|
||||
GDCLASS(PropertyTweener, Tweener);
|
||||
|
||||
double _get_custom_interpolated_value(const Variant &p_value);
|
||||
|
||||
public:
|
||||
Ref<PropertyTweener> from(const Variant &p_value);
|
||||
Ref<PropertyTweener> from_current();
|
||||
Ref<PropertyTweener> as_relative();
|
||||
Ref<PropertyTweener> set_trans(Tween::TransitionType p_trans);
|
||||
Ref<PropertyTweener> set_ease(Tween::EaseType p_ease);
|
||||
Ref<PropertyTweener> set_custom_interpolator(const Callable &p_method);
|
||||
Ref<PropertyTweener> set_delay(double p_delay);
|
||||
|
||||
void set_tween(const Ref<Tween> &p_tween) override;
|
||||
void start() override;
|
||||
bool step(double &r_delta) override;
|
||||
|
||||
PropertyTweener(const Object *p_target, const Vector<StringName> &p_property, const Variant &p_to, double p_duration);
|
||||
PropertyTweener();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
ObjectID target;
|
||||
Vector<StringName> property;
|
||||
Variant initial_val;
|
||||
Variant base_final_val;
|
||||
Variant final_val;
|
||||
Variant delta_val;
|
||||
|
||||
Ref<RefCounted> ref_copy; // Makes sure that RefCounted objects are not freed too early.
|
||||
|
||||
double duration = 0;
|
||||
Tween::TransitionType trans_type = Tween::TRANS_MAX; // This is set inside set_tween();
|
||||
Tween::EaseType ease_type = Tween::EASE_MAX;
|
||||
Callable custom_method;
|
||||
|
||||
double delay = 0;
|
||||
bool do_continue = true;
|
||||
bool do_continue_delayed = false;
|
||||
bool relative = false;
|
||||
};
|
||||
|
||||
class IntervalTweener : public Tweener {
|
||||
GDCLASS(IntervalTweener, Tweener);
|
||||
|
||||
public:
|
||||
bool step(double &r_delta) override;
|
||||
|
||||
IntervalTweener(double p_time);
|
||||
IntervalTweener();
|
||||
|
||||
private:
|
||||
double duration = 0;
|
||||
};
|
||||
|
||||
class CallbackTweener : public Tweener {
|
||||
GDCLASS(CallbackTweener, Tweener);
|
||||
|
||||
public:
|
||||
Ref<CallbackTweener> set_delay(double p_delay);
|
||||
|
||||
bool step(double &r_delta) override;
|
||||
|
||||
CallbackTweener(const Callable &p_callback);
|
||||
CallbackTweener();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
Callable callback;
|
||||
double delay = 0;
|
||||
|
||||
Ref<RefCounted> ref_copy;
|
||||
};
|
||||
|
||||
class MethodTweener : public Tweener {
|
||||
GDCLASS(MethodTweener, Tweener);
|
||||
|
||||
public:
|
||||
Ref<MethodTweener> set_trans(Tween::TransitionType p_trans);
|
||||
Ref<MethodTweener> set_ease(Tween::EaseType p_ease);
|
||||
Ref<MethodTweener> set_delay(double p_delay);
|
||||
|
||||
void set_tween(const Ref<Tween> &p_tween) override;
|
||||
bool step(double &r_delta) override;
|
||||
|
||||
MethodTweener(const Callable &p_callback, const Variant &p_from, const Variant &p_to, double p_duration);
|
||||
MethodTweener();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
double duration = 0;
|
||||
double delay = 0;
|
||||
Tween::TransitionType trans_type = Tween::TRANS_MAX;
|
||||
Tween::EaseType ease_type = Tween::EASE_MAX;
|
||||
|
||||
Variant initial_val;
|
||||
Variant delta_val;
|
||||
Variant final_val;
|
||||
Callable callback;
|
||||
|
||||
Ref<RefCounted> ref_copy;
|
||||
};
|
||||
|
||||
class SubtweenTweener : public Tweener {
|
||||
GDCLASS(SubtweenTweener, Tweener);
|
||||
|
||||
public:
|
||||
Ref<Tween> subtween;
|
||||
void start() override;
|
||||
bool step(double &r_delta) override;
|
||||
|
||||
Ref<SubtweenTweener> set_delay(double p_delay);
|
||||
|
||||
SubtweenTweener(const Ref<Tween> &p_subtween);
|
||||
SubtweenTweener();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
double delay = 0;
|
||||
};
|
Reference in New Issue
Block a user