Skip to content

Commit

Permalink
qgsmesh3geometry: Limit geometry to 3d scenes' 2d extent
Browse files Browse the repository at this point in the history
This is a follow-up of qgis#51304 which
did not handle mesh layers. This commit adds this support.

The meshes displayed in a 3d view do not take into account the scene
2d extent.

This issue is fixed by filtering the faces which are used
to build the Qt3D geometry. By using `mesh.faceIndexesForRectangle(
extent )`, it is possible to keep the faces inside `extent`. This list
is then used to populate the `IndexAttribute` of the qt3d
geometry. This way, only the faces inside the extent are displayed.
  • Loading branch information
ptitjano authored and nyalldawson committed Sep 22, 2023
1 parent 4f345e7 commit 4cf8344
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 26 deletions.
4 changes: 2 additions & 2 deletions src/3d/mesh/qgsmesh3dentity_p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ void QgsMeshDataset3DEntity::buildGeometry()
return;

Qt3DRender::QGeometryRenderer *mesh = new Qt3DRender::QGeometryRenderer;
mesh->setGeometry( new QgsMeshDataset3DGeometry( mTriangularMesh, layer(), mMapSettings.temporalRange(), mMapSettings.origin(), mSymbol.get(), mesh ) );
mesh->setGeometry( new QgsMeshDataset3DGeometry( mTriangularMesh, layer(), mMapSettings.temporalRange(), mMapSettings.origin(), mMapSettings.extent(), mSymbol.get(), mesh ) );
addComponent( mesh );
}

