Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix 3D zoom wheel distance formula #60778

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open

Conversation

dvdkon
Copy link
Contributor

@dvdkon dvdkon commented Feb 27, 2025

Description

This PR fixes an issue with the 3D camera controller identified by #60700, where multiple quick scroll wheel movements would result in jumping to some unexpected position. The previous distance coefficient would go negative for high enough accumulated values, throwing the user somewhere far below the terrain.

I feel the need to point out that this issue wasn't caused by #60246. It only became more prominent, because previously the performance of handling input events was so abysmal, that enough scroll wheel travel usually didn't accumulated before a frame.

@github-actions github-actions bot added this to the 3.44.0 milestone Feb 27, 2025
Copy link

github-actions bot commented Feb 27, 2025

🪟 Windows builds

Download Windows builds of this PR for testing.
Debug symbols for this build are available here.
(Built from commit e5407c5)

🪟 Windows Qt6 builds

Download Windows Qt6 builds of this PR for testing.
(Built from commit e5407c5)

Copy link

Tests failed for Qt 5

One or more tests failed using the build from commit e5407c5

depth_wheel_action_4 (testDepthBuffer)

depth_wheel_action_4

Test failed at testDepthBuffer at tests/src/3d/testqgs3drendering.cpp:2169

Rendered image did not match tests/testdata/control_images/3d/expected_depth_wheel_action_4/expected_depth_wheel_action_4.png (found 648 pixels different)

The full test report (included comparison of rendered vs expected images) can be found here.

Further documentation on the QGIS test infrastructure can be found in the Developer's Guide.

Copy link

Tests failed for Qt 6

One or more tests failed using the build from commit e5407c5

depth_wheel_action_2 (testDepthBuffer)

depth_wheel_action_2

Test failed at testDepthBuffer at tests/src/3d/testqgs3drendering.cpp:2135

Rendered image did not match tests/testdata/control_images/3d/expected_depth_wheel_action_2/expected_depth_wheel_action_2.png (found 9 pixels different)

The full test report (included comparison of rendered vs expected images) can be found here.

Further documentation on the QGIS test infrastructure can be found in the Developer's Guide.

@nyalldawson
Copy link
Collaborator

Thanks @dvdkon ! Lets bring back the original optimisations then -- #60792

@nyalldawson
Copy link
Collaborator

@dvdkon
I gave this a test, but it's still very sensitive to quick scroll operations. Check this screencast, the blank bits are where i've just done a scroll zoom in and the camera has zoomed right through the terrain

Peek.2025-02-28.09-31.mp4

@dvdkon
Copy link
Contributor Author

dvdkon commented Feb 28, 2025

@dvdkon I gave this a test, but it's still very sensitive to quick scroll operations. Check this screencast, the blank bits are where i've just done a scroll zoom in and the camera has zoomed right through the terrain

Hm, I can't reproduce this. You're testing with DEM terrain, right? Could you please film a new video with the debug pane open? I'd like to see what the coordinates look like in the blanked sections.

Maybe a minimum distance to the zoom point could fix this. Candidate patch follows:

diff --git a/src/3d/qgscameracontroller.cpp b/src/3d/qgscameracontroller.cpp
index 9efedd3caf0..bf1cc495abc 100644
--- a/src/3d/qgscameracontroller.cpp
+++ b/src/3d/qgscameracontroller.cpp
@@ -524,6 +524,11 @@ void QgsCameraController::handleTerrainNavigationWheelZoom()
   double oldDist = ( mZoomPoint - mCameraBefore->position() ).length();
   // Each step of the scroll wheel decreases distance by 20%
   double newDist = std::pow( 0.8, mCumulatedWheelY ) * oldDist;
+  if ( newDist < 1 )
+  {
+    // Make sure we don't clip the thing we're zooming to.
+    newDist = 1;
+  }
   double zoomFactor = newDist / oldDist;
 
   zoomCameraAroundPivot( mCameraBefore->position(), zoomFactor, mZoomPoint );

@nyalldawson
Copy link
Collaborator

@dvdkon unfortunately that patch didn't help.

I added some extra debug code, and here's what I see when the quick zoom glitches out:

