Consistent with NodeBlendSpace1D option NodeBlendSpace2D

Co-authored-by: Skrapion <rick@firefang.com>
This commit is contained in:
Silc Renew 2023-01-31 02:12:31 +09:00
parent 551f5191e5
commit a0c4f849e0
5 changed files with 188 additions and 69 deletions

View File

@ -67,6 +67,9 @@
</method> </method>
</methods> </methods>
<members> <members>
<member name="blend_mode" type="int" setter="set_blend_mode" getter="get_blend_mode" enum="AnimationNodeBlendSpace1D.BlendMode" default="0">
Controls the interpolation between animations. See [enum BlendMode] constants.
</member>
<member name="max_space" type="float" setter="set_max_space" getter="get_max_space" default="1.0"> <member name="max_space" type="float" setter="set_max_space" getter="get_max_space" default="1.0">
The blend space's axis's upper limit for the points' position. See [method add_blend_point]. The blend space's axis's upper limit for the points' position. See [method add_blend_point].
</member> </member>
@ -84,4 +87,15 @@
Label of the virtual axis of the blend space. Label of the virtual axis of the blend space.
</member> </member>
</members> </members>
<constants>
<constant name="BLEND_MODE_INTERPOLATED" value="0" enum="BlendMode">
The interpolation between animations is linear.
</constant>
<constant name="BLEND_MODE_DISCRETE" value="1" enum="BlendMode">
The blend space plays the animation of the node the blending position is closest to. Useful for frame-by-frame 2D animations.
</constant>
<constant name="BLEND_MODE_DISCRETE_CARRY" value="2" enum="BlendMode">
Similar to [constant BLEND_MODE_DISCRETE], but starts the new animation at the last animation's playback position.
</constant>
</constants>
</class> </class>

View File

@ -38,6 +38,7 @@
#include "editor/editor_undo_redo_manager.h" #include "editor/editor_undo_redo_manager.h"
#include "scene/animation/animation_blend_tree.h" #include "scene/animation/animation_blend_tree.h"
#include "scene/gui/check_box.h" #include "scene/gui/check_box.h"
#include "scene/gui/option_button.h"
#include "scene/gui/panel_container.h" #include "scene/gui/panel_container.h"
StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const { StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const {
@ -335,6 +336,7 @@ void AnimationNodeBlendSpace1DEditor::_update_space() {
min_value->set_value(blend_space->get_min_space()); min_value->set_value(blend_space->get_min_space());
sync->set_pressed(blend_space->is_using_sync()); sync->set_pressed(blend_space->is_using_sync());
interpolation->select(blend_space->get_blend_mode());
label_value->set_text(blend_space->get_value_label()); label_value->set_text(blend_space->get_value_label());
@ -361,6 +363,8 @@ void AnimationNodeBlendSpace1DEditor::_config_changed(double) {
undo_redo->add_undo_method(blend_space.ptr(), "set_snap", blend_space->get_snap()); undo_redo->add_undo_method(blend_space.ptr(), "set_snap", blend_space->get_snap());
undo_redo->add_do_method(blend_space.ptr(), "set_use_sync", sync->is_pressed()); undo_redo->add_do_method(blend_space.ptr(), "set_use_sync", sync->is_pressed());
undo_redo->add_undo_method(blend_space.ptr(), "set_use_sync", blend_space->is_using_sync()); undo_redo->add_undo_method(blend_space.ptr(), "set_use_sync", blend_space->is_using_sync());
undo_redo->add_do_method(blend_space.ptr(), "set_blend_mode", interpolation->get_selected());
undo_redo->add_undo_method(blend_space.ptr(), "set_blend_mode", blend_space->get_blend_mode());
undo_redo->add_do_method(this, "_update_space"); undo_redo->add_do_method(this, "_update_space");
undo_redo->add_undo_method(this, "_update_space"); undo_redo->add_undo_method(this, "_update_space");
undo_redo->commit_action(); undo_redo->commit_action();
@ -579,6 +583,10 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) {
tool_erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); tool_erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
snap->set_icon(get_theme_icon(SNAME("SnapGrid"), SNAME("EditorIcons"))); snap->set_icon(get_theme_icon(SNAME("SnapGrid"), SNAME("EditorIcons")));
open_editor->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); open_editor->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
interpolation->clear();
interpolation->add_icon_item(get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), "", 0);
interpolation->add_icon_item(get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), "", 1);
interpolation->add_icon_item(get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")), "", 2);
} break; } break;
case NOTIFICATION_PROCESS: { case NOTIFICATION_PROCESS: {
@ -639,6 +647,7 @@ void AnimationNodeBlendSpace1DEditor::edit(const Ref<AnimationNode> &p_node) {
min_value->set_editable(!read_only); min_value->set_editable(!read_only);
max_value->set_editable(!read_only); max_value->set_editable(!read_only);
sync->set_disabled(read_only); sync->set_disabled(read_only);
interpolation->set_disabled(read_only);
} }
AnimationNodeBlendSpace1DEditor *AnimationNodeBlendSpace1DEditor::singleton = nullptr; AnimationNodeBlendSpace1DEditor *AnimationNodeBlendSpace1DEditor::singleton = nullptr;
@ -707,6 +716,13 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() {
top_hb->add_child(sync); top_hb->add_child(sync);
sync->connect("toggled", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed)); sync->connect("toggled", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed));
top_hb->add_child(memnew(VSeparator));
top_hb->add_child(memnew(Label(TTR("Blend:"))));
interpolation = memnew(OptionButton);
top_hb->add_child(interpolation);
interpolation->connect("item_selected", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed));
edit_hb = memnew(HBoxContainer); edit_hb = memnew(HBoxContainer);
top_hb->add_child(edit_hb); top_hb->add_child(edit_hb);
edit_hb->add_child(memnew(VSeparator)); edit_hb->add_child(memnew(VSeparator));

