Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Add interpolation modes to Gradient #60982

Merged
merged 1 commit into from
May 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions doc/classes/Gradient.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,22 @@
<member name="colors" type="PoolColorArray" setter="set_colors" getter="get_colors" default="PoolColorArray( 0, 0, 0, 1, 1, 1, 1, 1 )">
Gradient's colors returned as a [PoolColorArray].
</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="PoolRealArray" setter="set_offsets" getter="get_offsets" default="PoolRealArray( 0, 1 )">
Gradient's offsets returned as a [PoolRealArray].
</member>
</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>
5 changes: 5 additions & 0 deletions editor/plugins/gradient_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ void GradientEditor::_gradient_changed() {
editing = true;
Vector<Gradient::Point> points = gradient->get_points();
set_points(points);
set_interpolation_mode(gradient->get_interpolation_mode());
update();
editing = false;
}

Expand All @@ -54,8 +56,10 @@ void GradientEditor::_ramp_changed() {
undo_redo->create_action(TTR("Gradient Edited"), UndoRedo::MERGE_ENDS);
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_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_colors", gradient->get_colors());
undo_redo->add_undo_method(gradient.ptr(), "set_interpolation_mode", gradient->get_interpolation_mode());
undo_redo->commit_action();
editing = false;
}
Expand All @@ -70,6 +74,7 @@ void GradientEditor::set_gradient(const Ref<Gradient> &p_gradient) {
connect("ramp_changed", this, "_ramp_changed");
gradient->connect("changed", this, "_gradient_changed");
set_points(gradient->get_points());
set_interpolation_mode(gradient->get_interpolation_mode());
}

GradientEditor::GradientEditor() {
Expand Down
57 changes: 17 additions & 40 deletions scene/gui/gradient_edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ GradientEdit::GradientEdit() {

add_child(popup);

gradient_cache.instance();
preview_texture.instance();

preview_texture->set_width(1024);

checker = Ref<ImageTexture>(memnew(ImageTexture));
Ref<Image> img = memnew(Image(checker_bg_png));
checker->create_from_image(img, ImageTexture::FLAG_REPEAT);
Expand Down Expand Up @@ -315,46 +320,10 @@ void GradientEdit::_notification(int p_what) {
_draw_checker(0, 0, total_w, h);

//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::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];
}

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;
}
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));

//Draw point markers
for (int i = 0; i < points.size(); i++) {
Expand Down Expand Up @@ -482,6 +451,14 @@ 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() {
ClassDB::bind_method(D_METHOD("_gui_input"), &GradientEdit::_gui_input);
ClassDB::bind_method(D_METHOD("_color_changed"), &GradientEdit::_color_changed);
Expand Down
7 changes: 7 additions & 0 deletions scene/gui/gradient_edit.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ class GradientEdit : public Control {
bool grabbing;
int grabbed;
Vector<Gradient::Point> points;
Gradient::InterpolationMode interpolation_mode = Gradient::GRADIENT_INTERPOLATE_LINEAR;

Ref<Gradient> gradient_cache;
Ref<GradientTexture> preview_texture;

void _draw_checker(int x, int y, int w, int h);
void _color_changed(const Color &p_color);
Expand All @@ -64,6 +68,9 @@ class GradientEdit : public Control {
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;

GradientEdit();
Expand Down
19 changes: 19 additions & 0 deletions scene/resources/gradient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,18 @@ void Gradient::_bind_methods() {
ClassDB::bind_method(D_METHOD(COLOR_RAMP_SET_COLORS, "colors"), &Gradient::set_colors);
ClassDB::bind_method(D_METHOD(COLOR_RAMP_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::POOL_REAL_ARRAY, "offsets"), COLOR_RAMP_SET_OFFSETS, COLOR_RAMP_GET_OFFSETS);
ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "colors"), COLOR_RAMP_SET_COLORS, COLOR_RAMP_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 {
Expand All @@ -93,6 +103,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++) {
Expand Down
51 changes: 50 additions & 1 deletion scene/resources/gradient.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Color color;
Expand All @@ -49,6 +55,8 @@ class Gradient : public Resource {
private:
Vector<Point> points;
bool is_sorted;
InterpolationMode interpolation_mode = GRADIENT_INTERPOLATE_LINEAR;

_FORCE_INLINE_ void _update_sorting() {
if (!is_sorted) {
points.sort();
Expand Down Expand Up @@ -81,6 +89,13 @@ class Gradient : public Resource {
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.empty()) {
return Color(0, 0, 0, 1);
Expand Down Expand Up @@ -124,10 +139,44 @@ class Gradient : public Resource {
}
const Point &pointFirst = points[first];
const Point &pointSecond = points[second];
return pointFirst.color.linear_interpolate(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset));

switch (interpolation_mode) {
case GRADIENT_INTERPOLATE_LINEAR: {
return pointFirst.color.linear_interpolate(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.linear_interpolate(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset));
}
}
}

int get_points_count() const;
};

VARIANT_ENUM_CAST(Gradient::InterpolationMode);

#endif // GRADIENT_H