QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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( int maxLevel, const Qgs3DMapSettings &map, Qt3DCore::QNode *parent )
61  : QgsChunkedEntity( map.terrainGenerator()->rootChunkBbox( map ),
62  map.terrainGenerator()->rootChunkError( map ),
63  map.maxTerrainScreenError(), maxLevel, map.terrainGenerator(), parent )
64  , mMap( map )
65 {
66  map.terrainGenerator()->setTerrain( this );
67  mIsValid = map.terrainGenerator()->isValid();
68 
69  connect( &map, &Qgs3DMapSettings::showTerrainBoundingBoxesChanged, this, &QgsTerrainEntity::onShowBoundingBoxesChanged );
70  connect( &map, &Qgs3DMapSettings::showTerrainTilesInfoChanged, this, &QgsTerrainEntity::invalidateMapImages );
71  connect( &map, &Qgs3DMapSettings::showLabelsChanged, this, &QgsTerrainEntity::invalidateMapImages );
72  connect( &map, &Qgs3DMapSettings::layersChanged, this, &QgsTerrainEntity::onLayersChanged );
73  connect( &map, &Qgs3DMapSettings::backgroundColorChanged, this, &QgsTerrainEntity::invalidateMapImages );
74  connect( &map, &Qgs3DMapSettings::terrainMapThemeChanged, this, &QgsTerrainEntity::invalidateMapImages );
75 
76  connectToLayersRepaintRequest();
77 
78  mTerrainToMapTransform = new QgsCoordinateTransform( map.terrainGenerator()->crs(), map.crs(), map.transformContext() );
79 
80  mTextureGenerator = new QgsTerrainTextureGenerator( map );
81 
82  mUpdateJobFactory.reset( new TerrainMapUpdateJobFactory( mTextureGenerator ) );
83 
84  mTerrainPicker = new Qt3DRender::QObjectPicker;
85  // add camera control's terrain picker as a component to be able to capture height where mouse was
86  // pressed in order to correctly pan camera when dragging mouse
87  addComponent( mTerrainPicker );
88 }
89 
90 QgsTerrainEntity::~QgsTerrainEntity()
91 {
92  // cancel / wait for jobs
93  cancelActiveJobs();
94 
95  delete mTextureGenerator;
96  delete mTerrainToMapTransform;
97 }
98 
99 bool QgsTerrainEntity::rayIntersection( const QgsRayCastingUtils::Ray3D &ray, QVector3D &intersectionPoint )
100 {
101  if ( !rootNode() )
102  return false;
103 
104  if ( mMap.terrainGenerator()->type() != QgsTerrainGenerator::Dem )
105  return false; // currently only working with DEM terrain
106 
107  float minDist = -1;
108 
109  QList<QgsChunkNode *> lst = activeNodes();
110  for ( QgsChunkNode *n : lst )
111  {
112  if ( n->entity() && ( minDist < 0 || n->bbox().distanceFromPoint( ray.origin() ) < minDist ) && QgsRayCastingUtils::rayBoxIntersection( ray, n->bbox() ) )
113  {
114  Qt3DRender::QGeometryRenderer *rend = n->entity()->findChild<Qt3DRender::QGeometryRenderer *>();
115  Qt3DRender::QGeometry *geom = rend->geometry();
116  DemTerrainTileGeometry *demGeom = static_cast<DemTerrainTileGeometry *>( geom );
117  Qt3DCore::QTransform *tr = n->entity()->findChild<Qt3DCore::QTransform *>();
118  QVector3D nodeIntPoint;
119  if ( demGeom->rayIntersection( ray, tr->matrix(), nodeIntPoint ) )
120  {
121  float dist = ( ray.origin() - intersectionPoint ).length();
122  if ( minDist < 0 || dist < minDist )
123  {
124  minDist = dist;
125  intersectionPoint = nodeIntPoint;
126  }
127  }
128  }
129  }
130 
131  return minDist >= 0;
132 }
133 
134 void QgsTerrainEntity::onShowBoundingBoxesChanged()
135 {
136  setShowBoundingBoxes( mMap.showTerrainBoundingBoxes() );
137 }
138 
139 
140 void QgsTerrainEntity::invalidateMapImages()
141 {
142  QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral( "3D" ), QStringLiteral( "Invalidate textures" ) );
143 
144  // handle active nodes
145 
146  updateNodes( mActiveNodes, mUpdateJobFactory.get() );
147 
148  // handle inactive nodes afterwards
149 
150  QList<QgsChunkNode *> inactiveNodes;
151  Q_FOREACH ( QgsChunkNode *node, mRootNode->descendants() )
152  {
153  if ( !node->entity() )
154  continue;
155  if ( mActiveNodes.contains( node ) )
156  continue;
157  inactiveNodes << node;
158  }
159 
160  updateNodes( inactiveNodes, mUpdateJobFactory.get() );
161 
162  setNeedsUpdate( true );
163 }
164 
165 void QgsTerrainEntity::onLayersChanged()
166 {
167  connectToLayersRepaintRequest();
168  invalidateMapImages();
169 }
170 
171 void QgsTerrainEntity::connectToLayersRepaintRequest()
172 {
173  Q_FOREACH ( QgsMapLayer *layer, mLayers )
174  {
175  disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
176  }
177 
178  mLayers = mMap.layers();
179 
180  Q_FOREACH ( QgsMapLayer *layer, mLayers )
181  {
182  connect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
183  }
184 }
185 
186 
187 // -----------
188 
189 
190 TerrainMapUpdateJob::TerrainMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
191  : QgsChunkQueueJob( node )
192  , mTextureGenerator( textureGenerator )
193 {
194  QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( node->entity() );
195  connect( textureGenerator, &QgsTerrainTextureGenerator::tileReady, this, &TerrainMapUpdateJob::onTileReady );
196  mJobId = textureGenerator->render( entity->textureImage()->imageExtent(), node->tileId(), entity->textureImage()->imageDebugText() );
197 }
198 
199 void TerrainMapUpdateJob::cancel()
200 {
201  if ( mJobId != -1 )
202  mTextureGenerator->cancelJob( mJobId );
203 }
204 
205 
206 void TerrainMapUpdateJob::onTileReady( int jobId, const QImage &image )
207 {
208  if ( mJobId == jobId )
209  {
210  QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( mNode->entity() );
211  entity->textureImage()->setImage( image );
212  mJobId = -1;
213  emit finished();
214  }
215 }
216 
Qgs3DMapSettings::backgroundColorChanged
void backgroundColorChanged()
Emitted when the background color has changed.
Qgs3DMapSettings::crs
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used in the 3D scene.
Definition: qgs3dmapsettings.h:94
QgsTerrainGenerator::Dem
@ Dem
Terrain is built from raster layer with digital elevation model.
Definition: qgsterraingenerator.h:55
QgsTerrainGenerator::crs
QgsCoordinateReferenceSystem crs() const
Returns CRS of the terrain.
Definition: qgsterraingenerator.h:100
qgsaabb.h
qgschunknode_p.h
qgsterraintileentity_p.h
Qgs3DMapSettings::transformContext
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
Definition: qgs3dmapsettings.cpp:329
qgseventtracing.h
QgsMapLayer::repaintRequested
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
qgsterraingenerator.h
qgsterraintextureimage_p.h
QgsTerrainGenerator::isValid
bool isValid() const
Returns whether the terrain generator is valid.
Definition: qgsterraingenerator.cpp:75
qgsdemterraintilegeometry_p.h
qgscoordinatetransform.h
Qgs3DMapSettings
Definition: qgs3dmapsettings.h:51
Qgs3DMapSettings::terrainGenerator
QgsTerrainGenerator * terrainGenerator() const
Returns terrain generator. It takes care of producing terrain tiles from the input data.
Definition: qgs3dmapsettings.h:228
qgsterrainentity_p.h
qgs3dmapsettings.h
Qgs3DMapSettings::showTerrainBoundingBoxesChanged
void showTerrainBoundingBoxesChanged()
Emitted when the flag whether terrain's bounding boxes are shown has changed.
QgsMapLayer
Definition: qgsmaplayer.h:81
Qgs3DMapSettings::terrainMapThemeChanged
void terrainMapThemeChanged()
Emitted when terrain's map theme has changed.
qgsterraintexturegenerator_p.h
QgsCoordinateTransform
Definition: qgscoordinatetransform.h:52
qgsraycastingutils_p.h
Qgs3DMapSettings::showLabelsChanged
void showLabelsChanged()
Emitted when the flag whether labels are displayed on terrain tiles has changed.
Qgs3DMapSettings::layersChanged
void layersChanged()
Emitted when the list of map layers for terrain texture has changed.
QgsTerrainGenerator::setTerrain
void setTerrain(QgsTerrainEntity *t)
Sets terrain entity for the generator (does not transfer ownership)
Definition: qgsterraingenerator.h:61
Qgs3DMapSettings::showTerrainTilesInfoChanged
void showTerrainTilesInfoChanged()
Emitted when the flag whether terrain's tile info is shown has changed.