Expand Down Expand Up @@ -89,7 +89,7 @@ QgsMesh3DTerrainTileEntity::QgsMesh3DTerrainTileEntity(
void QgsMesh3DTerrainTileEntity::buildGeometry()
{
Qt3DRender::QGeometryRenderer *mesh = new Qt3DRender::QGeometryRenderer;
mesh->setGeometry( new QgsMeshTerrain3DGeometry( mTriangularMesh, mMapSettings.origin(), mSymbol.get()->verticalScale(), mesh ) );
mesh->setGeometry( new QgsMeshTerrain3DGeometry( mTriangularMesh, mMapSettings.origin(), mMapSettings.extent(), mSymbol.get()->verticalScale(), mesh ) );
addComponent( mesh );
}

Expand Down
52 changes: 28 additions & 24 deletions src/3d/mesh/qgsmesh3dgeometry_p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,25 +132,25 @@ static QByteArray createDatasetVertexData(
return bufferBytes;
}

static QByteArray createIndexData( const QgsTriangularMesh &mesh )
static QByteArray createIndexData( const QgsTriangularMesh &mesh, const QgsRectangle &extent )
{
const int faces = mesh.triangles().count();
const quint32 indices = static_cast<quint32>( 3 * faces );
const QList<int> facesInExtent = mesh.faceIndexesForRectangle( extent );
const quint32 indices = static_cast<quint32>( 3 * facesInExtent.count() );
Q_ASSERT( indices < std::numeric_limits<quint32>::max() );

// count non void faces
int nonVoidFaces = 0;
for ( int i = 0; i < faces; ++i )
if ( !mesh.triangles().at( i ).isEmpty() )
for ( const int nativeFaceIndex : facesInExtent )
if ( !mesh.triangles().at( nativeFaceIndex ).isEmpty() )
nonVoidFaces++;

QByteArray indexBytes;
indexBytes.resize( int( nonVoidFaces * 3 * sizeof( quint32 ) ) );
quint32 *indexPtr = reinterpret_cast<quint32 *>( indexBytes.data() );

for ( int i = 0; i < faces; ++i )
for ( const int nativeFaceIndex : facesInExtent )
{
const QgsMeshFace &face = mesh.triangles().at( i );
const QgsMeshFace &face = mesh.triangles().at( nativeFaceIndex );
if ( face.isEmpty() )
continue;
for ( int j = 0; j < 3; ++j )
Expand All @@ -160,36 +160,34 @@ static QByteArray createIndexData( const QgsTriangularMesh &mesh )
return indexBytes;
}

static QByteArray createDatasetIndexData( const QgsTriangularMesh &mesh, const QgsMeshDataBlock &mActiveFaceFlagValues )
static QByteArray createDatasetIndexData( const QgsTriangularMesh &mesh, const QgsMeshDataBlock &mActiveFaceFlagValues, const QgsRectangle &extent )
{
const QList<int> facesInExtent = mesh.faceIndexesForRectangle( extent );
int activeFaceCount = 0;

// First we need to know about the count of active faces
if ( mActiveFaceFlagValues.active().isEmpty() )
activeFaceCount = mesh.triangles().count();
activeFaceCount = facesInExtent.count();
else
{
for ( int i = 0; i < mesh.triangles().count(); ++i )
for ( const int nativeFaceIndex : facesInExtent )
{
const int nativeIndex = mesh.trianglesToNativeFaces()[i];
if ( mActiveFaceFlagValues.active( nativeIndex ) )
if ( mActiveFaceFlagValues.active( nativeFaceIndex ) )
activeFaceCount++;
}
}

const int trianglesCount = mesh.triangles().count();
const quint32 indices = static_cast<quint32>( 3 * activeFaceCount );
QByteArray indexBytes;
indexBytes.resize( int( indices * sizeof( quint32 ) ) );
quint32 *indexPtr = reinterpret_cast<quint32 *>( indexBytes.data() );

for ( int i = 0; i < trianglesCount; ++i )
for ( const int nativeFaceIndex : facesInExtent )
{
const int nativeFaceIndex = mesh.trianglesToNativeFaces()[i];
const bool isActive = mActiveFaceFlagValues.active().isEmpty() || mActiveFaceFlagValues.active( nativeFaceIndex );
if ( !isActive )
continue;
const QgsMeshFace &face = mesh.triangles().at( i );
const QgsMeshFace &face = mesh.triangles().at( nativeFaceIndex );
for ( int j = 0; j < 3; ++j )
*indexPtr++ = quint32( face.at( j ) );
}
Expand All @@ -199,6 +197,7 @@ static QByteArray createDatasetIndexData( const QgsTriangularMesh &mesh, const Q

QgsMesh3DGeometry::QgsMesh3DGeometry( const QgsTriangularMesh &triangularMesh,
const QgsVector3D &origin,
const QgsRectangle &extent,
double verticalScale,
Qt3DCore::QNode *parent )
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Expand All @@ -207,6 +206,7 @@ QgsMesh3DGeometry::QgsMesh3DGeometry( const QgsTriangularMesh &triangularMesh,
: Qt3DCore::QGeometry( parent )
#endif
, mOrigin( origin )
, mExtent( extent )
, mVertScale( verticalScale )
, mTriangulaMesh( triangularMesh )
{
Expand All @@ -219,9 +219,10 @@ QgsMeshDataset3DGeometry::QgsMeshDataset3DGeometry(
QgsMeshLayer *layer,
const QgsDateTimeRange &timeRange,
const QgsVector3D &origin,
const QgsRectangle &extent,
const QgsMesh3DSymbol *symbol,
Qt3DCore::QNode *parent )
: QgsMesh3DGeometry( triangularMesh, origin, symbol->verticalScale(), parent )
: QgsMesh3DGeometry( triangularMesh, origin, extent, symbol->verticalScale(), parent )
, mIsVerticalMagnitudeRelative( symbol->isVerticalMagnitudeRelative() )
, mVerticalGroupDatasetIndex( symbol->verticalDatasetGroupIndex() )
, mTimeRange( timeRange )
Expand Down Expand Up @@ -310,7 +311,7 @@ void QgsMeshDataset3DGeometry::prepareData()
data.activeFaceFlagValues = layer->areFacesActive( scalarDatasetIndex, 0, nativeMesh.faces.count() );
data.isVerticalMagnitudeRelative = mIsVerticalMagnitudeRelative;

mBuilder = new QgsMeshDataset3DGeometryBuilder( mTriangulaMesh, nativeMesh, mOrigin, mVertScale, data, this );
mBuilder = new QgsMeshDataset3DGeometryBuilder( mTriangulaMesh, nativeMesh, mOrigin, mExtent, mVertScale, data, this );
connect( mBuilder, &QgsMeshDataset3DGeometryBuilder::dataIsReady, this, &QgsMeshDataset3DGeometry::getData );

mBuilder->start();
Expand All @@ -319,9 +320,10 @@ void QgsMeshDataset3DGeometry::prepareData()

QgsMeshTerrain3DGeometry::QgsMeshTerrain3DGeometry( const QgsTriangularMesh &triangularMesh,
const QgsVector3D &origin,
const QgsRectangle &extent,
double verticalScale,
Qt3DCore::QNode *parent )
: QgsMesh3DGeometry( triangularMesh, origin, verticalScale, parent )
: QgsMesh3DGeometry( triangularMesh, origin, extent, verticalScale, parent )
{

const int stride = ( 3 /*position*/ +
Expand All @@ -331,7 +333,7 @@ QgsMeshTerrain3DGeometry::QgsMeshTerrain3DGeometry( const QgsTriangularMesh &tri
prepareVerticesNormalAttribute( mVertexBuffer, stride, 3 );
prepareIndexesAttribute( mIndexBuffer );

mBuilder = new QgsMesh3DGeometryBuilder( triangularMesh, origin, mVertScale, this );
mBuilder = new QgsMesh3DGeometryBuilder( triangularMesh, origin, extent, mVertScale, this );
connect( mBuilder, &QgsMesh3DGeometryBuilder::dataIsReady, this, &QgsMeshTerrain3DGeometry::getData );
mBuilder->start();
}
Expand Down Expand Up @@ -416,10 +418,11 @@ void QgsMesh3DGeometry::prepareIndexesAttribute( Qt3DQBuffer *buffer )
}


QgsMesh3DGeometryBuilder::QgsMesh3DGeometryBuilder( const QgsTriangularMesh &mesh, const QgsVector3D &origin, float vertScale, QObject *parent ):
QgsMesh3DGeometryBuilder::QgsMesh3DGeometryBuilder( const QgsTriangularMesh &mesh, const QgsVector3D &origin, const QgsRectangle &extent, float vertScale, QObject *parent ):
QObject( parent )
, mMesh( mesh )
, mOrigin( origin )
, mExtent( extent )
, mVertScale( vertScale )
{}

Expand All @@ -434,7 +437,7 @@ void QgsMesh3DGeometryBuilder::start()

mWatcherIndex = new QFutureWatcher<QByteArray>( this );
connect( mWatcherIndex, &QFutureWatcher<int>::finished, this, &QgsMesh3DGeometryBuilder::indexFinished );
mFutureIndex = QtConcurrent::run( createIndexData, mMesh );
mFutureIndex = QtConcurrent::run( createIndexData, mMesh, mExtent );
mWatcherIndex->setFuture( mFutureIndex );
}

Expand Down Expand Up @@ -462,10 +465,11 @@ QgsMeshDataset3DGeometryBuilder::QgsMeshDataset3DGeometryBuilder
( const QgsTriangularMesh &mesh,
const QgsMesh &nativeMesh,
const QgsVector3D &origin,
const QgsRectangle &extent,
float vertScale,
const QgsMeshDataset3DGeometry::VertexData &vertexData,
QObject *parent ):
QgsMesh3DGeometryBuilder( mesh, origin, vertScale, parent )
QgsMesh3DGeometryBuilder( mesh, origin, extent, vertScale, parent )
, mNativeMesh( nativeMesh )
, mVertexData( vertexData )
{}
Expand All @@ -482,6 +486,6 @@ void QgsMeshDataset3DGeometryBuilder::start()
mWatcherIndex = new QFutureWatcher<QByteArray>( this );
connect( mWatcherIndex, &QFutureWatcher<int>::finished, this, &QgsMeshDataset3DGeometryBuilder::indexFinished );

mFutureIndex = QtConcurrent::run( createDatasetIndexData, mMesh, mVertexData.activeFaceFlagValues );
mFutureIndex = QtConcurrent::run( createDatasetIndexData, mMesh, mVertexData.activeFaceFlagValues, mExtent );
mWatcherIndex->setFuture( mFutureIndex );
}
8 changes: 8 additions & 0 deletions src/3d/mesh/qgsmesh3dgeometry_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#include "qgsmaplayerref.h"
#include "qgsmesh3dsymbol.h"
#include "qgsrectangle.h"
#include "qgstriangularmesh.h"

///@cond PRIVATE
Expand Down Expand Up @@ -72,6 +73,7 @@ class QgsMesh3DGeometryBuilder: public QObject
public:
QgsMesh3DGeometryBuilder( const QgsTriangularMesh &mesh,
const QgsVector3D &origin,
const QgsRectangle &extent,
float vertScale,
QObject *parent );

Expand All @@ -95,6 +97,7 @@ class QgsMesh3DGeometryBuilder: public QObject

QgsTriangularMesh mMesh;
QgsVector3D mOrigin;
QgsRectangle mExtent;
float mVertScale;

mutable QMutex mMutex;
Expand All @@ -118,6 +121,7 @@ class QgsMesh3DGeometry: public Qt3DCore::QGeometry
//! Constructor
explicit QgsMesh3DGeometry( const QgsTriangularMesh &triangularMesh,
const QgsVector3D &origin,
const QgsRectangle &extent,
double verticalScale,
QNode *parent );

Expand All @@ -134,6 +138,7 @@ class QgsMesh3DGeometry: public Qt3DCore::QGeometry
#endif

QgsVector3D mOrigin;
QgsRectangle mExtent;
float mVertScale;
QgsTriangularMesh mTriangulaMesh;

Expand Down Expand Up @@ -179,6 +184,7 @@ class QgsMeshDataset3DGeometry: public QgsMesh3DGeometry
QgsMeshLayer *layer,
const QgsDateTimeRange &timeRange,
const QgsVector3D &origin,
const QgsRectangle &extent,
const QgsMesh3DSymbol *symbol,
QNode *parent );

Expand Down Expand Up @@ -230,6 +236,7 @@ class QgsMeshDataset3DGeometryBuilder: public QgsMesh3DGeometryBuilder
QgsMeshDataset3DGeometryBuilder( const QgsTriangularMesh &mesh,
const QgsMesh &nativeMesh,
const QgsVector3D &origin,
const QgsRectangle &extent,
float vertScale,
const QgsMeshDataset3DGeometry::VertexData &vertexData,
QObject *parent );
Expand All @@ -253,6 +260,7 @@ class QgsMeshTerrain3DGeometry: public QgsMesh3DGeometry
//! Constructs a mesh layer geometry from triangular mesh.
explicit QgsMeshTerrain3DGeometry( const QgsTriangularMesh &triangularMesh,
const QgsVector3D &origin,
const QgsRectangle &extent,
double verticalScale,
QNode *parent );
};
Expand Down
46 changes: 46 additions & 0 deletions tests/src/3d/testqgs3drendering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class TestQgs3DRendering : public QgsTest
void testFilteredFlatTerrain();
void testFilteredDemTerrain();
void testFilteredExtrudedPolygons();
void testFilteredMesh();
void testDepthBuffer();
void testAmbientOcclusion();
void testDebugMap();
Expand Down Expand Up @@ -1456,6 +1457,51 @@ void TestQgs3DRendering::testFilteredExtrudedPolygons()
QVERIFY( imageCheck( "polygon3d_extrusion_filtered", "polygon3d_extrusion_filtered", img, QString(), 40, QSize( 0, 0 ), 2 ) );
}

void TestQgs3DRendering::testFilteredMesh()
{
const QgsRectangle fullExtent = mLayerMeshDataset->extent();
const QgsRectangle filteredExtent = QgsRectangle( fullExtent.xMinimum(), fullExtent.yMinimum(),
fullExtent.xMinimum() + fullExtent.width() / 3.0, fullExtent.yMinimum() + fullExtent.height() / 4.0 );

QgsMesh3DSymbol *symbolMesh3d = new QgsMesh3DSymbol;
symbolMesh3d->setVerticalDatasetGroupIndex( 0 );
symbolMesh3d->setVerticalScale( 10 );
symbolMesh3d->setRenderingStyle( QgsMesh3DSymbol::ColorRamp2DRendering );
symbolMesh3d->setArrowsEnabled( true );
symbolMesh3d->setArrowsSpacing( 300 );
QgsMeshLayer3DRenderer *meshDatasetRenderer3d = new QgsMeshLayer3DRenderer( symbolMesh3d );
mLayerMeshDataset->setRenderer3D( meshDatasetRenderer3d );

Qgs3DMapSettings *map = new Qgs3DMapSettings;
map->setCrs( mProject->crs() );
map->setExtent( filteredExtent );
map->setLayers( QList<QgsMapLayer *>() << mLayerMeshDataset );
QgsPointLightSettings defaultLight;
defaultLight.setIntensity( 0.5 );
defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) );
map->setLightSources( { defaultLight.clone() } );

QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
flatTerrain->setCrs( map->crs() );
map->setTerrainGenerator( flatTerrain );

QgsOffscreen3DEngine engine;
Qgs3DMapScene *scene = new Qgs3DMapScene( *map, &engine );
engine.setRootEntity( scene );
scene->cameraController()->setLookingAtPoint( QgsVector3D( 0, 0, 0 ), 3000, 25, 45 );

// When running the test on Travis, it would initially return empty rendered image.
// Capturing the initial image and throwing it away fixes that. Hopefully we will
// find a better fix in the future.
Qgs3DUtils::captureSceneImage( engine, scene );

QImage img = Qgs3DUtils::captureSceneImage( engine, scene );
delete scene;
delete map;

QVERIFY( imageCheck( "mesh3d_filtered", "mesh3d_filtered", img, QString(), 40, QSize( 0, 0 ), 2 ) );
}

void TestQgs3DRendering::testAmbientOcclusion()
{
// =============================================
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 4cf8344

Please sign in to comment.