diff --git a/src/3d/qgs3dutils.cpp b/src/3d/qgs3dutils.cpp index 8f86c1bc9a74..296472bc349d 100644 --- a/src/3d/qgs3dutils.cpp +++ b/src/3d/qgs3dutils.cpp @@ -59,8 +59,12 @@ typedef Qt3DCore::QBuffer Qt3DQBuffer; // declared here as Qgs3DTypes has no cpp file const char *Qgs3DTypes::PROP_NAME_3D_RENDERER_FLAG = "PROP_NAME_3D_RENDERER_FLAG"; -static void waitForFrame( Qgs3DMapScene *scene ) +void Qgs3DUtils::waitForFrame( QgsAbstract3DEngine &engine, Qgs3DMapScene *scene ) { + // Set policy to always render frame, so we don't wait forever. + Qt3DRender::QRenderSettings::RenderPolicy oldPolicy = engine.renderSettings()->renderPolicy(); + engine.renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::RenderPolicy::Always ); + // Wait for at least one frame to render Qt3DLogic::QFrameAction *frameAction = new Qt3DLogic::QFrameAction(); scene->addComponent( frameAction ); @@ -68,6 +72,8 @@ static void waitForFrame( Qgs3DMapScene *scene ) QObject::connect( frameAction, &Qt3DLogic::QFrameAction::triggered, &evLoop, &QEventLoop::quit ); evLoop.exec(); scene->removeComponent( frameAction ); + + engine.renderSettings()->setRenderPolicy( oldPolicy ); } QImage Qgs3DUtils::captureSceneImage( QgsAbstract3DEngine &engine, Qgs3DMapScene *scene ) @@ -78,7 +84,7 @@ QImage Qgs3DUtils::captureSceneImage( QgsAbstract3DEngine &engine, Qgs3DMapScene // We need to change render policy to RenderPolicy::Always, since otherwise render capture node won't work engine.renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::RenderPolicy::Always ); - waitForFrame( scene ); + waitForFrame( engine, scene ); auto saveImageFcn = [&evLoop, &resImage]( const QImage &img ) { resImage = img; @@ -124,11 +130,10 @@ QImage Qgs3DUtils::captureSceneDepthBuffer( QgsAbstract3DEngine &engine, Qgs3DMa // We need to change render policy to RenderPolicy::Always, since otherwise render capture node won't work engine.renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::RenderPolicy::Always ); - waitForFrame( scene ); - auto requestImageFcn = [&engine, scene] { if ( scene->sceneState() == Qgs3DMapScene::Ready ) { + waitForFrame( engine, scene ); engine.renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::RenderPolicy::OnDemand ); engine.requestDepthBufferCapture(); } diff --git a/src/3d/qgs3dutils.h b/src/3d/qgs3dutils.h index e2689ecb01ab..bda8d4d9ab64 100644 --- a/src/3d/qgs3dutils.h +++ b/src/3d/qgs3dutils.h @@ -65,6 +65,12 @@ class _3D_EXPORT Qgs3DUtils */ static QImage captureSceneImage( QgsAbstract3DEngine &engine, Qgs3DMapScene *scene ); + /** + * Waits for a frame to be rendered. Useful to trigger once-per-frame updates + * \since QGIS 3.42 + */ + static void waitForFrame( QgsAbstract3DEngine &engine, Qgs3DMapScene *scene ); + /** * Captures the depth buffer of the current 3D scene of a 3D engine. The function waits * until the scene is not fully loaded/updated before capturing the image. diff --git a/src/3d/qgscameracontroller.cpp b/src/3d/qgscameracontroller.cpp index 01f636c6a0da..1871bfb46a50 100644 --- a/src/3d/qgscameracontroller.cpp +++ b/src/3d/qgscameracontroller.cpp @@ -572,6 +572,7 @@ void QgsCameraController::onWheel( Qt3DInput::QWheelEvent *wheel ) if ( mCurrentOperation != MouseOperation::ZoomWheel ) { setMouseParameters( MouseOperation::ZoomWheel ); + // The actual zooming will happen after we get a new depth buffer } else { diff --git a/tests/src/3d/testqgs3dcameracontroller.cpp b/tests/src/3d/testqgs3dcameracontroller.cpp index 0b61369c09e7..86c12364cd6a 100644 --- a/tests/src/3d/testqgs3dcameracontroller.cpp +++ b/tests/src/3d/testqgs3dcameracontroller.cpp @@ -455,14 +455,22 @@ void TestQgs3DCameraController::testRotationCenterZoomWheelRotationCenter() // look from the top scene->cameraController()->setLookingAtPoint( QgsVector3D( 0, 0, 0 ), 2500, 0, 0 ); + // XXX: Sometimes the near/far planes aren't calculated correctly, so they're + // left at the too-deep default. This causes the rest of the test to fail in + // weird ways every once in a while, so loop until we get good values. + do { + // Force recalcualtion of near/far planes. + scene->cameraController()->mCameraChanged = true; + + // this call is not used but ensures to synchronize the scene + Qgs3DUtils::captureSceneImage( engine, scene ); + } while(scene->cameraController()->camera()->nearPlane() < 1000); + QVector3D initialCamViewCenter = scene->cameraController()->camera()->viewCenter(); QVector3D initialCamPosition = scene->cameraController()->camera()->position(); float initialPitch = scene->cameraController()->pitch(); float initialYaw = scene->cameraController()->yaw(); - // this call is not used but ensures to synchronize the scene - Qgs3DUtils::captureSceneImage( engine, scene ); - QMouseEvent mousePressEvent( QEvent::MouseButtonPress, midPos, Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier ); scene->cameraController()->onMousePressed( new Qt3DInput::QMouseEvent( mousePressEvent ) ); @@ -522,8 +530,8 @@ void TestQgs3DCameraController::testRotationCenterZoomWheelRotationCenter() depthImage = Qgs3DUtils::captureSceneDepthBuffer( engine, scene ); scene->cameraController()->depthBufferCaptured( depthImage ); - QGSCOMPARENEARVECTOR3D( scene->cameraController()->mZoomPoint, QVector3D( 283.2, -923.1, -27.0 ), 1.5 ); - QGSCOMPARENEARVECTOR3D( scene->cameraController()->cameraPose().centerPoint(), QVector3D( 99.4, -319.9, -8.8 ), 2.0 ); + QGSCOMPARENEARVECTOR3D( scene->cameraController()->mZoomPoint, QVector3D( 312.936, -950.772, -125.381 ), 1.5 ); + QGSCOMPARENEARVECTOR3D( scene->cameraController()->cameraPose().centerPoint(), QVector3D( 109.8, -329.4, -43.3 ), 2.0 ); QGSCOMPARENEAR( scene->cameraController()->cameraPose().distanceFromCenterPoint(), 1631.9, 2.0 ); QCOMPARE( scene->cameraController()->pitch(), initialPitch ); QCOMPARE( scene->cameraController()->yaw(), initialYaw ); @@ -540,6 +548,7 @@ void TestQgs3DCameraController::testRotationCenterZoomWheelRotationCenter() initialPitch = scene->cameraController()->pitch(); initialYaw = scene->cameraController()->yaw(); + Qgs3DUtils::waitForFrame(engine, scene); // the first mouse event only updates the mouse position // the second one will update the camera QMouseEvent mouseMoveEvent3( QEvent::MouseMove, midPos + movement1 + movement2, Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier ); @@ -558,9 +567,9 @@ void TestQgs3DCameraController::testRotationCenterZoomWheelRotationCenter() QCOMPARE( scene->cameraController()->mCurrentOperation, QgsCameraController::MouseOperation::RotationCenter ); diffViewCenter = scene->cameraController()->camera()->viewCenter() - initialCamViewCenter; - QGSCOMPARENEARVECTOR3D( diffViewCenter, QVector3D( 25.9, 7.1, 5.2 ), 1.0 ); + QGSCOMPARENEARVECTOR3D( diffViewCenter, QVector3D( 26.9, 7.3, 5.4 ), 2.0 ); diffPosition = scene->cameraController()->camera()->position() - initialCamPosition; - QGSCOMPARENEARVECTOR3D( diffPosition, QVector3D( -44.3, -9.1, -11.7 ), 1.0 ); + QGSCOMPARENEARVECTOR3D( diffPosition, QVector3D( -43.2, -9.1, -11.1 ), 1.0 ); diffPitch = scene->cameraController()->pitch() - initialPitch; diffYaw = scene->cameraController()->yaw() - initialYaw; QGSCOMPARENEAR( diffPitch, 2.5, 0.1 ); @@ -857,9 +866,9 @@ void TestQgs3DCameraController::testTranslateZoomWheelTranslate() QCOMPARE( scene->cameraController()->mCurrentOperation, QgsCameraController::MouseOperation::Translation ); diffViewCenter = scene->cameraController()->camera()->viewCenter() - initialCamViewCenter; - QGSCOMPARENEARVECTOR3D( diffViewCenter, QVector3D( -11.3, 11.3, 0.0 ), 1.0 ); + QGSCOMPARENEARVECTOR3D( diffViewCenter, QVector3D( -17.2, 17.2, 0.0 ), 1.0 ); diffPosition = scene->cameraController()->camera()->position() - initialCamPosition; - QGSCOMPARENEARVECTOR3D( diffPosition, QVector3D( -11.3, 11.3, 0.0 ), 1.0 ); + QGSCOMPARENEARVECTOR3D( diffPosition, QVector3D( -17.2, 17.2, 0.0 ), 1.0 ); QCOMPARE( scene->cameraController()->pitch(), initialPitch ); QCOMPARE( scene->cameraController()->yaw(), initialYaw );