qskinny/playground/shadows/ArcShadowNode.cpp
Uwe Rathmann 33a482505a internal macros ( not intended for application code ) moved from
QskGlobal.h to QskInternalMacros.h
2025-01-03 12:52:29 +01:00

370 lines
9.6 KiB
C++

/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "ArcShadowNode.h"
#include <QskInternalMacros.h>
#include <qcolor.h>
#include <qsgmaterial.h>
#include <qsgmaterialshader.h>
#include <qmath.h>
#include <cstring>
QSK_QT_PRIVATE_BEGIN
#include <private/qsgnode_p.h>
QSK_QT_PRIVATE_END
// QSGMaterialRhiShader became QSGMaterialShader in Qt6
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
#include <QSGMaterialRhiShader>
using RhiShader = QSGMaterialRhiShader;
#else
using RhiShader = QSGMaterialShader;
#endif
namespace
{
class Material final : public QSGMaterial
{
public:
Material();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QSGMaterialShader* createShader() const override;
#else
QSGMaterialShader* createShader( QSGRendererInterface::RenderMode ) const override;
#endif
QSGMaterialType* type() const override;
int compare( const QSGMaterial* other ) const override;
QVector4D m_color { 0, 0, 0, 1 };
QVector4D m_arc = {};
float m_spreadRadius = 0.0f;
float m_blurRadius = 0.0f;
};
}
namespace
{
class ShaderRhi final : public RhiShader
{
public:
ShaderRhi()
{
const QString root( ":/qskinny/shaders/" );
setShaderFileName( VertexStage, root + "arcshadow.vert.qsb" );
setShaderFileName( FragmentStage, root + "arcshadow.frag.qsb" );
}
bool updateUniformData( RenderState& state,
QSGMaterial* const newMaterial, QSGMaterial* const oldMaterial ) override
{
const auto matOld = static_cast< Material* >( oldMaterial );
const auto matNew = static_cast< Material* >( newMaterial );
Q_ASSERT( state.uniformData()->size() == 108 );
auto data = state.uniformData()->data();
bool changed = false;
if ( state.isMatrixDirty() )
{
const auto matrix = state.combinedMatrix();
memcpy( data + 0, matrix.constData(), 64 );
changed = true;
}
if ( matOld == nullptr || matNew->m_color != matOld->m_color )
{
memcpy( data + 64, &matNew->m_color, 16 );
changed = true;
}
if ( matOld == nullptr || matNew->m_arc != matOld->m_arc )
{
memcpy( data + 80, &matNew->m_arc, 16 );
changed = true;
}
if ( matOld == nullptr || matNew->m_spreadRadius != matOld->m_spreadRadius )
{
memcpy( data + 96, &matNew->m_spreadRadius, 4 );
changed = true;
}
if ( matOld == nullptr || matNew->m_blurRadius != matOld->m_blurRadius )
{
memcpy( data + 100, &matNew->m_blurRadius, 4 );
changed = true;
}
if ( state.isOpacityDirty() )
{
const float opacity = state.opacity();
memcpy( data + 104, &opacity, 4 );
changed = true;
}
return changed;
}
};
}
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
namespace
{
// the old type of shader - specific for OpenGL
class ShaderGL final : public QSGMaterialShader
{
struct Uniforms
{
int matrix = -1;
int color = -1;
int arc = -1;
int spreadRadius = -1;
int blurRadius = -1;
int opacity = -1;
};
public:
ShaderGL()
{
const QString root( ":/qskinny/shaders/" );
setShaderSourceFile( QOpenGLShader::Vertex, root + "arcshadow.vert" );
setShaderSourceFile( QOpenGLShader::Fragment, root + "arcshadow.frag" );
}
char const* const* attributeNames() const override
{
static char const* const names[] = { "in_vertex", "in_coord", nullptr };
return names;
}
void initialize() override
{
QSGMaterialShader::initialize();
const auto* const p = program();
id.matrix = p->uniformLocation( "matrix" );
id.color = p->uniformLocation( "color" );
id.arc = p->uniformLocation( "arc" );
id.spreadRadius = p->uniformLocation( "spreadRadius" );
id.blurRadius = p->uniformLocation( "blurRadius" );
id.opacity = p->uniformLocation( "opacity" );
}
void updateState( const QSGMaterialShader::RenderState& state,
QSGMaterial* const newMaterial, QSGMaterial* const oldMaterial ) override
{
auto* const p = program();
if ( state.isMatrixDirty() )
{
p->setUniformValue( id.matrix, state.combinedMatrix() );
}
if ( state.isOpacityDirty() )
{
p->setUniformValue( id.opacity, state.opacity() );
}
auto updateMaterial = ( oldMaterial == nullptr ) ||
( newMaterial->compare( oldMaterial ) != 0 );
updateMaterial |= state.isCachedMaterialDataDirty();
if ( updateMaterial )
{
const auto* const material = static_cast< const Material* >( newMaterial );
p->setUniformValue( id.color, material->m_color );
p->setUniformValue( id.arc, material->m_arc );
p->setUniformValue( id.spreadRadius, material->m_spreadRadius );
p->setUniformValue( id.blurRadius, material->m_blurRadius );
}
}
private:
Uniforms id;
};
}
#endif
namespace
{
Material::Material()
{
setFlag( QSGMaterial::Blending, true );
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
setFlag( QSGMaterial::SupportsRhiShader, true );
#endif
}
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QSGMaterialShader* Material::createShader() const
{
if ( !( flags() & QSGMaterial::RhiShaderWanted ) )
return new ShaderGL();
return new ShaderRhi();
}
#else
QSGMaterialShader* Material::createShader( QSGRendererInterface::RenderMode ) const
{
return new ShaderRhi();
}
#endif
QSGMaterialType* Material::type() const
{
static QSGMaterialType staticType;
return &staticType;
}
int Material::compare( const QSGMaterial* const other ) const
{
auto material = static_cast< const Material* >( other );
if ( ( material->m_color == m_color )
&& ( material->m_arc == m_arc )
&& qFuzzyCompare( material->m_spreadRadius, m_spreadRadius )
&& qFuzzyCompare( material->m_blurRadius, m_blurRadius ) )
{
return 0;
}
return QSGMaterial::compare( other );
}
}
class ArcShadowNodePrivate final : public QSGGeometryNodePrivate
{
public:
ArcShadowNodePrivate()
: geometry( QSGGeometry::defaultAttributes_TexturedPoint2D(), 4 )
{
}
QSGGeometry geometry;
Material material;
QRectF rect;
};
ArcShadowNode::ArcShadowNode()
: QSGGeometryNode( *new ArcShadowNodePrivate )
{
Q_D( ArcShadowNode );
setGeometry( &d->geometry );
setMaterial( &d->material );
d->geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
d->material.setFlag( QSGMaterial::Blending );
}
ArcShadowNode::~ArcShadowNode() = default;
void ArcShadowNode::setShadowData(
const QRectF& rect, qreal spreadRadius, qreal blurRadius,
qreal startAngle, qreal spanAngle, const QColor& color )
{
if ( qFuzzyIsNull( spanAngle ) || color.alpha() == 0 )
{
setBoundingRectangle( {} );
return;
}
Q_D( ArcShadowNode );
if ( d->rect != rect )
{
setBoundingRectangle( rect ); // bounding rectangle includig spread/blur
}
const auto size = qMin( rect.width(), rect.height() );
{
#if 1
const auto a = color.alphaF();
const QVector4D c( color.redF() * a, color.greenF() * a, color.blueF() * a, a );
#else
const QVector4D c( color.redF(), color.greenF(), color.blueF(), color.alphaF() );
#endif
if ( d->material.m_color != c )
{
d->material.m_color = c;
markDirty( QSGNode::DirtyMaterial );
}
}
{
const float r = spreadRadius / size;
if ( d->material.m_spreadRadius != r )
{
d->material.m_spreadRadius = r;
markDirty( QSGNode::DirtyMaterial );
}
}
{
const float r = blurRadius / size;
if ( d->material.m_blurRadius != r )
{
d->material.m_blurRadius = r;
markDirty( QSGNode::DirtyMaterial );
}
}
{
QVector4D arc( 0.0, 0.0, 1.0, 0.0 );
{
const auto a1 = qDegreesToRadians( startAngle + 0.5 * spanAngle );
const auto a2 = qDegreesToRadians( 0.5 * qAbs( spanAngle ) );
arc = QVector4D( ::cos( a1 ), ::sin( a1 ), ::cos( a2 ), ::sin( a2 ) );
}
if ( d->material.m_arc != arc )
{
d->material.m_arc = arc;
markDirty( QSGNode::DirtyMaterial );
}
}
}
void ArcShadowNode::setBoundingRectangle( const QRectF& rect )
{
Q_D( ArcShadowNode );
if ( d->rect == rect )
return;
d->rect = rect;
QSGGeometry::updateTexturedRectGeometry(
&d->geometry, d->rect, { -0.5, -0.5, 1.0, 1.0 } );
d->geometry.markVertexDataDirty();
markDirty( QSGNode::DirtyGeometry );
}