Add Discrete and Carry blend modes for BlendSpace2D, allows to fix #20135
This commit is contained in:
parent
03bd4d28a5
commit
9018e8b132
4 changed files with 164 additions and 66 deletions
|
@ -607,6 +607,8 @@ void AnimationNodeBlendSpace2DEditor::_update_space() {
|
|||
|
||||
auto_triangles->set_pressed(blend_space->get_auto_triangles());
|
||||
|
||||
interpolation->select(blend_space->get_blend_mode());
|
||||
|
||||
max_x_value->set_value(blend_space->get_max_space().x);
|
||||
max_y_value->set_value(blend_space->get_max_space().y);
|
||||
|
||||
|
@ -636,6 +638,8 @@ void AnimationNodeBlendSpace2DEditor::_config_changed(double) {
|
|||
undo_redo->add_undo_method(blend_space.ptr(), "set_min_space", blend_space->get_min_space());
|
||||
undo_redo->add_do_method(blend_space.ptr(), "set_snap", Vector2(snap_x->get_value(), snap_y->get_value()));
|
||||
undo_redo->add_undo_method(blend_space.ptr(), "set_snap", blend_space->get_snap());
|
||||
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_undo_method(this, "_update_space");
|
||||
undo_redo->commit_action();
|
||||
|
@ -752,6 +756,10 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) {
|
|||
snap->set_icon(get_icon("SnapGrid", "EditorIcons"));
|
||||
open_editor->set_icon(get_icon("Edit", "EditorIcons"));
|
||||
auto_triangles->set_icon(get_icon("AutoTriangle", "EditorIcons"));
|
||||
interpolation->clear();
|
||||
interpolation->add_icon_item(get_icon("TrackContinuous", "EditorIcons"), "", 0);
|
||||
interpolation->add_icon_item(get_icon("TrackDiscrete", "EditorIcons"), "", 1);
|
||||
interpolation->add_icon_item(get_icon("TrackCapture", "EditorIcons"), "", 2);
|
||||
}
|
||||
|
||||
if (p_what == NOTIFICATION_PROCESS) {
|
||||
|
@ -914,6 +922,13 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
|
|||
snap_y->set_step(0.01);
|
||||
snap_y->set_max(1000);
|
||||
|
||||
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", this, "_config_changed");
|
||||
|
||||
edit_hb = memnew(HBoxContainer);
|
||||
top_hb->add_child(edit_hb);
|
||||
edit_hb->add_child(memnew(VSeparator));
|
||||
|
|
|
@ -60,6 +60,7 @@ class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin {
|
|||
ToolButton *snap;
|
||||
SpinBox *snap_x;
|
||||
SpinBox *snap_y;
|
||||
OptionButton *interpolation;
|
||||
|
||||
ToolButton *auto_triangles;
|
||||
|
||||
|
|
|
@ -33,9 +33,17 @@
|
|||
|
||||
void AnimationNodeBlendSpace2D::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
r_list->push_back(PropertyInfo(Variant::VECTOR2, blend_position));
|
||||
r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", 0));
|
||||
r_list->push_back(PropertyInfo(Variant::REAL, length_internal, PROPERTY_HINT_NONE, "", 0));
|
||||
}
|
||||
Variant AnimationNodeBlendSpace2D::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
return Vector2();
|
||||
if (p_parameter == closest) {
|
||||
return -1;
|
||||
} else if (p_parameter == length_internal) {
|
||||
return 0;
|
||||
} else {
|
||||
return Vector2();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace2D::get_child_nodes(List<ChildNode> *r_child_nodes) {
|
||||
|
@ -412,84 +420,124 @@ float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) {
|
|||
_update_triangles();
|
||||
|
||||
Vector2 blend_pos = get_parameter(blend_position);
|
||||
int closest = get_parameter(this->closest);
|
||||
float length_internal = get_parameter(this->length_internal);
|
||||
float mind = 0; //time of min distance point
|
||||
|
||||
if (triangles.size() == 0)
|
||||
return 0;
|
||||
if (blend_mode == BLEND_MODE_INTERPOLATED) {
|
||||
|
||||
Vector2 best_point;
|
||||
bool first = true;
|
||||
int blend_triangle = -1;
|
||||
float blend_weights[3] = { 0, 0, 0 };
|
||||
if (triangles.size() == 0)
|
||||
return 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));
|
||||
}
|
||||
Vector2 best_point;
|
||||
bool first = true;
|
||||
int blend_triangle = -1;
|
||||
float blend_weights[3] = { 0, 0, 0 };
|
||||
|
||||
if (Geometry::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++) {
|
||||
Vector2 s[2] = {
|
||||
points[j],
|
||||
points[(j + 1) % 3]
|
||||
};
|
||||
Vector2 closest = Geometry::get_closest_point_to_segment_2d(blend_pos, s);
|
||||
if (first || closest.distance_to(blend_pos) < best_point.distance_to(blend_pos)) {
|
||||
best_point = closest;
|
||||
blend_triangle = i;
|
||||
first = false;
|
||||
float d = s[0].distance_to(s[1]);
|
||||
if (d == 0.0) {
|
||||
blend_weights[j] = 1.0;
|
||||
blend_weights[(j + 1) % 3] = 0.0;
|
||||
blend_weights[(j + 2) % 3] = 0.0;
|
||||
} else {
|
||||
float c = s[0].distance_to(closest) / d;
|
||||
|
||||
blend_weights[j] = 1.0 - c;
|
||||
blend_weights[(j + 1) % 3] = c;
|
||||
blend_weights[(j + 2) % 3] = 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(blend_triangle == -1, 0); //should never reach here
|
||||
if (Geometry::is_point_in_triangle(blend_pos, points[0], points[1], points[2])) {
|
||||
|
||||
int triangle_points[3];
|
||||
for (int j = 0; j < 3; j++) {
|
||||
triangle_points[j] = get_triangle_point(blend_triangle, j);
|
||||
}
|
||||
|
||||
first = true;
|
||||
float mind = 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
|
||||
float t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false);
|
||||
if (first || t < mind) {
|
||||
mind = t;
|
||||
first = false;
|
||||
}
|
||||
found = true;
|
||||
blend_triangle = i;
|
||||
_blend_triangle(blend_pos, points, blend_weights);
|
||||
break;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
Vector2 s[2] = {
|
||||
points[j],
|
||||
points[(j + 1) % 3]
|
||||
};
|
||||
Vector2 closest = Geometry::get_closest_point_to_segment_2d(blend_pos, s);
|
||||
if (first || closest.distance_to(blend_pos) < best_point.distance_to(blend_pos)) {
|
||||
best_point = closest;
|
||||
blend_triangle = i;
|
||||
first = false;
|
||||
float d = s[0].distance_to(s[1]);
|
||||
if (d == 0.0) {
|
||||
blend_weights[j] = 1.0;
|
||||
blend_weights[(j + 1) % 3] = 0.0;
|
||||
blend_weights[(j + 2) % 3] = 0.0;
|
||||
} else {
|
||||
float c = s[0].distance_to(closest) / d;
|
||||
|
||||
blend_weights[j] = 1.0 - c;
|
||||
blend_weights[(j + 1) % 3] = c;
|
||||
blend_weights[(j + 2) % 3] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
//ignore
|
||||
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false);
|
||||
ERR_FAIL_COND_V(blend_triangle == -1, 0); //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;
|
||||
|
||||
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
|
||||
float t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false);
|
||||
if (first || t < mind) {
|
||||
mind = t;
|
||||
first = false;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
//ignore
|
||||
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false);
|
||||
}
|
||||
}
|
||||
} 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 != closest) {
|
||||
|
||||
float from = 0;
|
||||
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && closest != -1) {
|
||||
//see how much animation remains
|
||||
from = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, true, 0.0, FILTER_IGNORE, false) - length_internal;
|
||||
}
|
||||
|
||||
mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, 1.0, FILTER_IGNORE, false) + from;
|
||||
length_internal = from + mind;
|
||||
|
||||
closest = new_closest;
|
||||
|
||||
} else {
|
||||
mind = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, p_seek, 1.0, FILTER_IGNORE, false);
|
||||
}
|
||||
}
|
||||
|
||||
set_parameter(this->closest, closest);
|
||||
set_parameter(this->length_internal, length_internal);
|
||||
return mind;
|
||||
}
|
||||
|
||||
|
@ -527,6 +575,14 @@ void AnimationNodeBlendSpace2D::_tree_changed() {
|
|||
emit_signal("tree_changed");
|
||||
}
|
||||
|
||||
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::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1));
|
||||
|
@ -565,6 +621,9 @@ void AnimationNodeBlendSpace2D::_bind_methods() {
|
|||
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("_tree_changed"), &AnimationNodeBlendSpace2D::_tree_changed);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_auto_triangles", "get_auto_triangles");
|
||||
|
@ -581,6 +640,11 @@ void AnimationNodeBlendSpace2D::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "x_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_x_label", "get_x_label");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "y_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_y_label", "get_y_label");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Interpolated,Discrete,Carry", PROPERTY_USAGE_NOEDITOR), "set_blend_mode", "get_blend_mode");
|
||||
|
||||
BIND_ENUM_CONSTANT(BLEND_MODE_INTERPOLATED);
|
||||
BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE);
|
||||
BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE_CARRY);
|
||||
}
|
||||
|
||||
AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() {
|
||||
|
@ -597,6 +661,9 @@ AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() {
|
|||
y_label = "y";
|
||||
trianges_dirty = false;
|
||||
blend_position = "blend_position";
|
||||
closest = "closest";
|
||||
length_internal = "length_internal";
|
||||
blend_mode = BLEND_MODE_INTERPOLATED;
|
||||
}
|
||||
|
||||
AnimationNodeBlendSpace2D::~AnimationNodeBlendSpace2D() {
|
||||
|
|
|
@ -35,7 +35,14 @@
|
|||
|
||||
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
|
||||
};
|
||||
|
@ -56,11 +63,14 @@ class AnimationNodeBlendSpace2D : public AnimationRootNode {
|
|||
Vector<BlendTriangle> triangles;
|
||||
|
||||
StringName blend_position;
|
||||
StringName closest;
|
||||
StringName length_internal;
|
||||
Vector2 max_space;
|
||||
Vector2 min_space;
|
||||
Vector2 snap;
|
||||
String x_label;
|
||||
String y_label;
|
||||
BlendMode blend_mode;
|
||||
|
||||
void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node);
|
||||
void _set_triangles(const Vector<int> &p_triangles);
|
||||
|
@ -122,10 +132,15 @@ public:
|
|||
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;
|
||||
|
||||
virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name);
|
||||
|
||||
AnimationNodeBlendSpace2D();
|
||||
~AnimationNodeBlendSpace2D();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationNodeBlendSpace2D::BlendMode)
|
||||
|
||||
#endif // ANIMATION_BLEND_SPACE_2D_H
|
||||
|
|
Loading…
Reference in a new issue