Skip to content
Permalink
Browse files

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.
  • Loading branch information
wonder-sk committed Nov 22, 2017
1 parent eeaf2d9 commit d03b6f435eed3823353556697faf0f1f1e9b9657
@@ -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; }
@@ -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() );
@@ -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 );

@@ -73,6 +73,11 @@ void Qgs3DMapCanvas::setMap( Qgs3DMapSettings *map )
resetView();
}

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

void Qgs3DMapCanvas::resetView()
{
mScene->viewZoomFull();
@@ -25,6 +25,7 @@ namespace Qt3DExtras

class Qgs3DMapSettings;
class Qgs3DMapScene;
class QgsCameraController;


class Qgs3DMapCanvas : public QWidget
@@ -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();

@@ -18,6 +18,7 @@
#include "qgisapp.h"
#include "qgs3dmapcanvas.h"
#include "qgs3dmapconfigwidget.h"
#include "qgscameracontroller.h"
#include "qgsmapcanvas.h"

#include "qgs3dmapsettings.h"
@@ -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 );
@@ -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()
@@ -72,6 +72,8 @@ void Qgs3DMapConfigWidget::apply()
{
QgsRasterLayer *demLayer = qobject_cast<QgsRasterLayer *>( cboTerrainLayer->currentLayer() );

bool needsUpdateOrigin = false;

if ( demLayer )
{
bool tGenNeedsUpdate = true;
@@ -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 )
@@ -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() );

0 comments on commit d03b6f4

Please sign in to comment.
You can’t perform that action at this time.