diff --git a/app/node/generator/shape/shapenode.cpp b/app/node/generator/shape/shapenode.cpp index 7344d6e3d..d4808d768 100644 --- a/app/node/generator/shape/shapenode.cpp +++ b/app/node/generator/shape/shapenode.cpp @@ -25,10 +25,14 @@ namespace olive { #define super ShapeNodeBase QString ShapeNode::kTypeInput = QStringLiteral("type_in"); +QString ShapeNode::kRadiusInput = QStringLiteral("radius_in"); ShapeNode::ShapeNode() { PrependInput(kTypeInput, NodeValue::kCombo); + + AddInput(kRadiusInput, NodeValue::kFloat, 20.0); + SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0); } QString ShapeNode::Name() const @@ -56,9 +60,10 @@ void ShapeNode::Retranslate() super::Retranslate(); SetInputName(kTypeInput, tr("Type")); + SetInputName(kRadiusInput, tr("Radius")); // Coordinate with Type enum - SetComboBoxStrings(kTypeInput, {tr("Rectangle"), tr("Ellipse")}); + SetComboBoxStrings(kTypeInput, {tr("Rectangle"), tr("Ellipse"), tr("Rounded Rectangle")}); } ShaderCode ShapeNode::GetShaderCode(const ShaderRequest &request) const @@ -81,4 +86,18 @@ void ShapeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, Nod PushMergableJob(value, QVariant::fromValue(job), table); } +void ShapeNode::InputValueChangedEvent(const QString &input, int element) +{ + if (input == kTypeInput) { + InputFlags i = GetInputFlags(kRadiusInput); + if (GetStandardValue(kTypeInput).toInt() == kRoundedRectangle) { + i &= InputFlag(~kInputFlagHidden); + } else { + i |= kInputFlagHidden; + } + SetInputFlags(kRadiusInput, i); + } + super::InputValueChangedEvent(input, element); +} + } diff --git a/app/node/generator/shape/shapenode.h b/app/node/generator/shape/shapenode.h index 3dfdccbaf..285fc5bb5 100644 --- a/app/node/generator/shape/shapenode.h +++ b/app/node/generator/shape/shapenode.h @@ -33,7 +33,8 @@ public: enum Type { kRectangle, - kEllipse + kEllipse, + kRoundedRectangle }; NODE_DEFAULT_FUNCTIONS(ShapeNode) @@ -49,6 +50,10 @@ public: virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; static QString kTypeInput; + static QString kRadiusInput; + +protected: + virtual void InputValueChangedEvent(const QString &input, int element) override; }; diff --git a/app/shaders/shape.frag b/app/shaders/shape.frag index f06b2ce7b..51b2c7270 100644 --- a/app/shaders/shape.frag +++ b/app/shaders/shape.frag @@ -3,14 +3,35 @@ in vec2 ove_texcoord; out vec4 frag_color; // Match with ShapeNode::Type -#define SHAPE_RECTANGLE 0 -#define SHAPE_ELLIPSE 1 +const int SHAPE_RECTANGLE = 0; +const int SHAPE_ELLIPSE = 1; +const int SHAPE_ROUNDEDRECT = 2; uniform vec2 pos_in; uniform vec2 size_in; uniform int type_in; uniform vec2 resolution_in; uniform vec4 color_in; +uniform float radius_in; + +vec4 draw_rect(vec2 real_position, vec2 real_size) +{ + if (ove_texcoord.x >= real_position.x && ove_texcoord.y >= real_position.y + && ove_texcoord.x < real_position.x+real_size.x && ove_texcoord.y < real_position.y+real_size.y) { + return color_in; + } else { + return vec4(0.0, 0.0, 0.0, 0.0); + } +} + +vec4 draw_ellipse(vec2 center, float radius, float aspect_ratio) { + vec2 offset = ove_texcoord*resolution_in - center; + offset.x /= aspect_ratio; + float d = length(offset)-radius; + float t = clamp(d, 0.0, 1.0); + + return color_in * (1.0-t); +} void main() { vec2 p = pos_in + resolution_in*0.5 - size_in*0.5; @@ -20,22 +41,42 @@ void main() { vec4 col = vec4(0.0); - if (type_in == SHAPE_RECTANGLE) { - if (ove_texcoord.x >= real_position.x && ove_texcoord.y >= real_position.y - && ove_texcoord.x < real_position.x+real_size.x && ove_texcoord.y < real_position.y+real_size.y) { - col = color_in; - } - } else if (type_in == SHAPE_ELLIPSE) { + switch (type_in) { + case SHAPE_RECTANGLE: + { + col = draw_rect(real_position, real_size); + break; + } + case SHAPE_ELLIPSE: + { vec2 center = p+size_in*0.5; float radius = size_in.y*0.5; float aspect_ratio = size_in.x/size_in.y; - - vec2 offset = ove_texcoord*resolution_in - center; - offset.x /= aspect_ratio; - float d = length(offset)-radius; - float t = clamp(d, 0.0, 1.0); - - col = color_in * (1.0-t); + col = draw_ellipse(center, radius, aspect_ratio); + break; + } + case SHAPE_ROUNDEDRECT: + { + // Limit radius so it is never larger than half the shortest size + float r = min(radius_in, min(size_in.y*0.5, size_in.x*0.5)); + vec2 real_rad = vec2(r / resolution_in.x, r / resolution_in.y); + if (ove_texcoord.x < real_position.x + real_rad.x && ove_texcoord.y < real_position.y + real_rad.y) { + // Top-left + col = draw_ellipse(p + r, r, 1.0); + } else if (ove_texcoord.x > real_position.x+real_size.x - real_rad.x && ove_texcoord.y < real_position.y + real_rad.y) { + // Top-right + col = draw_ellipse(vec2(p.x + size_in.x - r, p.y + r), r, 1.0); + } else if (ove_texcoord.x < real_position.x + real_rad.x && ove_texcoord.y > real_position.y + real_size.y - real_rad.y) { + // Bottom-left + col = draw_ellipse(vec2(p.x + r, p.y + size_in.y - r), r, 1.0); + } else if (ove_texcoord.x > real_position.x+real_size.x - real_rad.x && ove_texcoord.y > real_position.y + real_size.y - real_rad.y) { + // Bottom-right + col = draw_ellipse(vec2(p.x + size_in.x - r, p.y + size_in.y - r), r, 1.0); + } else { + col = draw_rect(real_position, real_size); + } + break; + } } frag_color = col;