diff --git a/src/3d/chunks/qgschunkedentity_p.cpp b/src/3d/chunks/qgschunkedentity_p.cpp index b734dda69a8..246989f459f 100644 --- a/src/3d/chunks/qgschunkedentity_p.cpp +++ b/src/3d/chunks/qgschunkedentity_p.cpp @@ -120,6 +120,8 @@ void QgsChunkedEntity::update( const SceneState &state ) QElapsedTimer t; t.start(); + int oldJobsCount = pendingJobsCount(); + QSet activeBefore = QSet::fromList( mActiveNodes ); mActiveNodes.clear(); mFrustumCulled = 0; @@ -170,6 +172,9 @@ void QgsChunkedEntity::update( const SceneState &state ) mNeedsUpdate = false; // just updated + if ( pendingJobsCount() != oldJobsCount ) + emit pendingJobsCountChanged(); + qDebug() << "update: active " << mActiveNodes.count() << " enabled " << enabled << " disabled " << disabled << " | culled " << mFrustumCulled << " | loading " << mChunkLoaderQueue->count() << " loaded " << mReplacementQueue->count() << " | unloaded " << unloaded << " elapsed " << t.elapsed() << "ms"; } @@ -215,6 +220,11 @@ void QgsChunkedEntity::updateNodes( const QList &nodes, QgsChunk startJob(); } +int QgsChunkedEntity::pendingJobsCount() const +{ + return mChunkLoaderQueue->count() + ( mActiveJob ? 1 : 0 ); +} + void QgsChunkedEntity::update( QgsChunkNode *node, const SceneState &state ) { @@ -306,6 +316,8 @@ void QgsChunkedEntity::requestResidency( QgsChunkNode *node ) void QgsChunkedEntity::onActiveJobFinished() { + int oldJobsCount = pendingJobsCount(); + QgsChunkQueueJob *job = qobject_cast( sender() ); Q_ASSERT( job ); Q_ASSERT( job == mActiveJob ); @@ -340,6 +352,9 @@ void QgsChunkedEntity::onActiveJobFinished() // start another job - if any startJob(); + + if ( pendingJobsCount() != oldJobsCount ) + emit pendingJobsCountChanged(); } void QgsChunkedEntity::startJob() diff --git a/src/3d/chunks/qgschunkedentity_p.h b/src/3d/chunks/qgschunkedentity_p.h index 3d7cd16f5c7..76a1b26a3d4 100644 --- a/src/3d/chunks/qgschunkedentity_p.h +++ b/src/3d/chunks/qgschunkedentity_p.h @@ -82,6 +82,9 @@ class QgsChunkedEntity : public Qt3DCore::QEntity //! Returns the root node of the whole quadtree hierarchy of nodes QgsChunkNode *rootNode() const { return mRootNode; } + //! Returns number of jobs pending for this entity until it is fully loaded/updated in the current view + int pendingJobsCount() const; + protected: //! Cancels the background job that is currently in progress void cancelActiveJob(); @@ -99,6 +102,10 @@ class QgsChunkedEntity : public Qt3DCore::QEntity private slots: void onActiveJobFinished(); + signals: + //! Emitted when the number of pending jobs changes (some jobs have finished or some jobs have been just created) + void pendingJobsCountChanged(); + protected: //! root node of the quadtree hierarchy QgsChunkNode *mRootNode = nullptr; diff --git a/src/3d/qgs3dmapscene.cpp b/src/3d/qgs3dmapscene.cpp index 0af795f8006..2d087a24f40 100644 --- a/src/3d/qgs3dmapscene.cpp +++ b/src/3d/qgs3dmapscene.cpp @@ -166,6 +166,11 @@ void Qgs3DMapScene::viewZoomFull() mCameraController->resetView( side ); // assuming FOV being 45 degrees } +int Qgs3DMapScene::terrainPendingJobsCount() const +{ + return mTerrain ? mTerrain->pendingJobsCount() : 0; +} + QgsChunkedEntity::SceneState _sceneState( QgsCameraController *cameraController ) { Qt3DRender::QCamera *camera = cameraController->camera(); @@ -315,6 +320,10 @@ void Qgs3DMapScene::createTerrainDeferred() } mTerrainUpdateScheduled = false; + + connect( mTerrain, &QgsTerrainEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::terrainPendingJobsCountChanged ); + + emit terrainEntityChanged(); } void Qgs3DMapScene::onBackgroundColorChanged() diff --git a/src/3d/qgs3dmapscene.h b/src/3d/qgs3dmapscene.h index 04278afd962..f2e4add22d2 100644 --- a/src/3d/qgs3dmapscene.h +++ b/src/3d/qgs3dmapscene.h @@ -57,11 +57,20 @@ class _3D_EXPORT Qgs3DMapScene : public Qt3DCore::QEntity //! Returns camera controller QgsCameraController *cameraController() { return mCameraController; } //! Returns terrain entity - QgsTerrainEntity *terrain() { return mTerrain; } + QgsTerrainEntity *terrainEntity() { return mTerrain; } //! Resets camera view to show the whole scene (top view) void viewZoomFull(); + //! Returns number of pending jobs of the terrain entity + int terrainPendingJobsCount() const; + + signals: + //! Emitted when the current terrain entity is replaced by a new one + void terrainEntityChanged(); + //! Emitted when the number of terrain's pending jobs changes + void terrainPendingJobsCountChanged(); + private slots: void onCameraChanged(); void onFrameTriggered( float dt ); diff --git a/src/app/3d/qgs3dmapcanvas.h b/src/app/3d/qgs3dmapcanvas.h index 964aa2f7617..6d45829b171 100644 --- a/src/app/3d/qgs3dmapcanvas.h +++ b/src/app/3d/qgs3dmapcanvas.h @@ -39,8 +39,12 @@ class Qgs3DMapCanvas : public QWidget //! Configure map scene being displayed. Takes ownership. void setMap( Qgs3DMapSettings *map ); + //! Returns access to the 3D scene configuration Qgs3DMapSettings *map() { return mMap; } + //! Returns access to the 3D scene (root 3D entity) + Qgs3DMapScene *scene() { return mScene; } + //! Returns access to the view's camera controller. Returns null pointer if the scene has not been initialized yet with setMap() QgsCameraController *cameraController(); diff --git a/src/app/3d/qgs3dmapcanvasdockwidget.cpp b/src/app/3d/qgs3dmapcanvasdockwidget.cpp index 61fa594eb42..4cd0d4469b4 100644 --- a/src/app/3d/qgs3dmapcanvasdockwidget.cpp +++ b/src/app/3d/qgs3dmapcanvasdockwidget.cpp @@ -18,6 +18,7 @@ #include "qgisapp.h" #include "qgs3dmapcanvas.h" #include "qgs3dmapconfigwidget.h" +#include "qgs3dmapscene.h" #include "qgscameracontroller.h" #include "qgsmapcanvas.h" @@ -27,6 +28,7 @@ #include #include #include +#include #include Qgs3DMapCanvasDockWidget::Qgs3DMapCanvasDockWidget( QWidget *parent ) @@ -45,19 +47,38 @@ Qgs3DMapCanvasDockWidget::Qgs3DMapCanvasDockWidget( QWidget *parent ) mCanvas = new Qgs3DMapCanvas( contentsWidget ); mCanvas->setMinimumSize( QSize( 200, 200 ) ); + mCanvas->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - QVBoxLayout *layout = new QVBoxLayout( contentsWidget ); + mLabelPendingJobs = new QLabel( this ); + mProgressPendingJobs = new QProgressBar( this ); + mProgressPendingJobs->setRange( 0, 0 ); + + QHBoxLayout *topLayout = new QHBoxLayout; + topLayout->setContentsMargins( 0, 0, 0, 0 ); + topLayout->setSpacing( style()->pixelMetric( QStyle::PM_LayoutHorizontalSpacing ) ); + topLayout->addWidget( toolBar ); + topLayout->addStretch( 1 ); + topLayout->addWidget( mLabelPendingJobs ); + topLayout->addWidget( mProgressPendingJobs ); + + QVBoxLayout *layout = new QVBoxLayout; layout->setContentsMargins( 0, 0, 0, 0 ); layout->setSpacing( 0 ); - layout->addWidget( toolBar ); - layout->addWidget( mCanvas, 1 ); + layout->addLayout( topLayout ); + layout->addWidget( mCanvas ); + + contentsWidget->setLayout( layout ); setWidget( contentsWidget ); + + onTerrainPendingJobsCountChanged(); } void Qgs3DMapCanvasDockWidget::setMapSettings( Qgs3DMapSettings *map ) { mCanvas->setMap( map ); + + connect( mCanvas->scene(), &Qgs3DMapScene::terrainPendingJobsCountChanged, this, &Qgs3DMapCanvasDockWidget::onTerrainPendingJobsCountChanged ); } void Qgs3DMapCanvasDockWidget::setMainCanvas( QgsMapCanvas *canvas ) @@ -116,3 +137,12 @@ void Qgs3DMapCanvasDockWidget::onMainCanvasColorChanged() { mCanvas->map()->setBackgroundColor( mMainCanvas->canvasColor() ); } + +void Qgs3DMapCanvasDockWidget::onTerrainPendingJobsCountChanged() +{ + int count = mCanvas->scene() ? mCanvas->scene()->terrainPendingJobsCount() : 0; + mProgressPendingJobs->setVisible( count ); + mLabelPendingJobs->setVisible( count ); + if ( count ) + mLabelPendingJobs->setText( tr( "Loading %1 tiles" ).arg( count ) ); +} diff --git a/src/app/3d/qgs3dmapcanvasdockwidget.h b/src/app/3d/qgs3dmapcanvasdockwidget.h index 9e3c1da6868..b40cb3af5a0 100644 --- a/src/app/3d/qgs3dmapcanvasdockwidget.h +++ b/src/app/3d/qgs3dmapcanvasdockwidget.h @@ -18,10 +18,12 @@ #include "qgsdockwidget.h" -class Qgs3DMapCanvas; -class QgsMapCanvas; +class QLabel; +class QProgressBar; +class Qgs3DMapCanvas; class Qgs3DMapSettings; +class QgsMapCanvas; class Qgs3DMapCanvasDockWidget : public QgsDockWidget @@ -43,10 +45,13 @@ class Qgs3DMapCanvasDockWidget : public QgsDockWidget void onMainCanvasLayersChanged(); void onMainCanvasColorChanged(); + void onTerrainPendingJobsCountChanged(); private: Qgs3DMapCanvas *mCanvas = nullptr; QgsMapCanvas *mMainCanvas = nullptr; + QProgressBar *mProgressPendingJobs = nullptr; + QLabel *mLabelPendingJobs = nullptr; }; #endif // QGS3DMAPCANVASDOCKWIDGET_H