Skip to content

Commit d03b6f4

Browse files
committed
Fix world origin and camera position on terrain change (fixes #17514)
This makes "zoom full" in 3D view working again after modification of terrain generator. Also we update the camera position so it looks at the same spot as it was before.
1 parent eeaf2d9 commit d03b6f4

File tree

7 files changed

+51
-2
lines changed

7 files changed

+51
-2
lines changed

src/3d/qgs3dmapsettings.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,17 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
6161
//! Resolves references to other objects (map layers) after the call to readXml()
6262
void resolveReferences( const QgsProject &project );
6363

64-
//! Sets coordinates in map CRS at which our 3D world has origin (0,0,0)
64+
/**
65+
* Sets coordinates in map CRS at which our 3D world has origin (0,0,0)
66+
*
67+
* We move the 3D world origin to the center of the extent of our terrain: this is done
68+
* to minimize the impact of numerical errors when operating with 32-bit floats.
69+
* Unfortunately this is not enough when working with a large area (still results in jitter
70+
* with scenes spanning hundreds of kilometers and zooming in a lot).
71+
*
72+
* Need to look into more advanced techniques like "relative to center" or "relative to eye"
73+
* to improve the precision.
74+
*/
6575
void setOrigin( double originX, double originY, double originZ );
6676
//! Returns X coordinate in map CRS at which 3D scene has origin (zero)
6777
double originX() const { return mOriginX; }

src/3d/qgscameracontroller.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,12 @@ void QgsCameraController::resetView( float distance )
290290
emit cameraChanged();
291291
}
292292

293+
void QgsCameraController::translateWorld( const QVector3D &vWorld )
294+
{
295+
setCameraData( mCameraData.x - vWorld.x(), mCameraData.y + vWorld.y(), mCameraData.dist, mCameraData.pitch, mCameraData.yaw );
296+
emit cameraChanged();
297+
}
298+
293299
void QgsCameraController::onPositionChanged( Qt3DInput::QMouseEvent *mouse )
294300
{
295301
mMousePos = QPoint( mouse->x(), mouse->y() );

src/3d/qgscameracontroller.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ class _3D_EXPORT QgsCameraController : public Qt3DCore::QEntity
5757
//! Move camera back to the initial position (looking down towards origin of world's coordinates)
5858
void resetView( float distance );
5959

60+
//! Moves the point toward which the camera is looking - this is used when world origin changes (e.g. after terrain generator changes)
61+
void translateWorld( const QVector3D &vWorld );
62+
6063
private:
6164
void setCameraData( float x, float y, float dist, float pitch = 0, float yaw = 0 );
6265

src/app/3d/qgs3dmapcanvas.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ void Qgs3DMapCanvas::setMap( Qgs3DMapSettings *map )
7373
resetView();
7474
}
7575

76+
QgsCameraController *Qgs3DMapCanvas::cameraController()
77+
{
78+
return mScene ? mScene->cameraController() : nullptr;
79+
}
80+
7681
void Qgs3DMapCanvas::resetView()
7782
{
7883
mScene->viewZoomFull();

src/app/3d/qgs3dmapcanvas.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ namespace Qt3DExtras
2525

2626
class Qgs3DMapSettings;
2727
class Qgs3DMapScene;
28+
class QgsCameraController;
2829

2930

3031
class Qgs3DMapCanvas : public QWidget
@@ -39,6 +40,9 @@ class Qgs3DMapCanvas : public QWidget
3940

4041
Qgs3DMapSettings *map() { return mMap; }
4142

43+
//! Returns access to the view's camera controller. Returns null pointer if the scene has not been initialized yet with setMap()
44+
QgsCameraController *cameraController();
45+
4246
//! Resets camera position to the default: looking down at the origin of world coordinates
4347
void resetView();
4448

src/app/3d/qgs3dmapcanvasdockwidget.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "qgisapp.h"
1919
#include "qgs3dmapcanvas.h"
2020
#include "qgs3dmapconfigwidget.h"
21+
#include "qgscameracontroller.h"
2122
#include "qgsmapcanvas.h"
2223

2324
#include "qgs3dmapsettings.h"
@@ -72,7 +73,8 @@ void Qgs3DMapCanvasDockWidget::resetView()
7273
void Qgs3DMapCanvasDockWidget::configure()
7374
{
7475
QDialog dlg;
75-
Qgs3DMapConfigWidget *w = new Qgs3DMapConfigWidget( mCanvas->map(), mMainCanvas, &dlg );
76+
Qgs3DMapSettings *map = mCanvas->map();
77+
Qgs3DMapConfigWidget *w = new Qgs3DMapConfigWidget( map, mMainCanvas, &dlg );
7678
QDialogButtonBox *buttons = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dlg );
7779
connect( buttons, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
7880
connect( buttons, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
@@ -83,8 +85,17 @@ void Qgs3DMapCanvasDockWidget::configure()
8385
if ( !dlg.exec() )
8486
return;
8587

88+
double oldOriginX = map->originX(), oldOriginY = map->originY(), oldOriginZ = map->originZ();
89+
8690
// update map
8791
w->apply();
92+
93+
double dx = map->originX() - oldOriginX, dy = map->originY() - oldOriginY, dz = map->originZ() - oldOriginZ;
94+
if ( dx || dy || dz )
95+
{
96+
// apply() call has moved origin of the world so let's move camera so we look still at the same place
97+
mCanvas->cameraController()->translateWorld( QVector3D( dx, dy, dz ) );
98+
}
8899
}
89100

90101
void Qgs3DMapCanvasDockWidget::onMainCanvasLayersChanged()

src/app/3d/qgs3dmapconfigwidget.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ void Qgs3DMapConfigWidget::apply()
7272
{
7373
QgsRasterLayer *demLayer = qobject_cast<QgsRasterLayer *>( cboTerrainLayer->currentLayer() );
7474

75+
bool needsUpdateOrigin = false;
76+
7577
if ( demLayer )
7678
{
7779
bool tGenNeedsUpdate = true;
@@ -92,6 +94,7 @@ void Qgs3DMapConfigWidget::apply()
9294
demTerrainGen->setResolution( spinTerrainResolution->value() );
9395
demTerrainGen->setSkirtHeight( spinTerrainSkirtHeight->value() );
9496
mMap->setTerrainGenerator( demTerrainGen );
97+
needsUpdateOrigin = true;
9598
}
9699
}
97100
else if ( !demLayer && mMap->terrainGenerator()->type() != QgsTerrainGenerator::Flat )
@@ -100,6 +103,13 @@ void Qgs3DMapConfigWidget::apply()
100103
flatTerrainGen->setCrs( mMap->crs() );
101104
flatTerrainGen->setExtent( mMainCanvas->fullExtent() );
102105
mMap->setTerrainGenerator( flatTerrainGen );
106+
needsUpdateOrigin = true;
107+
}
108+
109+
if ( needsUpdateOrigin )
110+
{
111+
QgsPointXY center = mMap->terrainGenerator()->extent().center();
112+
mMap->setOrigin( center.x(), center.y(), 0 );
103113
}
104114

105115
mMap->setTerrainVerticalScale( spinTerrainScale->value() );

0 commit comments

Comments
 (0)