QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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::layersChanged, 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  const QList<QgsChunkNode *> descendants = mRootNode->descendants();
156  for ( QgsChunkNode *node : descendants )
157  {
158  if ( !node->entity() )
159  continue;
160  if ( mActiveNodes.contains( node ) )
161  continue;
162  inactiveNodes << node;
163  }
164 
165  updateNodes( inactiveNodes, mUpdateJobFactory.get() );
166 
167  setNeedsUpdate( true );
168 }
169 
170 void QgsTerrainEntity::onLayersChanged()
171 {
172  connectToLayersRepaintRequest();
173  invalidateMapImages();
174 }
175 
176 void QgsTerrainEntity::connectToLayersRepaintRequest()
177 {
178  for ( QgsMapLayer *layer : std::as_const( mLayers ) )
179  {
180  disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
181  }
182 
183  mLayers = mMap.layers();
184 
185  for ( QgsMapLayer *layer : std::as_const( mLayers ) )
186  {
187  connect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
188  }
189 }
190 
191 void QgsTerrainEntity::onTerrainElevationOffsetChanged( float newOffset )
192 {
193  mTerrainTransform->setTranslation( QVector3D( 0.0f, newOffset, 0.0f ) );
194 }
195 
196 float QgsTerrainEntity::terrainElevationOffset() const
197 {
198  return mMap.terrainElevationOffset();
199 }
200 
201 
202 // -----------
203 
204 
205 TerrainMapUpdateJob::TerrainMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
206  : QgsChunkQueueJob( node )
207  , mTextureGenerator( textureGenerator )
208 {
209  QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( node->entity() );
210  connect( textureGenerator, &QgsTerrainTextureGenerator::tileReady, this, &TerrainMapUpdateJob::onTileReady );
211  mJobId = textureGenerator->render( entity->textureImage()->imageExtent(), node->tileId(), entity->textureImage()->imageDebugText() );
212 }
213 
214 void TerrainMapUpdateJob::cancel()
215 {
216  if ( mJobId != -1 )
217  mTextureGenerator->cancelJob( mJobId );
218 }
219 
220 
221 void TerrainMapUpdateJob::onTileReady( int jobId, const QImage &image )
222 {
223  if ( mJobId == jobId )
224  {
225  QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( mNode->entity() );
226  entity->textureImage()->setImage( image );
227  mJobId = -1;
228  emit finished();
229  }
230 }
231 
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:99
QgsTerrainGenerator::Dem
@ Dem
Terrain is built from raster layer with digital elevation model.
Definition: qgsterraingenerator.h:58
QgsTerrainGenerator::crs
QgsCoordinateReferenceSystem crs() const
Returns CRS of the terrain.
Definition: qgsterraingenerator.h:106
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:494
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:77
qgsdemterraintilegeometry_p.h
qgscoordinatetransform.h
Qgs3DMapSettings
Definition of the world.
Definition: qgs3dmapsettings.h:57
Qgs3DMapSettings::terrainElevationOffset
float terrainElevationOffset() const
Returns the elevation offset of the terrain (used to move the terrain up or down)
Definition: qgs3dmapsettings.h:252
Qgs3DMapSettings::terrainGenerator
QgsTerrainGenerator * terrainGenerator() const
Returns the terrain generator.
Definition: qgs3dmapsettings.h:277
qgsterrainentity_p.h
qgs3dmapsettings.h
Qgs3DMapSettings::showTerrainBoundingBoxesChanged
void showTerrainBoundingBoxesChanged()
Emitted when the flag whether terrain's bounding boxes are shown has changed.
Qgs3DMapSettings::terrainElevationOffsetChanged
void terrainElevationOffsetChanged(float newElevation)
Emitted when the terrain elevation offset is changed.
QgsMapLayer
Base class for all map layer types. This is the base class for all map layer types (vector,...
Definition: qgsmaplayer.h:72
Qgs3DMapSettings::terrainMapThemeChanged
void terrainMapThemeChanged()
Emitted when terrain's map theme has changed.
qgsterraintexturegenerator_p.h
QgsCoordinateTransform
Class for doing transforms between two map coordinate systems.
Definition: qgscoordinatetransform.h:57
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 3d rendering has changed.
QgsTerrainGenerator::setTerrain
void setTerrain(QgsTerrainEntity *t)
Sets terrain entity for the generator (does not transfer ownership)
Definition: qgsterraingenerator.h:64
Qgs3DMapSettings::showTerrainTilesInfoChanged
void showTerrainTilesInfoChanged()
Emitted when the flag whether terrain's tile info is shown has changed.