QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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 "qgsraycastingutils_p.h"
23 #include "qgsterraingenerator.h"
26 #include "qgsterraintileentity_p.h"
27 
28 #include "qgscoordinatetransform.h"
29 
30 #include <Qt3DCore/QTransform>
31 #include <Qt3DRender/QGeometryRenderer>
32 #include <Qt3DRender/QObjectPicker>
33 
34 
36 
38 class TerrainMapUpdateJobFactory : public QgsChunkQueueJobFactory
39 {
40  public:
41  TerrainMapUpdateJobFactory( QgsTerrainTextureGenerator *textureGenerator )
42  : mTextureGenerator( textureGenerator )
43  {
44  }
45 
46  QgsChunkQueueJob *createJob( QgsChunkNode *chunk ) override
47  {
48  return new TerrainMapUpdateJob( mTextureGenerator, chunk );
49  }
50 
51  private:
52  QgsTerrainTextureGenerator *mTextureGenerator = nullptr;
53 };
54 
55 
56 // -----------
57 
58 
59 QgsTerrainEntity::QgsTerrainEntity( int maxLevel, const Qgs3DMapSettings &map, Qt3DCore::QNode *parent )
60  : QgsChunkedEntity( map.terrainGenerator()->rootChunkBbox( map ),
61  map.terrainGenerator()->rootChunkError( map ),
62  map.maxTerrainScreenError(), maxLevel, map.terrainGenerator(), parent )
63  , mMap( map )
64 {
65  map.terrainGenerator()->setTerrain( this );
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::layersChanged, this, &QgsTerrainEntity::onLayersChanged );
71  connect( &map, &Qgs3DMapSettings::backgroundColorChanged, this, &QgsTerrainEntity::invalidateMapImages );
72  connect( &map, &Qgs3DMapSettings::terrainMapThemeChanged, this, &QgsTerrainEntity::invalidateMapImages );
73 
74  connectToLayersRepaintRequest();
75 
76  mTerrainToMapTransform = new QgsCoordinateTransform( map.terrainGenerator()->crs(), map.crs(), map.transformContext() );
77 
78  mTextureGenerator = new QgsTerrainTextureGenerator( map );
79 
80  mUpdateJobFactory.reset( new TerrainMapUpdateJobFactory( mTextureGenerator ) );
81 
82  mTerrainPicker = new Qt3DRender::QObjectPicker;
83  // add camera control's terrain picker as a component to be able to capture height where mouse was
84  // pressed in order to correctly pan camera when dragging mouse
85  addComponent( mTerrainPicker );
86 }
87 
88 QgsTerrainEntity::~QgsTerrainEntity()
89 {
90  // cancel / wait for jobs
91  if ( mActiveJob )
92  cancelActiveJob();
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  // handle active nodes
142 
143  updateNodes( mActiveNodes, mUpdateJobFactory.get() );
144 
145  // handle inactive nodes afterwards
146 
147  QList<QgsChunkNode *> inactiveNodes;
148  Q_FOREACH ( QgsChunkNode *node, mRootNode->descendants() )
149  {
150  if ( !node->entity() )
151  continue;
152  if ( mActiveNodes.contains( node ) )
153  continue;
154  inactiveNodes << node;
155  }
156 
157  updateNodes( inactiveNodes, mUpdateJobFactory.get() );
158 
159  setNeedsUpdate( true );
160 }
161 
162 void QgsTerrainEntity::onLayersChanged()
163 {
164  connectToLayersRepaintRequest();
165  invalidateMapImages();
166 }
167 
168 void QgsTerrainEntity::connectToLayersRepaintRequest()
169 {
170  Q_FOREACH ( QgsMapLayer *layer, mLayers )
171  {
172  disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
173  }
174 
175  mLayers = mMap.layers();
176 
177  Q_FOREACH ( QgsMapLayer *layer, mLayers )
178  {
179  connect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
180  }
181 }
182 
183 
184 // -----------
185 
186 
187 TerrainMapUpdateJob::TerrainMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
188  : QgsChunkQueueJob( node )
189  , mTextureGenerator( textureGenerator )
190 {
191  QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( node->entity() );
192  connect( textureGenerator, &QgsTerrainTextureGenerator::tileReady, this, &TerrainMapUpdateJob::onTileReady );
193  mJobId = textureGenerator->render( entity->textureImage()->imageExtent(), entity->textureImage()->imageDebugText() );
194 }
195 
196 void TerrainMapUpdateJob::cancel()
197 {
198  if ( mJobId != -1 )
199  mTextureGenerator->cancelJob( mJobId );
200 }
201 
202 
203 void TerrainMapUpdateJob::onTileReady( int jobId, const QImage &image )
204 {
205  if ( mJobId == jobId )
206  {
207  QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( mNode->entity() );
208  entity->textureImage()->setImage( image );
209  mJobId = -1;
210  emit finished();
211  }
212 }
213 
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...