From 184e77faad50f122bca99df93342bfdcb7316edc Mon Sep 17 00:00:00 2001 From: Jean Felder Date: Mon, 16 Dec 2024 17:47:06 +0100 Subject: [PATCH 1/6] qgs3dsceneexporter: Do not try to save if mObjects is empty --- src/3d/qgs3dsceneexporter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/3d/qgs3dsceneexporter.cpp b/src/3d/qgs3dsceneexporter.cpp index c8b73f0ff72f..a62ad52cba9e 100644 --- a/src/3d/qgs3dsceneexporter.cpp +++ b/src/3d/qgs3dsceneexporter.cpp @@ -810,6 +810,11 @@ Qgs3DExportObject *Qgs3DSceneExporter::processPoints( Qt3DCore::QEntity *entity, void Qgs3DSceneExporter::save( const QString &sceneName, const QString &sceneFolderPath, int precision ) { + if ( mObjects.isEmpty() ) + { + return; + } + const QString objFilePath = QDir( sceneFolderPath ).filePath( sceneName + QStringLiteral( ".obj" ) ); const QString mtlFilePath = QDir( sceneFolderPath ).filePath( sceneName + QStringLiteral( ".mtl" ) ); From 858f5c7a3b0fd3df5a0facf3b0efb824f32f9a05 Mon Sep 17 00:00:00 2001 From: Jean Felder Date: Mon, 16 Dec 2024 17:48:45 +0100 Subject: [PATCH 2/6] qgs3dsceneexporter: Return save operation success state --- src/3d/qgs3dsceneexporter.cpp | 9 +++++---- src/3d/qgs3dsceneexporter.h | 7 +++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/3d/qgs3dsceneexporter.cpp b/src/3d/qgs3dsceneexporter.cpp index a62ad52cba9e..8ed99418e37f 100644 --- a/src/3d/qgs3dsceneexporter.cpp +++ b/src/3d/qgs3dsceneexporter.cpp @@ -808,11 +808,11 @@ Qgs3DExportObject *Qgs3DSceneExporter::processPoints( Qt3DCore::QEntity *entity, return obj; } -void Qgs3DSceneExporter::save( const QString &sceneName, const QString &sceneFolderPath, int precision ) +bool Qgs3DSceneExporter::save( const QString &sceneName, const QString &sceneFolderPath, int precision ) { if ( mObjects.isEmpty() ) { - return; + return false; } const QString objFilePath = QDir( sceneFolderPath ).filePath( sceneName + QStringLiteral( ".obj" ) ); @@ -822,13 +822,13 @@ void Qgs3DSceneExporter::save( const QString &sceneName, const QString &sceneFol if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) ) { QgsDebugError( QStringLiteral( "Scene can not be exported to '%1'. File access error." ).arg( objFilePath ) ); - return; + return false; } QFile mtlFile( mtlFilePath ); if ( !mtlFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) ) { QgsDebugError( QStringLiteral( "Scene can not be exported to '%1'. File access error." ).arg( mtlFilePath ) ); - return; + return false; } float maxfloat = std::numeric_limits::max(), minFloat = std::numeric_limits::lowest(); @@ -868,6 +868,7 @@ void Qgs3DSceneExporter::save( const QString &sceneName, const QString &sceneFol } QgsDebugMsgLevel( QStringLiteral( "Scene exported to '%1'" ).arg( objFilePath ), 2 ); + return true; } QString Qgs3DSceneExporter::getObjectName( const QString &name ) diff --git a/src/3d/qgs3dsceneexporter.h b/src/3d/qgs3dsceneexporter.h index cefa70c98aa5..f286d868cde4 100644 --- a/src/3d/qgs3dsceneexporter.h +++ b/src/3d/qgs3dsceneexporter.h @@ -76,8 +76,11 @@ class _3D_EXPORT Qgs3DSceneExporter : public Qt3DCore::QEntity //! Creates terrain export objects from the terrain entity void parseTerrain( QgsTerrainEntity *terrain, const QString &layer ); - //! Saves the scene to a .obj file - void save( const QString &sceneName, const QString &sceneFolderPath, int precision = 6 ); + /** + * Saves the scene to a .obj file + * Returns FALSE if the operation failed + */ + bool save( const QString &sceneName, const QString &sceneFolderPath, int precision = 6 ); //! Sets whether the triangles will look smooth void setSmoothEdges( bool smoothEdges ) { mSmoothEdges = smoothEdges; } From d68922772b35ad290bd50702e9d2fa9138e6b38f Mon Sep 17 00:00:00 2001 From: Jean Felder Date: Mon, 16 Dec 2024 17:50:43 +0100 Subject: [PATCH 3/6] qgs3dmapscene: : Return export operation success state --- python/3d/auto_generated/qgs3dmapscene.sip.in | 3 ++- python/PyQt6/3d/auto_generated/qgs3dmapscene.sip.in | 3 ++- src/3d/qgs3dmapscene.cpp | 10 ++++++++-- src/3d/qgs3dmapscene.h | 7 +++++-- tests/src/3d/testqgs3drendering.cpp | 3 ++- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/python/3d/auto_generated/qgs3dmapscene.sip.in b/python/3d/auto_generated/qgs3dmapscene.sip.in index 9187bc5523f1..64132cf93156 100644 --- a/python/3d/auto_generated/qgs3dmapscene.sip.in +++ b/python/3d/auto_generated/qgs3dmapscene.sip.in @@ -79,9 +79,10 @@ Given screen error (in pixels) and distance from camera (in 3D world coordinates estimates the error in world space. Takes into account camera's field of view and the screen (3D view) size. %End - void exportScene( const Qgs3DMapExportSettings &exportSettings ); + bool exportScene( const Qgs3DMapExportSettings &exportSettings ); %Docstring Exports the scene according to the scene export settings +Returns ``False`` if the operation failed %End diff --git a/python/PyQt6/3d/auto_generated/qgs3dmapscene.sip.in b/python/PyQt6/3d/auto_generated/qgs3dmapscene.sip.in index 40b9008d6916..2fe32e26473d 100644 --- a/python/PyQt6/3d/auto_generated/qgs3dmapscene.sip.in +++ b/python/PyQt6/3d/auto_generated/qgs3dmapscene.sip.in @@ -79,9 +79,10 @@ Given screen error (in pixels) and distance from camera (in 3D world coordinates estimates the error in world space. Takes into account camera's field of view and the screen (3D view) size. %End - void exportScene( const Qgs3DMapExportSettings &exportSettings ); + bool exportScene( const Qgs3DMapExportSettings &exportSettings ); %Docstring Exports the scene according to the scene export settings +Returns ``False`` if the operation failed %End diff --git a/src/3d/qgs3dmapscene.cpp b/src/3d/qgs3dmapscene.cpp index 0b186e882897..66627026247e 100644 --- a/src/3d/qgs3dmapscene.cpp +++ b/src/3d/qgs3dmapscene.cpp @@ -1006,7 +1006,7 @@ void Qgs3DMapScene::onCameraNavigationModeChanged() mCameraController->setCameraNavigationMode( mMap.cameraNavigationMode() ); } -void Qgs3DMapScene::exportScene( const Qgs3DMapExportSettings &exportSettings ) +bool Qgs3DMapScene::exportScene( const Qgs3DMapExportSettings &exportSettings ) { QVector notParsedLayers; Qgs3DSceneExporter exporter; @@ -1045,7 +1045,11 @@ void Qgs3DMapScene::exportScene( const Qgs3DMapExportSettings &exportSettings ) if ( mTerrain ) exporter.parseTerrain( mTerrain, "Terrain" ); - exporter.save( exportSettings.sceneName(), exportSettings.sceneFolderPath() ); + const bool sceneSaved = exporter.save( exportSettings.sceneName(), exportSettings.sceneFolderPath() ); + if ( !sceneSaved ) + { + return false; + } if ( !notParsedLayers.empty() ) { @@ -1054,6 +1058,8 @@ void Qgs3DMapScene::exportScene( const Qgs3DMapExportSettings &exportSettings ) message += layerName + "\n"; QgsMessageOutput::showMessage( tr( "3D exporter warning" ), message, QgsMessageOutput::MessageText ); } + + return true; } QVector Qgs3DMapScene::getLayerActiveChunkNodes( QgsMapLayer *layer ) diff --git a/src/3d/qgs3dmapscene.h b/src/3d/qgs3dmapscene.h index 3bfc4a552c36..27afede28119 100644 --- a/src/3d/qgs3dmapscene.h +++ b/src/3d/qgs3dmapscene.h @@ -127,8 +127,11 @@ class _3D_EXPORT Qgs3DMapScene : public QObject */ float worldSpaceError( float epsilon, float distance ) const; - //! Exports the scene according to the scene export settings - void exportScene( const Qgs3DMapExportSettings &exportSettings ); + /** + * Exports the scene according to the scene export settings + * Returns FALSE if the operation failed + */ + bool exportScene( const Qgs3DMapExportSettings &exportSettings ); /** * Returns the active chunk nodes of \a layer diff --git a/tests/src/3d/testqgs3drendering.cpp b/tests/src/3d/testqgs3drendering.cpp index 3e462f32a291..a12d746aa6f0 100644 --- a/tests/src/3d/testqgs3drendering.cpp +++ b/tests/src/3d/testqgs3drendering.cpp @@ -2274,7 +2274,8 @@ void TestQgs3DRendering::do3DSceneExport( const QString &testName, int zoomLevel exporter.parseTerrain( terrainEntity, "DEM_Tile" ); QString objFileName = QString( "%1-%2" ).arg( testName ).arg( zoomLevelsCount ); - exporter.save( objFileName, QDir::tempPath(), 3 ); + const bool saved = exporter.save( objFileName, QDir::tempPath(), 3 ); + QVERIFY( saved ); int sum = 0; for ( auto o : qAsConst( exporter.mObjects ) ) From 62b7091b8336c640c67c7c103e146d3d750d374f Mon Sep 17 00:00:00 2001 From: Jean Felder Date: Tue, 17 Dec 2024 18:11:34 +0100 Subject: [PATCH 4/6] qgsmap3dexportwidget: Return export scene operation success state --- src/app/3d/qgsmap3dexportwidget.cpp | 4 ++-- src/app/3d/qgsmap3dexportwidget.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/3d/qgsmap3dexportwidget.cpp b/src/app/3d/qgsmap3dexportwidget.cpp index 1a22983f3c0f..b04fd247faad 100644 --- a/src/app/3d/qgsmap3dexportwidget.cpp +++ b/src/app/3d/qgsmap3dexportwidget.cpp @@ -80,7 +80,7 @@ void QgsMap3DExportWidget::settingsChanged() mExportSettings->setScale( ui->scaleSpinBox->value() ); } -void QgsMap3DExportWidget::exportScene() +bool QgsMap3DExportWidget::exportScene() { - mScene->exportScene( *mExportSettings ); + return mScene->exportScene( *mExportSettings ); } diff --git a/src/app/3d/qgsmap3dexportwidget.h b/src/app/3d/qgsmap3dexportwidget.h index 68c4707b9810..755d81d38682 100644 --- a/src/app/3d/qgsmap3dexportwidget.h +++ b/src/app/3d/qgsmap3dexportwidget.h @@ -35,7 +35,7 @@ class QgsMap3DExportWidget : public QWidget ~QgsMap3DExportWidget(); void loadSettings(); - void exportScene(); + bool exportScene(); private slots: void settingsChanged(); From 4e0f1a961dada15d240fb48629a5199d0d5acfae Mon Sep 17 00:00:00 2001 From: Jean Felder Date: Tue, 17 Dec 2024 19:08:36 +0100 Subject: [PATCH 5/6] qgs3dmapcanvaswidget: Give a proper name to QgsMap3DExportWidget --- src/app/3d/qgs3dmapcanvaswidget.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/3d/qgs3dmapcanvaswidget.cpp b/src/app/3d/qgs3dmapcanvaswidget.cpp index a208929605bf..87da3271f8c7 100644 --- a/src/app/3d/qgs3dmapcanvaswidget.cpp +++ b/src/app/3d/qgs3dmapcanvaswidget.cpp @@ -556,7 +556,7 @@ void Qgs3DMapCanvasWidget::exportScene() QgsGui::enableAutoGeometryRestore( &dlg ); Qgs3DMapExportSettings exportSettings; - QgsMap3DExportWidget w( mCanvas->scene(), &exportSettings ); + QgsMap3DExportWidget exportWidget( mCanvas->scene(), &exportSettings ); QDialogButtonBox *buttons = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok, &dlg ); @@ -565,10 +565,10 @@ void Qgs3DMapCanvasWidget::exportScene() connect( buttons, &QDialogButtonBox::helpRequested, &dlg, [=] { QgsHelp::openHelp( QStringLiteral( "map_views/3d_map_view.html" ) ); } ); QVBoxLayout *layout = new QVBoxLayout( &dlg ); - layout->addWidget( &w, 1 ); + layout->addWidget( &exportWidget, 1 ); layout->addWidget( buttons ); if ( dlg.exec() ) - w.exportScene(); + exportWidget.exportScene(); } void Qgs3DMapCanvasWidget::onMainCanvasLayersChanged() From e4793c1fb8af1d27363d6c85530c56215a0bc52c Mon Sep 17 00:00:00 2001 From: Jean Felder Date: Tue, 17 Dec 2024 19:07:39 +0100 Subject: [PATCH 6/6] qgs3dmapcanvaswidget: Display a message to report export success state With this change, the messagebar is used to show a message if the export operation failed or succeeded. --- src/app/3d/qgs3dmapcanvaswidget.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/app/3d/qgs3dmapcanvaswidget.cpp b/src/app/3d/qgs3dmapcanvaswidget.cpp index 87da3271f8c7..f21c393fac73 100644 --- a/src/app/3d/qgs3dmapcanvaswidget.cpp +++ b/src/app/3d/qgs3dmapcanvaswidget.cpp @@ -568,7 +568,18 @@ void Qgs3DMapCanvasWidget::exportScene() layout->addWidget( &exportWidget, 1 ); layout->addWidget( buttons ); if ( dlg.exec() ) - exportWidget.exportScene(); + { + const bool success = exportWidget.exportScene(); + const QString exportFilePath = QDir( exportSettings.sceneFolderPath() ).filePath( exportSettings.sceneName() + QStringLiteral( ".obj" ) ); + if ( success ) + { + mMessageBar->pushMessage( tr( "Export 3D scene" ), tr( "Successfully exported scene to %2" ).arg( QUrl::fromLocalFile( exportFilePath ).toString(), QDir::toNativeSeparators( exportFilePath ) ), Qgis::MessageLevel::Success, 0 ); + } + else + { + mMessageBar->pushMessage( tr( "Export 3D scene" ), tr( "Unable to export scene to %2" ).arg( QUrl::fromLocalFile( exportFilePath ).toString(), QDir::toNativeSeparators( exportFilePath ) ), Qgis::MessageLevel::Warning, 0 ); + } + } } void Qgs3DMapCanvasWidget::onMainCanvasLayersChanged()