View File

@ -41,6 +41,7 @@
#include "scene/gui/tree.h" #include "scene/gui/tree.h"
class CheckBox; class CheckBox;
class OptionButton;
class PanelContainer; class PanelContainer;
class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
@ -66,6 +67,7 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
SpinBox *min_value = nullptr; SpinBox *min_value = nullptr;
CheckBox *sync = nullptr; CheckBox *sync = nullptr;
OptionButton *interpolation = nullptr;
HBoxContainer *edit_hb = nullptr; HBoxContainer *edit_hb = nullptr;
SpinBox *edit_value = nullptr; SpinBox *edit_value = nullptr;

View File

@ -30,12 +30,20 @@
#include "animation_blend_space_1d.h" #include "animation_blend_space_1d.h"
#include "animation_blend_tree.h"
void AnimationNodeBlendSpace1D::get_parameter_list(List<PropertyInfo> *r_list) const { void AnimationNodeBlendSpace1D::get_parameter_list(List<PropertyInfo> *r_list) const {
r_list->push_back(PropertyInfo(Variant::FLOAT, blend_position)); r_list->push_back(PropertyInfo(Variant::FLOAT, blend_position));
r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, length_internal, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
} }
Variant AnimationNodeBlendSpace1D::get_parameter_default_value(const StringName &p_parameter) const { Variant AnimationNodeBlendSpace1D::get_parameter_default_value(const StringName &p_parameter) const {
if (p_parameter == closest) {
return -1;
} else {
return 0; return 0;
}
} }
Ref<AnimationNode> AnimationNodeBlendSpace1D::get_child_by_name(const StringName &p_name) { Ref<AnimationNode> AnimationNodeBlendSpace1D::get_child_by_name(const StringName &p_name) {
@ -77,6 +85,9 @@ void AnimationNodeBlendSpace1D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_value_label", "text"), &AnimationNodeBlendSpace1D::set_value_label); 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("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("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("is_using_sync"), &AnimationNodeBlendSpace1D::is_using_sync);
@ -91,7 +102,12 @@ void AnimationNodeBlendSpace1D::_bind_methods() {
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, "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::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::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"); 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) { void AnimationNodeBlendSpace1D::get_child_nodes(List<ChildNode> *r_child_nodes) {
@ -214,6 +230,14 @@ String AnimationNodeBlendSpace1D::get_value_label() const {
return value_label; 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) { void AnimationNodeBlendSpace1D::set_use_sync(bool p_sync) {
sync = p_sync; sync = p_sync;
} }
@ -241,7 +265,11 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_is_
} }
double blend_pos = get_parameter(blend_position); double blend_pos = get_parameter(blend_position);
int cur_closest = get_parameter(closest);
double cur_length_internal = get_parameter(length_internal);
double max_time_remaining = 0.0;
if (blend_mode == BLEND_MODE_INTERPOLATED) {
float weights[MAX_BLEND_POINTS] = {}; float weights[MAX_BLEND_POINTS] = {};
int point_lower = -1; int point_lower = -1;
@ -303,8 +331,6 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_is_
// actually blend the animations now // actually blend the animations now
double max_time_remaining = 0.0;
for (int i = 0; i < blend_points_used; i++) { for (int i = 0; i < blend_points_used; i++) {
if (i == point_lower || i == point_higher) { if (i == point_lower || i == point_higher) {
double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, weights[i], FILTER_IGNORE, true); double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, weights[i], FILTER_IGNORE, true);
@ -313,7 +339,51 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_is_
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true); blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true);
} }
} }
} else {
int new_closest = -1;
double new_closest_dist = 1e20;
for (int i = 0; i < blend_points_used; i++) {
double d = 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) {
double from = 0.0;
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) {
//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_null() && !na_n.is_null()) {
na_n->set_backward(na_c->is_backward());
}
//see how much animation remains
from = cur_length_internal - blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, false, p_is_external_seeking, 0.0, FILTER_IGNORE, true);
}
max_time_remaining = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true);
cur_length_internal = from + max_time_remaining;
cur_closest = new_closest;
} else {
max_time_remaining = blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true);
}
if (sync) {
for (int i = 0; i < blend_points_used; i++) {
if (i != cur_closest) {
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true);
}
}
}
}
set_parameter(this->closest, cur_closest);
set_parameter(this->length_internal, cur_length_internal);
return max_time_remaining; return max_time_remaining;
} }

