diff --git a/python/3d/auto_generated/qgscameracontroller.sip.in b/python/3d/auto_generated/qgscameracontroller.sip.in index a06948f69d27..b409e2d274d3 100644 --- a/python/3d/auto_generated/qgscameracontroller.sip.in +++ b/python/3d/auto_generated/qgscameracontroller.sip.in @@ -90,7 +90,7 @@ yaw angle in degrees. .. versionadded:: 3.4 %End - void setCameraPose( const QgsCameraPose &camPose ); + void setCameraPose( const QgsCameraPose &camPose, bool force = false ); %Docstring Sets camera pose diff --git a/python/PyQt6/3d/auto_generated/qgscameracontroller.sip.in b/python/PyQt6/3d/auto_generated/qgscameracontroller.sip.in index a06948f69d27..b409e2d274d3 100644 --- a/python/PyQt6/3d/auto_generated/qgscameracontroller.sip.in +++ b/python/PyQt6/3d/auto_generated/qgscameracontroller.sip.in @@ -90,7 +90,7 @@ yaw angle in degrees. .. versionadded:: 3.4 %End - void setCameraPose( const QgsCameraPose &camPose ); + void setCameraPose( const QgsCameraPose &camPose, bool force = false ); %Docstring Sets camera pose diff --git a/src/3d/qgscameracontroller.cpp b/src/3d/qgscameracontroller.cpp index d76cce8d947a..b2c4a4712d83 100644 --- a/src/3d/qgscameracontroller.cpp +++ b/src/3d/qgscameracontroller.cpp @@ -186,9 +186,11 @@ void QgsCameraController::setViewFromTop( float worldX, float worldY, float dist camPose.setDistanceFromCenterPoint( distance ); camPose.setHeadingAngle( yaw ); + // a basic setup to make frustum depth range long enough that it does not cull everything + mCamera->setNearPlane( distance / 2 ); + mCamera->setFarPlane( distance * 2 ); // we force the updateCameraNearFarPlanes() in Qgs3DMapScene to properly set the planes - mCameraPose = camPose; - updateCameraFromPose(); + setCameraPose( camPose, true ); } QgsVector3D QgsCameraController::lookingAtPoint() const @@ -206,9 +208,9 @@ void QgsCameraController::setLookingAtPoint( const QgsVector3D &point, float dis setCameraPose( camPose ); } -void QgsCameraController::setCameraPose( const QgsCameraPose &camPose ) +void QgsCameraController::setCameraPose( const QgsCameraPose &camPose, bool force ) { - if ( camPose == mCameraPose ) + if ( camPose == mCameraPose && !force ) return; mCameraPose = camPose; diff --git a/src/3d/qgscameracontroller.h b/src/3d/qgscameracontroller.h index e1b2e7304b6b..687028c8e4d2 100644 --- a/src/3d/qgscameracontroller.h +++ b/src/3d/qgscameracontroller.h @@ -130,7 +130,7 @@ class _3D_EXPORT QgsCameraController : public QObject * Sets camera pose * \since QGIS 3.4 */ - void setCameraPose( const QgsCameraPose &camPose ); + void setCameraPose( const QgsCameraPose &camPose, bool force = false ); /** * Returns camera pose diff --git a/src/core/pointcloud/qgspointcloudlayerelevationproperties.cpp b/src/core/pointcloud/qgspointcloudlayerelevationproperties.cpp index 5d8792557b1e..de43f05bebd7 100644 --- a/src/core/pointcloud/qgspointcloudlayerelevationproperties.cpp +++ b/src/core/pointcloud/qgspointcloudlayerelevationproperties.cpp @@ -140,13 +140,12 @@ QgsDoubleRange QgsPointCloudLayerElevationProperties::calculateZRange( QgsMapLay // try to fetch the elevation properties from virtual point cloud metadata else if ( QgsVirtualPointCloudProvider *virtualProvider = dynamic_cast< QgsVirtualPointCloudProvider * >( pcLayer->dataProvider() ) ) { - zMin = virtualProvider->subIndexes()[0].zRange().lower(); - zMax = virtualProvider->subIndexes()[0].zRange().upper(); for ( QgsPointCloudSubIndex subIndex : virtualProvider->subIndexes() ) { const QgsDoubleRange newRange = subIndex.zRange(); - zMin = std::min( zMin, newRange.lower() ); - zMax = std::max( zMax, newRange.upper() ); + if ( newRange.isInfinite() ) continue; + zMin = std::isnan( zMin ) ? newRange.lower() : std::min( zMin, newRange.lower() ); + zMax = std::isnan( zMax ) ? newRange.upper() : std::max( zMax, newRange.upper() ); } } diff --git a/tests/src/3d/testqgs3dcameracontroller.cpp b/tests/src/3d/testqgs3dcameracontroller.cpp index 7e8f76d161df..d0306d37d8b3 100644 --- a/tests/src/3d/testqgs3dcameracontroller.cpp +++ b/tests/src/3d/testqgs3dcameracontroller.cpp @@ -22,6 +22,8 @@ #include "qgsflatterraingenerator.h" #include "qgsoffscreen3dengine.h" #include "qgsphongmaterialsettings.h" +#include "qgspointcloudlayer.h" +#include "qgspointcloudlayer3drenderer.h" #include "qgspolygon3dsymbol.h" #include "qgsrasterlayer.h" #include "qgsvectorlayer.h" @@ -50,6 +52,8 @@ class TestQgs3DCameraController : public QgsTest void testTranslateZoomWheelTranslate(); void testTranslateRotationCameraTranslate(); void testRotationCenterRotationCameraRotationCenter(); + void testResetViewRaster(); + void testResetViewPointCloud(); private: void waitForNearPlane( QgsOffscreen3DEngine &engine, Qgs3DMapScene *scene, float atLeast ); //#spellok @@ -1190,5 +1194,75 @@ void TestQgs3DCameraController::waitForNearPlane( QgsOffscreen3DEngine &engine, } while ( scene->cameraController()->camera()->nearPlane() < atLeast ); //#spellok } +void TestQgs3DCameraController::testResetViewRaster() +{ + const QgsRectangle fullExtent = mLayerRgb->extent(); + + Qgs3DMapSettings *mapSettings = new Qgs3DMapSettings; + mapSettings->setCrs( mLayerRgb->crs() ); + mapSettings->setExtent( fullExtent ); + mapSettings->setLayers( QList() << mLayerRgb << mLayerBuildings ); + + QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator; + flatTerrain->setCrs( mapSettings->crs(), QgsCoordinateTransformContext() ); + mapSettings->setTerrainGenerator( flatTerrain ); + + constexpr QPoint winSize = QPoint( 640, 480 ); // default window size + QgsOffscreen3DEngine engine; + engine.setSize( QSize( winSize.x(), winSize.y() ) ); + Qgs3DMapScene *scene = new Qgs3DMapScene( *mapSettings, &engine ); + engine.setRootEntity( scene ); + + // compare raster layer + vector layer + scene->viewZoomFull(); + QGSCOMPARENEAR( scene->cameraController()->distance(), 2172, 1 ); +} + +void TestQgs3DCameraController::testResetViewPointCloud() +{ + QgsMapLayer *vpcLayer = new QgsPointCloudLayer( testDataPath( "/point_clouds/virtual/sunshine-coast/combined-with-overview.vpc" ), "vpc", "vpc" ); + QgsPointCloudLayer3DRenderer *renderer = new QgsPointCloudLayer3DRenderer; + QgsSingleColorPointCloud3DSymbol *symbol = new QgsSingleColorPointCloud3DSymbol; + symbol->setSingleColor( QColor( 255, 0, 0 ) ); + renderer->setSymbol( symbol ); + vpcLayer->setRenderer3D( renderer ); + const QgsRectangle fullExtent = vpcLayer->extent(); + + Qgs3DMapSettings *mapSettings = new Qgs3DMapSettings; + mapSettings->setCrs( vpcLayer->crs() ); + mapSettings->setExtent( fullExtent ); + mapSettings->setLayers( QList() << vpcLayer ); + + QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator; + flatTerrain->setCrs( mapSettings->crs(), QgsCoordinateTransformContext() ); + mapSettings->setTerrainGenerator( flatTerrain ); + + constexpr QPoint winSize = QPoint( 640, 480 ); // default window size + QgsOffscreen3DEngine engine; + engine.setSize( QSize( winSize.x(), winSize.y() ) ); + Qgs3DMapScene *scene = new Qgs3DMapScene( *mapSettings, &engine ); + engine.setRootEntity( scene ); + + // compare virtual point cloud layer + scene->viewZoomFull(); + QGSCOMPARENEAR( scene->cameraController()->distance(), 86, 1 ); + + QgsMapLayer *pcLayer = new QgsPointCloudLayer( testDataPath( "/point_clouds/copc/sunshine-coast.copc.laz" ), "test", "copc" ); + QgsPointCloudLayer3DRenderer *renderer2 = new QgsPointCloudLayer3DRenderer; + QgsSingleColorPointCloud3DSymbol *symbol2 = new QgsSingleColorPointCloud3DSymbol; + symbol2->setSingleColor( QColor( 255, 0, 0 ) ); + renderer2->setSymbol( symbol2 ); + pcLayer->setRenderer3D( renderer2 ); + mapSettings->setLayers( QList() << pcLayer ); + scene->cameraController()->setViewFromTop( 0, 0, 10 ); + + // compare point cloud layer + scene->viewZoomFull(); + QGSCOMPARENEAR( scene->cameraController()->distance(), 86, 1 ); + + delete vpcLayer; + delete pcLayer; +} + QGSTEST_MAIN( TestQgs3DCameraController ) #include "testqgs3dcameracontroller.moc"