summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editor/editor_node.cpp2
-rw-r--r--editor/plugins/curve_editor_plugin.cpp935
-rw-r--r--editor/plugins/curve_editor_plugin.h128
-rw-r--r--editor/plugins/texture_editor_plugin.cpp12
-rw-r--r--scene/3d/particles.cpp22
-rw-r--r--scene/register_scene_types.cpp1
-rw-r--r--scene/resources/curve.cpp359
-rw-r--r--scene/resources/curve.h72
-rw-r--r--scene/resources/texture.cpp242
-rw-r--r--scene/resources/texture.h27
10 files changed, 1163 insertions, 637 deletions
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index b700072dbe..2b29e4b08a 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -6114,7 +6114,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(GradientEditorPlugin(this)));
add_editor_plugin(memnew(GradientTextureEditorPlugin(this)));
add_editor_plugin(memnew(CollisionShape2DEditorPlugin(this)));
- add_editor_plugin(memnew(CurveTextureEditorPlugin(this)));
+ add_editor_plugin(memnew(CurveEditorPlugin(this)));
add_editor_plugin(memnew(TextureEditorPlugin(this)));
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
//add_editor_plugin( memnew( MaterialEditorPlugin(this) ) );
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index d869d703f1..50a625ddc1 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -27,528 +27,695 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#include "curve_editor_plugin.h"
#include "canvas_item_editor_plugin.h"
+#include "core_string_names.h"
+#include "os/input.h"
#include "os/keyboard.h"
-#include "spatial_editor_plugin.h"
-void CurveTextureEdit::_gui_input(const Ref<InputEvent> &p_event) {
+CurveEditor::CurveEditor() {
+ _selected_point = -1;
+ _hover_point = -1;
+ _selected_tangent = TANGENT_NONE;
+ _hover_radius = 6;
+ _tangents_length = 40;
+ _dragging = false;
+ _has_undo_data = false;
+ _world_rect = Rect2(0, 0, 1, 1);
- Ref<InputEventKey> k = p_event;
- if (k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_DELETE && grabbed != -1) {
+ set_focus_mode(FOCUS_ALL);
+ set_clip_contents(true);
+
+ _context_menu = memnew(PopupMenu);
+ _context_menu->connect("id_pressed", this, "_on_context_menu_item_selected");
+ add_child(_context_menu);
+
+ _presets_menu = memnew(PopupMenu);
+ _presets_menu->set_name("_presets_menu");
+ _presets_menu->add_item("Flat0", PRESET_FLAT0);
+ _presets_menu->add_item("Flat1", PRESET_FLAT1);
+ _presets_menu->add_item("Linear", PRESET_LINEAR);
+ _presets_menu->add_item("Ease in", PRESET_EASE_IN);
+ _presets_menu->add_item("Ease out", PRESET_EASE_OUT);
+ _presets_menu->add_item("Smoothstep", PRESET_SMOOTHSTEP);
+ _presets_menu->connect("id_pressed", this, "_on_preset_item_selected");
+ _context_menu->add_child(_presets_menu);
+}
- points.remove(grabbed);
- grabbed = -1;
- update();
- emit_signal("curve_changed");
- accept_event();
+void CurveEditor::set_curve(Ref<Curve> curve) {
+
+ if (curve == _curve_ref)
+ return;
+
+ if (_curve_ref.is_valid()) {
+ _curve_ref->disconnect("changed", this, "_curve_changed");
+ }
+ _curve_ref = curve;
+ if (_curve_ref.is_valid()) {
+ _curve_ref->connect("changed", this, "_curve_changed");
}
- Ref<InputEventMouseButton> mb = p_event;
+ _selected_point = -1;
+ _hover_point = -1;
+ _selected_tangent = TANGENT_NONE;
- if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
+ update();
- update();
- Ref<Font> font = get_font("font", "Label");
+ // Note: if you edit a curve, then set another, and try to undo,
+ // it will normally apply on the previous curve, but you won't see it
+}
+
+Size2 CurveEditor::get_minimum_size() const {
+ return Vector2(64, 64);
+}
+
+void CurveEditor::_notification(int p_what) {
+ if (p_what == NOTIFICATION_DRAW)
+ _draw();
+}
+
+void CurveEditor::on_gui_input(const Ref<InputEvent> &p_event) {
+
+ Ref<InputEventMouseButton> mb_ref = p_event;
+ if (mb_ref.is_valid()) {
+
+ const InputEventMouseButton &mb = **mb_ref;
- int font_h = font->get_height();
+ if (mb.is_pressed() && !_dragging) {
- Vector2 size = get_size();
- size.y -= font_h;
+ Vector2 mpos = mb.get_position();
- Point2 p = Vector2(mb->get_position().x, mb->get_position().y) / size;
- p.y = CLAMP(1.0 - p.y, 0, 1) * (max - min) + min;
- grabbed = -1;
- grabbing = true;
+ _selected_tangent = get_tangent_at(mpos);
+ if (_selected_tangent == TANGENT_NONE)
+ set_selected_point(get_point_at(mpos));
- for (int i = 0; i < points.size(); i++) {
+ switch (mb.get_button_index()) {
+ case BUTTON_RIGHT:
+ _context_click_pos = mpos;
+ open_context_menu(get_global_transform().xform(mpos));
+ break;
- Vector2 ps = p * get_size();
- Vector2 pt = Vector2(points[i].offset, points[i].height) * get_size();
- if (ps.distance_to(pt) < 4) {
- grabbed = i;
+ case BUTTON_MIDDLE:
+ remove_point(_hover_point);
+ break;
+
+ case BUTTON_LEFT:
+ _dragging = true;
+ break;
}
}
- //grab or select
- if (grabbed != -1) {
- return;
- }
- //insert
-
- Point np;
- np.offset = p.x;
- np.height = p.y;
-
- points.push_back(np);
- points.sort();
- for (int i = 0; i < points.size(); i++) {
- if (points[i].offset == p.x && points[i].height == p.y) {
- grabbed = i;
- break;
+ if (!mb.is_pressed() && _dragging && mb.get_button_index() == BUTTON_LEFT) {
+ _dragging = false;
+ if (_has_undo_data) {
+ push_undo(_undo_data);
+ _has_undo_data = false;
}
}
-
- emit_signal("curve_changed");
}
- if (mb.is_valid() && mb->get_button_index() == 1 && !mb->is_pressed()) {
+ Ref<InputEventMouseMotion> mm_ref = p_event;
+ if (mm_ref.is_valid()) {
- if (grabbing) {
- grabbing = false;
- emit_signal("curve_changed");
- }
- update();
- }
+ const InputEventMouseMotion &mm = **mm_ref;
- Ref<InputEventMouseMotion> mm = p_event;
+ Vector2 mpos = mm.get_position();
- if (mm.is_valid() && grabbing && grabbed != -1) {
+ if (_dragging && _curve_ref.is_valid()) {
+ if (_selected_point != -1) {
- Ref<Font> font = get_font("font", "Label");
- int font_h = font->get_height();
- Vector2 size = get_size();
- size.y -= font_h;
+ if (!_has_undo_data) {
+ // Save curve state before dragging points
+ _undo_data = _curve_ref->get_data();
+ _has_undo_data = true;
+ }
- Point2 p = mm->get_position() / size;
- p.y = CLAMP(1.0 - p.y, 0, 1) * (max - min) + min;
- p.x = CLAMP(p.x, 0.0, 1.0);
+ if (_selected_tangent == TANGENT_NONE) {
+ // Drag point
- bool valid = true;
+ Vector2 point_pos = get_world_pos(mpos);
- for (int i = 0; i < points.size(); i++) {
+ int i = _curve_ref->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);
- if (points[i].offset == p.x && points[i].height == p.y && i != grabbed) {
- valid = false;
- }
- }
+ // 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;
+
+ _curve_ref->set_point_value(_selected_point, point_pos.y);
- if (!valid)
- return;
+ //auto_calculate_tangents(i);
- points[grabbed].offset = p.x;
- points[grabbed].height = p.y;
+ } else {
+ // Drag tangent
- points.sort();
- for (int i = 0; i < points.size(); i++) {
- if (points[i].offset == p.x && points[i].height == p.y) {
- grabbed = i;
- break;
+ Vector2 point_pos = _curve_ref->get_point_pos(_selected_point);
+ Vector2 control_pos = get_world_pos(mpos);
+
+ Vector2 dir = (control_pos - point_pos).normalized();
+
+ real_t tangent;
+ if (Math::abs(dir.x) > CMP_EPSILON)
+ tangent = dir.y / dir.x;
+ else
+ tangent = 9999 * (dir.y >= 0 ? 1 : -1);
+
+ 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);
+ } else {
+ _curve_ref->set_point_right_tangent(_selected_point, tangent);
+ if (link && _selected_point != 0)
+ _curve_ref->set_point_left_tangent(_selected_point, tangent);
+ }
+ }
}
+
+ } else {
+ set_hover_point_index(get_point_at(mpos));
}
+ }
- emit_signal("curve_changed");
+ Ref<InputEventKey> key_ref = p_event;
+ if (key_ref.is_valid()) {
+ const InputEventKey &key = **key_ref;
- update();
+ if (key.is_pressed() && _selected_point != -1) {
+ if (key.get_scancode() == KEY_DELETE)
+ remove_point(_selected_point);
+ }
}
}
-void CurveTextureEdit::_plot_curve(const Vector2 &p_a, const Vector2 &p_b, const Vector2 &p_c, const Vector2 &p_d) {
+void CurveEditor::on_preset_item_selected(int preset_id) {
+ ERR_FAIL_COND(preset_id < 0 || preset_id >= PRESET_COUNT);
+ ERR_FAIL_COND(_curve_ref.is_null());
- Ref<Font> font = get_font("font", "Label");
-
- int font_h = font->get_height();
+ Curve &curve = **_curve_ref;
+ Array previous_data = curve.get_data();
- float geometry[4][4];
- float tmp1[4][4];
- float tmp2[4][4];
- float deltas[4][4];
- double x, dx, dx2, dx3;
- double y, dy, dy2, dy3;
- double d, d2, d3;
- int lastx, lasty;
- int newx, newy;
- int ntimes;
- int i, j;
+ curve.clear_points();
- int xmax = get_size().x;
- int ymax = get_size().y - font_h;
+ switch (preset_id) {
+ case PRESET_FLAT0:
+ curve.add_point(Vector2(0, 0));
+ curve.add_point(Vector2(1, 0));
+ break;
- int vsplits = 4;
+ case PRESET_FLAT1:
+ curve.add_point(Vector2(0, 1));
+ curve.add_point(Vector2(1, 1));
+ break;
- int zero_ofs = (1.0 - (0.0 - min) / (max - min)) * ymax;
+ case PRESET_LINEAR:
+ curve.add_point(Vector2(0, 0), 0, 1);
+ curve.add_point(Vector2(1, 1), 1, 0);
+ break;
- draw_line(Vector2(0, zero_ofs), Vector2(xmax, zero_ofs), Color(0.8, 0.8, 0.8, 0.15), 2.0);
+ case PRESET_EASE_IN:
+ curve.add_point(Vector2(0, 0));
+ curve.add_point(Vector2(1, 1), 1.4, 0);
+ break;
- for (int i = 0; i <= vsplits; i++) {
- float fofs = float(i) / vsplits;
- int yofs = fofs * ymax;
- draw_line(Vector2(xmax, yofs), Vector2(xmax - 4, yofs), Color(0.8, 0.8, 0.8, 0.8), 2.0);
+ case PRESET_EASE_OUT:
+ curve.add_point(Vector2(0, 0), 0, 1.4);
+ curve.add_point(Vector2(1, 1));
+ break;
- String text = rtos((1.0 - fofs) * (max - min) + min);
- int ppos = text.find(".");
- if (ppos != -1) {
- if (text.length() > ppos + 2)
- text = text.substr(0, ppos + 2);
- }
+ case PRESET_SMOOTHSTEP:
+ curve.add_point(Vector2(0, 0));
+ curve.add_point(Vector2(1, 1));
+ break;
- int size = font->get_string_size(text).x;
- int xofs = xmax - size - 4;
- yofs -= font_h / 2;
+ default:
+ break;
+ }
- if (yofs < 2) {
- yofs = 2;
- } else if (yofs + font_h > ymax - 2) {
- yofs = ymax - font_h - 2;
- }
+ push_undo(previous_data);
+}
- draw_string(font, Vector2(xofs, yofs + font->get_ascent()), text, Color(0.8, 0.8, 0.8, 1));
+void CurveEditor::_curve_changed() {
+ update();
+ // Point count can change in case of undo
+ if (_selected_point >= _curve_ref->get_point_count()) {
+ set_selected_point(-1);
}
+}
- /* construct the geometry matrix from the segment */
- for (i = 0; i < 4; i++) {
- geometry[i][2] = 0;
- geometry[i][3] = 0;
- }
+void CurveEditor::on_context_menu_item_selected(int action_id) {
+ switch (action_id) {
+ case CONTEXT_ADD_POINT:
+ add_point(_context_click_pos);
+ break;
- geometry[0][0] = (p_a[0] * xmax);
- geometry[1][0] = (p_b[0] * xmax);
- geometry[2][0] = (p_c[0] * xmax);
- geometry[3][0] = (p_d[0] * xmax);
-
- geometry[0][1] = ((p_a[1] - min) / (max - min) * ymax);
- geometry[1][1] = ((p_b[1] - min) / (max - min) * ymax);
- geometry[2][1] = ((p_c[1] - min) / (max - min) * ymax);
- geometry[3][1] = ((p_d[1] - min) / (max - min) * ymax);
-
- /* subdivide the curve ntimes (1000) times */
- ntimes = 4 * xmax;
- /* ntimes can be adjusted to give a finer or coarser curve */
- d = 1.0 / ntimes;
- d2 = d * d;
- d3 = d * d * d;
-
- /* construct a temporary matrix for determining the forward differencing deltas */
- tmp2[0][0] = 0;
- tmp2[0][1] = 0;
- tmp2[0][2] = 0;
- tmp2[0][3] = 1;
- tmp2[1][0] = d3;
- tmp2[1][1] = d2;
- tmp2[1][2] = d;
- tmp2[1][3] = 0;
- tmp2[2][0] = 6 * d3;
- tmp2[2][1] = 2 * d2;
- tmp2[2][2] = 0;
- tmp2[2][3] = 0;
- tmp2[3][0] = 6 * d3;
- tmp2[3][1] = 0;
- tmp2[3][2] = 0;
- tmp2[3][3] = 0;
-
- /* compose the basis and geometry matrices */
-
- static const float CR_basis[4][4] = {
- { -0.5, 1.5, -1.5, 0.5 },
- { 1.0, -2.5, 2.0, -0.5 },
- { -0.5, 0.0, 0.5, 0.0 },
- { 0.0, 1.0, 0.0, 0.0 },
- };
-
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
- tmp1[i][j] = (CR_basis[i][0] * geometry[0][j] +
- CR_basis[i][1] * geometry[1][j] +
- CR_basis[i][2] * geometry[2][j] +
- CR_basis[i][3] * geometry[3][j]);
- }
+ case CONTEXT_REMOVE_POINT:
+ remove_point(_selected_point);
+ break;
}
- /* compose the above results to get the deltas matrix */
-
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
- deltas[i][j] = (tmp2[i][0] * tmp1[0][j] +
- tmp2[i][1] * tmp1[1][j] +
- tmp2[i][2] * tmp1[2][j] +
- tmp2[i][3] * tmp1[3][j]);
+}
+
+void CurveEditor::open_context_menu(Vector2 pos) {
+ _context_menu->set_position(pos);
+
+ _context_menu->clear();
+
+ 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);
}
+ _context_menu->add_separator();
}
- /* extract the x deltas */
- x = deltas[0][0];
- dx = deltas[1][0];
- dx2 = deltas[2][0];
- dx3 = deltas[3][0];
+ _context_menu->add_submenu_item(TTR("Load preset"), _presets_menu->get_name());
- /* extract the y deltas */
- y = deltas[0][1];
- dy = deltas[1][1];
- dy2 = deltas[2][1];
- dy3 = deltas[3][1];
+ _context_menu->popup();
+}
- lastx = CLAMP(x, 0, xmax);
- lasty = CLAMP(y, 0, ymax);
+int CurveEditor::get_point_at(Vector2 pos) const {
+ if (_curve_ref.is_null())
+ return -1;
+ const Curve &curve = **_curve_ref;
- /* if (fix255)
- {
- cd->curve[cd->outline][lastx] = lasty;
- }
- else
- {
- cd->curve_ptr[cd->outline][lastx] = lasty;
- if(gb_debug) printf("bender_plot_curve xmax:%d ymax:%d\n", (int)xmax, (int)ymax);
+ const float r = _hover_radius * _hover_radius;
+
+ for (int i = 0; i < curve.get_point_count(); ++i) {
+ Vector2 p = get_view_pos(curve.get_point_pos(i));
+ if (p.distance_squared_to(pos) <= r) {
+ return i;
}
-*/
- /* loop over the curve */
- for (i = 0; i < ntimes; i++) {
- /* increment the x values */
- x += dx;
- dx += dx2;
- dx2 += dx3;
-
- /* increment the y values */
- y += dy;
- dy += dy2;
- dy2 += dy3;
-
- newx = CLAMP((Math::round(x)), 0, xmax);
- newy = CLAMP((Math::round(y)), 0, ymax);
-
- /* if this point is different than the last one...then draw it */
- if ((lastx != newx) || (lasty != newy)) {
-#if 0
- if(fix255)
- {
- /* use fixed array size (for the curve graph) */
- cd->curve[cd->outline][newx] = newy;
- }
- else
- {
- /* use dynamic allocated curve_ptr (for the real curve) */
- cd->curve_ptr[cd->outline][newx] = newy;
+ }
- if(gb_debug) printf("outline: %d cX: %d cY: %d\n", (int)cd->outline, (int)newx, (int)newy);
- }
-#endif
- draw_line(Vector2(lastx, ymax - lasty), Vector2(newx, ymax - newy), Color(0.8, 0.8, 0.8, 0.8), 2.0);
+ return -1;
+}
+
+int CurveEditor::get_tangent_at(Vector2 pos) const {
+ if (_curve_ref.is_null() || _selected_point < 0)
+ return TANGENT_NONE;
+
+ if (_selected_point != 0) {
+ Vector2 control_pos = get_tangent_view_pos(_selected_point, TANGENT_LEFT);
+ if (control_pos.distance_to(pos) < _hover_radius) {
+ return TANGENT_LEFT;
}
+ }
- lastx = newx;
- lasty = newy;
+ if (_selected_point != _curve_ref->get_point_count() - 1) {
+ Vector2 control_pos = get_tangent_view_pos(_selected_point, TANGENT_RIGHT);
+ if (control_pos.distance_to(pos) < _hover_radius) {
+ return TANGENT_RIGHT;
+ }
}
- int splits = 8;
+ return TANGENT_NONE;
+}
- draw_line(Vector2(0, ymax - 1), Vector2(xmax, ymax - 1), Color(0.8, 0.8, 0.8, 0.3), 2.0);
+void CurveEditor::add_point(Vector2 pos) {
+ ERR_FAIL_COND(_curve_ref.is_null());
- for (int i = 0; i <= splits; i++) {
- float fofs = float(i) / splits;
- draw_line(Vector2(fofs * xmax, ymax), Vector2(fofs * xmax, ymax - 2), Color(0.8, 0.8, 0.8, 0.8), 2.0);
+ Array prev_data = _curve_ref->get_data();
- String text = rtos(fofs);
- int size = font->get_string_size(text).x;
- int ofs = fofs * xmax - size * 0.5;
- if (ofs < 2) {
- ofs = 2;
- } else if (ofs + size > xmax - 2) {
- ofs = xmax - size - 2;
- }
+ Vector2 point_pos = get_world_pos(pos);
+ if (point_pos.y < 0.0)
+ point_pos.y = 0.0;
+ else if (point_pos.y > 1.0)
+ point_pos.y = 1.0;
- draw_string(font, Vector2(ofs, ymax + font->get_ascent()), text, Color(0.8, 0.8, 0.8, 1));
- }
+ _curve_ref->add_point(point_pos);
+
+ push_undo(prev_data);
}
-void CurveTextureEdit::_notification(int p_what) {
+void CurveEditor::remove_point(int index) {
+ ERR_FAIL_COND(_curve_ref.is_null());
- if (p_what == NOTIFICATION_DRAW) {
+ Array prev_data = _curve_ref->get_data();
- Ref<Font> font = get_font("font", "Label");
+ _curve_ref->remove_point(index);
- int font_h = font->get_height();
+ if (index == _selected_point)
+ set_selected_point(-1);
- draw_style_box(get_stylebox("bg", "Tree"), Rect2(Point2(), get_size()));
+ push_undo(prev_data);
+}
- int w = get_size().x;
- int h = get_size().y;
+void CurveEditor::set_selected_point(int index) {
+ if (index != _selected_point) {
+ _selected_point = index;
+ update();
+ }
+}
- Vector2 prev = Vector2(0, 0);
- Vector2 prev2 = Vector2(0, 0);
+void CurveEditor::set_hover_point_index(int index) {
+ if (index != _hover_point) {
+ _hover_point = index;
+ update();
+ }
+}
- for (int i = -1; i < points.size(); i++) {
+void CurveEditor::push_undo(Array previous_curve_data) {
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
- Vector2 next;
- Vector2 next2;
- if (i + 1 >= points.size()) {
- next = Vector2(1, 0);
- } else {
- next = Vector2(points[i + 1].offset, points[i + 1].height);
- }
+ ur->create_action(TTR("Modify Curve"));
+ ur->add_do_method(*_curve_ref, "_set_data", _curve_ref->get_data());
+ ur->add_undo_method(*_curve_ref, "_set_data", previous_curve_data);
- if (i + 2 >= points.size()) {
- next2 = Vector2(1, 0);
- } else {
- next2 = Vector2(points[i + 2].offset, points[i + 2].height);
- }
+ // This boolean is to prevent commit_action from executing the do method,
+ // because at this point it's already done, there is no point in doing it twice
+ _curve_ref->_disable_set_data = true;
+ ur->commit_action();
+ _curve_ref->_disable_set_data = false;
+}
- /*if (i==-1 && prev.offset==next.offset) {
- prev=next;
- continue;
- }*/
+void CurveEditor::update_view_transform() {
+ Vector2 control_size = get_size();
+ const real_t margin = 24;
- _plot_curve(prev2, prev, next, next2);
+ _world_rect = Rect2(Curve::MIN_X, 0, Curve::MAX_X, 1);
+ Vector2 wm = Vector2(margin, margin) / control_size;
+ _world_rect.position -= wm;
+ _world_rect.size += 2.0 * wm;
- prev2 = prev;
- prev = next;
- }
+ _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);
+}
- Vector2 size = get_size();
- size.y -= font_h;
- for (int i = 0; i < points.size(); i++) {
+Vector2 CurveEditor::get_tangent_view_pos(int i, TangentIndex tangent) const {
- Color col = i == grabbed ? Color(1, 0.0, 0.0, 0.9) : Color(1, 1, 1, 0.8);
+ Vector2 dir;
+ if (tangent == TANGENT_LEFT)
+ dir = -Vector2(1, _curve_ref->get_point_left_tangent(i));
+ else
+ dir = Vector2(1, _curve_ref->get_point_right_tangent(i));
- float h = (points[i].height - min) / (max - min);
- draw_rect(Rect2(Vector2(points[i].offset, 1.0 - h) * size - Vector2(2, 2), Vector2(5, 5)), col);
- }
+ Vector2 point_pos = get_view_pos(_curve_ref->get_point_pos(i));
+ Vector2 control_pos = get_view_pos(_curve_ref->get_point_pos(i) + dir);
- /* if (grabbed!=-1) {
+ return point_pos + _tangents_length * (control_pos - point_pos).normalized();
+}
- draw_rect(Rect2(total_w+3,0,h,h),points[grabbed].color);
- }
-*/
- if (has_focus()) {
+Vector2 CurveEditor::get_view_pos(Vector2 world_pos) const {
+ return _world_to_view.xform(world_pos);
+}
+
+Vector2 CurveEditor::get_world_pos(Vector2 view_pos) const {
+ return _world_to_view.affine_inverse().xform(view_pos);
+}
- draw_line(Vector2(-1, -1), Vector2(w + 1, -1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(w + 1, -1), Vector2(w + 1, h + 1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(w + 1, h + 1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(-1, -1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6));
+// Uses non-baked points, but takes advantage of ordered iteration to be faster
+template <typename T>
+static void plot_curve_accurate(const Curve &curve, float step, T plot_func) {
+
+ if (curve.get_point_count() <= 1) {
+ // Not enough points to make a curve, so it's just a straight line
+ float y = curve.interpolate(0);
+ plot_func(Vector2(0, y), Vector2(1.f, y), true);
+
+ } else {
+ Vector2 first_point = curve.get_point_pos(0);
+ Vector2 last_point = curve.get_point_pos(curve.get_point_count() - 1);
+
+ // Edge lines
+ plot_func(Vector2(0, first_point.y), first_point, false);
+ plot_func(Vector2(Curve::MAX_X, last_point.y), last_point, false);
+
+ // Draw section by section, so that we get maximum precision near points.
+ // It's an accurate representation, but slower than using the baked one.
+ for (int i = 1; i < curve.get_point_count(); ++i) {
+ Vector2 a = curve.get_point_pos(i - 1);
+ Vector2 b = curve.get_point_pos(i);
+
+ Vector2 pos = a;
+ Vector2 prev_pos = a;
+
+ float len = b.x - a.x;
+ //float step = 4.f / view_size.x;
+
+ for (float x = step; x < len; x += step) {
+ pos.x = a.x + x;
+ pos.y = curve.interpolate_local_nocheck(i - 1, x);
+ plot_func(prev_pos, pos, true);
+ prev_pos = pos;
+ }
+
+ plot_func(prev_pos, b, true);
}
}
}
-Size2 CurveTextureEdit::get_minimum_size() const {
+struct CanvasItemPlotCurve {
- return Vector2(64, 64);
-}
+ CanvasItem &ci;
+ Color color1;
+ Color color2;
-void CurveTextureEdit::set_range(float p_min, float p_max) {
- max = p_max;
- min = p_min;
- update();
-}
+ CanvasItemPlotCurve(CanvasItem &p_ci, Color p_color1, Color p_color2)
+ : ci(p_ci), color1(p_color1), color2(p_color2) {}
-void CurveTextureEdit::set_points(const Vector<Vector2> &p_points) {
+ void operator()(Vector2 pos0, Vector2 pos1, bool in_definition) {
+ ci.draw_line(pos0, pos1, in_definition ? color1 : color2);
+ }
+};
+
+void CurveEditor::_draw() {
+ if (_curve_ref.is_null())
+ return;
+ Curve &curve = **_curve_ref;
+
+ update_view_transform();
+
+ // Background
+
+ Vector2 view_size = get_rect().size;
+ draw_style_box(get_stylebox("bg", "Tree"), Rect2(Point2(), view_size));
+
+ // Grid
+
+ draw_set_transform_matrix(_world_to_view);
+
+ Vector2 min_edge = get_world_pos(Vector2(0, view_size.y));
+ Vector2 max_edge = get_world_pos(Vector2(view_size.x, 0));
+
+ 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(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);
- points.clear();
- for (int i = 0; i < p_points.size(); i++) {
- Point p;
- p.offset = p_points[i].x;
- p.height = p_points[i].y;
- points.push_back(p);
+ const Vector2 grid_step(0.25, 0.5);
+
+ 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) {
+ draw_line(Vector2(min_edge.x, y), Vector2(max_edge.x, y), grid_color1);
}
- points.sort();
- update();
-}
+ // Markings
-Vector<Vector2> CurveTextureEdit::get_points() const {
- Vector<Vector2> ret;
- for (int i = 0; i < points.size(); i++)
- ret.push_back(Vector2(points[i].offset, points[i].height));
- return ret;
-}
+ draw_set_transform_matrix(Transform2D());
-void CurveTextureEdit::_bind_methods() {
+ Ref<Font> font = get_font("font", "Label");
+ const Color text_color(1, 1, 1, 0.3);
- ClassDB::bind_method(D_METHOD("_gui_input"), &CurveTextureEdit::_gui_input);
+ draw_string(font, get_view_pos(Vector2(0, 0)), "0.0", text_color);
- ADD_SIGNAL(MethodInfo("curve_changed"));
-}
+ 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);
-CurveTextureEdit::CurveTextureEdit() {
+ 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);
- grabbed = -1;
- grabbing = false;
- max = 1;
- min = 0;
- set_focus_mode(FOCUS_ALL);
-}
+ // Draw tangents for current point
-void CurveTextureEditorPlugin::_curve_settings_changed() {
+ if (_selected_point >= 0) {
- if (!curve_texture_ref.is_valid())
- return;
- curve_editor->set_points(Variant(curve_texture_ref->get_points()));
- curve_editor->set_range(curve_texture_ref->get_min(), curve_texture_ref->get_max());
-}
+ const Color tangent_color(0.5, 0.5, 1, 1);
+
+ int i = _selected_point;
+ Vector2 pos = curve.get_point_pos(i);
+
+ if (i != 0) {
+ Vector2 control_pos = get_tangent_view_pos(i, TANGENT_LEFT);
+ draw_line(get_view_pos(pos), control_pos, tangent_color);
+ draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(2), tangent_color);
+ }
-CurveTextureEditorPlugin::CurveTextureEditorPlugin(EditorNode *p_node) {
+ if (i != curve.get_point_count() - 1) {
+ Vector2 control_pos = get_tangent_view_pos(i, TANGENT_RIGHT);
+ draw_line(get_view_pos(pos), control_pos, tangent_color);
+ draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(2), tangent_color);
+ }
+ }
- editor = p_node;
- curve_editor = memnew(CurveTextureEdit);
+ // Draw lines
- curve_button = editor->add_bottom_panel_item("CurveTexture", curve_editor);
+ draw_set_transform_matrix(_world_to_view);
- curve_button->hide();
- curve_editor->set_custom_minimum_size(Size2(100, 128 * EDSCALE));
- curve_editor->hide();
- curve_editor->connect("curve_changed", this, "curve_changed");
-}
+ const Color line_color(1, 1, 1, 0.85);
+ const Color edge_line_color(1, 1, 1, 0.4);
+
+ CanvasItemPlotCurve plot_func(*this, line_color, edge_line_color);
+ plot_curve_accurate(curve, 4.f / view_size.x, plot_func);
+
+ /*// TEST draw baked curve
+ {
+ Vector2 pos = Vector2(0, curve.interpolate_baked(0));
+ Vector2 prev_pos = pos;
-void CurveTextureEditorPlugin::edit(Object *p_object) {
+ float len = 1.0;
+ float step = 4.f / view_size.x;
- if (curve_texture_ref.is_valid()) {
- curve_texture_ref->disconnect("changed", this, "_curve_settings_changed");
+ for(float x = step; x < len; x += step) {
+ pos.x = x;
+ pos.y = curve.interpolate_baked(x);
+ draw_line(get_point_view_pos(prev_pos), get_point_view_pos(pos), Color(0,1,0));
+ prev_pos = pos;
+ }
+
+ draw_line(get_point_view_pos(prev_pos), get_point_view_pos(Vector2(1, curve.interpolate_baked(1))), Color(0,1,0));
+ }//*/
+
+ // Draw points
+
+ draw_set_transform_matrix(Transform2D());
+
+ const Color point_color(1, 1, 1);
+ const Color selected_point_color(1, 0.5, 0.5);
+
+ for (int i = 0; i < curve.get_point_count(); ++i) {
+ Vector2 pos = curve.get_point_pos(i);
+ draw_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(3), i == _selected_point ? selected_point_color : point_color);
+ // TODO Circles are prettier. Needs a fix! Or a texture
+ //draw_circle(pos, 2, point_color);
}
- CurveTexture *curve_texture = p_object->cast_to<CurveTexture>();
- if (!curve_texture)
- return;
- curve_texture_ref = Ref<CurveTexture>(curve_texture);
- curve_editor->set_points(Variant(curve_texture_ref->get_points()));
- curve_editor->set_range(curve_texture_ref->get_min(), curve_texture_ref->get_max());
- if (!curve_texture_ref->is_connected("changed", this, "_curve_settings_changed")) {
- curve_texture_ref->connect("changed", this, "_curve_settings_changed");
+
+ // Hover
+
+ if (_hover_point != -1) {
+ const Color hover_color = line_color;
+ Vector2 pos = curve.get_point_pos(_hover_point);
+ stroke_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(_hover_radius), hover_color);
}
}
-bool CurveTextureEditorPlugin::handles(Object *p_object) const {
+// TODO That should be part of the drawing API...
+void CurveEditor::stroke_rect(Rect2 rect, Color color) {
+
+ // a---b
+ // | |
+ // c---d
+ Vector2 a(rect.position);
+ Vector2 b(rect.position.x + rect.size.x, rect.position.y);
+ Vector2 c(rect.position.x, rect.position.y + rect.size.y);
+ Vector2 d(rect.position + rect.size);
+
+ draw_line(a, b, color);
+ draw_line(b, d, color);
+ draw_line(d, c, color);
+ draw_line(c, a, color);
+}
- return p_object->is_class("CurveTexture");
+void CurveEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_gui_input"), &CurveEditor::on_gui_input);
+ ClassDB::bind_method(D_METHOD("_on_preset_item_selected"), &CurveEditor::on_preset_item_selected);
+ ClassDB::bind_method(D_METHOD("_curve_changed"), &CurveEditor::_curve_changed);
+ ClassDB::bind_method(D_METHOD("_on_context_menu_item_selected"), &CurveEditor::on_context_menu_item_selected);
}
-void CurveTextureEditorPlugin::make_visible(bool p_visible) {
+//---------------
- if (p_visible) {
- curve_button->show();
- editor->make_bottom_panel_item_visible(curve_editor);
+CurveEditorPlugin::CurveEditorPlugin(EditorNode *p_node) {
+ _editor_node = p_node;
- } else {
+ _view = memnew(CurveEditor);
+ _view->set_custom_minimum_size(Size2(100, 128 * EDSCALE));
+ _view->hide();
- curve_button->hide();
- if (curve_editor->is_visible_in_tree())
- editor->hide_bottom_panel();
- }
+ _toggle_button = _editor_node->add_bottom_panel_item(get_name(), _view);
+ _toggle_button->hide();
}
-void CurveTextureEditorPlugin::_curve_changed() {
+CurveEditorPlugin::~CurveEditorPlugin() {
+}
- if (curve_texture_ref.is_valid()) {
+void CurveEditorPlugin::edit(Object *p_object) {
- UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ Ref<Curve> curve_ref;
- Vector<Vector2> points = curve_editor->get_points();
- PoolVector<Vector2> ppoints = Variant(points);
+ if (_current_ref.is_valid()) {
+ CurveTexture *ct = _current_ref->cast_to<CurveTexture>();
+ if (ct)
+ ct->disconnect(CoreStringNames::get_singleton()->changed, this, "_curve_texture_changed");
+ }
+
+ if (p_object) {
+ Resource *res = p_object->cast_to<Resource>();
+ ERR_FAIL_COND(res == NULL);
+ ERR_FAIL_COND(!handles(p_object));
+
+ _current_ref = Ref<Resource>(p_object->cast_to<Resource>());
+
+ if (_current_ref.is_valid()) {
+ Curve *curve = _current_ref->cast_to<Curve>();
+ if (curve)
+ curve_ref = Ref<Curve>(curve);
+ else {
+ CurveTexture *ct = _current_ref->cast_to<CurveTexture>();
+ if (ct) {
+ ct->connect(CoreStringNames::get_singleton()->changed, this, "_curve_texture_changed");
+ curve_ref = ct->get_curve();
+ }
+ }
+ }
- ur->create_action(TTR("Modify Curve"), UndoRedo::MERGE_ENDS);
- ur->add_do_method(this, "undo_redo_curve_texture", ppoints);
- ur->add_undo_method(this, "undo_redo_curve_texture", curve_texture_ref->get_points());
- ur->commit_action();
+ } else {
+ _current_ref = Ref<Resource>();
}
+
+ _view->set_curve(curve_ref);
}
-void CurveTextureEditorPlugin::_undo_redo_curve_texture(const PoolVector<Vector2> &points) {
+bool CurveEditorPlugin::handles(Object *p_object) const {
+ // Both handled so that we can keep the curve editor open
+ return p_object->cast_to<Curve>() || p_object->cast_to<CurveTexture>();
+}
- curve_texture_ref->set_points(points);
- curve_editor->set_points(Variant(curve_texture_ref->get_points()));
- curve_editor->update();
+void CurveEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ _toggle_button->show();
+ _editor_node->make_bottom_panel_item_visible(_view);
+ } else {
+ _toggle_button->hide();
+ if (_view->is_visible_in_tree())
+ _editor_node->hide_bottom_panel();
+ }
}
-CurveTextureEditorPlugin::~CurveTextureEditorPlugin() {
+void CurveEditorPlugin::_curve_texture_changed() {
+ // If the curve is shown indirectly as a CurveTexture is edited,
+ // we need to monitor when the curve property gets assigned
+ CurveTexture *ct = _current_ref->cast_to<CurveTexture>();
+ if (ct) {
+ _view->set_curve(ct->get_curve());
+ }
}
-void CurveTextureEditorPlugin::_bind_methods() {
- ClassDB::bind_method(D_METHOD("curve_changed"), &CurveTextureEditorPlugin::_curve_changed);
- ClassDB::bind_method(D_METHOD("_curve_settings_changed"), &CurveTextureEditorPlugin::_curve_settings_changed);
- ClassDB::bind_method(D_METHOD("undo_redo_curve_texture", "points"), &CurveTextureEditorPlugin::_undo_redo_curve_texture);
+void CurveEditorPlugin::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_curve_texture_changed"), &CurveEditorPlugin::_curve_texture_changed);
}
diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h
index 4e75ba407c..0ed4ee3517 100644
--- a/editor/plugins/curve_editor_plugin.h
+++ b/editor/plugins/curve_editor_plugin.h
@@ -27,69 +27,119 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#ifndef CURVE_EDITOR_PLUGIN_H
#define CURVE_EDITOR_PLUGIN_H
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
+#include "scene/resources/curve.h"
-class CurveTextureEdit : public Control {
+// Edits a y(x) curve
+class CurveEditor : public Control {
+ GDCLASS(CurveEditor, Control)
+public:
+ CurveEditor();
- GDCLASS(CurveTextureEdit, Control);
+ Size2 get_minimum_size() const;
- struct Point {
+ void set_curve(Ref<Curve> curve);
- float offset;
- float height;
- bool operator<(const Point &p_ponit) const {
- return offset < p_ponit.offset;
- }
+ enum PresetID {
+ PRESET_FLAT0 = 0,
+ PRESET_FLAT1,
+ PRESET_LINEAR,
+ PRESET_EASE_IN,
+ PRESET_EASE_OUT,
+ PRESET_SMOOTHSTEP,
+ PRESET_COUNT
};
- bool grabbing;
- int grabbed;
- Vector<Point> points;
- float max, min;
+ enum ContextAction {
+ CONTEXT_ADD_POINT = 0,
+ CONTEXT_REMOVE_POINT
+ };
- void _plot_curve(const Vector2 &p_a, const Vector2 &p_b, const Vector2 &p_c, const Vector2 &p_d);
+ enum TangentIndex {
+ TANGENT_NONE = -1,
+ TANGENT_LEFT = 0,
+ TANGENT_RIGHT = 1
+ };
protected:
- void _gui_input(const Ref<InputEvent> &p_event);
void _notification(int p_what);
+
static void _bind_methods();
-public:
- void set_range(float p_min, float p_max);
- void set_points(const Vector<Vector2> &p_points);
- Vector<Vector2> get_points() const;
- virtual Size2 get_minimum_size() const;
- CurveTextureEdit();
+private:
+ void on_gui_input(const Ref<InputEvent> &p_event);
+ void on_preset_item_selected(int preset_id);
+ void _curve_changed();
+ void on_context_menu_item_selected(int action_id);
+
+ void open_context_menu(Vector2 pos);
+ int get_point_at(Vector2 pos) const;
+ int get_tangent_at(Vector2 pos) const;
+ void add_point(Vector2 pos);
+ void remove_point(int index);
+ void set_selected_point(int index);
+ void set_hover_point_index(int index);
+ void push_undo(Array previous_curve_data);
+ void update_view_transform();
+
+ Vector2 get_tangent_view_pos(int i, TangentIndex tangent) const;
+ Vector2 get_view_pos(Vector2 world_pos) const;
+ Vector2 get_world_pos(Vector2 view_pos) const;
+
+ void _draw();
+
+ void stroke_rect(Rect2 rect, Color color);
+
+private:
+ Rect2 _world_rect;
+ Transform2D _world_to_view;
+
+ Ref<Curve> _curve_ref;
+ PopupMenu *_context_menu;
+ PopupMenu *_presets_menu;
+
+ Array _undo_data;
+ bool _has_undo_data;
+ bool _undo_no_commit;
+
+ Vector2 _context_click_pos;
+ int _selected_point;
+ int _hover_point;
+ int _selected_tangent;
+ bool _dragging;
+
+ // Constant
+ float _hover_radius;
+ float _tangents_length;
};
-class CurveTextureEditorPlugin : public EditorPlugin {
-
- GDCLASS(CurveTextureEditorPlugin, EditorPlugin);
+class CurveEditorPlugin : public EditorPlugin {
+ GDCLASS(CurveEditorPlugin, EditorPlugin)
+public:
+ CurveEditorPlugin(EditorNode *p_node);
+ ~CurveEditorPlugin();
- CurveTextureEdit *curve_editor;
- Ref<CurveTexture> curve_texture_ref;
- EditorNode *editor;
- ToolButton *curve_button;
+ String get_name() const { return "Curve"; }
+ bool has_main_screen() const { return false; }
+ void edit(Object *p_object);
+ bool handles(Object *p_object) const;
+ void make_visible(bool p_visible);
-protected:
+private:
static void _bind_methods();
- void _curve_changed();
- void _undo_redo_curve_texture(const PoolVector<Vector2> &points);
- void _curve_settings_changed();
-public:
- virtual String get_name() const { return "CurveTexture"; }
- bool has_main_screen() const { return false; }
- virtual void edit(Object *p_node);
- virtual bool handles(Object *p_node) const;
- virtual void make_visible(bool p_visible);
+ void _curve_texture_changed();
- CurveTextureEditorPlugin(EditorNode *p_node);
- ~CurveTextureEditorPlugin();
+private:
+ CurveEditor *_view;
+ Ref<Resource> _current_ref;
+ EditorNode *_editor_node;
+ ToolButton *_toggle_button;
};
#endif // CURVE_EDITOR_PLUGIN_H
diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp
index 676e50d61c..c4fe15e61c 100644
--- a/editor/plugins/texture_editor_plugin.cpp
+++ b/editor/plugins/texture_editor_plugin.cpp
@@ -61,9 +61,21 @@ void TextureEditor::_notification(int p_what) {
tex_height = texture->get_height() * tex_width / texture->get_width();
}
+ // Prevent the texture from being unpreviewable after the rescale, so that we can still see something
+ if (tex_height <= 0)
+ tex_height = 1;
+ if (tex_width <= 0)
+ tex_width = 1;
+
int ofs_x = (size.width - tex_width) / 2;
int ofs_y = (size.height - tex_height) / 2;
+ if (texture->cast_to<CurveTexture>()) {
+ // In the case of CurveTextures we know they are 1 in height, so fill the preview to see the gradient
+ ofs_y = 0;
+ tex_height = size.height;
+ }
+
draw_texture_rect(texture, Rect2(ofs_x, ofs_y, tex_width, tex_height));
Ref<Font> font = get_font("font", "Label");
diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp
index 8508962521..722b698b75 100644
--- a/scene/3d/particles.cpp
+++ b/scene/3d/particles.cpp
@@ -1082,16 +1082,9 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture>
case PARAM_SCALE: {
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_texture, p_texture);
- Ref<CurveTexture> curve = p_texture;
- if (curve.is_valid()) {
- if (curve->get_min() == 0 && curve->get_max() == 1) {
-
- curve->set_max(32);
- PoolVector<Vector2> points;
- points.push_back(Vector2(0, 1));
- points.push_back(Vector2(1, 1));
- curve->set_points(points);
- }
+ Ref<CurveTexture> curve_tex = p_texture;
+ if (curve_tex.is_valid()) {
+ curve_tex->ensure_default_setup();
}
} break;
@@ -1257,14 +1250,7 @@ void ParticlesMaterial::set_trail_size_modifier(const Ref<CurveTexture> &p_trail
Ref<CurveTexture> curve = trail_size_modifier;
if (curve.is_valid()) {
- if (curve->get_min() == 0 && curve->get_max() == 1) {
-
- curve->set_max(32);
- PoolVector<Vector2> points;
- points.push_back(Vector2(0, 1));
- points.push_back(Vector2(1, 1));
- curve->set_points(points);
- }
+ curve->ensure_default_setup();
}
RID texture;
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 69d1a3aeb8..151bc80321 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -580,6 +580,7 @@ void register_scene_types() {
ClassDB::register_class<Animation>();
ClassDB::register_virtual_class<Font>();
ClassDB::register_class<BitmapFont>();
+ ClassDB::register_class<Curve>();
ClassDB::register_class<DynamicFontData>();
ClassDB::register_class<DynamicFont>();
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 10c12c9411..006e7de562 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -380,6 +380,365 @@ Curve2D::Curve2D()
#endif
+Curve::Curve() {
+ _bake_resolution = 100;
+ _baked_cache_dirty = false;
+#ifdef TOOLS_ENABLED
+ _disable_set_data = false;
+#endif
+}
+
+int Curve::add_point(Vector2 p_pos, real_t left_tangent, real_t right_tangent) {
+ // Add a point and preserve order
+
+ // Curve bounds is in 0..1
+ if (p_pos.x > MAX_X)
+ p_pos.x = MAX_X;
+ else if (p_pos.x < MIN_X)
+ p_pos.x = MIN_X;
+
+ int ret = -1;
+
+ if (_points.size() == 0) {
+ _points.push_back(Point(p_pos, left_tangent, right_tangent));
+ ret = 0;
+
+ } else if (_points.size() == 1) {
+ // TODO Is the `else` able to handle this block already?
+
+ real_t diff = p_pos.x - _points[0].pos.x;
+
+ if (diff > 0) {
+ _points.push_back(Point(p_pos, left_tangent, right_tangent));
+ ret = 1;
+ } else {
+ _points.insert(0, Point(p_pos, left_tangent, right_tangent));
+ ret = 0;
+ }
+
+ } else {
+
+ int i = get_index(p_pos.x);
+
+ int nearest_index = i;
+ if (i + 1 < _points.size()) {
+ real_t diff0 = p_pos.x - _points[i].pos.x;
+ real_t diff1 = _points[i + 1].pos.x - p_pos.x;
+
+ if (diff1 < diff0)
+ nearest_index = i + 1;
+ }
+
+ if (i == 0 && p_pos.x < _points[0].pos.x) {
+ // Insert before anything else
+ _points.insert(0, Point(p_pos, left_tangent, right_tangent));
+ ret = 0;
+ } else {
+ // Insert between i and i+1
+ ++i;
+ _points.insert(i, Point(p_pos, left_tangent, right_tangent));
+ ret = i;
+ }
+ }
+
+ mark_dirty();
+
+ return ret;
+}
+
+int Curve::get_index(real_t offset) const {
+
+ // Lower-bound float binary search
+
+ int imin = 0;
+ int imax = _points.size() - 1;
+
+ while (imax - imin > 1) {
+ int m = (imin + imax) / 2;
+
+ real_t a = _points[m].pos.x;
+ real_t b = _points[m + 1].pos.x;
+
+ if (a < offset && b < offset) {
+ imin = m;
+
+ } else if (a > offset) {
+ imax = m;
+
+ } else {
+ return m;
+ }
+ }
+
+ // Will happen if the offset is out of bounds
+ if (offset > _points[imax].pos.x)
+ return imax;
+ return imin;
+}
+
+void Curve::clean_dupes() {
+
+ bool dirty = false;
+
+ for (int i = 1; i < _points.size(); ++i) {
+ real_t diff = _points[i - 1].pos.x - _points[i].pos.x;
+ if (diff <= CMP_EPSILON) {
+ _points.remove(i);
+ --i;
+ dirty = true;
+ }
+ }
+
+ if (dirty)
+ mark_dirty();
+}
+
+void Curve::set_point_left_tangent(int i, real_t tangent) {
+ ERR_FAIL_INDEX(i, _points.size());
+ _points[i].left_tangent = tangent;
+ mark_dirty();
+}
+
+void Curve::set_point_right_tangent(int i, real_t tangent) {
+ ERR_FAIL_INDEX(i, _points.size());
+ _points[i].right_tangent = tangent;
+ mark_dirty();
+}
+
+real_t Curve::get_point_left_tangent(int i) const {
+ ERR_FAIL_INDEX_V(i, _points.size(), 0);
+ return _points[i].left_tangent;
+}
+
+real_t Curve::get_point_right_tangent(int i) const {
+ ERR_FAIL_INDEX_V(i, _points.size(), 0);
+ return _points[i].right_tangent;
+}
+
+void Curve::remove_point(int p_index) {
+ ERR_FAIL_INDEX(p_index, _points.size());
+ _points.remove(p_index);
+ mark_dirty();
+}
+
+void Curve::clear_points() {
+ _points.clear();
+ mark_dirty();
+}
+
+void Curve::set_point_value(int p_index, real_t pos) {
+ ERR_FAIL_INDEX(p_index, _points.size());
+ _points[p_index].pos.y = pos;
+ mark_dirty();
+}
+
+int Curve::set_point_offset(int p_index, float offset) {
+ ERR_FAIL_INDEX_V(p_index, _points.size(), -1);
+ Point p = _points[p_index];
+ remove_point(p_index);
+ int i = add_point(Vector2(offset, p.pos.y));
+ _points[i].left_tangent = p.left_tangent;
+ _points[i].right_tangent = p.right_tangent;
+ return i;
+}
+
+Vector2 Curve::get_point_pos(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, _points.size(), Vector2(0, 0));
+ return _points[p_index].pos;
+}
+
+real_t Curve::interpolate(real_t offset) const {
+ if (_points.size() == 0)
+ return 0;
+ if (_points.size() == 1)
+ return _points[0].pos.y;
+
+ int i = get_index(offset);
+
+ if (i == _points.size() - 1)
+ return _points[i].pos.y;
+
+ real_t local = offset - _points[i].pos.x;
+
+ if (i == 0 && local <= 0)
+ return _points[0].pos.y;
+
+ return interpolate_local_nocheck(i, local);
+}
+
+real_t Curve::interpolate_local_nocheck(int index, real_t local_offset) const {
+
+ const Point a = _points[index];
+ const Point b = _points[index + 1];
+
+ // Cubic bezier
+
+ // ac-----bc
+ // / \
+ // / \ Here with a.right_tangent > 0
+ // / \ and b.left_tangent < 0
+ // / \
+ // a b
+ //
+ // |-d1--|-d2--|-d3--|
+ //
+ // d1 == d2 == d3 == d / 3
+
+ // Control points are chosen at equal distances
+ real_t d = b.pos.x - a.pos.x;
+ if (Math::abs(d) <= CMP_EPSILON)
+ return b.pos.y;
+ local_offset /= d;
+ d /= 3.0;
+ real_t yac = a.pos.y + d * a.right_tangent;
+ real_t ybc = b.pos.y - d * b.left_tangent;
+
+ real_t y = _bezier_interp(local_offset, a.pos.y, yac, ybc, b.pos.y);
+
+ return y;
+}
+
+void Curve::mark_dirty() {
+ _baked_cache_dirty = true;
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
+Array Curve::get_data() const {
+
+ Array output;
+ output.resize(_points.size() * 3);
+
+ for (int j = 0; j < _points.size(); ++j) {
+
+ const Point p = _points[j];
+ int i = j * 3;
+
+ output[i] = p.pos;
+ output[i + 1] = p.left_tangent;
+ output[i + 2] = p.right_tangent;
+ }
+
+ return output;
+}
+
+void Curve::set_data(Array input) {
+ ERR_FAIL_COND(input.size() % 3 != 0);
+
+#ifdef TOOLS_ENABLED
+ if (_disable_set_data)
+ return;
+#endif
+
+ _points.clear();
+
+ // Validate input
+ for (int i = 0; i < input.size(); i += 3) {
+ 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);
+ }
+
+ _points.resize(input.size() / 3);
+
+ for (int j = 0; j < _points.size(); ++j) {
+
+ Point &p = _points[j];
+ int i = j * 3;
+
+ p.pos = input[i];
+ p.left_tangent = input[i + 1];
+ p.right_tangent = input[i + 2];
+ }
+
+ mark_dirty();
+}
+
+void Curve::bake() {
+ _baked_cache.clear();
+
+ _baked_cache.resize(_bake_resolution);
+
+ for (int i = 1; i < _bake_resolution - 1; ++i) {
+ real_t x = i / static_cast<real_t>(_bake_resolution);
+ real_t y = interpolate(x);
+ _baked_cache[i] = y;
+ }
+
+ if (_points.size() != 0) {
+ _baked_cache[0] = _points[0].pos.y;
+ _baked_cache[_baked_cache.size() - 1] = _points[_points.size() - 1].pos.y;
+ }
+
+ _baked_cache_dirty = false;
+}
+
+void Curve::set_bake_resolution(int p_resolution) {
+ ERR_FAIL_COND(p_resolution < 1);
+ ERR_FAIL_COND(p_resolution > 1000);
+ _bake_resolution = p_resolution;
+ _baked_cache_dirty = true;
+}
+
+real_t Curve::interpolate_baked(real_t offset) {
+ if (_baked_cache_dirty) {
+ // Last-second bake if not done already
+ bake();
+ }
+
+ // Special cases if the cache is too small
+ if (_baked_cache.size() == 0) {
+ if (_points.size() == 0)
+ return 0;
+ return _points[0].pos.y;
+ } else if (_baked_cache.size() == 1) {
+ return _baked_cache[0];
+ }
+
+ // Get interpolation index
+ real_t fi = offset * _baked_cache.size();
+ int i = Math::floor(fi);
+ if (i < 0) {
+ i = 0;
+ fi = 0;
+ } else if (i >= _baked_cache.size()) {
+ i = _baked_cache.size() - 1;
+ fi = 0;
+ }
+
+ // Interpolate
+ if (i + 1 < _baked_cache.size()) {
+ real_t t = fi - i;
+ return Math::lerp(_baked_cache[i], _baked_cache[i + 1], t);
+ } else {
+ return _baked_cache[_baked_cache.size() - 1];
+ }
+}
+
+void Curve::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("add_point", "pos", "left_tangent", "right_tangent"), &Curve::add_point, DEFVAL(0), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("remove_point", "index"), &Curve::remove_point);
+ ClassDB::bind_method(D_METHOD("clear_points"), &Curve::clear_points);
+ ClassDB::bind_method(D_METHOD("get_point_pos", "index"), &Curve::get_point_pos);
+ ClassDB::bind_method(D_METHOD("set_point_value", "index, y"), &Curve::set_point_value);
+ ClassDB::bind_method(D_METHOD("set_point_offset", "index, offset"), &Curve::set_point_value);
+ ClassDB::bind_method(D_METHOD("interpolate", "offset"), &Curve::interpolate);
+ 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("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("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);
+ ClassDB::bind_method(D_METHOD("set_bake_resolution", "resolution"), &Curve::set_bake_resolution);
+ 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::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");
+}
+
int Curve2D::get_point_count() const {
return points.size();
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 17c0ac9f5e..63b9a07f07 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -82,6 +82,78 @@ public:
#endif
+// y(x) curve
+class Curve : public Resource {
+ GDCLASS(Curve, Resource)
+public:
+ static const int MIN_X = 0.f;
+ static const int MAX_X = 1.f;
+
+#ifdef TOOLS_ENABLED
+ bool _disable_set_data;
+#endif
+
+ struct Point {
+ Vector2 pos;
+ real_t left_tangent;
+ real_t right_tangent;
+
+ Point() {
+ left_tangent = 0;
+ right_tangent = 0;
+ }
+
+ Point(Vector2 p, real_t left = 0, real_t right = 0) {
+ pos = p;
+ left_tangent = left;
+ right_tangent = right;
+ }
+ };
+
+ Curve();
+
+ int get_point_count() const { return _points.size(); }
+
+ int add_point(Vector2 p_pos, real_t left_tangent = 0, real_t right_tangent = 0);
+ void remove_point(int p_index);
+ void clear_points();
+
+ int get_index(real_t offset) const;
+
+ void set_point_value(int p_index, real_t pos);
+ int set_point_offset(int p_index, float offset);
+ Vector2 get_point_pos(int p_index) const;
+
+ real_t interpolate(real_t offset) const;
+ real_t interpolate_local_nocheck(int index, real_t local_offset) const;
+
+ void clean_dupes();
+
+ void set_point_left_tangent(int i, real_t tangent);
+ void set_point_right_tangent(int i, real_t tangent);
+ real_t get_point_left_tangent(int i) const;
+ real_t get_point_right_tangent(int i) const;
+
+ Array get_data() const;
+ void set_data(Array input);
+
+ void bake();
+ int get_bake_resolution() const { return _bake_resolution; }
+ void set_bake_resolution(int p_interval);
+ real_t interpolate_baked(real_t offset);
+
+protected:
+ static void _bind_methods();
+
+private:
+ void mark_dirty();
+
+ Vector<Point> _points;
+ bool _baked_cache_dirty;
+ Vector<real_t> _baked_cache;
+ int _bake_resolution;
+};
+
class Curve2D : public Resource {
GDCLASS(Curve2D, Resource);
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 2120b37497..7b393233f1 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -30,7 +30,9 @@
#include "texture.h"
#include "core/method_bind_ext.inc"
#include "core/os/os.h"
+#include "core_string_names.h"
#include "io/image_loader.h"
+
Size2 Texture::get_size() const {
return Size2(get_width(), get_height());
@@ -1376,254 +1378,126 @@ void CurveTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveTexture::set_width);
- ClassDB::bind_method(D_METHOD("set_points", "points"), &CurveTexture::set_points);
- ClassDB::bind_method(D_METHOD("get_points"), &CurveTexture::get_points);
+ ClassDB::bind_method(D_METHOD("set_curve", "curve:Curve"), &CurveTexture::set_curve);
+ ClassDB::bind_method(D_METHOD("get_curve:Curve"), &CurveTexture::get_curve);
+
+ 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::POOL_VECTOR2_ARRAY, "points"), "set_points", "get_points");
+ 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;
+ _max = p_max;
emit_changed();
}
float CurveTexture::get_max() const {
- return max;
+ return _max;
}
void CurveTexture::set_min(float p_min) {
- min = p_min;
+ _min = p_min;
emit_changed();
}
float CurveTexture::get_min() const {
- return min;
+ return _min;
}
void CurveTexture::set_width(int p_width) {
ERR_FAIL_COND(p_width < 32 || p_width > 4096);
- width = p_width;
- if (points.size())
- set_points(points);
+ _width = p_width;
+ _update();
}
int CurveTexture::get_width() const {
- return width;
+ return _width;
}
-static void _plot_curve(const Vector2 &p_a, const Vector2 &p_b, const Vector2 &p_c, const Vector2 &p_d, float *p_heights, bool *p_useds, int p_width, float p_min, float p_max) {
-
- float geometry[4][4];
- float tmp1[4][4];
- float tmp2[4][4];
- float deltas[4][4];
- double x, dx, dx2, dx3;
- double y, dy, dy2, dy3;
- double d, d2, d3;
- int lastx;
- int newx;
- float lasty;
- float newy;
- int ntimes;
- int i, j;
-
- int xmax = p_width;
-
- /* construct the geometry matrix from the segment */
- for (i = 0; i < 4; i++) {
- geometry[i][2] = 0;
- geometry[i][3] = 0;
- }
+void CurveTexture::ensure_default_setup() {
- geometry[0][0] = (p_a[0] * xmax);
- geometry[1][0] = (p_b[0] * xmax);
- geometry[2][0] = (p_c[0] * xmax);
- geometry[3][0] = (p_d[0] * xmax);
-
- geometry[0][1] = (p_a[1]);
- geometry[1][1] = (p_b[1]);
- geometry[2][1] = (p_c[1]);
- geometry[3][1] = (p_d[1]);
-
- /* subdivide the curve ntimes (1000) times */
- ntimes = 4 * xmax;
- /* ntimes can be adjusted to give a finer or coarser curve */
- d = 1.0 / ntimes;
- d2 = d * d;
- d3 = d * d * d;
-
- /* construct a temporary matrix for determining the forward differencing deltas */
- tmp2[0][0] = 0;
- tmp2[0][1] = 0;
- tmp2[0][2] = 0;
- tmp2[0][3] = 1;
- tmp2[1][0] = d3;
- tmp2[1][1] = d2;
- tmp2[1][2] = d;
- tmp2[1][3] = 0;
- tmp2[2][0] = 6 * d3;
- tmp2[2][1] = 2 * d2;
- tmp2[2][2] = 0;
- tmp2[2][3] = 0;
- tmp2[3][0] = 6 * d3;
- tmp2[3][1] = 0;
- tmp2[3][2] = 0;
- tmp2[3][3] = 0;
-
- /* compose the basis and geometry matrices */
-
- static const float CR_basis[4][4] = {
- { -0.5, 1.5, -1.5, 0.5 },
- { 1.0, -2.5, 2.0, -0.5 },
- { -0.5, 0.0, 0.5, 0.0 },
- { 0.0, 1.0, 0.0, 0.0 },
- };
-
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
- tmp1[i][j] = (CR_basis[i][0] * geometry[0][j] +
- CR_basis[i][1] * geometry[1][j] +
- CR_basis[i][2] * geometry[2][j] +
- CR_basis[i][3] * geometry[3][j]);
- }
+ if (_curve.is_null()) {
+ Ref<Curve> curve = Ref<Curve>(memnew(Curve));
+ curve->add_point(Vector2(0, 1));
+ curve->add_point(Vector2(1, 1));
+ set_curve(curve);
}
- /* compose the above results to get the deltas matrix */
-
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
- deltas[i][j] = (tmp2[i][0] * tmp1[0][j] +
- tmp2[i][1] * tmp1[1][j] +
- tmp2[i][2] * tmp1[2][j] +
- tmp2[i][3] * tmp1[3][j]);
- }
+
+ if (get_min() == 0 && get_max() == 1) {
+ set_max(32);
}
+}
- /* extract the x deltas */
- x = deltas[0][0];
- dx = deltas[1][0];
- dx2 = deltas[2][0];
- dx3 = deltas[3][0];
-
- /* extract the y deltas */
- y = deltas[0][1];
- dy = deltas[1][1];
- dy2 = deltas[2][1];
- dy3 = deltas[3][1];
-
- lastx = CLAMP(x, 0, xmax);
- lasty = y;
-
- p_heights[lastx] = lasty;
- p_useds[lastx] = true;
-
- /* loop over the curve */
- for (i = 0; i < ntimes; i++) {
- /* increment the x values */
- x += dx;
- dx += dx2;
- dx2 += dx3;
-
- /* increment the y values */
- y += dy;
- dy += dy2;
- dy2 += dy3;
-
- newx = CLAMP((Math::round(x)), 0, xmax);
- newy = CLAMP(y, p_min, p_max);
-
- /* if this point is different than the last one...then draw it */
- if ((lastx != newx) || (lasty != newy)) {
- p_useds[newx] = true;
- p_heights[newx] = newy;
+void CurveTexture::set_curve(Ref<Curve> p_curve) {
+ if (_curve != p_curve) {
+ if (_curve.is_valid()) {
+ _curve->disconnect(CoreStringNames::get_singleton()->changed, this, "_update");
}
-
- lastx = newx;
- lasty = newy;
+ _curve = p_curve;
+ if (_curve.is_valid()) {
+ _curve->connect(CoreStringNames::get_singleton()->changed, this, "_update");
+ }
+ _update();
}
}
-void CurveTexture::set_points(const PoolVector<Vector2> &p_points) {
-
- points = p_points;
+void CurveTexture::_update() {
PoolVector<uint8_t> data;
- PoolVector<bool> used;
- data.resize(width * sizeof(float));
- used.resize(width);
+ data.resize(_width * sizeof(float));
+
+ // The array is locked in that scope
{
PoolVector<uint8_t>::Write wd8 = data.write();
float *wd = (float *)wd8.ptr();
- PoolVector<bool>::Write wu = used.write();
- int pc = p_points.size();
- PoolVector<Vector2>::Read pr = p_points.read();
- for (int i = 0; i < width; i++) {
- wd[i] = 0.0;
- wu[i] = false;
- }
-
- Vector2 prev = Vector2(0, 0);
- Vector2 prev2 = Vector2(0, 0);
-
- for (int i = -1; i < pc; i++) {
-
- Vector2 next;
- Vector2 next2;
- if (i + 1 >= pc) {
- next = Vector2(1, 0);
- } else {
- next = Vector2(pr[i + 1].x, pr[i + 1].y);
+ if (_curve.is_valid()) {
+ Curve &curve = **_curve;
+ float height = _max - _min;
+ for (int i = 0; i < _width; ++i) {
+ float t = i / static_cast<float>(_width);
+ float v = curve.interpolate_baked(t);
+ wd[i] = CLAMP(_min + v * height, _min, _max);
}
- if (i + 2 >= pc) {
- next2 = Vector2(1, 0);
- } else {
- next2 = Vector2(pr[i + 2].x, pr[i + 2].y);
+ } else {
+ for (int i = 0; i < _width; ++i) {
+ wd[i] = 0;
}
-
- /*if (i==-1 && prev.offset==next.offset) {
- prev=next;
- continue;
- }*/
-
- _plot_curve(prev2, prev, next, next2, wd, wu.ptr(), width, min, max);
-
- prev2 = prev;
- prev = next;
}
}
- Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RF, data));
+ Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RF, data));
- VS::get_singleton()->texture_allocate(texture, width, 1, Image::FORMAT_RF, VS::TEXTURE_FLAG_FILTER);
- VS::get_singleton()->texture_set_data(texture, image);
+ VS::get_singleton()->texture_allocate(_texture, _width, 1, Image::FORMAT_RF, VS::TEXTURE_FLAG_FILTER);
+ VS::get_singleton()->texture_set_data(_texture, image);
emit_changed();
}
-PoolVector<Vector2> CurveTexture::get_points() const {
+Ref<Curve> CurveTexture::get_curve() const {
- return points;
+ return _curve;
}
RID CurveTexture::get_rid() const {
- return texture;
+ return _texture;
}
CurveTexture::CurveTexture() {
- max = 1;
- min = 0;
- width = 2048;
- texture = VS::get_singleton()->texture_create();
+ _max = 1;
+ _min = 0;
+ _width = 2048;
+ _texture = VS::get_singleton()->texture_create();
}
CurveTexture::~CurveTexture() {
- VS::get_singleton()->free(texture);
+ VS::get_singleton()->free(_texture);
}
//////////////////
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 2b82dbd21f..2c36cdef87 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -30,6 +30,7 @@
#ifndef TEXTURE_H
#define TEXTURE_H
+#include "curve.h"
#include "io/resource_loader.h"
#include "math_2d.h"
#include "resource.h"
@@ -389,20 +390,22 @@ public:
~CubeMap();
};
-VARIANT_ENUM_CAST(CubeMap::Flags);
-VARIANT_ENUM_CAST(CubeMap::Side);
-VARIANT_ENUM_CAST(CubeMap::Storage);
+VARIANT_ENUM_CAST(CubeMap::Flags)
+VARIANT_ENUM_CAST(CubeMap::Side)
+VARIANT_ENUM_CAST(CubeMap::Storage)
class CurveTexture : public Texture {
- GDCLASS(CurveTexture, Texture);
- RES_BASE_EXTENSION("curvetex");
+ GDCLASS(CurveTexture, Texture)
+ RES_BASE_EXTENSION("curvetex")
private:
- RID texture;
- PoolVector<Vector2> points;
- float min, max;
- int width;
+ RID _texture;
+ Ref<Curve> _curve;
+ float _min, _max;
+ int _width;
+
+ void _update();
protected:
static void _bind_methods();
@@ -417,8 +420,10 @@ public:
void set_width(int p_width);
int get_width() const;
- void set_points(const PoolVector<Vector2> &p_points);
- PoolVector<Vector2> get_points() const;
+ void ensure_default_setup();
+
+ void set_curve(Ref<Curve> p_curve);
+ Ref<Curve> get_curve() const;
virtual RID get_rid() const;