View File

@ -36,6 +36,14 @@
class AnimationNodeBlendSpace1D : public AnimationRootNode { class AnimationNodeBlendSpace1D : public AnimationRootNode {
GDCLASS(AnimationNodeBlendSpace1D, AnimationRootNode); GDCLASS(AnimationNodeBlendSpace1D, AnimationRootNode);
public:
enum BlendMode {
BLEND_MODE_INTERPOLATED,
BLEND_MODE_DISCRETE,
BLEND_MODE_DISCRETE_CARRY,
};
protected:
enum { enum {
MAX_BLEND_POINTS = 64 MAX_BLEND_POINTS = 64
}; };
@ -61,6 +69,10 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode {
void _tree_changed(); void _tree_changed();
StringName blend_position = "blend_position"; StringName blend_position = "blend_position";
StringName closest = "closest";
StringName length_internal = "length_internal";
BlendMode blend_mode = BLEND_MODE_INTERPOLATED;
protected: protected:
bool sync = false; bool sync = false;
@ -95,6 +107,9 @@ public:
void set_value_label(const String &p_label); void set_value_label(const String &p_label);
String get_value_label() const; 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); void set_use_sync(bool p_sync);
bool is_using_sync() const; bool is_using_sync() const;
@ -107,4 +122,6 @@ public:
~AnimationNodeBlendSpace1D(); ~AnimationNodeBlendSpace1D();
}; };
VARIANT_ENUM_CAST(AnimationNodeBlendSpace1D::BlendMode)
#endif // ANIMATION_BLEND_SPACE_1D_H #endif // ANIMATION_BLEND_SPACE_1D_H