diff --git a/src/3d/qgsframegraph.cpp b/src/3d/qgsframegraph.cpp index 76c6ae09041b..373d34f80ba8 100644 --- a/src/3d/qgsframegraph.cpp +++ b/src/3d/qgsframegraph.cpp @@ -478,10 +478,19 @@ Qt3DRender::QFrameGraphNode *QgsFrameGraph::constructRubberBandsPass() mRubberBandsLayerFilter = new Qt3DRender::QLayerFilter( mRubberBandsCameraSelector ); mRubberBandsLayerFilter->addLayer( mRubberBandsLayer ); + Qt3DRender::QBlendEquationArguments *blendState = new Qt3DRender::QBlendEquationArguments; + blendState->setSourceRgb( Qt3DRender::QBlendEquationArguments::SourceAlpha ); + blendState->setDestinationRgb( Qt3DRender::QBlendEquationArguments::OneMinusSourceAlpha ); + + Qt3DRender::QBlendEquation *blendEquation = new Qt3DRender::QBlendEquation; + blendEquation->setBlendFunction( Qt3DRender::QBlendEquation::Add ); + mRubberBandsStateSet = new Qt3DRender::QRenderStateSet( mRubberBandsLayerFilter ); Qt3DRender::QDepthTest *depthTest = new Qt3DRender::QDepthTest; depthTest->setDepthFunction( Qt3DRender::QDepthTest::Always ); mRubberBandsStateSet->addRenderState( depthTest ); + mRubberBandsStateSet->addRenderState( blendState ); + mRubberBandsStateSet->addRenderState( blendEquation ); // Here we attach our drawings to the render target also used by forward pass. // This is kind of okay, but as a result, post-processing effects get applied diff --git a/src/3d/qgsrubberband3d.cpp b/src/3d/qgsrubberband3d.cpp index d326e7c12c15..f33bb8393267 100644 --- a/src/3d/qgsrubberband3d.cpp +++ b/src/3d/qgsrubberband3d.cpp @@ -38,6 +38,10 @@ #include #endif +#include "qgs3dutils.h" +#include "qgsmessagelog.h" +#include "qgstessellatedpolygongeometry.h" + #include #include @@ -45,44 +49,35 @@ /// @cond PRIVATE -QgsRubberBand3D::QgsRubberBand3D( Qgs3DMapSettings &map, QgsWindow3DEngine *engine, Qt3DCore::QEntity *parentEntity, Qgis::GeometryType geometryType ) +QgsRubberBand3D::QgsRubberBand3D( Qgs3DMapSettings &map, QgsWindow3DEngine *engine, Qt3DCore::QEntity *parentEntity, const Qgis::GeometryType geometryType, const bool isOutlineDashed ) : mMapSettings( &map ) , mEngine( engine ) , mGeometryType( geometryType ) + , mIsOutlineDashed( isOutlineDashed ) { - if ( mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon ) + switch ( mGeometryType ) { - // Rubberband line - mLineEntity = new Qt3DCore::QEntity( parentEntity ); - - QgsLineVertexData dummyLineData; - mGeometry = dummyLineData.createGeometry( mLineEntity ); - - Q_ASSERT( mGeometry->attributes().count() == 2 ); - mPositionAttribute = mGeometry->attributes().at( 0 ); - mIndexAttribute = mGeometry->attributes().at( 1 ); - - mLineGeomRenderer = new Qt3DRender::QGeometryRenderer; - mLineGeomRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency ); - mLineGeomRenderer->setGeometry( mGeometry ); - mLineGeomRenderer->setPrimitiveRestartEnabled( true ); - mLineGeomRenderer->setRestartIndexValue( 0 ); - - mLineEntity->addComponent( mLineGeomRenderer ); - - mLineMaterial = new QgsLineMaterial; - mLineMaterial->setLineWidth( mWidth ); - mLineMaterial->setLineColor( mColor ); - - QObject::connect( engine, &QgsAbstract3DEngine::sizeChanged, mLineMaterial, [this, engine] { - mLineMaterial->setViewportSize( engine->size() ); - } ); - mLineMaterial->setViewportSize( engine->size() ); - - mLineEntity->addComponent( mLineMaterial ); + case Qgis::GeometryType::Point: + setupMarker( parentEntity ); + break; + case Qgis::GeometryType::Line: + setupLine( parentEntity, engine ); + setupMarker( parentEntity ); + break; + case Qgis::GeometryType::Polygon: + setupMarker( parentEntity ); + setupLine( parentEntity, engine ); + setupPolygon( parentEntity ); + break; + case Qgis::GeometryType::Null: + case Qgis::GeometryType::Unknown: + QgsMessageLog::logMessage( QObject::tr( "Unknown GeometryType used in QgsRubberband3D" ), QObject::tr( "3D" ) ); + break; } +} - // Rubberband vertex markers +void QgsRubberBand3D::setupMarker( Qt3DCore::QEntity *parentEntity ) +{ mMarkerEntity = new Qt3DCore::QEntity( parentEntity ); mMarkerGeometry = new QgsBillboardGeometry(); mMarkerGeometryRenderer = new Qt3DRender::QGeometryRenderer; @@ -94,11 +89,63 @@ QgsRubberBand3D::QgsRubberBand3D( Qgs3DMapSettings &map, QgsWindow3DEngine *engi mMarkerEntity->addComponent( mMarkerGeometryRenderer ); } +void QgsRubberBand3D::setupLine( Qt3DCore::QEntity *parentEntity, QgsWindow3DEngine *engine ) +{ + mLineEntity = new Qt3DCore::QEntity( parentEntity ); + + QgsLineVertexData dummyLineData; + mLineGeometry = dummyLineData.createGeometry( mLineEntity ); + + Q_ASSERT( mLineGeometry->attributes().count() == 2 ); + mPositionAttribute = mLineGeometry->attributes().at( 0 ); + mIndexAttribute = mLineGeometry->attributes().at( 1 ); + + mLineGeometryRenderer = new Qt3DRender::QGeometryRenderer; + mLineGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency ); + mLineGeometryRenderer->setGeometry( mLineGeometry ); + mLineGeometryRenderer->setPrimitiveRestartEnabled( true ); + mLineGeometryRenderer->setRestartIndexValue( 0 ); + + mLineEntity->addComponent( mLineGeometryRenderer ); + + mLineMaterial = new QgsLineMaterial; + mLineMaterial->setLineWidth( mWidth ); + mLineMaterial->setLineColor( mColor ); + + QObject::connect( engine, &QgsAbstract3DEngine::sizeChanged, mLineMaterial, [this, engine] { + mLineMaterial->setViewportSize( engine->size() ); + } ); + mLineMaterial->setViewportSize( engine->size() ); + + mLineEntity->addComponent( mLineMaterial ); +} + +void QgsRubberBand3D::setupPolygon( Qt3DCore::QEntity *parentEntity ) +{ + mPolygonEntity = new Qt3DCore::QEntity( parentEntity ); + + mPolygonGeometry = new QgsTessellatedPolygonGeometry(); + + mPolygonGeometryRenderer = new Qt3DRender::QGeometryRenderer; + mPolygonGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles ); + mPolygonGeometryRenderer->setGeometry( mPolygonGeometry ); + mPolygonEntity->addComponent( mPolygonGeometryRenderer ); + + mPolygonMaterial = new QgsPhongMaterialSettings(); + mPolygonMaterial->setAmbient( mColor ); + mPolygonMaterial->setDiffuse( mColor ); + mPolygonMaterial->setOpacity( 0.25 ); + mPolygonEntity->addComponent( mPolygonMaterial->toMaterial( QgsMaterialSettingsRenderingTechnique::Triangles, QgsMaterialContext() ) ); +} + QgsRubberBand3D::~QgsRubberBand3D() { + if ( mPolygonEntity ) + mPolygonEntity->deleteLater(); if ( mLineEntity ) mLineEntity->deleteLater(); - mMarkerEntity->deleteLater(); + if ( mMarkerEntity ) + mMarkerEntity->deleteLater(); } float QgsRubberBand3D::width() const @@ -110,15 +157,18 @@ void QgsRubberBand3D::setWidth( float width ) { mWidth = width; - if ( mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon ) + if ( mMarkerType != None ) { - // when highlighting lines, the vertex markers should be wider - mLineMaterial->setLineWidth( width ); - width *= 3; + if ( mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon ) + { + // when highlighting lines, the vertex markers should be wider + mLineMaterial->setLineWidth( width ); + width *= 3; + } + + mMarkerSymbol->setSize( width ); + updateMarkerMaterial(); } - - mMarkerSymbol->setSize( width ); - updateMarkerMaterial(); } QColor QgsRubberBand3D::color() const @@ -130,19 +180,46 @@ void QgsRubberBand3D::setColor( QColor color ) { mColor = color; - if ( mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon ) + if ( mMarkerType != None ) { - mLineMaterial->setLineColor( color ); - mMarkerSymbol->setColor( color.lighter( 130 ) ); + if ( mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon ) + { + mLineMaterial->setLineColor( color ); + mMarkerSymbol->setColor( color.lighter( 130 ) ); + } + else + { + mMarkerSymbol->setColor( color ); + } + + if ( mMarkerSymbol->symbolLayerCount() > 0 && mMarkerSymbol->symbolLayer( 0 )->layerType() == QLatin1String( "SimpleMarker" ) && !mOutlineColor.value() ) + { + mMarkerSymbol->symbolLayer( 0 )->setStrokeColor( color ); + } + updateMarkerMaterial(); } - else + + if ( mGeometryType == Qgis::GeometryType::Polygon ) { - mMarkerSymbol->setColor( color ); + mPolygonEntity->removeComponent( mPolygonMaterial->toMaterial( QgsMaterialSettingsRenderingTechnique::Triangles, QgsMaterialContext() ) ); + mPolygonMaterial->setAmbient( mColor ); + mPolygonMaterial->setDiffuse( mColor ); + mPolygonEntity->addComponent( mPolygonMaterial->toMaterial( QgsMaterialSettingsRenderingTechnique::Triangles, QgsMaterialContext() ) ); } +} + +QColor QgsRubberBand3D::outlineColor() const +{ + return mOutlineColor; +} + +void QgsRubberBand3D::setOutlineColor( const QColor color ) +{ + mOutlineColor = color; if ( mMarkerSymbol->symbolLayerCount() > 0 && mMarkerSymbol->symbolLayer( 0 )->layerType() == QLatin1String( "SimpleMarker" ) ) { - static_cast( mMarkerSymbol->symbolLayer( 0 ) )->setStrokeColor( color ); + mMarkerSymbol->symbolLayer( 0 )->setStrokeColor( color ); } updateMarkerMaterial(); } @@ -157,7 +234,8 @@ void QgsRubberBand3D::setMarkerType( MarkerType marker ) { QStringLiteral( "color" ), lineOrPolygon ? mColor.lighter( 130 ).name() : mColor.name() }, { QStringLiteral( "size_unit" ), QStringLiteral( "pixel" ) }, { QStringLiteral( "size" ), QString::number( lineOrPolygon ? mWidth * 3.f : mWidth ) }, - { QStringLiteral( "outline_color" ), mColor.name() }, + { QStringLiteral( "outline_color" ), mOutlineColor.value() ? mOutlineColor.name() : mColor.name() }, + { QStringLiteral( "outline_style" ), mIsOutlineDashed ? QStringLiteral( "dot" ) : QStringLiteral( "solid" ) }, { QStringLiteral( "outline_width" ), QString::number( lineOrPolygon ? 0.5 : 1 ) }, { QStringLiteral( "name" ), mMarkerType == Square ? QStringLiteral( "square" ) : QStringLiteral( "circle" ) } }; @@ -174,18 +252,41 @@ QgsRubberBand3D::MarkerType QgsRubberBand3D::markerType() const void QgsRubberBand3D::reset() { mLineString.clear(); + mPolygon.clear(); updateGeometry(); } void QgsRubberBand3D::addPoint( const QgsPoint &pt ) { mLineString.addVertex( pt ); + if ( mGeometryType == Qgis::GeometryType::Polygon && mLineString.numPoints() >= 3 ) + mPolygon = QgsPolygon( new QgsLineString( mLineString ) ); + else + mPolygon.clear(); + updateGeometry(); } void QgsRubberBand3D::setPoints( const QgsLineString &points ) { mLineString = points; + if ( mGeometryType == Qgis::GeometryType::Polygon && mLineString.numPoints() >= 3 ) + mPolygon = QgsPolygon( new QgsLineString( mLineString ) ); + else + mPolygon.clear(); + + updateGeometry(); +} + +void QgsRubberBand3D::setPolygon( const QgsPolygon &polygon ) +{ + mPolygon = polygon; + if ( mMarkerType != None ) + { + const QgsLineString *exteriorRing = dynamic_cast( mPolygon.exteriorRing() ); + mLineString = *exteriorRing; + } + updateGeometry(); } @@ -193,6 +294,10 @@ void QgsRubberBand3D::removeLastPoint() { const int lastVertexIndex = mLineString.numPoints() - 1; mLineString.deleteVertex( QgsVertexId( 0, 0, lastVertexIndex ) ); + if ( mGeometryType == Qgis::GeometryType::Polygon && mLineString.numPoints() >= 3 ) + mPolygon = QgsPolygon( new QgsLineString( mLineString ) ); + else + mPolygon.clear(); updateGeometry(); } @@ -200,33 +305,55 @@ void QgsRubberBand3D::moveLastPoint( const QgsPoint &pt ) { const int lastVertexIndex = mLineString.numPoints() - 1; mLineString.moveVertex( QgsVertexId( 0, 0, lastVertexIndex ), pt ); + if ( mGeometryType == Qgis::GeometryType::Polygon && mLineString.numPoints() >= 3 ) + mPolygon = QgsPolygon( new QgsLineString( mLineString ) ); + else + mPolygon.clear(); updateGeometry(); } void QgsRubberBand3D::updateGeometry() { - QgsLineVertexData lineData; - lineData.withAdjacency = true; - lineData.init( Qgis::AltitudeClamping::Absolute, Qgis::AltitudeBinding::Vertex, 0, Qgs3DRenderContext::fromMapSettings( mMapSettings ), mMapSettings->origin() ); - const bool closed = mGeometryType == Qgis::GeometryType::Polygon; - lineData.addLineString( mLineString, 0, closed ); - - if ( mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon ) + if ( mMarkerType != None ) { - mPositionAttribute->buffer()->setData( lineData.createVertexBuffer() ); - mIndexAttribute->buffer()->setData( lineData.createIndexBuffer() ); - mLineGeomRenderer->setVertexCount( lineData.indexes.count() ); + QgsLineVertexData lineData; + lineData.withAdjacency = true; + lineData.init( Qgis::AltitudeClamping::Absolute, Qgis::AltitudeBinding::Vertex, 0, Qgs3DRenderContext::fromMapSettings( mMapSettings ), mMapSettings->origin() ); + const bool closed = mGeometryType == Qgis::GeometryType::Polygon; + lineData.addLineString( mLineString, 0, closed ); + + if ( mGeometryType == Qgis::GeometryType::Line || mGeometryType == Qgis::GeometryType::Polygon ) + { + mPositionAttribute->buffer()->setData( lineData.createVertexBuffer() ); + mIndexAttribute->buffer()->setData( lineData.createIndexBuffer() ); + mLineGeometryRenderer->setVertexCount( lineData.indexes.count() ); + } + + // first entry is empty for primitive restart + lineData.vertices.pop_front(); + + // we may not want a marker on the last point as it's tracked by the mouse cursor + if ( mHideLastMarker && !lineData.vertices.isEmpty() ) + lineData.vertices.pop_back(); + + mMarkerGeometry->setPoints( lineData.vertices ); + mMarkerGeometryRenderer->setVertexCount( lineData.vertices.count() ); } - // first entry is empty for primitive restart - lineData.vertices.pop_front(); - - // we may not want a marker on the last point as it's tracked by the mouse cursor - if ( mHideLastMarker && !lineData.vertices.isEmpty() ) - lineData.vertices.pop_back(); - - mMarkerGeometry->setPoints( lineData.vertices ); - mMarkerGeometryRenderer->setVertexCount( lineData.vertices.count() ); + if ( mGeometryType == Qgis::GeometryType::Polygon ) + { + QgsTessellator tessellator( mMapSettings->origin().x(), mMapSettings->origin().y(), true ); + tessellator.setOutputZUp( true ); + tessellator.addPolygon( mPolygon, 0 ); + if ( !tessellator.error().isEmpty() ) + { + QgsMessageLog::logMessage( tessellator.error(), QObject::tr( "3D" ) ); + } + // extract vertex buffer data from tessellator + const QByteArray data( reinterpret_cast( tessellator.data().constData() ), static_cast( tessellator.data().count() * sizeof( float ) ) ); + const int vertexCount = data.count() / tessellator.stride(); + mPolygonGeometry->setData( data, vertexCount, QVector() << -10, QVector() << 0 ); + } } void QgsRubberBand3D::updateMarkerMaterial() diff --git a/src/3d/qgsrubberband3d.h b/src/3d/qgsrubberband3d.h index 0331d4fc52ec..a3a5dc919805 100644 --- a/src/3d/qgsrubberband3d.h +++ b/src/3d/qgsrubberband3d.h @@ -34,7 +34,12 @@ // #include "qgslinestring.h" +#include "qgspolygon.h" +#include "qgstessellator.h" +class QgsPhongMaterialSettings; +class QgsMaterial; +class QgsTessellatedPolygonGeometry; class QgsWindow3DEngine; class QgsLineMaterial; class Qgs3DMapSettings; @@ -79,7 +84,6 @@ class _3D_EXPORT QgsRubberBand3D //! Icons enum MarkerType { - /** * A box is used to highlight points (□) */ @@ -88,10 +92,15 @@ class _3D_EXPORT QgsRubberBand3D /** * A circle is used to highlight points (○) */ - Circle + Circle, + + /** + * Points are not highlighted, neither the edges connecting them + */ + None }; - QgsRubberBand3D( Qgs3DMapSettings &map, QgsWindow3DEngine *engine, Qt3DCore::QEntity *parentEntity, Qgis::GeometryType geometryType = Qgis::GeometryType::Line ); + QgsRubberBand3D( Qgs3DMapSettings &map, QgsWindow3DEngine *engine, Qt3DCore::QEntity *parentEntity, Qgis::GeometryType geometryType = Qgis::GeometryType::Line, bool isOutlineDashed = false ); ~QgsRubberBand3D(); //! Returns the rubber band width in pixels @@ -106,6 +115,18 @@ class _3D_EXPORT QgsRubberBand3D //! Sets the rubber band color. void setColor( QColor color ); + /** + * Returns the rubber band outline color + * \since QGIS 3.44 + */ + QColor outlineColor() const; + + /** + * Sets the rubber band outline color + * \since QGIS 3.44 + */ + void setOutlineColor( QColor color ); + /** * Sets the \a marker type to highlight point geometries and line vertices. */ @@ -122,6 +143,14 @@ class _3D_EXPORT QgsRubberBand3D void setPoints( const QgsLineString &points ); + /** + * Sets the polygon geometry to be shown + * + * \note Function doesn't set \a GeometryType to Polygon + * \since QGIS 3.44 + */ + void setPolygon( const QgsPolygon &polygon ); + void removeLastPoint(); void moveLastPoint( const QgsPoint &pt ); @@ -134,9 +163,12 @@ class _3D_EXPORT QgsRubberBand3D private: void updateGeometry(); void updateMarkerMaterial(); + void setupMarker( Qt3DCore::QEntity *parentEntity ); + void setupLine( Qt3DCore::QEntity *parentEntity, QgsWindow3DEngine *engine ); + void setupPolygon( Qt3DCore::QEntity *parentEntity ); - private: QgsLineString mLineString; + QgsPolygon mPolygon; bool mHideLastMarker = false; Qgs3DMapSettings *mMapSettings = nullptr; // not owned @@ -147,18 +179,26 @@ class _3D_EXPORT QgsRubberBand3D MarkerType mMarkerType = Circle; float mWidth = 3.f; QColor mColor = Qt::red; + QColor mOutlineColor; + bool mIsOutlineDashed = false; + + Qt3DCore::QEntity *mLineEntity = nullptr; // owned by parentEntity (from constructor) + Qt3DCore::QEntity *mPolygonEntity = nullptr; // owned by parentEntity (from constructor) + Qt3DCore::QEntity *mMarkerEntity = nullptr; // owned by parentEntity (from constructor) - Qt3DCore::QEntity *mLineEntity = nullptr; // owned by parentEntity (from constructor) - Qt3DCore::QEntity *mMarkerEntity = nullptr; // owned by parentEntity (from constructor) + // all these are owned by mPolygonEntity + Qt3DRender::QGeometryRenderer *mPolygonGeometryRenderer = nullptr; + QgsTessellatedPolygonGeometry *mPolygonGeometry = nullptr; + QgsPhongMaterialSettings *mPolygonMaterial = nullptr; // all these are owned by mLineEntity - Qt3DRender::QGeometryRenderer *mLineGeomRenderer = nullptr; + Qt3DRender::QGeometryRenderer *mLineGeometryRenderer = nullptr; #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - Qt3DRender::QGeometry *mGeometry = nullptr; + Qt3DRender::QGeometry *mLineGeometry = nullptr; Qt3DRender::QAttribute *mPositionAttribute = nullptr; Qt3DRender::QAttribute *mIndexAttribute = nullptr; #else - Qt3DCore::QGeometry *mGeometry = nullptr; + Qt3DCore::QGeometry *mLineGeometry = nullptr; Qt3DCore::QAttribute *mPositionAttribute = nullptr; Qt3DCore::QAttribute *mIndexAttribute = nullptr; #endif