src/3d/qgscameracontroller.cpp:584 : (onWheel) [6111ms] mCurrentOperation: None angle delta: 104 mCumulatedWheelY: 520
src/3d/qgscameracontroller.cpp:584 : (onWheel) [0ms] mCurrentOperation: ZoomWheel angle delta: 80 mCumulatedWheelY: 920
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:584 : (onWheel) [16ms] mCurrentOperation: ZoomWheel angle delta: 80 mCumulatedWheelY: 1320
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:584 : (onWheel) [0ms] mCurrentOperation: ZoomWheel angle delta: 88 mCumulatedWheelY: 1760
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:584 : (onWheel) [15ms] mCurrentOperation: ZoomWheel angle delta: 80 mCumulatedWheelY: 2160
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:584 : (onWheel) [0ms] mCurrentOperation: ZoomWheel angle delta: 72 mCumulatedWheelY: 2520
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:584 : (onWheel) [15ms] mCurrentOperation: ZoomWheel angle delta: 48 mCumulatedWheelY: 2760
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:584 : (onWheel) [0ms] mCurrentOperation: ZoomWheel angle delta: 32 mCumulatedWheelY: 2920
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [0ms] f: 1.01389 oldDist: 2577.13 newDist: 1 zoomFactor: 0.000388029
src/3d/qgscameracontroller.cpp:584 : (onWheel) [15ms] mCurrentOperation: None angle delta: 32 mCumulatedWheelY: 160
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [49ms] f: 0.0555556 oldDist: 4782.11 newDist: 4516.44 zoomFactor: 0.944444

Heres' what I see in a normal slow wheel zoom:

