diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 5681613c04cb..214230d79fe2 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -32,6 +32,7 @@ #include "core/core_string_names.h" #include "core/io/image_loader.h" +#include "core/math/geometry_2d.h" #include "core/method_bind_ext.gen.inc" #include "core/os/os.h" #include "mesh.h" @@ -1444,6 +1445,13 @@ CurveTexture::~CurveTexture() { GradientTexture::GradientTexture() { update_pending = false; width = 2048; + height = 1; + + fill_from = Vector2(); + fill_to = Vector2(1, 0); + + fill_method = GradientTextureFillMethod::FILL_LINEAR; + repeat_method = GradientTextureRepeatMethod::NO_REPEAT; _queue_update(); } @@ -1459,11 +1467,35 @@ void GradientTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture::get_gradient); ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture::set_width); + ClassDB::bind_method(D_METHOD("set_height", "height"), &GradientTexture::set_height); + + ClassDB::bind_method(D_METHOD("set_fill_from", "fill_from"), &GradientTexture::set_fill_from); + ClassDB::bind_method(D_METHOD("get_fill_from"), &GradientTexture::get_fill_from); + ClassDB::bind_method(D_METHOD("set_fill_to", "fill_to"), &GradientTexture::set_fill_to); + ClassDB::bind_method(D_METHOD("get_fill_to"), &GradientTexture::get_fill_to); + + ClassDB::bind_method(D_METHOD("set_fill_method", "fill_method"), &GradientTexture::set_fill_method); + ClassDB::bind_method(D_METHOD("get_fill_method"), &GradientTexture::get_fill_method); + + ClassDB::bind_method(D_METHOD("set_repeat_method", "repeat_method"), &GradientTexture::set_repeat_method); + ClassDB::bind_method(D_METHOD("get_repeat_method"), &GradientTexture::get_repeat_method); ClassDB::bind_method(D_METHOD("_update"), &GradientTexture::_update); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient"); ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fill_from"), "set_fill_from", "get_fill_from"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fill_to"), "set_fill_to", "get_fill_to"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_method", PROPERTY_HINT_ENUM, "Fill Linear,Fill Radial"), "set_fill_method", "get_fill_method"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "repeat_method", PROPERTY_HINT_ENUM, "No Repeat,Repeat,Repeat Mirror"), "set_repeat_method", "get_repeat_method"); + + BIND_ENUM_CONSTANT(FILL_LINEAR); + BIND_ENUM_CONSTANT(FILL_RADIAL); + + BIND_ENUM_CONSTANT(NO_REPEAT); + BIND_ENUM_CONSTANT(REPEAT); + BIND_ENUM_CONSTANT(REPEAT_MIRROR); } void GradientTexture::set_gradient(Ref p_gradient) { @@ -1502,23 +1534,25 @@ void GradientTexture::_update() { } Vector data; - data.resize(width * 4); + data.resize(width * height * 4); { uint8_t *wd8 = data.ptrw(); Gradient &g = **gradient; - for (int i = 0; i < width; i++) { - float ofs = float(i) / (width - 1); - Color color = g.get_color_at_offset(ofs); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + float ofs = get_gradient_offset_at(x, y); + Color color = g.get_color_at_offset(ofs); - wd8[i * 4 + 0] = uint8_t(CLAMP(color.r * 255.0, 0, 255)); - wd8[i * 4 + 1] = uint8_t(CLAMP(color.g * 255.0, 0, 255)); - wd8[i * 4 + 2] = uint8_t(CLAMP(color.b * 255.0, 0, 255)); - wd8[i * 4 + 3] = uint8_t(CLAMP(color.a * 255.0, 0, 255)); + wd8[(x + (y * width)) * 4 + 0] = uint8_t(CLAMP(color.r * 255.0, 0, 255)); + wd8[(x + (y * width)) * 4 + 1] = uint8_t(CLAMP(color.g * 255.0, 0, 255)); + wd8[(x + (y * width)) * 4 + 2] = uint8_t(CLAMP(color.b * 255.0, 0, 255)); + wd8[(x + (y * width)) * 4 + 3] = uint8_t(CLAMP(color.a * 255.0, 0, 255)); + } } } - Ref image = memnew(Image(width, 1, false, Image::FORMAT_RGBA8, data)); + Ref image = memnew(Image(width, height, false, Image::FORMAT_RGBA8, data)); if (texture.is_valid()) { RID new_texture = RS::get_singleton()->texture_2d_create(image); @@ -1530,6 +1564,44 @@ void GradientTexture::_update() { emit_changed(); } +float GradientTexture::get_gradient_offset_at(int x, int y) const { + if (fill_to == fill_from) + return 0; + float off = 0; + Vector2 pos; + if (width > 1) + pos.x = (float)x / (float)(width - 1); + if (height > 1) + pos.y = (float)y / (float)(height - 1); + if (fill_method == GradientTextureFillMethod::FILL_LINEAR) { + Vector2 segment[2]; + segment[0] = fill_from; + segment[1] = fill_to; + Vector2 closest = Geometry2D::get_closest_point_to_segment_uncapped(pos, &segment[0]); + off = (closest - fill_from).length() / (fill_to - fill_from).length(); + if ((closest - fill_from).dot(fill_to - fill_from) < 0) + off *= -1; + } else if (fill_method == GradientTextureFillMethod::FILL_RADIAL) { + off = (pos - fill_from).length() / (fill_to - fill_from).length(); + } + + if (repeat_method == GradientTextureRepeatMethod::NO_REPEAT) { + off = CLAMP(off, 0.0, 1.0); + } else if (repeat_method == GradientTextureRepeatMethod::REPEAT) { + off = Math::fmod(off, (float)1.0); + if (off < 0) + off = 1 + off; + } else if (repeat_method == GradientTextureRepeatMethod::REPEAT_MIRROR) { + off = Math::abs(off); + off = Math::fmod(off, (float)2.0); + if (off > 1.0) { + off = 2.0 - off; + } + } + + return off; +} + void GradientTexture::set_width(int p_width) { width = p_width; _queue_update(); @@ -1539,6 +1611,50 @@ int GradientTexture::get_width() const { return width; } +void GradientTexture::set_height(int p_height) { + height = p_height; + _queue_update(); +} +int GradientTexture::get_height() const { + return height; +} + +void GradientTexture::set_fill_from(Vector2 p_fill_from) { + fill_from = p_fill_from; + _queue_update(); +} + +Vector2 GradientTexture::get_fill_from() const { + return fill_from; +} + +void GradientTexture::set_fill_to(Vector2 p_fill_to) { + fill_to = p_fill_to; + _queue_update(); +} + +Vector2 GradientTexture::get_fill_to() const { + return fill_to; +} + +void GradientTexture::set_fill_method(GradientTextureFillMethod p_fill_method) { + fill_method = p_fill_method; + _queue_update(); +} + +GradientTexture::GradientTextureFillMethod GradientTexture::get_fill_method() const { + return fill_method; +} + +void GradientTexture::set_repeat_method(GradientTextureRepeatMethod p_repeat_method) { + repeat_method = p_repeat_method; + _queue_update(); +} + +GradientTexture::GradientTextureRepeatMethod GradientTexture::get_repeat_method() const { + return repeat_method; +} + Ref GradientTexture::get_data() const { if (!texture.is_valid()) { return Ref(); diff --git a/scene/resources/texture.h b/scene/resources/texture.h index fd213859b732..4d1c715b4ec2 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -572,12 +572,32 @@ class GradientTexture : public Texture2D { } }; + enum GradientTextureFillMethod { + FILL_LINEAR, + FILL_RADIAL + }; + + enum GradientTextureRepeatMethod { + NO_REPEAT, + REPEAT, + REPEAT_MIRROR + }; + private: Ref gradient; bool update_pending; RID texture; + int width; + int height; + + Vector2 fill_from; + Vector2 fill_to; + + GradientTextureFillMethod fill_method; + GradientTextureRepeatMethod repeat_method; + float get_gradient_offset_at(int x, int y) const; void _queue_update(); void _update(); @@ -590,17 +610,31 @@ class GradientTexture : public Texture2D { void set_width(int p_width); int get_width() const override; + void set_height(int p_height); + int get_height() const override; + + void set_fill_from(Vector2 p_fill_from); + Vector2 get_fill_from() const; + void set_fill_to(Vector2 p_fill_to); + Vector2 get_fill_to() const; + + void set_fill_method(GradientTextureFillMethod p_fill_method); + GradientTextureFillMethod get_fill_method() const; + + void set_repeat_method(GradientTextureRepeatMethod p_repeat_method); + GradientTextureRepeatMethod get_repeat_method() const; virtual RID get_rid() const override { return texture; } - virtual int get_height() const override { return 1; } virtual bool has_alpha() const override { return true; } - virtual Ref get_data() const override; GradientTexture(); virtual ~GradientTexture(); }; +VARIANT_ENUM_CAST(GradientTexture::GradientTextureFillMethod); +VARIANT_ENUM_CAST(GradientTexture::GradientTextureRepeatMethod); + class ProxyTexture : public Texture2D { GDCLASS(ProxyTexture, Texture2D);