Merge pull request #53321 from Geometror/gradient-improvements
This commit is contained in:
commit
38c698c485
8 changed files with 210 additions and 66 deletions
|
@ -51,6 +51,12 @@
|
||||||
Removes the color at the index [code]point[/code].
|
Removes the color at the index [code]point[/code].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="reverse">
|
||||||
|
<return type="void" />
|
||||||
|
<description>
|
||||||
|
Reverses/mirrors the gradient.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="set_color">
|
<method name="set_color">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<argument index="0" name="point" type="int" />
|
<argument index="0" name="point" type="int" />
|
||||||
|
@ -72,8 +78,22 @@
|
||||||
<member name="colors" type="PackedColorArray" setter="set_colors" getter="get_colors" default="PackedColorArray(0, 0, 0, 1, 1, 1, 1, 1)">
|
<member name="colors" type="PackedColorArray" setter="set_colors" getter="get_colors" default="PackedColorArray(0, 0, 0, 1, 1, 1, 1, 1)">
|
||||||
Gradient's colors returned as a [PackedColorArray].
|
Gradient's colors returned as a [PackedColorArray].
|
||||||
</member>
|
</member>
|
||||||
|
<member name="interpolation_mode" type="int" setter="set_interpolation_mode" getter="get_interpolation_mode" enum="Gradient.InterpolationMode" default="0">
|
||||||
|
Defines how the colors between points of the gradient are interpolated. See [enum InterpolationMode] for available modes.
|
||||||
|
</member>
|
||||||
<member name="offsets" type="PackedFloat32Array" setter="set_offsets" getter="get_offsets" default="PackedFloat32Array(0, 1)">
|
<member name="offsets" type="PackedFloat32Array" setter="set_offsets" getter="get_offsets" default="PackedFloat32Array(0, 1)">
|
||||||
Gradient's offsets returned as a [PackedFloat32Array].
|
Gradient's offsets returned as a [PackedFloat32Array].
|
||||||
</member>
|
</member>
|
||||||
</members>
|
</members>
|
||||||
|
<constants>
|
||||||
|
<constant name="GRADIENT_INTERPOLATE_LINEAR" value="0" enum="InterpolationMode">
|
||||||
|
Linear interpolation.
|
||||||
|
</constant>
|
||||||
|
<constant name="GRADIENT_INTERPOLATE_CONSTANT" value="1" enum="InterpolationMode">
|
||||||
|
Constant interpolation, color changes abruptly at each point and stays uniform between. This might cause visible aliasing when used for a gradient texture in some cases.
|
||||||
|
</constant>
|
||||||
|
<constant name="GRADIENT_INTERPOLATE_CUBIC" value="2" enum="InterpolationMode">
|
||||||
|
Cubic interpolation.
|
||||||
|
</constant>
|
||||||
|
</constants>
|
||||||
</class>
|
</class>
|
||||||
|
|
1
editor/icons/ReverseGradient.svg
Normal file
1
editor/icons/ReverseGradient.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><svg version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><linearGradient id="b" x1=".26458" x2="3.9688" y1=".79375" y2=".79375" gradientTransform="scale(3.7795)" gradientUnits="userSpaceOnUse"><stop stop-color="#ccc" offset="0"/><stop stop-color="#ccc" stop-opacity="0" offset="1"/></linearGradient><linearGradient id="a" x1=".26458" x2="3.9688" y1="3.4396" y2="3.4396" gradientTransform="matrix(3.7795 0 0 3.7795 -16 -1.1865e-7)" gradientUnits="userSpaceOnUse"><stop stop-color="#ccc" offset="0"/><stop stop-color="#ccc" stop-opacity="0" offset="1"/></linearGradient></defs><g><rect x="1" y="1" width="14" height="4" ry="1" fill="url(#b)"/><rect transform="scale(-1,1)" x="-15" y="11" width="14" height="4" ry="1" fill="url(#a)" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.7795"/><path d="m6 6 2 4 2-4z" fill="#ccc"/></g></svg>
|
After Width: | Height: | Size: 952 B |
|
@ -46,6 +46,8 @@ void GradientEditor::_gradient_changed() {
|
||||||
editing = true;
|
editing = true;
|
||||||
Vector<Gradient::Point> points = gradient->get_points();
|
Vector<Gradient::Point> points = gradient->get_points();
|
||||||
set_points(points);
|
set_points(points);
|
||||||
|
set_interpolation_mode(gradient->get_interpolation_mode());
|
||||||
|
update();
|
||||||
editing = false;
|
editing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,8 +57,10 @@ void GradientEditor::_ramp_changed() {
|
||||||
undo_redo->create_action(TTR("Gradient Edited"));
|
undo_redo->create_action(TTR("Gradient Edited"));
|
||||||
undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets());
|
undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets());
|
||||||
undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors());
|
undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors());
|
||||||
|
undo_redo->add_do_method(gradient.ptr(), "set_interpolation_mode", get_interpolation_mode());
|
||||||
undo_redo->add_undo_method(gradient.ptr(), "set_offsets", gradient->get_offsets());
|
undo_redo->add_undo_method(gradient.ptr(), "set_offsets", gradient->get_offsets());
|
||||||
undo_redo->add_undo_method(gradient.ptr(), "set_colors", gradient->get_colors());
|
undo_redo->add_undo_method(gradient.ptr(), "set_colors", gradient->get_colors());
|
||||||
|
undo_redo->add_undo_method(gradient.ptr(), "set_interpolation_mode", gradient->get_interpolation_mode());
|
||||||
undo_redo->commit_action();
|
undo_redo->commit_action();
|
||||||
editing = false;
|
editing = false;
|
||||||
}
|
}
|
||||||
|
@ -69,6 +73,14 @@ void GradientEditor::set_gradient(const Ref<Gradient> &p_gradient) {
|
||||||
connect("ramp_changed", callable_mp(this, &GradientEditor::_ramp_changed));
|
connect("ramp_changed", callable_mp(this, &GradientEditor::_ramp_changed));
|
||||||
gradient->connect("changed", callable_mp(this, &GradientEditor::_gradient_changed));
|
gradient->connect("changed", callable_mp(this, &GradientEditor::_gradient_changed));
|
||||||
set_points(gradient->get_points());
|
set_points(gradient->get_points());
|
||||||
|
set_interpolation_mode(gradient->get_interpolation_mode());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GradientEditor::reverse_gradient() {
|
||||||
|
gradient->reverse();
|
||||||
|
set_points(gradient->get_points());
|
||||||
|
emit_signal(SNAME("ramp_changed"));
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
GradientEditor::GradientEditor() {
|
GradientEditor::GradientEditor() {
|
||||||
|
@ -77,6 +89,23 @@ GradientEditor::GradientEditor() {
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
|
|
||||||
|
void GradientReverseButton::_notification(int p_what) {
|
||||||
|
if (p_what == NOTIFICATION_DRAW) {
|
||||||
|
Ref<Texture2D> icon = get_theme_icon(SNAME("ReverseGradient"), SNAME("EditorIcons"));
|
||||||
|
if (is_pressed()) {
|
||||||
|
draw_texture_rect(icon, Rect2(margin, margin, icon->get_width(), icon->get_height()), false, get_theme_color(SNAME("icon_pressed_color"), SNAME("Button")));
|
||||||
|
} else {
|
||||||
|
draw_texture_rect(icon, Rect2(margin, margin, icon->get_width(), icon->get_height()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Size2 GradientReverseButton::get_minimum_size() const {
|
||||||
|
return (get_theme_icon(SNAME("ReverseGradient"), SNAME("EditorIcons"))->get_size() + Size2(margin * 2, margin * 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////
|
||||||
|
|
||||||
bool EditorInspectorPluginGradient::can_handle(Object *p_object) {
|
bool EditorInspectorPluginGradient::can_handle(Object *p_object) {
|
||||||
return Object::cast_to<Gradient>(p_object) != nullptr;
|
return Object::cast_to<Gradient>(p_object) != nullptr;
|
||||||
}
|
}
|
||||||
|
@ -85,9 +114,23 @@ void EditorInspectorPluginGradient::parse_begin(Object *p_object) {
|
||||||
Gradient *gradient = Object::cast_to<Gradient>(p_object);
|
Gradient *gradient = Object::cast_to<Gradient>(p_object);
|
||||||
Ref<Gradient> g(gradient);
|
Ref<Gradient> g(gradient);
|
||||||
|
|
||||||
GradientEditor *editor = memnew(GradientEditor);
|
editor = memnew(GradientEditor);
|
||||||
editor->set_gradient(g);
|
editor->set_gradient(g);
|
||||||
add_custom_control(editor);
|
add_custom_control(editor);
|
||||||
|
|
||||||
|
reverse_btn = memnew(GradientReverseButton);
|
||||||
|
|
||||||
|
gradient_tools_hbox = memnew(HBoxContainer);
|
||||||
|
gradient_tools_hbox->add_child(reverse_btn);
|
||||||
|
|
||||||
|
add_custom_control(gradient_tools_hbox);
|
||||||
|
|
||||||
|
reverse_btn->connect("pressed", callable_mp(this, &EditorInspectorPluginGradient::_reverse_button_pressed));
|
||||||
|
reverse_btn->set_tooltip(TTR("Reverse/mirror gradient."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorInspectorPluginGradient::_reverse_button_pressed() {
|
||||||
|
editor->reverse_gradient();
|
||||||
}
|
}
|
||||||
|
|
||||||
GradientEditorPlugin::GradientEditorPlugin(EditorNode *p_node) {
|
GradientEditorPlugin::GradientEditorPlugin(EditorNode *p_node) {
|
||||||
|
|
|
@ -50,12 +50,28 @@ protected:
|
||||||
public:
|
public:
|
||||||
virtual Size2 get_minimum_size() const override;
|
virtual Size2 get_minimum_size() const override;
|
||||||
void set_gradient(const Ref<Gradient> &p_gradient);
|
void set_gradient(const Ref<Gradient> &p_gradient);
|
||||||
|
void reverse_gradient();
|
||||||
GradientEditor();
|
GradientEditor();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GradientReverseButton : public BaseButton {
|
||||||
|
GDCLASS(GradientReverseButton, BaseButton);
|
||||||
|
|
||||||
|
int margin = 2;
|
||||||
|
|
||||||
|
void _notification(int p_what);
|
||||||
|
virtual Size2 get_minimum_size() const override;
|
||||||
|
};
|
||||||
|
|
||||||
class EditorInspectorPluginGradient : public EditorInspectorPlugin {
|
class EditorInspectorPluginGradient : public EditorInspectorPlugin {
|
||||||
GDCLASS(EditorInspectorPluginGradient, EditorInspectorPlugin);
|
GDCLASS(EditorInspectorPluginGradient, EditorInspectorPlugin);
|
||||||
|
|
||||||
|
GradientEditor *editor;
|
||||||
|
HBoxContainer *gradient_tools_hbox;
|
||||||
|
GradientReverseButton *reverse_btn;
|
||||||
|
|
||||||
|
void _reverse_button_pressed();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual bool can_handle(Object *p_object) override;
|
virtual bool can_handle(Object *p_object) override;
|
||||||
virtual void parse_begin(Object *p_object) override;
|
virtual void parse_begin(Object *p_object) override;
|
||||||
|
|
|
@ -39,6 +39,10 @@ GradientEdit::GradientEdit() {
|
||||||
picker = memnew(ColorPicker);
|
picker = memnew(ColorPicker);
|
||||||
popup->add_child(picker);
|
popup->add_child(picker);
|
||||||
|
|
||||||
|
gradient_cache.instantiate();
|
||||||
|
preview_texture.instantiate();
|
||||||
|
|
||||||
|
preview_texture->set_width(1024);
|
||||||
add_child(popup, false, INTERNAL_MODE_FRONT);
|
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;
|
int total_w = get_size().width - get_size().height - draw_spacing;
|
||||||
float min_distance = 1e20;
|
float min_distance = 1e20;
|
||||||
for (int i = 0; i < points.size(); i++) {
|
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 distance = ABS(x - points[i].offset * total_w);
|
||||||
float min = (draw_point_width / 2 * 1.7); //make it easier to grab
|
float min = (draw_point_width / 2 * 1.7); //make it easier to grab
|
||||||
if (distance <= min && distance < min_distance) {
|
if (distance <= min && distance < min_distance) {
|
||||||
|
@ -94,14 +98,14 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<InputEventMouseButton> mb = 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()) {
|
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);
|
grabbed = _get_point_from_pos(mb->get_position().x);
|
||||||
_show_color_picker();
|
_show_color_picker();
|
||||||
accept_event();
|
accept_event();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Delete point on right click
|
// Delete point on right click.
|
||||||
if (mb.is_valid() && mb->get_button_index() == 2 && mb->is_pressed()) {
|
if (mb.is_valid() && mb->get_button_index() == 2 && mb->is_pressed()) {
|
||||||
grabbed = _get_point_from_pos(mb->get_position().x);
|
grabbed = _get_point_from_pos(mb->get_position().x);
|
||||||
if (grabbed != -1) {
|
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()) {
|
if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed() && mb->is_alt_pressed()) {
|
||||||
int x = mb->get_position().x;
|
int x = mb->get_position().x;
|
||||||
grabbed = _get_point_from_pos(x);
|
grabbed = _get_point_from_pos(x);
|
||||||
|
|
||||||
if (grabbed != -1) {
|
if (grabbed != -1) {
|
||||||
int total_w = get_size().width - get_size().height - draw_spacing;
|
int total_w = get_size().width - get_size().height - draw_spacing;
|
||||||
Gradient::Point newPoint = points[grabbed];
|
Gradient::Point new_point = points[grabbed];
|
||||||
newPoint.offset = CLAMP(x / float(total_w), 0, 1);
|
new_point.offset = CLAMP(x / float(total_w), 0, 1);
|
||||||
|
|
||||||
points.push_back(newPoint);
|
points.push_back(new_point);
|
||||||
points.sort();
|
points.sort();
|
||||||
for (int i = 0; i < points.size(); ++i) {
|
for (int i = 0; i < points.size(); ++i) {
|
||||||
if (points[i].offset == newPoint.offset) {
|
if (points[i].offset == new_point.offset) {
|
||||||
grabbed = i;
|
grabbed = i;
|
||||||
break;
|
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()) {
|
if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
|
||||||
update();
|
update();
|
||||||
int x = mb->get_position().x;
|
int x = mb->get_position().x;
|
||||||
|
@ -158,16 +162,16 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//insert
|
// Insert point.
|
||||||
Gradient::Point newPoint;
|
Gradient::Point new_point;
|
||||||
newPoint.offset = CLAMP(x / float(total_w), 0, 1);
|
new_point.offset = CLAMP(x / float(total_w), 0, 1);
|
||||||
|
|
||||||
Gradient::Point prev;
|
Gradient::Point prev;
|
||||||
Gradient::Point next;
|
Gradient::Point next;
|
||||||
|
|
||||||
int pos = -1;
|
int pos = -1;
|
||||||
for (int i = 0; i < points.size(); i++) {
|
for (int i = 0; i < points.size(); i++) {
|
||||||
if (points[i].offset < newPoint.offset) {
|
if (points[i].offset < new_point.offset) {
|
||||||
pos = i;
|
pos = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,12 +195,12 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
|
||||||
prev = points[pos];
|
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();
|
points.sort();
|
||||||
for (int i = 0; i < points.size(); i++) {
|
for (int i = 0; i < points.size(); i++) {
|
||||||
if (points[i].offset == newPoint.offset) {
|
if (points[i].offset == new_point.offset) {
|
||||||
grabbed = i;
|
grabbed = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -223,7 +227,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
|
||||||
float newofs = CLAMP(x / float(total_w), 0, 1);
|
float newofs = CLAMP(x / float(total_w), 0, 1);
|
||||||
|
|
||||||
// Snap to "round" coordinates if holding Ctrl.
|
// 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()) {
|
if (mm->is_ctrl_pressed()) {
|
||||||
newofs = Math::snapped(newofs, mm->is_shift_pressed() ? 0.025 : 0.1);
|
newofs = Math::snapped(newofs, mm->is_shift_pressed() ? 0.025 : 0.1);
|
||||||
} else if (mm->is_shift_pressed()) {
|
} else if (mm->is_shift_pressed()) {
|
||||||
|
@ -299,57 +303,22 @@ void GradientEdit::_notification(int p_what) {
|
||||||
int h = get_size().y;
|
int h = get_size().y;
|
||||||
|
|
||||||
if (w == 0 || h == 0) {
|
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;
|
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_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true);
|
||||||
|
|
||||||
//Draw color ramp
|
// 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.
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = -1; i < points.size(); i++) {
|
gradient_cache->set_points(points);
|
||||||
Gradient::Point next;
|
gradient_cache->set_interpolation_mode(interpolation_mode);
|
||||||
//If there is no next point
|
preview_texture->set_gradient(gradient_cache);
|
||||||
if (i + 1 == points.size()) {
|
draw_texture_rect(preview_texture, Rect2(0, 0, total_w, h));
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev.offset == next.offset) {
|
// Draw point markers.
|
||||||
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
|
|
||||||
for (int i = 0; i < points.size(); i++) {
|
for (int i = 0; i < points.size(); i++) {
|
||||||
Color col = points[i].color.inverted();
|
Color col = points[i].color.inverted();
|
||||||
col.a = 0.9;
|
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_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()) {
|
if (has_focus()) {
|
||||||
draw_line(Vector2(-1, -1), Vector2(total_w + 1, -1), Color(1, 1, 1, 0.6));
|
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));
|
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.clear();
|
||||||
points = p_points;
|
points = p_points;
|
||||||
|
points.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Gradient::Point> &GradientEdit::get_points() {
|
Vector<Gradient::Point> &GradientEdit::get_points() {
|
||||||
return 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() {
|
void GradientEdit::_bind_methods() {
|
||||||
ADD_SIGNAL(MethodInfo("ramp_changed"));
|
ADD_SIGNAL(MethodInfo("ramp_changed"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,10 @@ class GradientEdit : public Control {
|
||||||
bool grabbing = false;
|
bool grabbing = false;
|
||||||
int grabbed = -1;
|
int grabbed = -1;
|
||||||
Vector<Gradient::Point> points;
|
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.
|
// Make sure to use the scaled value below.
|
||||||
const int BASE_SPACING = 3;
|
const int BASE_SPACING = 3;
|
||||||
|
@ -69,6 +73,9 @@ public:
|
||||||
Vector<Color> get_colors() const;
|
Vector<Color> get_colors() const;
|
||||||
void set_points(Vector<Gradient::Point> &p_points);
|
void set_points(Vector<Gradient::Point> &p_points);
|
||||||
Vector<Gradient::Point> &get_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;
|
virtual Size2 get_minimum_size() const override;
|
||||||
|
|
||||||
GradientEdit();
|
GradientEdit();
|
||||||
|
|
|
@ -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("set_offset", "point", "offset"), &Gradient::set_offset);
|
||||||
ClassDB::bind_method(D_METHOD("get_offset", "point"), &Gradient::get_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("set_color", "point", "color"), &Gradient::set_color);
|
||||||
ClassDB::bind_method(D_METHOD("get_color", "point"), &Gradient::get_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("set_colors", "colors"), &Gradient::set_colors);
|
||||||
ClassDB::bind_method(D_METHOD("get_colors"), &Gradient::get_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_FLOAT32_ARRAY, "offsets"), "set_offsets", "get_offsets");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "colors"), "set_colors", "get_colors");
|
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 {
|
Vector<float> Gradient::get_offsets() const {
|
||||||
|
@ -86,6 +98,15 @@ Vector<Color> Gradient::get_colors() const {
|
||||||
return colors;
|
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) {
|
void Gradient::set_offsets(const Vector<float> &p_offsets) {
|
||||||
points.resize(p_offsets.size());
|
points.resize(p_offsets.size());
|
||||||
for (int i = 0; i < points.size(); i++) {
|
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);
|
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) {
|
void Gradient::set_points(Vector<Gradient::Point> &p_points) {
|
||||||
points = p_points;
|
points = p_points;
|
||||||
is_sorted = false;
|
is_sorted = false;
|
||||||
|
|
|
@ -38,6 +38,12 @@ class Gradient : public Resource {
|
||||||
OBJ_SAVE_TYPE(Gradient);
|
OBJ_SAVE_TYPE(Gradient);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum InterpolationMode {
|
||||||
|
GRADIENT_INTERPOLATE_LINEAR,
|
||||||
|
GRADIENT_INTERPOLATE_CONSTANT,
|
||||||
|
GRADIENT_INTERPOLATE_CUBIC,
|
||||||
|
};
|
||||||
|
|
||||||
struct Point {
|
struct Point {
|
||||||
float offset = 0.0;
|
float offset = 0.0;
|
||||||
Color color;
|
Color color;
|
||||||
|
@ -49,6 +55,8 @@ public:
|
||||||
private:
|
private:
|
||||||
Vector<Point> points;
|
Vector<Point> points;
|
||||||
bool is_sorted = true;
|
bool is_sorted = true;
|
||||||
|
InterpolationMode interpolation_mode = GRADIENT_INTERPOLATE_LINEAR;
|
||||||
|
|
||||||
_FORCE_INLINE_ void _update_sorting() {
|
_FORCE_INLINE_ void _update_sorting() {
|
||||||
if (!is_sorted) {
|
if (!is_sorted) {
|
||||||
points.sort();
|
points.sort();
|
||||||
|
@ -65,9 +73,9 @@ public:
|
||||||
|
|
||||||
void add_point(float p_offset, const Color &p_color);
|
void add_point(float p_offset, const Color &p_color);
|
||||||
void remove_point(int p_index);
|
void remove_point(int p_index);
|
||||||
|
|
||||||
void set_points(Vector<Point> &p_points);
|
void set_points(Vector<Point> &p_points);
|
||||||
Vector<Point> &get_points();
|
Vector<Point> &get_points();
|
||||||
|
void reverse();
|
||||||
|
|
||||||
void set_offset(int pos, const float offset);
|
void set_offset(int pos, const float offset);
|
||||||
float get_offset(int pos);
|
float get_offset(int pos);
|
||||||
|
@ -81,6 +89,13 @@ public:
|
||||||
void set_colors(const Vector<Color> &p_colors);
|
void set_colors(const Vector<Color> &p_colors);
|
||||||
Vector<Color> get_colors() const;
|
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) {
|
_FORCE_INLINE_ Color get_color_at_offset(float p_offset) {
|
||||||
if (points.is_empty()) {
|
if (points.is_empty()) {
|
||||||
return Color(0, 0, 0, 1);
|
return Color(0, 0, 0, 1);
|
||||||
|
@ -88,7 +103,7 @@ public:
|
||||||
|
|
||||||
_update_sorting();
|
_update_sorting();
|
||||||
|
|
||||||
//binary search
|
// Binary search.
|
||||||
int low = 0;
|
int low = 0;
|
||||||
int high = points.size() - 1;
|
int high = points.size() - 1;
|
||||||
int middle = 0;
|
int middle = 0;
|
||||||
|
@ -111,7 +126,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//return interpolated value
|
// Return interpolated value.
|
||||||
if (points[middle].offset > p_offset) {
|
if (points[middle].offset > p_offset) {
|
||||||
middle--;
|
middle--;
|
||||||
}
|
}
|
||||||
|
@ -125,10 +140,44 @@ public:
|
||||||
}
|
}
|
||||||
const Point &pointFirst = points[first];
|
const Point &pointFirst = points[first];
|
||||||
const Point &pointSecond = points[second];
|
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;
|
int get_points_count() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
VARIANT_ENUM_CAST(Gradient::InterpolationMode);
|
||||||
|
|
||||||
#endif // GRADIENT_H
|
#endif // GRADIENT_H
|
||||||
|
|
Loading…
Reference in a new issue