src/3d/qgscameracontroller.cpp:584 : (onWheel) [105551ms] mCurrentOperation: None angle delta: 72 mCumulatedWheelY: 360
src/3d/qgscameracontroller.cpp:584 : (onWheel) [32ms] mCurrentOperation: ZoomWheel angle delta: 24 mCumulatedWheelY: 480
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [15ms] f: 0.166667 oldDist: 2608.49 newDist: 2173.74 zoomFactor: 0.833333
src/3d/qgscameracontroller.cpp:584 : (onWheel) [160ms] mCurrentOperation: None angle delta: 16 mCumulatedWheelY: 80
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [47ms] f: 0.0277778 oldDist: 2173.88 newDist: 2113.5 zoomFactor: 0.972222
src/3d/qgscameracontroller.cpp:584 : (onWheel) [31ms] mCurrentOperation: None angle delta: 16 mCumulatedWheelY: 80
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [48ms] f: 0.0277778 oldDist: 2113.52 newDist: 2054.81 zoomFactor: 0.972222
src/3d/qgscameracontroller.cpp:584 : (onWheel) [47ms] mCurrentOperation: None angle delta: 24 mCumulatedWheelY: 120
src/3d/qgscameracontroller.cpp:584 : (onWheel) [16ms] mCurrentOperation: ZoomWheel angle delta: 16 mCumulatedWheelY: 200
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:584 : (onWheel) [16ms] mCurrentOperation: ZoomWheel angle delta: 16 mCumulatedWheelY: 280
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:584 : (onWheel) [16ms] mCurrentOperation: ZoomWheel angle delta: 24 mCumulatedWheelY: 400
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [0ms] f: 0.138889 oldDist: 2054.83 newDist: 1769.43 zoomFactor: 0.861111
src/3d/qgscameracontroller.cpp:584 : (onWheel) [47ms] mCurrentOperation: None angle delta: 16 mCumulatedWheelY: 80
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [48ms] f: 0.0277778 oldDist: 1772.9 newDist: 1723.66 zoomFactor: 0.972222
src/3d/qgscameracontroller.cpp:584 : (onWheel) [127ms] mCurrentOperation: None angle delta: 16 mCumulatedWheelY: 80
src/3d/qgscameracontroller.cpp:584 : (onWheel) [48ms] mCurrentOperation: ZoomWheel angle delta: 16 mCumulatedWheelY: 160
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [0ms] f: 0.0555556 oldDist: 1723.68 newDist: 1627.92 zoomFactor: 0.944444
src/3d/qgscameracontroller.cpp:584 : (onWheel) [79ms] mCurrentOperation: None angle delta: 32 mCumulatedWheelY: 160
src/3d/qgscameracontroller.cpp:584 : (onWheel) [0ms] mCurrentOperation: ZoomWheel angle delta: 16 mCumulatedWheelY: 240
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:584 : (onWheel) [15ms] mCurrentOperation: ZoomWheel angle delta: 24 mCumulatedWheelY: 360
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:584 : (onWheel) [0ms] mCurrentOperation: ZoomWheel angle delta: 24 mCumulatedWheelY: 480
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [32ms] f: 0.166667 oldDist: 1627.95 newDist: 1356.63 zoomFactor: 0.833333
src/3d/qgscameracontroller.cpp:584 : (onWheel) [15ms] mCurrentOperation: None angle delta: 16 mCumulatedWheelY: 80
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [48ms] f: 0.0277778 oldDist: 1684.54 newDist: 1637.75 zoomFactor: 0.972222
src/3d/qgscameracontroller.cpp:584 : (onWheel) [144ms] mCurrentOperation: None angle delta: 16 mCumulatedWheelY: 80
src/3d/qgscameracontroller.cpp:584 : (onWheel) [47ms] mCurrentOperation: ZoomWheel angle delta: 24 mCumulatedWheelY: 200
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [0ms] f: 0.0694444 oldDist: 1310.27 newDist: 1219.28 zoomFactor: 0.930556
src/3d/qgscameracontroller.cpp:584 : (onWheel) [15ms] mCurrentOperation: None angle delta: 16 mCumulatedWheelY: 80
src/3d/qgscameracontroller.cpp:584 : (onWheel) [48ms] mCurrentOperation: ZoomWheel angle delta: 16 mCumulatedWheelY: 160
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [0ms] f: 0.0555556 oldDist: 1333.33 newDist: 1259.25 zoomFactor: 0.944444
src/3d/qgscameracontroller.cpp:584 : (onWheel) [15ms] mCurrentOperation: None angle delta: 24 mCumulatedWheelY: 120
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [48ms] f: 0.0416667 oldDist: 1240.12 newDist: 1188.45 zoomFactor: 0.958333
src/3d/qgscameracontroller.cpp:584 : (onWheel) [15ms] mCurrentOperation: None angle delta: 16 mCumulatedWheelY: 80
src/3d/qgscameracontroller.cpp:584 : (onWheel) [48ms] mCurrentOperation: ZoomWheel angle delta: 16 mCumulatedWheelY: 160
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [0ms] f: 0.0555556 oldDist: 1157.79 newDist: 1093.47 zoomFactor: 0.944444
src/3d/qgscameracontroller.cpp:584 : (onWheel) [224ms] mCurrentOperation: None angle delta: 16 mCumulatedWheelY: 80
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [47ms] f: 0.0277778 oldDist: 1029.59 newDist: 1000.99 zoomFactor: 0.972222
src/3d/qgscameracontroller.cpp:584 : (onWheel) [47ms] mCurrentOperation: None angle delta: 24 mCumulatedWheelY: 120
src/3d/qgscameracontroller.cpp:584 : (onWheel) [16ms] mCurrentOperation: ZoomWheel angle delta: 16 mCumulatedWheelY: 200
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:584 : (onWheel) [0ms] mCurrentOperation: ZoomWheel angle delta: 24 mCumulatedWheelY: 320
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:584 : (onWheel) [16ms] mCurrentOperation: ZoomWheel angle delta: 16 mCumulatedWheelY: 400
src/3d/qgscameracontroller.cpp:529 : (handleTerrainNavigationWheelZoom) [0ms] depth buffer not ready
src/3d/qgscameracontroller.cpp:555 : (handleTerrainNavigationWheelZoom) [15ms] f: 0.138889 oldDist: 1001 newDist: 861.972 zoomFactor: 0.861111

Debug panel before glitchy zoom:
image

after:
image

@dvdkon
Copy link
Contributor Author

dvdkon commented Mar 3, 2025

Thanks for the debug logs @nyalldawson. It looks like you somehow haven't applied this PR? The mCumulatedWheelY values look unchanged (now they should be in the low units) and handleTerrainNavigationWheelZoom() no longer has any variable named f.

dvdkon added 3 commits March 3, 2025 10:20
The previous distance coefficient would go negative for high enough
accumulated values, throwing the user somewhere far below the terrain.

The expected behaviour here is that successive scroll wheel events,
accumulated or not, bring the camera closer to the zoom point, but never
beyond.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants