Add interpolation modes to Gradient
- Linear, Constant, and Cubic interpolation modes - Partial backport of #53321
This commit is contained in:
parent
288370609c
commit
f17c5fa7bd
6 changed files with 110 additions and 41 deletions
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
@ -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++) {
|
||||
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
@ -64,6 +68,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;
|
||||
|
||||
GradientEdit();
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
|
@ -49,6 +55,8 @@ public:
|
|||
private:
|
||||
Vector<Point> points;
|
||||
bool is_sorted;
|
||||
InterpolationMode interpolation_mode = GRADIENT_INTERPOLATE_LINEAR;
|
||||
|
||||
_FORCE_INLINE_ void _update_sorting() {
|
||||
if (!is_sorted) {
|
||||
points.sort();
|
||||
|
@ -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.empty()) {
|
||||
return Color(0, 0, 0, 1);
|
||||
|
@ -124,10 +139,44 @@ public:
|
|||
}
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue