diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 50a625ddc18..6b0da509279 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -43,7 +43,6 @@ CurveEditor::CurveEditor() { _tangents_length = 40; _dragging = false; _has_undo_data = false; - _world_rect = Rect2(0, 0, 1, 1); set_focus_mode(FOCUS_ALL); set_clip_contents(true); @@ -70,11 +69,15 @@ void CurveEditor::set_curve(Ref curve) { return; if (_curve_ref.is_valid()) { - _curve_ref->disconnect("changed", this, "_curve_changed"); + _curve_ref->disconnect(CoreStringNames::get_singleton()->changed, this, "_curve_changed"); + _curve_ref->disconnect(Curve::SIGNAL_RANGE_CHANGED, this, "_curve_changed"); } + _curve_ref = curve; + if (_curve_ref.is_valid()) { - _curve_ref->connect("changed", this, "_curve_changed"); + _curve_ref->connect(CoreStringNames::get_singleton()->changed, this, "_curve_changed"); + _curve_ref->connect(Curve::SIGNAL_RANGE_CHANGED, this, "_curve_changed"); } _selected_point = -1; @@ -144,11 +147,13 @@ void CurveEditor::on_gui_input(const Ref &p_event) { Vector2 mpos = mm.get_position(); if (_dragging && _curve_ref.is_valid()) { + Curve &curve = **_curve_ref; + if (_selected_point != -1) { if (!_has_undo_data) { // Save curve state before dragging points - _undo_data = _curve_ref->get_data(); + _undo_data = curve.get_data(); _has_undo_data = true; } @@ -157,26 +162,25 @@ void CurveEditor::on_gui_input(const Ref &p_event) { Vector2 point_pos = get_world_pos(mpos); - int i = _curve_ref->set_point_offset(_selected_point, point_pos.x); + int i = curve.set_point_offset(_selected_point, point_pos.x); // The index may change if the point is dragged across another one set_hover_point_index(i); set_selected_point(i); - // TODO Get rid of this clamp if zoom is implemented in this editor. // This is to prevent the user from loosing a point out of view. - if (point_pos.y < 0.0) - point_pos.y = 0.0; - else if (point_pos.y > 1.0) - point_pos.y = 1.0; + if (point_pos.y < curve.get_min_value()) + point_pos.y = curve.get_min_value(); + else if (point_pos.y > curve.get_max_value()) + point_pos.y = curve.get_max_value(); - _curve_ref->set_point_value(_selected_point, point_pos.y); + curve.set_point_value(_selected_point, point_pos.y); //auto_calculate_tangents(i); } else { // Drag tangent - Vector2 point_pos = _curve_ref->get_point_pos(_selected_point); + Vector2 point_pos = curve.get_point_pos(_selected_point); Vector2 control_pos = get_world_pos(mpos); Vector2 dir = (control_pos - point_pos).normalized(); @@ -190,13 +194,17 @@ void CurveEditor::on_gui_input(const Ref &p_event) { bool link = !Input::get_singleton()->is_key_pressed(KEY_SHIFT); if (_selected_tangent == TANGENT_LEFT) { - _curve_ref->set_point_left_tangent(_selected_point, tangent); - if (link && _selected_point != _curve_ref->get_point_count() - 1) - _curve_ref->set_point_right_tangent(_selected_point, tangent); + curve.set_point_left_tangent(_selected_point, tangent); + + // Note: if a tangent is set to linear, it shouldn't be linked to the other + if (link && _selected_point != curve.get_point_count() - 1 && !curve.get_point_right_mode(_selected_point) != Curve::TANGENT_FREE) + curve.set_point_right_tangent(_selected_point, tangent); + } else { - _curve_ref->set_point_right_tangent(_selected_point, tangent); - if (link && _selected_point != 0) - _curve_ref->set_point_left_tangent(_selected_point, tangent); + curve.set_point_right_tangent(_selected_point, tangent); + + if (link && _selected_point != 0 && !curve.get_point_left_mode(_selected_point) != Curve::TANGENT_FREE) + curve.set_point_left_tangent(_selected_point, tangent); } } } @@ -230,25 +238,31 @@ void CurveEditor::on_preset_item_selected(int preset_id) { case PRESET_FLAT0: curve.add_point(Vector2(0, 0)); curve.add_point(Vector2(1, 0)); + curve.set_point_right_mode(0, Curve::TANGENT_LINEAR); + curve.set_point_left_mode(1, Curve::TANGENT_LINEAR); break; case PRESET_FLAT1: curve.add_point(Vector2(0, 1)); curve.add_point(Vector2(1, 1)); + curve.set_point_right_mode(0, Curve::TANGENT_LINEAR); + curve.set_point_left_mode(1, Curve::TANGENT_LINEAR); break; case PRESET_LINEAR: - curve.add_point(Vector2(0, 0), 0, 1); - curve.add_point(Vector2(1, 1), 1, 0); + curve.add_point(Vector2(0, 0)); + curve.add_point(Vector2(1, 1)); + curve.set_point_right_mode(0, Curve::TANGENT_LINEAR); + curve.set_point_left_mode(1, Curve::TANGENT_LINEAR); break; case PRESET_EASE_IN: curve.add_point(Vector2(0, 0)); - curve.add_point(Vector2(1, 1), 1.4, 0); + curve.add_point(Vector2(1, 1), (curve.get_max_value() - curve.get_min_value()) * 1.4, 0); break; case PRESET_EASE_OUT: - curve.add_point(Vector2(0, 0), 0, 1.4); + curve.add_point(Vector2(0, 0), 0, (curve.get_max_value() - curve.get_min_value()) * 1.4); curve.add_point(Vector2(1, 1)); break; @@ -281,6 +295,18 @@ void CurveEditor::on_context_menu_item_selected(int action_id) { case CONTEXT_REMOVE_POINT: remove_point(_selected_point); break; + + case CONTEXT_LINEAR: + toggle_linear(); + break; + + case CONTEXT_LEFT_LINEAR: + toggle_linear(TANGENT_LEFT); + break; + + case CONTEXT_RIGHT_LINEAR: + toggle_linear(TANGENT_RIGHT); + break; } } @@ -291,9 +317,37 @@ void CurveEditor::open_context_menu(Vector2 pos) { if (_curve_ref.is_valid()) { _context_menu->add_item(TTR("Add point"), CONTEXT_ADD_POINT); + if (_selected_point >= 0) { _context_menu->add_item(TTR("Remove point"), CONTEXT_REMOVE_POINT); + + if (_selected_tangent != TANGENT_NONE) { + _context_menu->add_separator(); + + _context_menu->add_check_item(TTR("Linear"), CONTEXT_LINEAR); + + bool is_linear = _selected_tangent == TANGENT_LEFT ? + _curve_ref->get_point_left_mode(_selected_point) == Curve::TANGENT_LINEAR : + _curve_ref->get_point_right_mode(_selected_point) == Curve::TANGENT_LINEAR; + + _context_menu->set_item_checked(CONTEXT_LINEAR, is_linear); + + } else { + _context_menu->add_separator(); + + if (_selected_point > 0) { + _context_menu->add_check_item(TTR("Left linear"), CONTEXT_LEFT_LINEAR); + _context_menu->set_item_checked(CONTEXT_LEFT_LINEAR, + _curve_ref->get_point_left_mode(_selected_point) == Curve::TANGENT_LINEAR); + } + if (_selected_point + 1 < _curve_ref->get_point_count()) { + _context_menu->add_check_item(TTR("Right linear"), CONTEXT_RIGHT_LINEAR); + _context_menu->set_item_checked(CONTEXT_RIGHT_LINEAR, + _curve_ref->get_point_right_mode(_selected_point) == Curve::TANGENT_LINEAR); + } + } } + _context_menu->add_separator(); } @@ -319,7 +373,7 @@ int CurveEditor::get_point_at(Vector2 pos) const { return -1; } -int CurveEditor::get_tangent_at(Vector2 pos) const { +CurveEditor::TangentIndex CurveEditor::get_tangent_at(Vector2 pos) const { if (_curve_ref.is_null() || _selected_point < 0) return TANGENT_NONE; @@ -369,6 +423,25 @@ void CurveEditor::remove_point(int index) { push_undo(prev_data); } +void CurveEditor::toggle_linear(TangentIndex tangent) { + ERR_FAIL_COND(_curve_ref.is_null()); + + Array prev_data = _curve_ref->get_data(); + + if (tangent == TANGENT_NONE) + tangent = _selected_tangent; + + if (tangent == TANGENT_LEFT) { + bool is_linear = _curve_ref->get_point_left_mode(_selected_point) == Curve::TANGENT_LINEAR; + _curve_ref->set_point_left_mode(_selected_point, is_linear ? Curve::TANGENT_FREE : Curve::TANGENT_LINEAR); + } else { + bool is_linear = _curve_ref->get_point_right_mode(_selected_point) == Curve::TANGENT_LINEAR; + _curve_ref->set_point_right_mode(_selected_point, is_linear ? Curve::TANGENT_FREE : Curve::TANGENT_LINEAR); + } + + push_undo(prev_data); +} + void CurveEditor::set_selected_point(int index) { if (index != _selected_point) { _selected_point = index; @@ -401,14 +474,23 @@ void CurveEditor::update_view_transform() { Vector2 control_size = get_size(); const real_t margin = 24; - _world_rect = Rect2(Curve::MIN_X, 0, Curve::MAX_X, 1); + float min_y = 0; + float max_y = 1; + + if (_curve_ref.is_valid()) { + min_y = _curve_ref->get_min_value(); + max_y = _curve_ref->get_max_value(); + } + + Rect2 world_rect = Rect2(Curve::MIN_X, min_y, Curve::MAX_X, max_y - min_y); Vector2 wm = Vector2(margin, margin) / control_size; - _world_rect.position -= wm; - _world_rect.size += 2.0 * wm; + wm.y *= (max_y - min_y); + world_rect.position -= wm; + world_rect.size += 2.0 * wm; _world_to_view = Transform2D(); - _world_to_view.translate(-_world_rect.position - Vector2(0, _world_rect.size.y)); - _world_to_view.scale(Vector2(control_size.x, -control_size.y) / _world_rect.size); + _world_to_view.translate(-world_rect.position - Vector2(0, world_rect.size.y)); + _world_to_view.scale(Vector2(control_size.x, -control_size.y) / world_rect.size); } Vector2 CurveEditor::get_tangent_view_pos(int i, TangentIndex tangent) const { @@ -509,17 +591,18 @@ void CurveEditor::_draw() { const Color grid_color0(0, 0, 0, 0.5); const Color grid_color1(0, 0, 0, 0.15); - draw_line(Vector2(min_edge.x, 0), Vector2(max_edge.x, 0), grid_color0); + draw_line(Vector2(min_edge.x, curve.get_min_value()), Vector2(max_edge.x, curve.get_min_value()), grid_color0); + draw_line(Vector2(max_edge.x, curve.get_max_value()), Vector2(min_edge.x, curve.get_max_value()), grid_color0); draw_line(Vector2(0, min_edge.y), Vector2(0, max_edge.y), grid_color0); draw_line(Vector2(1, max_edge.y), Vector2(1, min_edge.y), grid_color0); - draw_line(Vector2(max_edge.x, 1), Vector2(min_edge.x, 1), grid_color0); - const Vector2 grid_step(0.25, 0.5); + float curve_height = (curve.get_max_value() - curve.get_min_value()); + const Vector2 grid_step(0.25, 0.5 * curve_height); for (real_t x = 0; x < 1.0; x += grid_step.x) { draw_line(Vector2(x, min_edge.y), Vector2(x, max_edge.y), grid_color1); } - for (real_t y = 0; y < 1.0; y += grid_step.y) { + for (real_t y = curve.get_min_value(); y < curve.get_max_value(); y += grid_step.y) { draw_line(Vector2(min_edge.x, y), Vector2(max_edge.x, y), grid_color1); } @@ -528,17 +611,30 @@ void CurveEditor::_draw() { draw_set_transform_matrix(Transform2D()); Ref font = get_font("font", "Label"); + float font_height = font->get_height(); const Color text_color(1, 1, 1, 0.3); - draw_string(font, get_view_pos(Vector2(0, 0)), "0.0", text_color); + { + // X axis + float y = curve.get_min_value(); + Vector2 off(0, font_height - 1); + draw_string(font, get_view_pos(Vector2(0, y)) + off, "0.0", text_color); + draw_string(font, get_view_pos(Vector2(0.25, y)) + off, "0.25", text_color); + draw_string(font, get_view_pos(Vector2(0.5, y)) + off, "0.5", text_color); + draw_string(font, get_view_pos(Vector2(0.75, y)) + off, "0.75", text_color); + draw_string(font, get_view_pos(Vector2(1, y)) + off, "1.0", text_color); + } - draw_string(font, get_view_pos(Vector2(0.25, 0)), "0.25", text_color); - draw_string(font, get_view_pos(Vector2(0.5, 0)), "0.5", text_color); - draw_string(font, get_view_pos(Vector2(0.75, 0)), "0.75", text_color); - draw_string(font, get_view_pos(Vector2(1, 0)), "1.0", text_color); - - draw_string(font, get_view_pos(Vector2(0, 0.5)), "0.5", text_color); - draw_string(font, get_view_pos(Vector2(0, 1)), "1.0", text_color); + { + // Y axis + float m0 = curve.get_min_value(); + float m1 = 0.5 * (curve.get_min_value() + curve.get_max_value()); + float m2 = curve.get_max_value(); + Vector2 off(1, -1); + draw_string(font, get_view_pos(Vector2(0, m0)) + off, String::num(m0, 2), text_color); + draw_string(font, get_view_pos(Vector2(0, m1)) + off, String::num(m1, 2), text_color); + draw_string(font, get_view_pos(Vector2(0, m2)) + off, String::num(m2, 3), text_color); + } // Draw tangents for current point @@ -611,6 +707,12 @@ void CurveEditor::_draw() { Vector2 pos = curve.get_point_pos(_hover_point); stroke_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(_hover_radius), hover_color); } + + // Help text + + if (_selected_point > 0 && _selected_point + 1 < curve.get_point_count()) { + draw_string(font, Vector2(50, font_height), TTR("Hold Shift to edit tangents individually"), text_color); + } } // TODO That should be part of the drawing API... diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h index 0ed4ee3517b..39d9b84d889 100644 --- a/editor/plugins/curve_editor_plugin.h +++ b/editor/plugins/curve_editor_plugin.h @@ -57,7 +57,10 @@ public: enum ContextAction { CONTEXT_ADD_POINT = 0, - CONTEXT_REMOVE_POINT + CONTEXT_REMOVE_POINT, + CONTEXT_LINEAR, + CONTEXT_LEFT_LINEAR, + CONTEXT_RIGHT_LINEAR }; enum TangentIndex { @@ -79,9 +82,10 @@ private: void open_context_menu(Vector2 pos); int get_point_at(Vector2 pos) const; - int get_tangent_at(Vector2 pos) const; + TangentIndex get_tangent_at(Vector2 pos) const; void add_point(Vector2 pos); void remove_point(int index); + void toggle_linear(TangentIndex tangent = TANGENT_NONE); void set_selected_point(int index); void set_hover_point_index(int index); void push_undo(Array previous_curve_data); @@ -96,7 +100,6 @@ private: void stroke_rect(Rect2 rect, Color color); private: - Rect2 _world_rect; Transform2D _world_to_view; Ref _curve_ref; @@ -110,7 +113,7 @@ private: Vector2 _context_click_pos; int _selected_point; int _hover_point; - int _selected_tangent; + TangentIndex _selected_tangent; bool _dragging; // Constant diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp index c291aa33ed7..edf97bc2481 100644 --- a/scene/3d/particles.cpp +++ b/scene/3d/particles.cpp @@ -1052,16 +1052,11 @@ float ParticlesMaterial::get_param_randomness(Parameter p_param) const { static void _adjust_curve_range(const Ref &p_texture, float p_min, float p_max) { - Ref curve = p_texture; - if (!curve.is_valid()) + Ref curve_tex = p_texture; + if (!curve_tex.is_valid()) return; - if (curve->get_max() == 1.0) { - curve->set_max(p_max); - } - if (curve->get_min() == 0.0) { - curve->set_min(p_min); - } + curve_tex->ensure_default_setup(p_min, p_max); } void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref &p_texture) { diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 3957923c6af..0cd41f557a5 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -380,9 +380,13 @@ Curve2D::Curve2D() #endif +const char *Curve::SIGNAL_RANGE_CHANGED = "range_changed"; + Curve::Curve() { _bake_resolution = 100; _baked_cache_dirty = false; + _min_value = 0; + _max_value = 1; #ifdef TOOLS_ENABLED _disable_set_data = false; #endif @@ -496,12 +500,38 @@ void Curve::clean_dupes() { void Curve::set_point_left_tangent(int i, real_t tangent) { ERR_FAIL_INDEX(i, _points.size()); _points[i].left_tangent = tangent; + _points[i].left_mode = TANGENT_FREE; mark_dirty(); } void Curve::set_point_right_tangent(int i, real_t tangent) { ERR_FAIL_INDEX(i, _points.size()); _points[i].right_tangent = tangent; + _points[i].right_mode = TANGENT_FREE; + mark_dirty(); +} + +void Curve::set_point_left_mode(int i, TangentMode p_mode) { + ERR_FAIL_INDEX(i, _points.size()); + _points[i].left_mode = p_mode; + if (i > 0) { + if (p_mode == TANGENT_LINEAR) { + Vector2 v = (_points[i - 1].pos - _points[i].pos).normalized(); + _points[i].left_tangent = v.y / v.x; + } + } + mark_dirty(); +} + +void Curve::set_point_right_mode(int i, TangentMode p_mode) { + ERR_FAIL_INDEX(i, _points.size()); + _points[i].right_mode = p_mode; + if (i + 1 < _points.size()) { + if (p_mode == TANGENT_LINEAR) { + Vector2 v = (_points[i + 1].pos - _points[i].pos).normalized(); + _points[i].right_tangent = v.y / v.x; + } + } mark_dirty(); } @@ -515,6 +545,16 @@ real_t Curve::get_point_right_tangent(int i) const { return _points[i].right_tangent; } +Curve::TangentMode Curve::get_point_left_mode(int i) const { + ERR_FAIL_INDEX_V(i, _points.size(), TANGENT_FREE); + return _points[i].left_mode; +} + +Curve::TangentMode Curve::get_point_right_mode(int i) const { + ERR_FAIL_INDEX_V(i, _points.size(), TANGENT_FREE); + return _points[i].right_mode; +} + void Curve::remove_point(int p_index) { ERR_FAIL_INDEX(p_index, _points.size()); _points.remove(p_index); @@ -529,6 +569,7 @@ void Curve::clear_points() { void Curve::set_point_value(int p_index, real_t pos) { ERR_FAIL_INDEX(p_index, _points.size()); _points[p_index].pos.y = pos; + update_auto_tangents(p_index); mark_dirty(); } @@ -539,6 +580,11 @@ int Curve::set_point_offset(int p_index, float offset) { int i = add_point(Vector2(offset, p.pos.y)); _points[i].left_tangent = p.left_tangent; _points[i].right_tangent = p.right_tangent; + _points[i].left_mode = p.left_mode; + _points[i].right_mode = p.right_mode; + if (p_index != i) + update_auto_tangents(p_index); + update_auto_tangents(i); return i; } @@ -547,6 +593,53 @@ Vector2 Curve::get_point_pos(int p_index) const { return _points[p_index].pos; } +void Curve::update_auto_tangents(int i) { + + Point &p = _points[i]; + + if (i > 0) { + if (p.left_mode == TANGENT_LINEAR) { + Vector2 v = (_points[i - 1].pos - p.pos).normalized(); + p.left_tangent = v.y / v.x; + } + if (_points[i - 1].right_mode == TANGENT_LINEAR) { + Vector2 v = (_points[i - 1].pos - p.pos).normalized(); + _points[i - 1].right_tangent = v.y / v.x; + } + } + + if (i + 1 < _points.size()) { + if (p.right_mode == TANGENT_LINEAR && i + 1 < _points.size()) { + Vector2 v = (_points[i + 1].pos - p.pos).normalized(); + p.right_tangent = v.y / v.x; + } + if (_points[i + 1].left_mode == TANGENT_LINEAR) { + Vector2 v = (_points[i + 1].pos - p.pos).normalized(); + _points[i + 1].left_tangent = v.y / v.x; + } + } +} + +#define MIN_Y_RANGE 0.01 + +void Curve::set_min_value(float p_min) { + if (p_min > _max_value - MIN_Y_RANGE) + _min_value = _max_value - MIN_Y_RANGE; + else + _min_value = p_min; + // Note: min and max are indicative values, + // it's still possible that existing points are out of range at this point. + emit_signal(SIGNAL_RANGE_CHANGED); +} + +void Curve::set_max_value(float p_max) { + if (p_max < _min_value + MIN_Y_RANGE) + _max_value = _min_value + MIN_Y_RANGE; + else + _max_value = p_max; + emit_signal(SIGNAL_RANGE_CHANGED); +} + real_t Curve::interpolate(real_t offset) const { if (_points.size() == 0) return 0; @@ -636,6 +729,14 @@ void Curve::set_data(Array input) { ERR_FAIL_COND(input[i].get_type() != Variant::VECTOR2); ERR_FAIL_COND(input[i + 1].get_type() != Variant::REAL); ERR_FAIL_COND(input[i + 2].get_type() != Variant::REAL); + + ERR_FAIL_COND(input[i + 3].get_type() != Variant::INT); + int left_mode = input[i + 3]; + ERR_FAIL_COND(left_mode < 0 || left_mode >= TANGENT_MODE_COUNT); + + ERR_FAIL_COND(input[i + 4].get_type() != Variant::INT); + int right_mode = input[i + 4]; + ERR_FAIL_COND(right_mode < 0 || right_mode >= TANGENT_MODE_COUNT); } _points.resize(input.size() / 3); @@ -648,6 +749,11 @@ void Curve::set_data(Array input) { p.pos = input[i]; p.left_tangent = input[i + 1]; p.right_tangent = input[i + 2]; + // TODO For some reason the compiler won't convert from Variant to enum + int left_mode = input[i + 3]; + int right_mode = input[i + 4]; + p.left_mode = (TangentMode)left_mode; + p.right_mode = (TangentMode)right_mode; } mark_dirty(); @@ -726,8 +832,16 @@ void Curve::_bind_methods() { ClassDB::bind_method(D_METHOD("interpolate_baked", "offset"), &Curve::interpolate_baked); ClassDB::bind_method(D_METHOD("get_point_left_tangent", "index"), &Curve::get_point_left_tangent); ClassDB::bind_method(D_METHOD("get_point_right_tangent", "index"), &Curve::get_point_left_tangent); + ClassDB::bind_method(D_METHOD("get_point_left_mode", "index"), &Curve::get_point_left_mode); + ClassDB::bind_method(D_METHOD("get_point_right_mode", "index"), &Curve::get_point_left_mode); ClassDB::bind_method(D_METHOD("set_point_left_tangent", "index", "tangent"), &Curve::set_point_left_tangent); ClassDB::bind_method(D_METHOD("set_point_right_tangent", "index", "tangent"), &Curve::set_point_left_tangent); + ClassDB::bind_method(D_METHOD("set_point_left_mode", "index", "mode"), &Curve::set_point_left_mode); + ClassDB::bind_method(D_METHOD("set_point_right_mode", "index", "mode"), &Curve::set_point_left_mode); + ClassDB::bind_method(D_METHOD("get_min_value"), &Curve::get_min_value); + ClassDB::bind_method(D_METHOD("set_min_value", "min"), &Curve::set_min_value); + ClassDB::bind_method(D_METHOD("get_max_value"), &Curve::get_max_value); + ClassDB::bind_method(D_METHOD("set_max_value", "max"), &Curve::set_max_value); ClassDB::bind_method(D_METHOD("clean_dupes"), &Curve::clean_dupes); ClassDB::bind_method(D_METHOD("bake"), &Curve::bake); ClassDB::bind_method(D_METHOD("get_bake_resolution"), &Curve::get_bake_resolution); @@ -735,8 +849,12 @@ void Curve::_bind_methods() { ClassDB::bind_method(D_METHOD("_get_data"), &Curve::get_data); ClassDB::bind_method(D_METHOD("_set_data", "data"), &Curve::set_data); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_min_value", "get_min_value"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_max_value", "get_max_value"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_resolution", PROPERTY_HINT_RANGE, "1,1000,1"), "set_bake_resolution", "get_bake_resolution"); ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data"); + + ADD_SIGNAL(MethodInfo(SIGNAL_RANGE_CHANGED)); } int Curve2D::get_point_count() const { diff --git a/scene/resources/curve.h b/scene/resources/curve.h index 63b9a07f07e..d45f9e202f7 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -89,24 +89,38 @@ public: static const int MIN_X = 0.f; static const int MAX_X = 1.f; + static const char *SIGNAL_RANGE_CHANGED; + #ifdef TOOLS_ENABLED bool _disable_set_data; #endif + enum TangentMode { + TANGENT_FREE = 0, + TANGENT_LINEAR, + TANGENT_MODE_COUNT + }; + struct Point { Vector2 pos; real_t left_tangent; real_t right_tangent; + TangentMode left_mode; + TangentMode right_mode; Point() { left_tangent = 0; right_tangent = 0; + left_mode = TANGENT_FREE; + right_mode = TANGENT_FREE; } Point(Vector2 p, real_t left = 0, real_t right = 0) { pos = p; left_tangent = left; right_tangent = right; + left_mode = TANGENT_FREE; + right_mode = TANGENT_FREE; } }; @@ -124,6 +138,12 @@ public: int set_point_offset(int p_index, float offset); Vector2 get_point_pos(int p_index) const; + float get_min_value() const { return _min_value; } + void set_min_value(float p_min); + + float get_max_value() const { return _max_value; } + void set_max_value(float p_max); + real_t interpolate(real_t offset) const; real_t interpolate_local_nocheck(int index, real_t local_offset) const; @@ -131,8 +151,15 @@ public: void set_point_left_tangent(int i, real_t tangent); void set_point_right_tangent(int i, real_t tangent); + void set_point_left_mode(int i, TangentMode p_mode); + void set_point_right_mode(int i, TangentMode p_mode); + real_t get_point_left_tangent(int i) const; real_t get_point_right_tangent(int i) const; + TangentMode get_point_left_mode(int i) const; + TangentMode get_point_right_mode(int i) const; + + void update_auto_tangents(int i); Array get_data() const; void set_data(Array input); @@ -152,8 +179,12 @@ private: bool _baked_cache_dirty; Vector _baked_cache; int _bake_resolution; + float _min_value; + float _max_value; }; +VARIANT_ENUM_CAST(Curve::TangentMode) + class Curve2D : public Resource { GDCLASS(Curve2D, Resource); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 0bd8c41228e..6c7ae2445cb 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -1370,12 +1370,6 @@ CubeMap::~CubeMap() { void CurveTexture::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_max", "max"), &CurveTexture::set_max); - ClassDB::bind_method(D_METHOD("get_max"), &CurveTexture::get_max); - - ClassDB::bind_method(D_METHOD("set_min", "min"), &CurveTexture::set_min); - ClassDB::bind_method(D_METHOD("get_min"), &CurveTexture::get_min); - ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveTexture::set_width); ClassDB::bind_method(D_METHOD("set_curve", "curve:Curve"), &CurveTexture::set_curve); @@ -1383,52 +1377,31 @@ void CurveTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("_update"), &CurveTexture::_update); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "min", PROPERTY_HINT_RANGE, "-1024,1024"), "set_min", "get_min"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "max", PROPERTY_HINT_RANGE, "-1024,1024"), "set_max", "get_max"); ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "32,4096"), "set_width", "get_width"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve"); } -void CurveTexture::set_max(float p_max) { - _max = p_max; - emit_changed(); -} -float CurveTexture::get_max() const { - - return _max; -} - -void CurveTexture::set_min(float p_min) { - - _min = p_min; - emit_changed(); -} -float CurveTexture::get_min() const { - - return _min; -} void CurveTexture::set_width(int p_width) { ERR_FAIL_COND(p_width < 32 || p_width > 4096); _width = p_width; _update(); } + int CurveTexture::get_width() const { return _width; } -void CurveTexture::ensure_default_setup() { - +void CurveTexture::ensure_default_setup(float p_min, float p_max) { if (_curve.is_null()) { Ref curve = Ref(memnew(Curve)); curve->add_point(Vector2(0, 1)); curve->add_point(Vector2(1, 1)); + curve->set_min_value(p_min); + curve->set_max_value(p_max); set_curve(curve); - } - - if (get_min() == 0 && get_max() == 1) { - set_max(32); + // Min and max is 0..1 by default } } @@ -1457,11 +1430,9 @@ void CurveTexture::_update() { if (_curve.is_valid()) { Curve &curve = **_curve; - float height = _max - _min; for (int i = 0; i < _width; ++i) { float t = i / static_cast(_width); - float v = curve.interpolate_baked(t); - wd[i] = CLAMP(_min + v * height, _min, _max); + wd[i] = curve.interpolate_baked(t); } } else { @@ -1490,9 +1461,6 @@ RID CurveTexture::get_rid() const { } CurveTexture::CurveTexture() { - - _max = 1; - _min = 0; _width = 2048; _texture = VS::get_singleton()->texture_create(); } diff --git a/scene/resources/texture.h b/scene/resources/texture.h index ff5a58c221f..9bbbd1d091b 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -403,7 +403,6 @@ class CurveTexture : public Texture { private: RID _texture; Ref _curve; - float _min, _max; int _width; void _update(); @@ -412,16 +411,10 @@ protected: static void _bind_methods(); public: - void set_max(float p_max); - float get_max() const; - - void set_min(float p_min); - float get_min() const; - void set_width(int p_width); int get_width() const; - void ensure_default_setup(); + void ensure_default_setup(float p_min=0, float p_max=1); void set_curve(Ref p_curve); Ref get_curve() const;