Skip to content

Commit

Permalink
Fix world origin and camera position on terrain change (fixes #17514)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wonder-sk committed Nov 22, 2017
1 parent eeaf2d9 commit d03b6f4
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 2 deletions.
12 changes: 11 additions & 1 deletion src/3d/qgs3dmapsettings.h
Expand Up @@ -61,7 +61,17 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
//! Resolves references to other objects (map layers) after the call to readXml()
void resolveReferences( const QgsProject &project );

//! Sets coordinates in map CRS at which our 3D world has origin (0,0,0)
/**
* Sets coordinates in map CRS at which our 3D world has origin (0,0,0)
*
* We move the 3D world origin to the center of the extent of our terrain: this is done
* to minimize the impact of numerical errors when operating with 32-bit floats.
* Unfortunately this is not enough when working with a large area (still results in jitter
* with scenes spanning hundreds of kilometers and zooming in a lot).
*
* Need to look into more advanced techniques like "relative to center" or "relative to eye"
* to improve the precision.
*/
void setOrigin( double originX, double originY, double originZ );
//! Returns X coordinate in map CRS at which 3D scene has origin (zero)
double originX() const { return mOriginX; }
Expand Down
6 changes: 6 additions & 0 deletions src/3d/qgscameracontroller.cpp
Expand Up @@ -290,6 +290,12 @@ void QgsCameraController::resetView( float distance )
emit cameraChanged();
}

void QgsCameraController::translateWorld( const QVector3D &vWorld )
{
setCameraData( mCameraData.x - vWorld.x(), mCameraData.y + vWorld.y(), mCameraData.dist, mCameraData.pitch, mCameraData.yaw );
emit cameraChanged();
}

void QgsCameraController::onPositionChanged( Qt3DInput::QMouseEvent *mouse )
{
mMousePos = QPoint( mouse->x(), mouse->y() );
Expand Down
3 changes: 3 additions & 0 deletions src/3d/qgscameracontroller.h
Expand Up @@ -57,6 +57,9 @@ class _3D_EXPORT QgsCameraController : public Qt3DCore::QEntity
//! Move camera back to the initial position (looking down towards origin of world's coordinates)
void resetView( float distance );

//! Moves the point toward which the camera is looking - this is used when world origin changes (e.g. after terrain generator changes)
void translateWorld( const QVector3D &vWorld );

private:
void setCameraData( float x, float y, float dist, float pitch = 0, float yaw = 0 );

Expand Down
5 changes: 5 additions & 0 deletions src/app/3d/qgs3dmapcanvas.cpp
Expand Up @@ -73,6 +73,11 @@ void Qgs3DMapCanvas::setMap( Qgs3DMapSettings *map )
resetView();
}

QgsCameraController *Qgs3DMapCanvas::cameraController()
{
return mScene ? mScene->cameraController() : nullptr;
}

void Qgs3DMapCanvas::resetView()
{
mScene->viewZoomFull();
Expand Down
4 changes: 4 additions & 0 deletions src/app/3d/qgs3dmapcanvas.h
Expand Up @@ -25,6 +25,7 @@ namespace Qt3DExtras

class Qgs3DMapSettings;
class Qgs3DMapScene;
class QgsCameraController;


class Qgs3DMapCanvas : public QWidget
Expand All @@ -39,6 +40,9 @@ class Qgs3DMapCanvas : public QWidget

Qgs3DMapSettings *map() { return mMap; }

//! Returns access to the view's camera controller. Returns null pointer if the scene has not been initialized yet with setMap()
QgsCameraController *cameraController();

//! Resets camera position to the default: looking down at the origin of world coordinates
void resetView();

Expand Down
13 changes: 12 additions & 1 deletion src/app/3d/qgs3dmapcanvasdockwidget.cpp
Expand Up @@ -18,6 +18,7 @@
#include "qgisapp.h"
#include "qgs3dmapcanvas.h"
#include "qgs3dmapconfigwidget.h"
#include "qgscameracontroller.h"
#include "qgsmapcanvas.h"

#include "qgs3dmapsettings.h"
Expand Down Expand Up @@ -72,7 +73,8 @@ void Qgs3DMapCanvasDockWidget::resetView()
void Qgs3DMapCanvasDockWidget::configure()
{
QDialog dlg;
Qgs3DMapConfigWidget *w = new Qgs3DMapConfigWidget( mCanvas->map(), mMainCanvas, &dlg );
Qgs3DMapSettings *map = mCanvas->map();
Qgs3DMapConfigWidget *w = new Qgs3DMapConfigWidget( map, mMainCanvas, &dlg );
QDialogButtonBox *buttons = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dlg );
connect( buttons, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
connect( buttons, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
Expand All @@ -83,8 +85,17 @@ void Qgs3DMapCanvasDockWidget::configure()
if ( !dlg.exec() )
return;

double oldOriginX = map->originX(), oldOriginY = map->originY(), oldOriginZ = map->originZ();

// update map
w->apply();

double dx = map->originX() - oldOriginX, dy = map->originY() - oldOriginY, dz = map->originZ() - oldOriginZ;
if ( dx || dy || dz )
{
// apply() call has moved origin of the world so let's move camera so we look still at the same place
mCanvas->cameraController()->translateWorld( QVector3D( dx, dy, dz ) );
}
}

void Qgs3DMapCanvasDockWidget::onMainCanvasLayersChanged()
Expand Down
10 changes: 10 additions & 0 deletions src/app/3d/qgs3dmapconfigwidget.cpp
Expand Up @@ -72,6 +72,8 @@ void Qgs3DMapConfigWidget::apply()
{
QgsRasterLayer *demLayer = qobject_cast<QgsRasterLayer *>( cboTerrainLayer->currentLayer() );

bool needsUpdateOrigin = false;

if ( demLayer )
{
bool tGenNeedsUpdate = true;
Expand All @@ -92,6 +94,7 @@ void Qgs3DMapConfigWidget::apply()
demTerrainGen->setResolution( spinTerrainResolution->value() );
demTerrainGen->setSkirtHeight( spinTerrainSkirtHeight->value() );
mMap->setTerrainGenerator( demTerrainGen );
needsUpdateOrigin = true;
}
}
else if ( !demLayer && mMap->terrainGenerator()->type() != QgsTerrainGenerator::Flat )
Expand All @@ -100,6 +103,13 @@ void Qgs3DMapConfigWidget::apply()
flatTerrainGen->setCrs( mMap->crs() );
flatTerrainGen->setExtent( mMainCanvas->fullExtent() );
mMap->setTerrainGenerator( flatTerrainGen );
needsUpdateOrigin = true;
}

if ( needsUpdateOrigin )
{
QgsPointXY center = mMap->terrainGenerator()->extent().center();
mMap->setOrigin( center.x(), center.y(), 0 );
}

mMap->setTerrainVerticalScale( spinTerrainScale->value() );
Expand Down

0 comments on commit d03b6f4

Please sign in to comment.