From 24c1c860a93fdec8e831073cd9a8293a261aa57a Mon Sep 17 00:00:00 2001 From: Martin Dobias Date: Fri, 8 Dec 2017 17:16:52 +0100 Subject: [PATCH] [3d] Show feedback when loading tiles for 3D view (fixes #17565) There was no indication whether something is going on behind the scenes, leaving user to wonder whether there is something to wait for or the scene is already loaded in full detail. --- src/3d/chunks/qgschunkedentity_p.cpp | 15 +++++++++++ src/3d/chunks/qgschunkedentity_p.h | 7 +++++ src/3d/qgs3dmapscene.cpp | 9 +++++++ src/3d/qgs3dmapscene.h | 11 +++++++- src/app/3d/qgs3dmapcanvas.h | 4 +++ src/app/3d/qgs3dmapcanvasdockwidget.cpp | 36 ++++++++++++++++++++++--- src/app/3d/qgs3dmapcanvasdockwidget.h | 9 +++++-- 7 files changed, 85 insertions(+), 6 deletions(-) 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