QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgsterrainentity_p.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsterrainentity_p.cpp
3  --------------------------------------
4  Date : July 2017
5  Copyright : (C) 2017 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsterrainentity_p.h"
17 
18 #include "qgsaabb.h"
19 #include "qgs3dmapsettings.h"
20 #include "qgschunknode_p.h"
22 #include "qgseventtracing.h"
23 #include "qgsraycastingutils_p.h"
24 #include "qgsterraingenerator.h"
27 #include "qgsterraintileentity_p.h"
28 
29 #include "qgscoordinatetransform.h"
30 
31 #include <Qt3DCore/QTransform>
32 #include <Qt3DRender/QGeometryRenderer>
33 #include <Qt3DRender/QObjectPicker>
34 
35 
37 
39 class TerrainMapUpdateJobFactory : public QgsChunkQueueJobFactory
40 {
41  public:
42  TerrainMapUpdateJobFactory( QgsTerrainTextureGenerator *textureGenerator )
43  : mTextureGenerator( textureGenerator )
44  {
45  }
46 
47  QgsChunkQueueJob *createJob( QgsChunkNode *chunk ) override
48  {
49  return new TerrainMapUpdateJob( mTextureGenerator, chunk );
50  }
51 
52  private:
53  QgsTerrainTextureGenerator *mTextureGenerator = nullptr;
54 };
55 
56 
57 // -----------
58 
59 
60 QgsTerrainEntity::QgsTerrainEntity( const Qgs3DMapSettings &map, Qt3DCore::QNode *parent )
61  : QgsChunkedEntity( map.maxTerrainScreenError(), map.terrainGenerator(), false, std::numeric_limits<int>::max(), parent )
62  , mMap( map )
63 {
64  map.terrainGenerator()->setTerrain( this );
65  mIsValid = map.terrainGenerator()->isValid();
66 
67  connect( &map, &Qgs3DMapSettings::showTerrainBoundingBoxesChanged, this, &QgsTerrainEntity::onShowBoundingBoxesChanged );
68  connect( &map, &Qgs3DMapSettings::showTerrainTilesInfoChanged, this, &QgsTerrainEntity::invalidateMapImages );
69  connect( &map, &Qgs3DMapSettings::showLabelsChanged, this, &QgsTerrainEntity::invalidateMapImages );
70  connect( &map, &Qgs3DMapSettings::terrainLayersChanged, this, &QgsTerrainEntity::onLayersChanged );
71  connect( &map, &Qgs3DMapSettings::backgroundColorChanged, this, &QgsTerrainEntity::invalidateMapImages );
72  connect( &map, &Qgs3DMapSettings::terrainMapThemeChanged, this, &QgsTerrainEntity::invalidateMapImages );
73  connect( &map, &Qgs3DMapSettings::terrainElevationOffsetChanged, this, &QgsTerrainEntity::onTerrainElevationOffsetChanged );
74 
75  connectToLayersRepaintRequest();
76 
77  mTerrainToMapTransform = new QgsCoordinateTransform( map.terrainGenerator()->crs(), map.crs(), map.transformContext() );
78 
79  mTextureGenerator = new QgsTerrainTextureGenerator( map );
80 
81  mUpdateJobFactory.reset( new TerrainMapUpdateJobFactory( mTextureGenerator ) );
82 
83  mTerrainPicker = new Qt3DRender::QObjectPicker;
84  // add camera control's terrain picker as a component to be able to capture height where mouse was
85  // pressed in order to correctly pan camera when dragging mouse
86  addComponent( mTerrainPicker );
87 
88  mTerrainTransform = new Qt3DCore::QTransform;
89  mTerrainTransform->setScale( 1.0f );
90  mTerrainTransform->setTranslation( QVector3D( 0.0f, map.terrainElevationOffset(), 0.0f ) );
91  addComponent( mTerrainTransform );
92 }
93 
94 QgsTerrainEntity::~QgsTerrainEntity()
95 {
96  // cancel / wait for jobs
97  cancelActiveJobs();
98 
99  delete mTextureGenerator;
100  delete mTerrainToMapTransform;
101 }
102 
103 bool QgsTerrainEntity::rayIntersection( const QgsRayCastingUtils::Ray3D &ray, QVector3D &intersectionPoint )
104 {
105  if ( !rootNode() )
106  return false;
107 
108  if ( mMap.terrainGenerator()->type() != QgsTerrainGenerator::Dem )
109  return false; // currently only working with DEM terrain
110 
111  float minDist = -1;
112 
113  QList<QgsChunkNode *> lst = activeNodes();
114  for ( QgsChunkNode *n : lst )
115  {
116  if ( n->entity() && ( minDist < 0 || n->bbox().distanceFromPoint( ray.origin() ) < minDist ) && QgsRayCastingUtils::rayBoxIntersection( ray, n->bbox() ) )
117  {
118  Qt3DRender::QGeometryRenderer *rend = n->entity()->findChild<Qt3DRender::QGeometryRenderer *>();
119  Qt3DRender::QGeometry *geom = rend->geometry();
120  DemTerrainTileGeometry *demGeom = static_cast<DemTerrainTileGeometry *>( geom );
121  Qt3DCore::QTransform *tr = n->entity()->findChild<Qt3DCore::QTransform *>();
122  QVector3D nodeIntPoint;
123  if ( demGeom->rayIntersection( ray, tr->matrix(), nodeIntPoint ) )
124  {
125  float dist = ( ray.origin() - intersectionPoint ).length();
126  if ( minDist < 0 || dist < minDist )
127  {
128  minDist = dist;
129  intersectionPoint = nodeIntPoint;
130  }
131  }
132  }
133  }
134 
135  return minDist >= 0;
136 }
137 
138 void QgsTerrainEntity::onShowBoundingBoxesChanged()
139 {
140  setShowBoundingBoxes( mMap.showTerrainBoundingBoxes() );
141 }
142 
143 
144 void QgsTerrainEntity::invalidateMapImages()
145 {
146  QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral( "3D" ), QStringLiteral( "Invalidate textures" ) );
147 
148  // handle active nodes
149 
150  updateNodes( mActiveNodes, mUpdateJobFactory.get() );
151 
152  // handle inactive nodes afterwards
153 
154  QList<QgsChunkNode *> inactiveNodes;
155  Q_FOREACH ( QgsChunkNode *node, mRootNode->descendants() )
156  {
157  if ( !node->entity() )
158  continue;
159  if ( mActiveNodes.contains( node ) )
160  continue;
161  inactiveNodes << node;
162  }
163 
164  updateNodes( inactiveNodes, mUpdateJobFactory.get() );
165 
166  setNeedsUpdate( true );
167 }
168 
169 void QgsTerrainEntity::onLayersChanged()
170 {
171  connectToLayersRepaintRequest();
172  invalidateMapImages();
173 }
174 
175 void QgsTerrainEntity::connectToLayersRepaintRequest()
176 {
177  Q_FOREACH ( QgsMapLayer *layer, mLayers )
178  {
179  disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
180  }
181 
182  mLayers = mMap.terrainLayers();
183 
184  Q_FOREACH ( QgsMapLayer *layer, mLayers )
185  {
186  connect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
187  }
188 }
189 
190 void QgsTerrainEntity::onTerrainElevationOffsetChanged( float newOffset )
191 {
192  mTerrainTransform->setTranslation( QVector3D( 0.0f, newOffset, 0.0f ) );
193 }
194 
195 float QgsTerrainEntity::terrainElevationOffset() const
196 {
197  return mMap.terrainElevationOffset();
198 }
199 
200 
201 // -----------
202 
203 
204 TerrainMapUpdateJob::TerrainMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
205  : QgsChunkQueueJob( node )
206  , mTextureGenerator( textureGenerator )
207 {
208  QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( node->entity() );
209  connect( textureGenerator, &QgsTerrainTextureGenerator::tileReady, this, &TerrainMapUpdateJob::onTileReady );
210  mJobId = textureGenerator->render( entity->textureImage()->imageExtent(), node->tileId(), entity->textureImage()->imageDebugText() );
211 }
212 
213 void TerrainMapUpdateJob::cancel()
214 {
215  if ( mJobId != -1 )
216  mTextureGenerator->cancelJob( mJobId );
217 }
218 
219 
220 void TerrainMapUpdateJob::onTileReady( int jobId, const QImage &image )
221 {
222  if ( mJobId == jobId )
223  {
224  QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( mNode->entity() );
225  entity->textureImage()->setImage( image );
226  mJobId = -1;
227  emit finished();
228  }
229 }
230 
QgsTerrainGenerator * terrainGenerator() const
Returns terrain generator. It takes care of producing terrain tiles from the input data.
void backgroundColorChanged()
Emitted when the background color has changed.
void showTerrainBoundingBoxesChanged()
Emitted when the flag whether terrain's bounding boxes are shown has changed.
void terrainLayersChanged()
Emitted when the list of map layers for terrain texture has changed.
void terrainMapThemeChanged()
Emitted when terrain's map theme has changed.
float terrainElevationOffset() const
Returns the elevation offset of the terrain (used to move the terrain up or down)
void showLabelsChanged()
Emitted when the flag whether labels are displayed on terrain tiles has changed.
void terrainElevationOffsetChanged(float newElevation)
Emitted when the terrain elevation offset is changed.
void showTerrainTilesInfoChanged()
Emitted when the flag whether terrain's tile info is shown has changed.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used in the 3D scene.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
Class for doing transforms between two map coordinate systems.
Base class for all map layer types.
Definition: qgsmaplayer.h:85
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
@ Dem
Terrain is built from raster layer with digital elevation model.
QgsCoordinateReferenceSystem crs() const
Returns CRS of the terrain.
void setTerrain(QgsTerrainEntity *t)
Sets terrain entity for the generator (does not transfer ownership)
bool isValid() const
Returns whether the terrain generator is valid.