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

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

22
scene/animation/SCsub Normal file
View 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)

View 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() {
}

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

View 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() {
}

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

File diff suppressed because it is too large Load Diff

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

View 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

File diff suppressed because it is too large Load Diff

View 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);

View 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;
}

View 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);
};

File diff suppressed because it is too large Load Diff

View 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();
};

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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
View 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] = &current_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
View 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;
};