From 2b7c460df2bf4a68f09c8eb32a56c882f6c91d43 Mon Sep 17 00:00:00 2001 From: Martin Dobias Date: Tue, 22 Oct 2024 23:06:14 +0200 Subject: [PATCH] Make coordinates of vector data chunks independent from scene origin This is a continuation of work started in #58948, handling all the cases of data from vector layers. --- src/3d/qgs3dutils.cpp | 4 +- src/3d/qgs3dutils.h | 2 +- src/3d/qgsfeature3dhandler_p.h | 2 +- src/3d/qgsrubberband3d.cpp | 2 +- src/3d/qgsrulebased3drenderer.cpp | 6 +- src/3d/qgsrulebased3drenderer.h | 2 +- src/3d/qgsrulebasedchunkloader_p.cpp | 11 ++-- src/3d/qgstessellatedpolygongeometry.cpp | 36 ------------ src/3d/qgstessellatedpolygongeometry.h | 3 - src/3d/qgsvectorlayerchunkloader_p.cpp | 11 ++-- src/3d/symbols/qgsline3dsymbol_p.cpp | 39 ++++++++++--- src/3d/symbols/qgslinevertexdata_p.cpp | 15 ++--- src/3d/symbols/qgslinevertexdata_p.h | 5 +- src/3d/symbols/qgspoint3dsymbol_p.cpp | 71 +++++++++++++++++------- src/3d/symbols/qgspolygon3dsymbol_p.cpp | 27 +++++++-- 15 files changed, 137 insertions(+), 99 deletions(-) diff --git a/src/3d/qgs3dutils.cpp b/src/3d/qgs3dutils.cpp index 291f1868d67a..6193eb46f0ae 100644 --- a/src/3d/qgs3dutils.cpp +++ b/src/3d/qgs3dutils.cpp @@ -488,7 +488,7 @@ QMatrix4x4 Qgs3DUtils::stringToMatrix4x4( const QString &str ) return m; } -void Qgs3DUtils::extractPointPositions( const QgsFeature &f, const Qgs3DRenderContext &context, Qgis::AltitudeClamping altClamp, QVector &positions ) +void Qgs3DUtils::extractPointPositions( const QgsFeature &f, const Qgs3DRenderContext &context, const QgsVector3D &chunkOrigin, Qgis::AltitudeClamping altClamp, QVector &positions ) { const QgsAbstractGeometry *g = f.geometry().constGet(); for ( auto it = g->vertices_begin(); it != g->vertices_end(); ++it ) @@ -513,7 +513,7 @@ void Qgs3DUtils::extractPointPositions( const QgsFeature &f, const Qgs3DRenderCo h = terrainZ + geomZ; break; } - positions.append( QVector3D( pt.x() - context.origin().x(), h, -( pt.y() - context.origin().y() ) ) ); + positions.append( QVector3D( pt.x() - chunkOrigin.x(), h, -( pt.y() - chunkOrigin.y() ) ) ); QgsDebugMsgLevel( QStringLiteral( "%1 %2 %3" ).arg( positions.last().x() ).arg( positions.last().y() ).arg( positions.last().z() ), 2 ); } } diff --git a/src/3d/qgs3dutils.h b/src/3d/qgs3dutils.h index 388a219fae67..0c8454890065 100644 --- a/src/3d/qgs3dutils.h +++ b/src/3d/qgs3dutils.h @@ -143,7 +143,7 @@ class _3D_EXPORT Qgs3DUtils static QMatrix4x4 stringToMatrix4x4( const QString &str ); //! Calculates (x,y,z) positions of (multi)point from the given feature - static void extractPointPositions( const QgsFeature &f, const Qgs3DRenderContext &context, Qgis::AltitudeClamping altClamp, QVector &positions ); + static void extractPointPositions( const QgsFeature &f, const Qgs3DRenderContext &context, const QgsVector3D &chunkOrigin, Qgis::AltitudeClamping altClamp, QVector &positions ); /** * Returns TRUE if bbox is completely outside the current viewing volume. diff --git a/src/3d/qgsfeature3dhandler_p.h b/src/3d/qgsfeature3dhandler_p.h index a6e3366f8cb9..a1f89d6a4d1c 100644 --- a/src/3d/qgsfeature3dhandler_p.h +++ b/src/3d/qgsfeature3dhandler_p.h @@ -49,7 +49,7 @@ class QgsFeature3DHandler * Called before feature iteration starts to initialize, get required attributes. * \returns TRUE on success (on FALSE the handler failed to initialize and processFeature() / finalize() should not be called */ - virtual bool prepare( const Qgs3DRenderContext &context, QSet &attributeNames ) = 0; + virtual bool prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin ) = 0; /** * Called for every feature to extract information out of it into some diff --git a/src/3d/qgsrubberband3d.cpp b/src/3d/qgsrubberband3d.cpp index f00f5c177d4f..ff0e77e9f7a2 100644 --- a/src/3d/qgsrubberband3d.cpp +++ b/src/3d/qgsrubberband3d.cpp @@ -167,7 +167,7 @@ void QgsRubberBand3D::updateGeometry() { QgsLineVertexData lineData; lineData.withAdjacency = true; - lineData.init( Qgis::AltitudeClamping::Absolute, Qgis::AltitudeBinding::Vertex, 0, Qgs3DRenderContext::fromMapSettings( mMapSettings ) ); + lineData.init( Qgis::AltitudeClamping::Absolute, Qgis::AltitudeBinding::Vertex, 0, Qgs3DRenderContext::fromMapSettings( mMapSettings ), mMapSettings->origin() ); lineData.addLineString( mLineString ); mPositionAttribute->buffer()->setData( lineData.createVertexBuffer() ); diff --git a/src/3d/qgsrulebased3drenderer.cpp b/src/3d/qgsrulebased3drenderer.cpp index 7afb11e6f1b5..cc0e1e51ed63 100644 --- a/src/3d/qgsrulebased3drenderer.cpp +++ b/src/3d/qgsrulebased3drenderer.cpp @@ -262,12 +262,12 @@ void QgsRuleBased3DRenderer::Rule::createHandlers( QgsVectorLayer *layer, QgsRul } -void QgsRuleBased3DRenderer::Rule::prepare( const Qgs3DRenderContext &context, QSet &attributeNames, QgsRuleBased3DRenderer::RuleToHandlerMap &handlers ) const +void QgsRuleBased3DRenderer::Rule::prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin, QgsRuleBased3DRenderer::RuleToHandlerMap &handlers ) const { if ( mSymbol ) { QgsFeature3DHandler *handler = handlers[this]; - if ( !handler->prepare( context, attributeNames ) ) + if ( !handler->prepare( context, attributeNames, chunkOrigin ) ) { handlers.remove( this ); delete handler; @@ -283,7 +283,7 @@ void QgsRuleBased3DRenderer::Rule::prepare( const Qgs3DRenderContext &context, Q // call recursively for ( Rule *rule : std::as_const( mChildren ) ) { - rule->prepare( context, attributeNames, handlers ); + rule->prepare( context, attributeNames, chunkOrigin, handlers ); } } diff --git a/src/3d/qgsrulebased3drenderer.h b/src/3d/qgsrulebased3drenderer.h index f1dbc32880ac..a6855f307b6f 100644 --- a/src/3d/qgsrulebased3drenderer.h +++ b/src/3d/qgsrulebased3drenderer.h @@ -242,7 +242,7 @@ class _3D_EXPORT QgsRuleBased3DRenderer : public QgsAbstractVectorLayer3DRendere * call prepare() on handlers and populate attributeNames * \note not available in Python bindings */ - void prepare( const Qgs3DRenderContext &context, QSet &attributeNames, RuleToHandlerMap &handlers ) const SIP_SKIP; + void prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin, RuleToHandlerMap &handlers ) const SIP_SKIP; /** * register individual features diff --git a/src/3d/qgsrulebasedchunkloader_p.cpp b/src/3d/qgsrulebasedchunkloader_p.cpp index 0fc3985e435b..60d8ce848b33 100644 --- a/src/3d/qgsrulebasedchunkloader_p.cpp +++ b/src/3d/qgsrulebasedchunkloader_p.cpp @@ -50,6 +50,12 @@ QgsRuleBasedChunkLoader::QgsRuleBasedChunkLoader( const QgsRuleBasedChunkLoaderF QgsVectorLayer *layer = mFactory->mLayer; + // only a subset of data to be queried + const QgsRectangle rect = Qgs3DUtils::worldToMapExtent( node->bbox(), mContext.origin() ); + // origin for coordinates of the chunk - it is kind of arbitrary, but it should be + // picked so that the coordinates are relatively small to avoid numerical precision issues + QgsVector3D chunkOrigin( rect.center().x(), rect.center().y(), 0 ); + QgsExpressionContext exprContext( Qgs3DUtils::globalProjectLayerExpressionContext( layer ) ); exprContext.setFields( layer->fields() ); mContext.setExpressionContext( exprContext ); @@ -63,15 +69,12 @@ QgsRuleBasedChunkLoader::QgsRuleBasedChunkLoader( const QgsRuleBasedChunkLoaderF mRootRule->createHandlers( layer, mHandlers ); QSet attributeNames; - mRootRule->prepare( mContext, attributeNames, mHandlers ); + mRootRule->prepare( mContext, attributeNames, chunkOrigin, mHandlers ); // build the feature request QgsFeatureRequest req; req.setDestinationCrs( mContext.crs(), mContext.transformContext() ); req.setSubsetOfAttributes( attributeNames, layer->fields() ); - - // only a subset of data to be queried - const QgsRectangle rect = Qgs3DUtils::worldToMapExtent( node->bbox(), mContext.origin() ); req.setFilterRect( rect ); // diff --git a/src/3d/qgstessellatedpolygongeometry.cpp b/src/3d/qgstessellatedpolygongeometry.cpp index 010a66e01a51..a233476aa042 100644 --- a/src/3d/qgstessellatedpolygongeometry.cpp +++ b/src/3d/qgstessellatedpolygongeometry.cpp @@ -82,42 +82,6 @@ QgsTessellatedPolygonGeometry::QgsTessellatedPolygonGeometry( bool _withNormals, } } -void QgsTessellatedPolygonGeometry::setPolygons( const QList &polygons, const QList &featureIds, const QgsPointXY &origin, float extrusionHeight, const QList &extrusionHeightPerPolygon ) -{ - Q_ASSERT( polygons.count() == featureIds.count() ); - mTriangleIndexStartingIndices.reserve( polygons.count() ); - mTriangleIndexFids.reserve( polygons.count() ); - - QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals, mInvertNormals, mAddBackFaces, false, mAddTextureCoords ); - for ( int i = 0; i < polygons.count(); ++i ) - { - Q_ASSERT( tessellator.dataVerticesCount() % 3 == 0 ); - const uint startingTriangleIndex = static_cast( tessellator.dataVerticesCount() / 3 ); - mTriangleIndexStartingIndices.append( startingTriangleIndex ); - mTriangleIndexFids.append( featureIds[i] ); - - QgsPolygon *polygon = polygons.at( i ); - const float extr = extrusionHeightPerPolygon.isEmpty() ? extrusionHeight : extrusionHeightPerPolygon.at( i ); - tessellator.addPolygon( *polygon, extr ); - } - if ( !tessellator.error().isEmpty() ) - { - QgsMessageLog::logMessage( tessellator.error(), QObject::tr( "3D" ) ); - } - - qDeleteAll( polygons ); - - const QByteArray data( ( const char * )tessellator.data().constData(), tessellator.data().count() * sizeof( float ) ); - const int nVerts = data.count() / tessellator.stride(); - - mVertexBuffer->setData( data ); - mPositionAttribute->setCount( nVerts ); - if ( mNormalAttribute ) - mNormalAttribute->setCount( nVerts ); - if ( mAddTextureCoords ) - mTextureCoordsAttribute->setCount( nVerts ); -} - void QgsTessellatedPolygonGeometry::setData( const QByteArray &vertexBufferData, int vertexCount, const QVector &triangleIndexFids, const QVector &triangleIndexStartingIndices ) { mTriangleIndexStartingIndices = triangleIndexStartingIndices; diff --git a/src/3d/qgstessellatedpolygongeometry.h b/src/3d/qgstessellatedpolygongeometry.h index b2d8fa2007e2..20cb2cc9a7ad 100644 --- a/src/3d/qgstessellatedpolygongeometry.h +++ b/src/3d/qgstessellatedpolygongeometry.h @@ -91,9 +91,6 @@ class QgsTessellatedPolygonGeometry : public Qt3DCore::QGeometry */ void setAddTextureCoords( bool add ) { mAddTextureCoords = add; } - //! Initializes vertex buffer from given polygons. Takes ownership of passed polygon geometries - void setPolygons( const QList &polygons, const QList &featureIds, const QgsPointXY &origin, float extrusionHeight, const QList &extrusionHeightPerPolygon = QList() ); - /** * Initializes vertex buffer (and other members) from data that were already tessellated. * This is an alternative to setPolygons() - this method does not do any expensive work in the body. diff --git a/src/3d/qgsvectorlayerchunkloader_p.cpp b/src/3d/qgsvectorlayerchunkloader_p.cpp index 525fafd3bcaa..2de914cbc933 100644 --- a/src/3d/qgsvectorlayerchunkloader_p.cpp +++ b/src/3d/qgsvectorlayerchunkloader_p.cpp @@ -59,12 +59,18 @@ QgsVectorLayerChunkLoader::QgsVectorLayerChunkLoader( const QgsVectorLayerChunkL } mHandler.reset( handler ); + // only a subset of data to be queried + const QgsRectangle rect = Qgs3DUtils::worldToMapExtent( node->bbox(), mRenderContext.origin() ); + // origin for coordinates of the chunk - it is kind of arbitrary, but it should be + // picked so that the coordinates are relatively small to avoid numerical precision issues + QgsVector3D chunkOrigin( rect.center().x(), rect.center().y(), 0 ); + QgsExpressionContext exprContext( Qgs3DUtils::globalProjectLayerExpressionContext( layer ) ); exprContext.setFields( layer->fields() ); mRenderContext.setExpressionContext( exprContext ); QSet attributeNames; - if ( !mHandler->prepare( mRenderContext, attributeNames ) ) + if ( !mHandler->prepare( mRenderContext, attributeNames, chunkOrigin ) ) { QgsDebugError( QStringLiteral( "Failed to prepare 3D feature handler!" ) ); return; @@ -76,9 +82,6 @@ QgsVectorLayerChunkLoader::QgsVectorLayerChunkLoader( const QgsVectorLayerChunkL QgsCoordinateTransform( layer->crs3D(), mRenderContext.crs(), mRenderContext.transformContext() ) ); req.setSubsetOfAttributes( attributeNames, layer->fields() ); - - // only a subset of data to be queried - const QgsRectangle rect = Qgs3DUtils::worldToMapExtent( node->bbox(), mRenderContext.origin() ); req.setFilterRect( rect ); // diff --git a/src/3d/symbols/qgsline3dsymbol_p.cpp b/src/3d/symbols/qgsline3dsymbol_p.cpp index 5a585d044d2b..fe3aadc0df2d 100644 --- a/src/3d/symbols/qgsline3dsymbol_p.cpp +++ b/src/3d/symbols/qgsline3dsymbol_p.cpp @@ -33,6 +33,7 @@ #include "qgsphongtexturedmaterialsettings.h" #include "qgsmessagelog.h" +#include #include #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include @@ -63,7 +64,7 @@ class QgsBufferedLine3DSymbolHandler : public QgsFeature3DHandler : mSymbol( static_cast< QgsLine3DSymbol *>( symbol->clone() ) ) , mSelectedIds( selectedIds ) {} - bool prepare( const Qgs3DRenderContext &context, QSet &attributeNames ) override; + bool prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin ) override; void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override; void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override; @@ -86,6 +87,9 @@ class QgsBufferedLine3DSymbolHandler : public QgsFeature3DHandler // inputs - generic QgsFeatureIds mSelectedIds; + //! origin (in the map coordinates) for output geometries (e.g. at the center of the chunk) + QgsVector3D mChunkOrigin; + // outputs LineData outNormal; //!< Features that are not selected LineData outSelected; //!< Features that are selected @@ -93,17 +97,19 @@ class QgsBufferedLine3DSymbolHandler : public QgsFeature3DHandler -bool QgsBufferedLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet &attributeNames ) +bool QgsBufferedLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin ) { Q_UNUSED( attributeNames ) + mChunkOrigin = chunkOrigin; + const QgsPhongTexturedMaterialSettings *texturedMaterialSettings = dynamic_cast< const QgsPhongTexturedMaterialSettings * >( mSymbol->materialSettings() ); - outNormal.tessellator.reset( new QgsTessellator( context.origin().x(), context.origin().y(), true, + outNormal.tessellator.reset( new QgsTessellator( chunkOrigin.x(), chunkOrigin.y(), true, false, false, false, texturedMaterialSettings ? texturedMaterialSettings->requiresTextureCoordinates() : false, 3, texturedMaterialSettings ? texturedMaterialSettings->textureRotation() : 0 ) ); - outSelected.tessellator.reset( new QgsTessellator( context.origin().x(), context.origin().y(), true, + outSelected.tessellator.reset( new QgsTessellator( chunkOrigin.x(), chunkOrigin.y(), true, false, false, false, texturedMaterialSettings ? texturedMaterialSettings->requiresTextureCoordinates() : false, 3, texturedMaterialSettings ? texturedMaterialSettings->textureRotation() : 0 ) ); @@ -217,10 +223,16 @@ void QgsBufferedLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, cons Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer; renderer->setGeometry( geometry ); + // add transform (our geometry has coordinates relative to mChunkOrigin) + Qt3DCore::QTransform *tr = new Qt3DCore::QTransform; + QVector3D nodeTranslation = ( mChunkOrigin - context.origin() ).toVector3D(); + tr->setTranslation( QVector3D( nodeTranslation.x(), nodeTranslation.z(), -nodeTranslation.y() ) ); + // make entity Qt3DCore::QEntity *entity = new Qt3DCore::QEntity; entity->addComponent( renderer ); entity->addComponent( mat ); + entity->addComponent( tr ); entity->setParent( parent ); if ( !selected ) @@ -243,7 +255,7 @@ class QgsThickLine3DSymbolHandler : public QgsFeature3DHandler { } - bool prepare( const Qgs3DRenderContext &context, QSet &attributeNames ) override; + bool prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin ) override; void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override; void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override; @@ -259,6 +271,9 @@ class QgsThickLine3DSymbolHandler : public QgsFeature3DHandler // inputs - generic QgsFeatureIds mSelectedIds; + //! origin (in the map coordinates) for output geometries (e.g. at the center of the chunk) + QgsVector3D mChunkOrigin; + // outputs QgsLineVertexData outNormal; //!< Features that are not selected QgsLineVertexData outSelected; //!< Features that are selected @@ -266,14 +281,16 @@ class QgsThickLine3DSymbolHandler : public QgsFeature3DHandler -bool QgsThickLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet &attributeNames ) +bool QgsThickLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin ) { Q_UNUSED( attributeNames ) + mChunkOrigin = chunkOrigin; + outNormal.withAdjacency = true; outSelected.withAdjacency = true; - outNormal.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), mSymbol->offset(), context ); - outSelected.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), mSymbol->offset(), context ); + outNormal.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), mSymbol->offset(), context, chunkOrigin ); + outSelected.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), mSymbol->offset(), context, chunkOrigin ); QSet attrs = mSymbol->dataDefinedProperties().referencedFields( context.expressionContext() ); attributeNames.unite( attrs ); @@ -374,9 +391,15 @@ void QgsThickLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Q renderer->setPrimitiveRestartEnabled( true ); renderer->setRestartIndexValue( 0 ); + // add transform (our geometry has coordinates relative to mChunkOrigin) + Qt3DCore::QTransform *tr = new Qt3DCore::QTransform; + QVector3D nodeTranslation = ( mChunkOrigin - context.origin() ).toVector3D(); + tr->setTranslation( QVector3D( nodeTranslation.x(), nodeTranslation.z(), -nodeTranslation.y() ) ); + // make entity entity->addComponent( renderer ); entity->addComponent( mat ); + entity->addComponent( tr ); entity->setParent( parent ); } diff --git a/src/3d/symbols/qgslinevertexdata_p.cpp b/src/3d/symbols/qgslinevertexdata_p.cpp index 2d51a95b9b65..d77493e4e851 100644 --- a/src/3d/symbols/qgslinevertexdata_p.cpp +++ b/src/3d/symbols/qgslinevertexdata_p.cpp @@ -46,12 +46,13 @@ QgsLineVertexData::QgsLineVertexData() vertices << QVector3D(); } -void QgsLineVertexData::init( Qgis::AltitudeClamping clamping, Qgis::AltitudeBinding binding, float height, const Qgs3DRenderContext &context ) +void QgsLineVertexData::init( Qgis::AltitudeClamping clamping, Qgis::AltitudeBinding binding, float height, const Qgs3DRenderContext &context, const QgsVector3D &chunkOrigin ) { altClamping = clamping; altBinding = binding; baseHeight = height; renderContext = context; + origin = chunkOrigin; } QByteArray QgsLineVertexData::createVertexBuffer() @@ -135,9 +136,9 @@ void QgsLineVertexData::addLineString( const QgsLineString &lineString, float ex QgsPoint p = lineString.pointN( i ); float z = Qgs3DUtils::clampAltitude( p, altClamping, altBinding, baseHeight + extraHeightOffset, centroid, renderContext ); - vertices << QVector3D( static_cast< float >( p.x() - renderContext.origin().x() ), + vertices << QVector3D( static_cast< float >( p.x() - origin.x() ), z, - static_cast< float >( -( p.y() - renderContext.origin().y() ) ) ); + static_cast< float >( -( p.y() - origin.y() ) ) ); indexes << vertices.count() - 1; } @@ -168,13 +169,13 @@ void QgsLineVertexData::addVerticalLines( const QgsLineString &lineString, float if ( withAdjacency ) indexes << vertices.count(); // add the following vertex (for adjacency) - vertices << QVector3D( static_cast< float >( p.x() - renderContext.origin().x() ), + vertices << QVector3D( static_cast< float >( p.x() - origin.x() ), z, - static_cast< float >( -( p.y() - renderContext.origin().y() ) ) ); + static_cast< float >( -( p.y() - origin.y() ) ) ); indexes << vertices.count() - 1; - vertices << QVector3D( static_cast< float >( p.x() - renderContext.origin().x() ), + vertices << QVector3D( static_cast< float >( p.x() - origin.x() ), z2, - static_cast< float >( -( p.y() - renderContext.origin().y() ) ) ); + static_cast< float >( -( p.y() - origin.y() ) ) ); indexes << vertices.count() - 1; if ( withAdjacency ) diff --git a/src/3d/symbols/qgslinevertexdata_p.h b/src/3d/symbols/qgslinevertexdata_p.h index f91b2e73028d..8710ceb2cc3b 100644 --- a/src/3d/symbols/qgslinevertexdata_p.h +++ b/src/3d/symbols/qgslinevertexdata_p.h @@ -80,11 +80,12 @@ struct QgsLineVertexData Qgis::AltitudeClamping altClamping = Qgis::AltitudeClamping::Relative; Qgis::AltitudeBinding altBinding = Qgis::AltitudeBinding::Vertex; float baseHeight = 0; - Qgs3DRenderContext renderContext; + Qgs3DRenderContext renderContext; // used for altitude clamping + QgsVector3D origin; // all coordinates are relative to this origin (e.g. center of the chunk) QgsLineVertexData(); - void init( Qgis::AltitudeClamping clamping, Qgis::AltitudeBinding binding, float height, const Qgs3DRenderContext &renderContext ); + void init( Qgis::AltitudeClamping clamping, Qgis::AltitudeBinding binding, float height, const Qgs3DRenderContext &renderContext, const QgsVector3D &chunkOrigin ); QByteArray createVertexBuffer(); QByteArray createIndexBuffer(); diff --git a/src/3d/symbols/qgspoint3dsymbol_p.cpp b/src/3d/symbols/qgspoint3dsymbol_p.cpp index 92124999656a..3014f141a234 100644 --- a/src/3d/symbols/qgspoint3dsymbol_p.cpp +++ b/src/3d/symbols/qgspoint3dsymbol_p.cpp @@ -77,7 +77,7 @@ class QgsInstancedPoint3DSymbolHandler : public QgsFeature3DHandler : mSymbol( static_cast< QgsPoint3DSymbol *>( symbol->clone() ) ) , mSelectedIds( selectedIds ) {} - bool prepare( const Qgs3DRenderContext &context, QSet &attributeNames ) override; + bool prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin ) override; void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override; void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override; @@ -100,16 +100,22 @@ class QgsInstancedPoint3DSymbolHandler : public QgsFeature3DHandler // inputs - generic QgsFeatureIds mSelectedIds; + //! origin (in the map coordinates) for output geometries (e.g. at the center of the chunk) + QgsVector3D mChunkOrigin; + // outputs PointData outNormal; //!< Features that are not selected PointData outSelected; //!< Features that are selected }; -bool QgsInstancedPoint3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet &attributeNames ) +bool QgsInstancedPoint3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin ) { Q_UNUSED( context ) Q_UNUSED( attributeNames ) + + mChunkOrigin = chunkOrigin; + return true; } @@ -120,7 +126,7 @@ void QgsInstancedPoint3DSymbolHandler::processFeature( const QgsFeature &feature if ( feature.geometry().isNull() ) return; - Qgs3DUtils::extractPointPositions( feature, context, mSymbol->altitudeClamping(), out.positions ); + Qgs3DUtils::extractPointPositions( feature, context, mChunkOrigin, mSymbol->altitudeClamping(), out.positions ); mFeatureCount++; } @@ -207,10 +213,16 @@ void QgsInstancedPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, co materialContext.setSelectionColor( context.selectionColor() ); QgsMaterial *mat = material( mSymbol.get(), materialContext ); + // add transform (our geometry has coordinates relative to mChunkOrigin) + Qt3DCore::QTransform *tr = new Qt3DCore::QTransform; + QVector3D nodeTranslation = ( mChunkOrigin - context.origin() ).toVector3D(); + tr->setTranslation( QVector3D( nodeTranslation.x(), nodeTranslation.z(), -nodeTranslation.y() ) ); + // build the entity Qt3DCore::QEntity *entity = new Qt3DCore::QEntity; entity->addComponent( renderer( mSymbol.get(), out.positions ) ); entity->addComponent( mat ); + entity->addComponent( tr ); entity->setParent( parent ); // cppcheck wrongly believes entity will leak @@ -401,15 +413,15 @@ class QgsModelPoint3DSymbolHandler : public QgsFeature3DHandler : mSymbol( static_cast< QgsPoint3DSymbol * >( symbol->clone() ) ) , mSelectedIds( selectedIds ) {} - bool prepare( const Qgs3DRenderContext &context, QSet &attributeNames ) override; + bool prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin ) override; void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override; void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override; private: - static void addSceneEntities( const Qgs3DRenderContext &context, const QVector &positions, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent ); - static void addMeshEntities( const Qgs3DRenderContext &context, const QVector &positions, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent, bool are_selected ); - static Qt3DCore::QTransform *transform( QVector3D position, const QgsPoint3DSymbol *symbol ); + static void addSceneEntities( const Qgs3DRenderContext &context, const QVector &positions, const QgsVector3D &chunkOrigin, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent ); + static void addMeshEntities( const Qgs3DRenderContext &context, const QVector &positions, const QgsVector3D &chunkOrigin, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent, bool are_selected ); + static Qt3DCore::QTransform *transform( QVector3D position, const QgsPoint3DSymbol *symbol, const QgsVector3D &chunkOrigin, const QgsVector3D &contextOrigin ); //! temporary data we will pass to the tessellator struct PointData @@ -424,15 +436,21 @@ class QgsModelPoint3DSymbolHandler : public QgsFeature3DHandler // inputs - generic QgsFeatureIds mSelectedIds; + //! origin (in the map coordinates) for output geometries (e.g. at the center of the chunk) + QgsVector3D mChunkOrigin; + // outputs PointData outNormal; //!< Features that are not selected PointData outSelected; //!< Features that are selected }; -bool QgsModelPoint3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet &attributeNames ) +bool QgsModelPoint3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin ) { Q_UNUSED( context ) Q_UNUSED( attributeNames ) + + mChunkOrigin = chunkOrigin; + return true; } @@ -443,7 +461,7 @@ void QgsModelPoint3DSymbolHandler::processFeature( const QgsFeature &feature, co if ( feature.geometry().isNull() ) return; - Qgs3DUtils::extractPointPositions( feature, context, mSymbol->altitudeClamping(), out.positions ); + Qgs3DUtils::extractPointPositions( feature, context, mChunkOrigin, mSymbol->altitudeClamping(), out.positions ); mFeatureCount++; } @@ -465,7 +483,7 @@ void QgsModelPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const { if ( selected ) { - addMeshEntities( context, out.positions, mSymbol.get(), parent, true ); + addMeshEntities( context, out.positions, mChunkOrigin, mSymbol.get(), parent, true ); } else { @@ -473,18 +491,18 @@ void QgsModelPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const if ( mSymbol->shapeProperty( QStringLiteral( "overwriteMaterial" ) ).toBool() || ( mSymbol->materialSettings() && mSymbol->materialSettings()->type() != QLatin1String( "null" ) ) ) { - addMeshEntities( context, out.positions, mSymbol.get(), parent, false ); + addMeshEntities( context, out.positions, mChunkOrigin, mSymbol.get(), parent, false ); } else { - addSceneEntities( context, out.positions, mSymbol.get(), parent ); + addSceneEntities( context, out.positions, mChunkOrigin, mSymbol.get(), parent ); } } } -void QgsModelPoint3DSymbolHandler::addSceneEntities( const Qgs3DRenderContext &, const QVector &positions, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent ) +void QgsModelPoint3DSymbolHandler::addSceneEntities( const Qgs3DRenderContext &context, const QVector &positions, const QgsVector3D &chunkOrigin, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent ) { for ( const QVector3D &position : positions ) { @@ -500,7 +518,7 @@ void QgsModelPoint3DSymbolHandler::addSceneEntities( const Qgs3DRenderContext &, modelLoader->setSource( url ); entity->addComponent( modelLoader ); - entity->addComponent( transform( position, symbol ) ); + entity->addComponent( transform( position, symbol, chunkOrigin, context.origin() ) ); entity->setParent( parent ); // cppcheck wrongly believes entity will leak @@ -509,7 +527,7 @@ void QgsModelPoint3DSymbolHandler::addSceneEntities( const Qgs3DRenderContext &, } } -void QgsModelPoint3DSymbolHandler::addMeshEntities( const Qgs3DRenderContext &context, const QVector &positions, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent, bool are_selected ) +void QgsModelPoint3DSymbolHandler::addMeshEntities( const Qgs3DRenderContext &context, const QVector &positions, const QgsVector3D &chunkOrigin, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent, bool are_selected ) { if ( positions.empty() ) return; @@ -535,7 +553,7 @@ void QgsModelPoint3DSymbolHandler::addMeshEntities( const Qgs3DRenderContext &co entity->addComponent( mesh ); entity->addComponent( mat ); - entity->addComponent( transform( position, symbol ) ); + entity->addComponent( transform( position, symbol, chunkOrigin, context.origin() ) ); entity->setParent( parent ); // cppcheck wrongly believes entity will leak @@ -544,11 +562,13 @@ void QgsModelPoint3DSymbolHandler::addMeshEntities( const Qgs3DRenderContext &co } } -Qt3DCore::QTransform *QgsModelPoint3DSymbolHandler::transform( QVector3D position, const QgsPoint3DSymbol *symbol ) +Qt3DCore::QTransform *QgsModelPoint3DSymbolHandler::transform( QVector3D position, const QgsPoint3DSymbol *symbol, const QgsVector3D &chunkOrigin, const QgsVector3D &contextOrigin ) { Qt3DCore::QTransform *tr = new Qt3DCore::QTransform; tr->setMatrix( symbol->transform() ); - tr->setTranslation( position + tr->translation() ); + // position is relative to chunkOrigin + QVector3D nodeTranslation = ( chunkOrigin - contextOrigin ).toVector3D(); + tr->setTranslation( position + tr->translation() + QVector3D( nodeTranslation.x(), nodeTranslation.z(), -nodeTranslation.y() ) ); return tr; } @@ -563,7 +583,7 @@ class QgsPoint3DBillboardSymbolHandler : public QgsFeature3DHandler : mSymbol( static_cast< QgsPoint3DSymbol * >( symbol->clone() ) ) , mSelectedIds( selectedIds ) {} - bool prepare( const Qgs3DRenderContext &context, QSet &attributeNames ) override; + bool prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin ) override; void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override; void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override; @@ -582,15 +602,21 @@ class QgsPoint3DBillboardSymbolHandler : public QgsFeature3DHandler // inputs - generic QgsFeatureIds mSelectedIds; + //! origin (in the map coordinates) for output geometries (e.g. at the center of the chunk) + QgsVector3D mChunkOrigin; + // outputs PointData outNormal; //!< Features that are not selected PointData outSelected; //!< Features that are selected }; -bool QgsPoint3DBillboardSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet &attributeNames ) +bool QgsPoint3DBillboardSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin ) { Q_UNUSED( context ) Q_UNUSED( attributeNames ) + + mChunkOrigin = chunkOrigin; + return true; } @@ -601,7 +627,7 @@ void QgsPoint3DBillboardSymbolHandler::processFeature( const QgsFeature &feature if ( feature.geometry().isNull() ) return; - Qgs3DUtils::extractPointPositions( feature, context, mSymbol->altitudeClamping(), out.positions ); + Qgs3DUtils::extractPointPositions( feature, context, mChunkOrigin, mSymbol->altitudeClamping(), out.positions ); mFeatureCount++; } @@ -647,6 +673,9 @@ void QgsPoint3DBillboardSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, co // Billboard Transform Qt3DCore::QTransform *billboardTransform = new Qt3DCore::QTransform(); billboardTransform->setMatrix( mSymbol->billboardTransform() ); + // our geometry has coordinates relative to mChunkOrigin + QVector3D nodeTranslation = ( mChunkOrigin - context.origin() ).toVector3D(); + billboardTransform->setTranslation( billboardTransform->translation() + QVector3D( nodeTranslation.x(), nodeTranslation.z(), -nodeTranslation.y() ) ); // Build the entity Qt3DCore::QEntity *entity = new Qt3DCore::QEntity; diff --git a/src/3d/symbols/qgspolygon3dsymbol_p.cpp b/src/3d/symbols/qgspolygon3dsymbol_p.cpp index 68e7ea2139c1..fa601eaa8377 100644 --- a/src/3d/symbols/qgspolygon3dsymbol_p.cpp +++ b/src/3d/symbols/qgspolygon3dsymbol_p.cpp @@ -54,7 +54,7 @@ class QgsPolygon3DSymbolHandler : public QgsFeature3DHandler : mSymbol( static_cast< QgsPolygon3DSymbol *>( symbol->clone() ) ) , mSelectedIds( selectedIds ) {} - bool prepare( const Qgs3DRenderContext &context, QSet &attributeNames ) override; + bool prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin ) override; void processFeature( const QgsFeature &f, const Qgs3DRenderContext &context ) override; void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override; @@ -79,6 +79,9 @@ class QgsPolygon3DSymbolHandler : public QgsFeature3DHandler // inputs - generic QgsFeatureIds mSelectedIds; + //! origin (in the map coordinates) for output geometries (e.g. at the center of the chunk) + QgsVector3D mChunkOrigin; + // outputs PolygonData outNormal; //!< Features that are not selected PolygonData outSelected; //!< Features that are selected @@ -87,18 +90,20 @@ class QgsPolygon3DSymbolHandler : public QgsFeature3DHandler }; -bool QgsPolygon3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet &attributeNames ) +bool QgsPolygon3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet &attributeNames, const QgsVector3D &chunkOrigin ) { outEdges.withAdjacency = true; - outEdges.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), 0, context ); + outEdges.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), 0, context, chunkOrigin ); + + mChunkOrigin = chunkOrigin; const QgsPhongTexturedMaterialSettings *texturedMaterialSettings = dynamic_cast< const QgsPhongTexturedMaterialSettings * >( mSymbol->materialSettings() ); - outNormal.tessellator.reset( new QgsTessellator( context.origin().x(), context.origin().y(), true, mSymbol->invertNormals(), mSymbol->addBackFaces(), false, + outNormal.tessellator.reset( new QgsTessellator( chunkOrigin.x(), chunkOrigin.y(), true, mSymbol->invertNormals(), mSymbol->addBackFaces(), false, texturedMaterialSettings && texturedMaterialSettings->requiresTextureCoordinates(), mSymbol->renderedFacade(), texturedMaterialSettings ? texturedMaterialSettings->textureRotation() : 0 ) ); - outSelected.tessellator.reset( new QgsTessellator( context.origin().x(), context.origin().y(), true, mSymbol->invertNormals(), + outSelected.tessellator.reset( new QgsTessellator( chunkOrigin.x(), chunkOrigin.y(), true, mSymbol->invertNormals(), mSymbol->addBackFaces(), false, texturedMaterialSettings && texturedMaterialSettings->requiresTextureCoordinates(), mSymbol->renderedFacade(), @@ -253,9 +258,15 @@ void QgsPolygon3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3D renderer->setPrimitiveRestartEnabled( true ); renderer->setRestartIndexValue( 0 ); + // add transform (our geometry has coordinates relative to mChunkOrigin) + Qt3DCore::QTransform *tr = new Qt3DCore::QTransform; + QVector3D nodeTranslation = ( mChunkOrigin - context.origin() ).toVector3D(); + tr->setTranslation( QVector3D( nodeTranslation.x(), nodeTranslation.z(), -nodeTranslation.y() ) ); + // make entity entity->addComponent( renderer ); entity->addComponent( mat ); + entity->addComponent( tr ); entity->setParent( parent ); } } @@ -285,11 +296,17 @@ void QgsPolygon3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer; renderer->setGeometry( geometry ); + // add transform (our geometry has coordinates relative to mChunkOrigin) + Qt3DCore::QTransform *tr = new Qt3DCore::QTransform; + QVector3D nodeTranslation = ( mChunkOrigin - context.origin() ).toVector3D(); + tr->setTranslation( QVector3D( nodeTranslation.x(), nodeTranslation.z(), -nodeTranslation.y() ) ); + // make entity Qt3DCore::QEntity *entity = new Qt3DCore::QEntity; entity->setObjectName( parent->objectName() + "_CHUNK_MESH" ); entity->addComponent( renderer ); entity->addComponent( mat ); + entity->addComponent( tr ); entity->setParent( parent ); if ( !selected )