Skip to content

Commit

Permalink
Improve GradientTexture to allow more advanced types of fills
Browse files Browse the repository at this point in the history
  • Loading branch information
Mariano Suligoy authored and MarianoGnu committed Sep 5, 2020
1 parent c486592 commit 8ad231c
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 11 deletions.
134 changes: 125 additions & 9 deletions scene/resources/texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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();
}
Expand All @@ -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<Gradient> p_gradient) {
Expand Down Expand Up @@ -1502,23 +1534,25 @@ void GradientTexture::_update() {
}

Vector<uint8_t> 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> image = memnew(Image(width, 1, false, Image::FORMAT_RGBA8, data));
Ref<Image> image = memnew(Image(width, height, false, Image::FORMAT_RGBA8, data));

if (texture.is_valid()) {
RID new_texture = RS::get_singleton()->texture_2d_create(image);
Expand All @@ -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();
Expand All @@ -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<Image> GradientTexture::get_data() const {
if (!texture.is_valid()) {
return Ref<Image>();
Expand Down
38 changes: 36 additions & 2 deletions scene/resources/texture.h
Original file line number Diff line number Diff line change
Expand Up @@ -572,12 +572,32 @@ class GradientTexture : public Texture2D {
}
};

enum GradientTextureFillMethod {
FILL_LINEAR,
FILL_RADIAL
};

enum GradientTextureRepeatMethod {
NO_REPEAT,
REPEAT,
REPEAT_MIRROR
};

private:
Ref<Gradient> 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();

Expand All @@ -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<Image> get_data() const override;

GradientTexture();
virtual ~GradientTexture();
};

VARIANT_ENUM_CAST(GradientTexture::GradientTextureFillMethod);
VARIANT_ENUM_CAST(GradientTexture::GradientTextureRepeatMethod);

class ProxyTexture : public Texture2D {
GDCLASS(ProxyTexture, Texture2D);

Expand Down

0 comments on commit 8ad231c

Please sign in to comment.