From 9f8bf9deaf4e566e4e7abecbed865c7c3d1ced44 Mon Sep 17 00:00:00 2001 From: "Vogel, Rick" Date: Mon, 4 Dec 2023 17:33:28 +0100 Subject: [PATCH] initial commit --- src/nodes/QskArcNode.cpp | 146 +++++++++++++++++++++++++++++-- src/nodes/shaders.qrc | 3 + src/nodes/shaders/arcshadow.frag | 51 +++++++++++ src/nodes/shaders/arcshadow.vert | 7 ++ 4 files changed, 202 insertions(+), 5 deletions(-) create mode 100644 src/nodes/shaders/arcshadow.frag create mode 100644 src/nodes/shaders/arcshadow.vert diff --git a/src/nodes/QskArcNode.cpp b/src/nodes/QskArcNode.cpp index a58a72df..b14a97cc 100644 --- a/src/nodes/QskArcNode.cpp +++ b/src/nodes/QskArcNode.cpp @@ -14,6 +14,98 @@ #include #include +#include +#include +#include +#include + +namespace +{ + struct QskArcShadowMaterialProperties + { + QColor color = Qt::red; + QRectF rect; + qreal radius = 1.0; // [0.0,1.0] + qreal thickness = 0.2; + qreal startAngle = 0.0; //< degree [0.0,360.0] + qreal spanAngle = 270.0; //< degree [0.0,360.0] + qreal extend = 16.0; //< pixel >= 0.0 + }; + + class QskArcShadowMaterial final : public QSGSimpleMaterialShader + { + QSG_DECLARE_SIMPLE_SHADER(QskArcShadowMaterial, QskArcShadowMaterialProperties) + + public: + + QskArcShadowMaterial() + { + const QString root( ":/qskinny/shaders/" ); + setShaderSourceFile( QOpenGLShader::Vertex, root + "arcshadow.vert" ); + setShaderSourceFile( QOpenGLShader::Fragment, root + "arcshadow.frag" ); + } + + QList attributes() const override { + return QList() << "vertex"; + } + + void updateState( const QskArcShadowMaterialProperties* newState, + const QskArcShadowMaterialProperties* oldState ) override + { + std::ignore = oldState; + + const auto& color = newState->color; + const auto& rect = newState->rect; + const auto& radius = newState->radius; + const auto& thickness = newState->thickness; + const auto& startAngle = newState->startAngle; + const auto& spanAngle = newState->spanAngle; + const auto& extend = newState->extend; + + auto& p = *program(); + p.setUniformValue("color", color.redF(), color.greenF(), color.blueF(), 1.0f); + p.setUniformValue("rect", rect.x(), rect.y(), rect.width(), rect.height()); + p.setUniformValue("radius", (float) radius); + p.setUniformValue("thickness", (float) thickness); + p.setUniformValue("startAngle", (float) startAngle - 90.0f); + p.setUniformValue("spanAngle", (float) spanAngle); + p.setUniformValue("extend", (float) extend); + } + + }; + + class QskArcShadowNode : public QSGGeometryNode + { + public: + QskArcShadowNode() : m_geometry(QSGGeometry::defaultAttributes_Point2D(), 4) + { + setGeometry(&m_geometry); + setMaterial(QskArcShadowMaterial::createMaterial()); + m_geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); + material()->setFlag(QSGMaterial::Blending); + } + + void update(const QRectF& rect, const QskArcMetrics& metrics, const QColor& color, const qreal extend) + { + const auto size = qMin(rect.width(), rect.height()); + auto* const material = static_cast*>(this->material()); + auto& state = *material->state(); + state.color = color; + state.rect = rect; + state.radius = 1.0 - metrics.thickness() / size; + state.thickness = 2 * metrics.thickness() / size; + state.startAngle = metrics.startAngle(); + state.spanAngle = metrics.spanAngle(); + state.extend = extend; + markDirty( QSGNode::DirtyMaterial ); + } + + private: + QSGGeometry m_geometry; + }; +} + + static inline QskGradient qskEffectiveGradient( const QskGradient& gradient, const QskArcMetrics& metrics ) { @@ -73,6 +165,7 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics { enum NodeRole { + ShadowRole, FillRole, BorderRole }; @@ -80,6 +173,9 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics const auto metrics = qskEffectiveMetrics( arcMetrics, rect ); const auto gradient = qskEffectiveGradient( fillGradient, metrics ); + auto shadowNode = static_cast< QskArcShadowNode* >( + QskSGNode::findChildNode( this, ShadowRole ) ); + auto fillNode = static_cast< QskShapeNode* >( QskSGNode::findChildNode( this, FillRole ) ); @@ -89,22 +185,45 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics const auto arcRect = qskEffectiveRect( rect, borderWidth ); if ( arcRect.isEmpty() ) { + delete shadowNode; delete fillNode; delete borderNode; - return; } const auto path = metrics.painterPath( arcRect ); + if ( true /* TODO */ ) + { + if ( shadowNode == nullptr ) + { + shadowNode = new QskArcShadowNode; + QskSGNode::setNodeRole( shadowNode, ShadowRole ); + } + + const auto extend = 16.0; + const auto e = extend / 4; + auto* const vertices = shadowNode->geometry()->vertexDataAsPoint2D(); + const auto shadowRect = arcRect.adjusted( -e, +e, e, -e ); + vertices[0].set(shadowRect.left(), shadowRect.top()); + vertices[1].set(shadowRect.left(), shadowRect.bottom()); + vertices[2].set(shadowRect.right(), shadowRect.top()); + vertices[3].set(shadowRect.right(), shadowRect.bottom()); + shadowNode->update(shadowRect, metrics, Qt::black, extend); + qDebug() << shadowRect << arcRect; + } + else + { + delete shadowNode; + shadowNode = nullptr; + } + if ( gradient.isVisible() && !metrics.isNull() ) { if ( fillNode == nullptr ) { fillNode = new QskShapeNode; QskSGNode::setNodeRole( fillNode, FillRole ); - - prependChildNode( fillNode ); } fillNode->updateNode( path, QTransform(), arcRect, gradient ); @@ -112,6 +231,7 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics else { delete fillNode; + fillNode = nullptr; } if ( borderWidth > 0.0 && borderColor.alpha() > 0 ) @@ -120,8 +240,6 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics { borderNode = new QskStrokeNode; QskSGNode::setNodeRole( borderNode, BorderRole ); - - appendChildNode( borderNode ); } QPen pen( borderColor, borderWidth ); @@ -132,5 +250,23 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics else { delete borderNode; + borderNode = nullptr; + } + + const auto oldChildCount = this->childCount(); + const auto newChildCount = + ( shadowNode ? 1 : 0 ) + ( fillNode ? 1 : 0 ) + ( borderNode ? 1 : 0 ); + + if ( oldChildCount != newChildCount ) + { + removeAllChildNodes(); + + for ( QSGNode* node : { ( QSGNode* ) shadowNode, ( QSGNode* ) fillNode, ( QSGNode* ) borderNode }) + { + if ( node != nullptr ) + { + appendChildNode( node ); + } + } } } diff --git a/src/nodes/shaders.qrc b/src/nodes/shaders.qrc index 82a42748..7f74a1ac 100644 --- a/src/nodes/shaders.qrc +++ b/src/nodes/shaders.qrc @@ -2,6 +2,9 @@ + shaders/arcshadow.frag + shaders/arcshadow.vert + shaders/boxshadow.vert.qsb shaders/boxshadow.frag.qsb shaders/boxshadow.vert diff --git a/src/nodes/shaders/arcshadow.frag b/src/nodes/shaders/arcshadow.frag new file mode 100644 index 00000000..77d369a8 --- /dev/null +++ b/src/nodes/shaders/arcshadow.frag @@ -0,0 +1,51 @@ +uniform lowp float qt_Opacity; +uniform lowp vec4 color; +uniform lowp vec4 rect; +uniform lowp float radius; +uniform lowp float thickness; +uniform lowp float startAngle; +uniform lowp float spanAngle; +uniform lowp float extend; + +const float M_PI = 3.141592653589793; + +float sdRing( in vec2 p, in vec2 n, in float r, in float th ) +{ + p.x = abs(p.x); + + p = mat2(n.x,n.y,-n.y,n.x)*p; + + return max( abs(length(p)-r)-th*0.5, + length(vec2(p.x,max(0.0,abs(r-p.y)-th*0.5)))*sign(p.x) ); +} + +void main() +{ + // uniforms + float shadowSize = extend; // px >= 0.0 + vec2 iResolution = rect.zw; + + // normalized pixel coordinates + vec2 p = (2.0 * gl_FragCoord.xy - iResolution.xy) / iResolution.xy; // xy for ellipse + float shadowSizeUv = shadowSize / min(iResolution.x, iResolution.y); + + float t = radians(abs(spanAngle) / 2.0); + vec2 cs = vec2(cos(t),sin(t)); + + // rotation + float ra = radians(startAngle + spanAngle / 2.0); + { + float sin_ra = sin(ra); + float cos_ra = cos(ra); + p = mat2(cos_ra, -sin_ra, sin_ra, cos_ra) * p; + } + + // distance + float d = sdRing(p, cs, radius, thickness); + float e = 1.0 / shadowSizeUv; + + // coloring + float v = 1.0 - abs(d) * e; + float a = d >= 0.0 && abs(d) < e ? v : 0.0; // alpha + gl_FragColor = vec4(color.rgb, a) * qt_Opacity; +} \ No newline at end of file diff --git a/src/nodes/shaders/arcshadow.vert b/src/nodes/shaders/arcshadow.vert new file mode 100644 index 00000000..9a5507ce --- /dev/null +++ b/src/nodes/shaders/arcshadow.vert @@ -0,0 +1,7 @@ +attribute highp vec4 vertex; +uniform highp mat4 qt_Matrix; + +void main() +{ + gl_Position = qt_Matrix * vertex; +} \ No newline at end of file