QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 
73  connectToLayersRepaintRequest();
74 
75  mTerrainToMapTransform = new QgsCoordinateTransform( map.terrainGenerator()->crs(), map.crs(), map.transformContext() );
76 
77  mTextureGenerator = new QgsTerrainTextureGenerator( map );
78 
79  mUpdateJobFactory.reset( new TerrainMapUpdateJobFactory( mTextureGenerator ) );
80 
81  mTerrainPicker = new Qt3DRender::QObjectPicker;
82  // add camera control's terrain picker as a component to be able to capture height where mouse was
83  // pressed in order to correcly pan camera when draggin mouse
84  addComponent( mTerrainPicker );
85 }
86 
87 QgsTerrainEntity::~QgsTerrainEntity()
88 {
89  // cancel / wait for jobs
90  if ( mActiveJob )
91  cancelActiveJob();
92 
93  delete mTextureGenerator;
94  delete mTerrainToMapTransform;
95 }
96 
97 bool QgsTerrainEntity::rayIntersection( const QgsRayCastingUtils::Ray3D &ray, QVector3D &intersectionPoint )
98 {
99  if ( !rootNode() )
100  return false;
101 
102  if ( mMap.terrainGenerator()->type() != QgsTerrainGenerator::Dem )
103  return false; // currently only working with DEM terrain
104 
105  float minDist = -1;
106 
107  QList<QgsChunkNode *> lst = activeNodes();
108  for ( QgsChunkNode *n : lst )
109  {
110  if ( n->entity() && ( minDist < 0 || n->bbox().distanceFromPoint( ray.origin() ) < minDist ) && QgsRayCastingUtils::rayBoxIntersection( ray, n->bbox() ) )
111  {
112  Qt3DRender::QGeometryRenderer *rend = n->entity()->findChild<Qt3DRender::QGeometryRenderer *>();
113  Qt3DRender::QGeometry *geom = rend->geometry();
114  DemTerrainTileGeometry *demGeom = static_cast<DemTerrainTileGeometry *>( geom );
115  Qt3DCore::QTransform *tr = n->entity()->findChild<Qt3DCore::QTransform *>();
116  QVector3D nodeIntPoint;
117  if ( demGeom->rayIntersection( ray, tr->matrix(), nodeIntPoint ) )
118  {
119  float dist = ( ray.origin() - intersectionPoint ).length();
120  if ( minDist < 0 || dist < minDist )
121  {
122  minDist = dist;
123  intersectionPoint = nodeIntPoint;
124  }
125  }
126  }
127  }
128 
129  return minDist >= 0;
130 }
131 
132 void QgsTerrainEntity::onShowBoundingBoxesChanged()
133 {
134  setShowBoundingBoxes( mMap.showTerrainBoundingBoxes() );
135 }
136 
137 
138 void QgsTerrainEntity::invalidateMapImages()
139 {
140  // handle active nodes
141 
142  updateNodes( mActiveNodes, mUpdateJobFactory.get() );
143 
144  // handle inactive nodes afterwards
145 
146  QList<QgsChunkNode *> inactiveNodes;
147  Q_FOREACH ( QgsChunkNode *node, mRootNode->descendants() )
148  {
149  if ( !node->entity() )
150  continue;
151  if ( mActiveNodes.contains( node ) )
152  continue;
153  inactiveNodes << node;
154  }
155 
156  updateNodes( inactiveNodes, mUpdateJobFactory.get() );
157 
158  setNeedsUpdate( true );
159 }
160 
161 void QgsTerrainEntity::onLayersChanged()
162 {
163  connectToLayersRepaintRequest();
164  invalidateMapImages();
165 }
166 
167 void QgsTerrainEntity::connectToLayersRepaintRequest()
168 {
169  Q_FOREACH ( QgsMapLayer *layer, mLayers )
170  {
171  disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
172  }
173 
174  mLayers = mMap.layers();
175 
176  Q_FOREACH ( QgsMapLayer *layer, mLayers )
177  {
178  connect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
179  }
180 }
181 
182 
183 // -----------
184 
185 
186 TerrainMapUpdateJob::TerrainMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
187  : QgsChunkQueueJob( node )
188  , mTextureGenerator( textureGenerator )
189 {
190  QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( node->entity() );
191  connect( textureGenerator, &QgsTerrainTextureGenerator::tileReady, this, &TerrainMapUpdateJob::onTileReady );
192  mJobId = textureGenerator->render( entity->textureImage()->imageExtent(), entity->textureImage()->imageDebugText() );
193 }
194 
195 void TerrainMapUpdateJob::cancel()
196 {
197  if ( mJobId != -1 )
198  mTextureGenerator->cancelJob( mJobId );
199 }
200 
201 
202 void TerrainMapUpdateJob::onTileReady( int jobId, const QImage &image )
203 {
204  if ( mJobId == jobId )
205  {
206  QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( mNode->entity() );
207  entity->textureImage()->setImage( image );
208  mJobId = -1;
209  emit finished();
210  }
211 }
212 
Base class for all map layer types.
Definition: qgsmaplayer.h:63
void layersChanged()
Emitted when the list of map layers for terrain texture has changed.
3 Definition of the world
Terrain is built from raster layer with digital elevation model.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
QgsCoordinateReferenceSystem crs() const
Returns CRS of the terrain.
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.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used in the 3D scene.
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...