summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/gui/gradient_edit.cpp100
-rw-r--r--scene/gui/gradient_edit.h7
-rw-r--r--scene/resources/gradient.cpp30
-rw-r--r--scene/resources/gradient.h57
4 files changed, 129 insertions, 65 deletions
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index 5d024d3be7..e5b23d8006 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -39,6 +39,10 @@ GradientEdit::GradientEdit() {
picker = memnew(ColorPicker);
popup->add_child(picker);
+ gradient_cache.instantiate();
+ preview_texture.instantiate();
+
+ preview_texture->set_width(1024);
add_child(popup, false, INTERNAL_MODE_FRONT);
}
@@ -47,7 +51,7 @@ int GradientEdit::_get_point_from_pos(int x) {
int total_w = get_size().width - get_size().height - draw_spacing;
float min_distance = 1e20;
for (int i = 0; i < points.size(); i++) {
- //Check if we clicked at point
+ // Check if we clicked at point.
float distance = ABS(x - points[i].offset * total_w);
float min = (draw_point_width / 2 * 1.7); //make it easier to grab
if (distance <= min && distance < min_distance) {
@@ -94,14 +98,14 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
}
Ref<InputEventMouseButton> mb = p_event;
- //Show color picker on double click.
+ // Show color picker on double click.
if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_double_click() && mb->is_pressed()) {
grabbed = _get_point_from_pos(mb->get_position().x);
_show_color_picker();
accept_event();
}
- //Delete point on right click
+ // Delete point on right click.
if (mb.is_valid() && mb->get_button_index() == 2 && mb->is_pressed()) {
grabbed = _get_point_from_pos(mb->get_position().x);
if (grabbed != -1) {
@@ -114,20 +118,20 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
- //Hold alt key to duplicate selected color
+ // Hold alt key to duplicate selected color.
if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed() && mb->is_alt_pressed()) {
int x = mb->get_position().x;
grabbed = _get_point_from_pos(x);
if (grabbed != -1) {
int total_w = get_size().width - get_size().height - draw_spacing;
- Gradient::Point newPoint = points[grabbed];
- newPoint.offset = CLAMP(x / float(total_w), 0, 1);
+ Gradient::Point new_point = points[grabbed];
+ new_point.offset = CLAMP(x / float(total_w), 0, 1);
- points.push_back(newPoint);
+ points.push_back(new_point);
points.sort();
for (int i = 0; i < points.size(); ++i) {
- if (points[i].offset == newPoint.offset) {
+ if (points[i].offset == new_point.offset) {
grabbed = i;
break;
}
@@ -138,7 +142,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
- //select
+ // Select.
if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
update();
int x = mb->get_position().x;
@@ -158,16 +162,16 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- //insert
- Gradient::Point newPoint;
- newPoint.offset = CLAMP(x / float(total_w), 0, 1);
+ // Insert point.
+ Gradient::Point new_point;
+ new_point.offset = CLAMP(x / float(total_w), 0, 1);
Gradient::Point prev;
Gradient::Point next;
int pos = -1;
for (int i = 0; i < points.size(); i++) {
- if (points[i].offset < newPoint.offset) {
+ if (points[i].offset < new_point.offset) {
pos = i;
}
}
@@ -191,12 +195,12 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
prev = points[pos];
}
- newPoint.color = prev.color.lerp(next.color, (newPoint.offset - prev.offset) / (next.offset - prev.offset));
+ new_point.color = prev.color.lerp(next.color, (new_point.offset - prev.offset) / (next.offset - prev.offset));
- points.push_back(newPoint);
+ points.push_back(new_point);
points.sort();
for (int i = 0; i < points.size(); i++) {
- if (points[i].offset == newPoint.offset) {
+ if (points[i].offset == new_point.offset) {
grabbed = i;
break;
}
@@ -223,7 +227,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
float newofs = CLAMP(x / float(total_w), 0, 1);
// Snap to "round" coordinates if holding Ctrl.
- // Be more precise if holding Shift as well
+ // Be more precise if holding Shift as well.
if (mm->is_ctrl_pressed()) {
newofs = Math::snapped(newofs, mm->is_shift_pressed() ? 0.025 : 0.1);
} else if (mm->is_shift_pressed()) {
@@ -299,57 +303,22 @@ void GradientEdit::_notification(int p_what) {
int h = get_size().y;
if (w == 0 || h == 0) {
- return; //Safety check. We have division by 'h'. And in any case there is nothing to draw with such size
+ return; // Safety check. We have division by 'h'. And in any case there is nothing to draw with such size.
}
int total_w = get_size().width - get_size().height - draw_spacing;
- //Draw checker pattern for ramp
+ // Draw checker pattern for ramp.
draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true);
- //Draw color ramp
- Gradient::Point prev;
- prev.offset = 0;
- if (points.size() == 0) {
- prev.color = Color(0, 0, 0); //Draw black rectangle if we have no points
- } else {
- prev.color = points[0].color; //Extend color of first point to the beginning.
- }
+ // Draw color ramp.
- for (int i = -1; i < points.size(); i++) {
- Gradient::Point next;
- //If there is no next point
- if (i + 1 == points.size()) {
- if (points.size() == 0) {
- next.color = Color(0, 0, 0); //Draw black rectangle if we have no points
- } else {
- next.color = points[i].color; //Extend color of last point to the end.
- }
- next.offset = 1;
- } else {
- next = points[i + 1];
- }
+ gradient_cache->set_points(points);
+ gradient_cache->set_interpolation_mode(interpolation_mode);
+ preview_texture->set_gradient(gradient_cache);
+ draw_texture_rect(preview_texture, Rect2(0, 0, total_w, h));
- if (prev.offset == next.offset) {
- prev = next;
- continue;
- }
-
- Vector<Vector2> points;
- Vector<Color> colors;
- points.push_back(Vector2(prev.offset * total_w, h));
- points.push_back(Vector2(prev.offset * total_w, 0));
- points.push_back(Vector2(next.offset * total_w, 0));
- points.push_back(Vector2(next.offset * total_w, h));
- colors.push_back(prev.color);
- colors.push_back(prev.color);
- colors.push_back(next.color);
- colors.push_back(next.color);
- draw_primitive(points, colors, Vector<Point2>());
- prev = next;
- }
-
- //Draw point markers
+ // Draw point markers.
for (int i = 0; i < points.size(); i++) {
Color col = points[i].color.inverted();
col.a = 0.9;
@@ -383,7 +352,7 @@ void GradientEdit::_notification(int p_what) {
draw_line(Vector2(total_w + draw_spacing, h), Vector2(total_w + draw_spacing + h, 0), Color(1, 1, 1, 0.6));
}
- //Draw borders around color ramp if in focus
+ // Draw borders around color ramp if in focus.
if (has_focus()) {
draw_line(Vector2(-1, -1), Vector2(total_w + 1, -1), Color(1, 1, 1, 0.6));
draw_line(Vector2(total_w + 1, -1), Vector2(total_w + 1, h + 1), Color(1, 1, 1, 0.6));
@@ -448,12 +417,21 @@ void GradientEdit::set_points(Vector<Gradient::Point> &p_points) {
}
points.clear();
points = p_points;
+ points.sort();
}
Vector<Gradient::Point> &GradientEdit::get_points() {
return points;
}
+void GradientEdit::set_interpolation_mode(Gradient::InterpolationMode p_interp_mode) {
+ interpolation_mode = p_interp_mode;
+}
+
+Gradient::InterpolationMode GradientEdit::get_interpolation_mode() {
+ return interpolation_mode;
+}
+
void GradientEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("ramp_changed"));
}
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index f3a39daaf6..66b60d87c7 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -45,6 +45,10 @@ class GradientEdit : public Control {
bool grabbing = false;
int grabbed = -1;
Vector<Gradient::Point> points;
+ Gradient::InterpolationMode interpolation_mode = Gradient::GRADIENT_INTERPOLATE_LINEAR;
+
+ Ref<Gradient> gradient_cache;
+ Ref<GradientTexture1D> preview_texture;
// Make sure to use the scaled value below.
const int BASE_SPACING = 3;
@@ -69,6 +73,9 @@ public:
Vector<Color> get_colors() const;
void set_points(Vector<Gradient::Point> &p_points);
Vector<Gradient::Point> &get_points();
+ void set_interpolation_mode(Gradient::InterpolationMode p_interp_mode);
+ Gradient::InterpolationMode get_interpolation_mode();
+
virtual Size2 get_minimum_size() const override;
GradientEdit();
diff --git a/scene/resources/gradient.cpp b/scene/resources/gradient.cpp
index 7b9b942142..4559b4ce0a 100644
--- a/scene/resources/gradient.cpp
+++ b/scene/resources/gradient.cpp
@@ -51,6 +51,8 @@ void Gradient::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_offset", "point", "offset"), &Gradient::set_offset);
ClassDB::bind_method(D_METHOD("get_offset", "point"), &Gradient::get_offset);
+ ClassDB::bind_method(D_METHOD("reverse"), &Gradient::reverse);
+
ClassDB::bind_method(D_METHOD("set_color", "point", "color"), &Gradient::set_color);
ClassDB::bind_method(D_METHOD("get_color", "point"), &Gradient::get_color);
@@ -64,8 +66,18 @@ void Gradient::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_colors", "colors"), &Gradient::set_colors);
ClassDB::bind_method(D_METHOD("get_colors"), &Gradient::get_colors);
+ ClassDB::bind_method(D_METHOD("set_interpolation_mode", "interpolation_mode"), &Gradient::set_interpolation_mode);
+ ClassDB::bind_method(D_METHOD("get_interpolation_mode"), &Gradient::get_interpolation_mode);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "interpolation_mode", PROPERTY_HINT_ENUM, "Linear,Constant,Cubic"), "set_interpolation_mode", "get_interpolation_mode");
+
+ ADD_GROUP("Raw data", "");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "offsets"), "set_offsets", "get_offsets");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "colors"), "set_colors", "get_colors");
+
+ BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_LINEAR);
+ BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_CONSTANT);
+ BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_CUBIC);
}
Vector<float> Gradient::get_offsets() const {
@@ -86,6 +98,15 @@ Vector<Color> Gradient::get_colors() const {
return colors;
}
+void Gradient::set_interpolation_mode(Gradient::InterpolationMode p_interp_mode) {
+ interpolation_mode = p_interp_mode;
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
+Gradient::InterpolationMode Gradient::get_interpolation_mode() {
+ return interpolation_mode;
+}
+
void Gradient::set_offsets(const Vector<float> &p_offsets) {
points.resize(p_offsets.size());
for (int i = 0; i < points.size(); i++) {
@@ -127,6 +148,15 @@ void Gradient::remove_point(int p_index) {
emit_signal(CoreStringNames::get_singleton()->changed);
}
+void Gradient::reverse() {
+ for (int i = 0; i < points.size(); i++) {
+ points.write[i].offset = 1.0 - points[i].offset;
+ }
+
+ _update_sorting();
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
void Gradient::set_points(Vector<Gradient::Point> &p_points) {
points = p_points;
is_sorted = false;
diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h
index cf5b179c45..eb438d0bba 100644
--- a/scene/resources/gradient.h
+++ b/scene/resources/gradient.h
@@ -38,6 +38,12 @@ class Gradient : public Resource {
OBJ_SAVE_TYPE(Gradient);
public:
+ enum InterpolationMode {
+ GRADIENT_INTERPOLATE_LINEAR,
+ GRADIENT_INTERPOLATE_CONSTANT,
+ GRADIENT_INTERPOLATE_CUBIC,
+ };
+
struct Point {
float offset = 0.0;
Color color;
@@ -49,6 +55,8 @@ public:
private:
Vector<Point> points;
bool is_sorted = true;
+ InterpolationMode interpolation_mode = GRADIENT_INTERPOLATE_LINEAR;
+
_FORCE_INLINE_ void _update_sorting() {
if (!is_sorted) {
points.sort();
@@ -65,9 +73,9 @@ public:
void add_point(float p_offset, const Color &p_color);
void remove_point(int p_index);
-
void set_points(Vector<Point> &p_points);
Vector<Point> &get_points();
+ void reverse();
void set_offset(int pos, const float offset);
float get_offset(int pos);
@@ -81,6 +89,13 @@ public:
void set_colors(const Vector<Color> &p_colors);
Vector<Color> get_colors() const;
+ void set_interpolation_mode(InterpolationMode p_interp_mode);
+ InterpolationMode get_interpolation_mode();
+
+ _FORCE_INLINE_ float cubic_interpolate(float p0, float p1, float p2, float p3, float x) {
+ return p1 + 0.5 * x * (p2 - p0 + x * (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3 + x * (3.0 * (p1 - p2) + p3 - p0)));
+ }
+
_FORCE_INLINE_ Color get_color_at_offset(float p_offset) {
if (points.is_empty()) {
return Color(0, 0, 0, 1);
@@ -88,7 +103,7 @@ public:
_update_sorting();
- //binary search
+ // Binary search.
int low = 0;
int high = points.size() - 1;
int middle = 0;
@@ -111,7 +126,7 @@ public:
}
}
- //return interpolated value
+ // Return interpolated value.
if (points[middle].offset > p_offset) {
middle--;
}
@@ -125,10 +140,44 @@ public:
}
const Point &pointFirst = points[first];
const Point &pointSecond = points[second];
- return pointFirst.color.lerp(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset));
+
+ switch (interpolation_mode) {
+ case GRADIENT_INTERPOLATE_LINEAR: {
+ return pointFirst.color.lerp(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset));
+ } break;
+ case GRADIENT_INTERPOLATE_CONSTANT: {
+ return pointFirst.color;
+ } break;
+ case GRADIENT_INTERPOLATE_CUBIC: {
+ int p0 = first - 1;
+ int p3 = second + 1;
+ if (p3 >= points.size()) {
+ p3 = second;
+ }
+ if (p0 < 0) {
+ p0 = first;
+ }
+ const Point &pointP0 = points[p0];
+ const Point &pointP3 = points[p3];
+
+ float x = (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset);
+ float r = cubic_interpolate(pointP0.color.r, pointFirst.color.r, pointSecond.color.r, pointP3.color.r, x);
+ float g = cubic_interpolate(pointP0.color.g, pointFirst.color.g, pointSecond.color.g, pointP3.color.g, x);
+ float b = cubic_interpolate(pointP0.color.b, pointFirst.color.b, pointSecond.color.b, pointP3.color.b, x);
+ float a = cubic_interpolate(pointP0.color.a, pointFirst.color.a, pointSecond.color.a, pointP3.color.a, x);
+
+ return Color(r, g, b, a);
+ } break;
+ default: {
+ // Fallback to linear interpolation.
+ return pointFirst.color.lerp(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset));
+ }
+ }
}
int get_points_count() const;
};
+VARIANT_ENUM_CAST(Gradient::InterpolationMode);
+
#endif // GRADIENT_H