QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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 
68  connect( &map, &Qgs3DMapSettings::showTerrainBoundingBoxesChanged, this, &QgsTerrainEntity::onShowBoundingBoxesChanged );
69  connect( &map, &Qgs3DMapSettings::showTerrainTilesInfoChanged, this, &QgsTerrainEntity::invalidateMapImages );
70  connect( &map, &Qgs3DMapSettings::showLabelsChanged, this, &QgsTerrainEntity::invalidateMapImages );
71  connect( &map, &Qgs3DMapSettings::layersChanged, this, &QgsTerrainEntity::onLayersChanged );
72  connect( &map, &Qgs3DMapSettings::backgroundColorChanged, this, &QgsTerrainEntity::invalidateMapImages );
73  connect( &map, &Qgs3DMapSettings::terrainMapThemeChanged, this, &QgsTerrainEntity::invalidateMapImages );
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 
89 QgsTerrainEntity::~QgsTerrainEntity()
90 {
91  // cancel / wait for jobs
92  cancelActiveJobs();
93 
94  delete mTextureGenerator;
95  delete mTerrainToMapTransform;
96 }
97 
98 bool QgsTerrainEntity::rayIntersection( const QgsRayCastingUtils::Ray3D &ray, QVector3D &intersectionPoint )
99 {
100  if ( !rootNode() )
101  return false;
102 
103  if ( mMap.terrainGenerator()->type() != QgsTerrainGenerator::Dem )
104  return false; // currently only working with DEM terrain
105 
106  float minDist = -1;
107 
108  QList<QgsChunkNode *> lst = activeNodes();
109  for ( QgsChunkNode *n : lst )
110  {
111  if ( n->entity() && ( minDist < 0 || n->bbox().distanceFromPoint( ray.origin() ) < minDist ) && QgsRayCastingUtils::rayBoxIntersection( ray, n->bbox() ) )
112  {
113  Qt3DRender::QGeometryRenderer *rend = n->entity()->findChild<Qt3DRender::QGeometryRenderer *>();
114  Qt3DRender::QGeometry *geom = rend->geometry();
115  DemTerrainTileGeometry *demGeom = static_cast<DemTerrainTileGeometry *>( geom );
116  Qt3DCore::QTransform *tr = n->entity()->findChild<Qt3DCore::QTransform *>();
117  QVector3D nodeIntPoint;
118  if ( demGeom->rayIntersection( ray, tr->matrix(), nodeIntPoint ) )
119  {
120  float dist = ( ray.origin() - intersectionPoint ).length();
121  if ( minDist < 0 || dist < minDist )
122  {
123  minDist = dist;
124  intersectionPoint = nodeIntPoint;
125  }
126  }
127  }
128  }
129 
130  return minDist >= 0;
131 }
132 
133 void QgsTerrainEntity::onShowBoundingBoxesChanged()
134 {
135  setShowBoundingBoxes( mMap.showTerrainBoundingBoxes() );
136 }
137 
138 
139 void QgsTerrainEntity::invalidateMapImages()
140 {
141  QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral( "3D" ), QStringLiteral( "Invalidate textures" ) );
142 
143  // handle active nodes
144 
145  updateNodes( mActiveNodes, mUpdateJobFactory.get() );
146 
147  // handle inactive nodes afterwards
148 
149  QList<QgsChunkNode *> inactiveNodes;
150  Q_FOREACH ( QgsChunkNode *node, mRootNode->descendants() )
151  {
152  if ( !node->entity() )
153  continue;
154  if ( mActiveNodes.contains( node ) )
155  continue;
156  inactiveNodes << node;
157  }
158 
159  updateNodes( inactiveNodes, mUpdateJobFactory.get() );
160 
161  setNeedsUpdate( true );
162 }
163 
164 void QgsTerrainEntity::onLayersChanged()
165 {
166  connectToLayersRepaintRequest();
167  invalidateMapImages();
168 }
169 
170 void QgsTerrainEntity::connectToLayersRepaintRequest()
171 {
172  Q_FOREACH ( QgsMapLayer *layer, mLayers )
173  {
174  disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
175  }
176 
177  mLayers = mMap.layers();
178 
179  Q_FOREACH ( QgsMapLayer *layer, mLayers )
180  {
181  connect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
182  }
183 }
184 
185 
186 // -----------
187 
188 
189 TerrainMapUpdateJob::TerrainMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
190  : QgsChunkQueueJob( node )
191  , mTextureGenerator( textureGenerator )
192 {
193  QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( node->entity() );
194  connect( textureGenerator, &QgsTerrainTextureGenerator::tileReady, this, &TerrainMapUpdateJob::onTileReady );
195  mJobId = textureGenerator->render( entity->textureImage()->imageExtent(), node->tileId(), entity->textureImage()->imageDebugText() );
196 }
197 
198 void TerrainMapUpdateJob::cancel()
199 {
200  if ( mJobId != -1 )
201  mTextureGenerator->cancelJob( mJobId );
202 }
203 
204 
205 void TerrainMapUpdateJob::onTileReady( int jobId, const QImage &image )
206 {
207  if ( mJobId == jobId )
208  {
209  QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( mNode->entity() );
210  entity->textureImage()->setImage( image );
211  mJobId = -1;
212  emit finished();
213  }
214 }
215 
Base class for all map layer types.
Definition: qgsmaplayer.h:79
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used in the 3D scene.
void layersChanged()
Emitted when the list of map layers for terrain texture has changed.
QgsCoordinateReferenceSystem crs() const
Returns CRS of the terrain.
3 Definition of the world
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
Terrain is built from raster layer with digital elevation model.
void terrainMapThemeChanged()
Emitted when terrain&#39;s map theme has changed.
void showTerrainBoundingBoxesChanged()
Emitted when the flag whether terrain&#39;s bounding boxes are shown has changed.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
void showTerrainTilesInfoChanged()
Emitted when the flag whether terrain&#39;s tile info is shown has changed.
void showLabelsChanged()
Emitted when the flag whether labels are displayed on terrain tiles has changed.
Class for doing transforms between two map coordinate systems.
void setTerrain(QgsTerrainEntity *t)
Sets terrain entity for the generator (does not transfer ownership)
void backgroundColorChanged()
Emitted when the background color has changed.
QgsTerrainGenerator * terrainGenerator() const
Returns terrain generator. It takes care of producing terrain tiles from the input data...