QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
qgsterrainentity.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsterrainentity.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.h"
17#include "moc_qgsterrainentity.cpp"
18
19#include "qgsaabb.h"
20#include "qgs3dmapsettings.h"
21#include "qgschunknode.h"
23#include "qgseventtracing.h"
25#include "qgsterraingenerator.h"
29#include "qgs3dutils.h"
30
32
33#include <Qt3DCore/QTransform>
34#include <Qt3DRender/QGeometryRenderer>
35
36
38
40class TerrainMapUpdateJobFactory : public QgsChunkQueueJobFactory
41{
42 public:
43 TerrainMapUpdateJobFactory( QgsTerrainTextureGenerator *textureGenerator )
44 : mTextureGenerator( textureGenerator )
45 {
46 }
47
48 QgsChunkQueueJob *createJob( QgsChunkNode *chunk ) override
49 {
50 return new TerrainMapUpdateJob( mTextureGenerator, chunk );
51 }
52
53 private:
54 QgsTerrainTextureGenerator *mTextureGenerator = nullptr;
55};
56
57
58// -----------
59
60
61QgsTerrainEntity::QgsTerrainEntity( Qgs3DMapSettings *map, Qt3DCore::QNode *parent )
62 : QgsChunkedEntity( map, map->maxTerrainScreenError(), map->terrainGenerator(), false, std::numeric_limits<int>::max(), parent )
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 mTextureGenerator = new QgsTerrainTextureGenerator( *map );
78
79 mUpdateJobFactory.reset( new TerrainMapUpdateJobFactory( mTextureGenerator ) );
80
81 mTerrainTransform = new Qt3DCore::QTransform;
82 mTerrainTransform->setScale( 1.0f );
83 mTerrainTransform->setTranslation( QVector3D( 0.0f, 0.0f, map->terrainElevationOffset() ) );
84 addComponent( mTerrainTransform );
85}
86
87QgsTerrainEntity::~QgsTerrainEntity()
88{
89 // cancel / wait for jobs
90 cancelActiveJobs();
91
92 delete mTextureGenerator;
93}
94
95QVector<QgsRayCastingUtils::RayHit> QgsTerrainEntity::rayIntersection( const QgsRayCastingUtils::Ray3D &ray, const QgsRayCastingUtils::RayCastContext &context ) const
96{
97 Q_UNUSED( context )
98 QVector<QgsRayCastingUtils::RayHit> result;
99
100 float minDist = -1;
101 QVector3D intersectionPoint;
102 switch ( mMapSettings->terrainGenerator()->type() )
103 {
105 {
106 if ( ray.direction().z() == 0 )
107 break; // the ray is parallel to the flat terrain
108
109 const float dist = static_cast<float>( mMapSettings->terrainElevationOffset() - ray.origin().z() ) / ray.direction().z();
110 const QVector3D terrainPlanePoint = ray.origin() + ray.direction() * dist;
111 const QgsVector3D mapCoords = Qgs3DUtils::worldToMapCoordinates( terrainPlanePoint, mMapSettings->origin() );
112 if ( mMapSettings->extent().contains( mapCoords.x(), mapCoords.y() ) )
113 {
114 minDist = dist;
115 intersectionPoint = terrainPlanePoint;
116 }
117 break;
118 }
120 {
121 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
122 for ( QgsChunkNode *node : activeNodes )
123 {
124
125 QgsAABB nodeBbox = Qgs3DUtils::mapToWorldExtent( node->box3D(), mMapSettings->origin() );
126
127 if ( node->entity() &&
128 ( minDist < 0 || nodeBbox.distanceFromPoint( ray.origin() ) < minDist ) &&
129 QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
130 {
131 Qt3DRender::QGeometryRenderer *rend = node->entity()->findChild<Qt3DRender::QGeometryRenderer *>();
132 auto *geom = rend->geometry();
133 Qt3DCore::QTransform *tr = node->entity()->findChild<Qt3DCore::QTransform *>();
134 QVector3D nodeIntPoint;
135 DemTerrainTileGeometry *demGeom = static_cast<DemTerrainTileGeometry *>( geom );
136 if ( demGeom->rayIntersection( ray, tr->matrix(), nodeIntPoint ) )
137 {
138 const float dist = ( ray.origin() - intersectionPoint ).length();
139 if ( minDist < 0 || dist < minDist )
140 {
141 minDist = dist;
142 intersectionPoint = nodeIntPoint;
143 }
144 }
145 }
146 }
147 break;
148 }
152 // not supported
153 break;
154 }
155 if ( !intersectionPoint.isNull() )
156 {
157 QgsRayCastingUtils::RayHit hit( minDist, intersectionPoint );
158 result.append( hit );
159 }
160 return result;
161}
162
163void QgsTerrainEntity::onShowBoundingBoxesChanged()
164{
165 setShowBoundingBoxes( mMapSettings->showTerrainBoundingBoxes() );
166}
167
168
169void QgsTerrainEntity::invalidateMapImages()
170{
171 QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral( "3D" ), QStringLiteral( "Invalidate textures" ) );
172
173 // handle active nodes
174
175 updateNodes( mActiveNodes, mUpdateJobFactory.get() );
176
177 // handle inactive nodes afterwards
178
179 QList<QgsChunkNode *> inactiveNodes;
180 const QList<QgsChunkNode *> descendants = mRootNode->descendants();
181 for ( QgsChunkNode *node : descendants )
182 {
183 if ( !node->entity() )
184 continue;
185 if ( mActiveNodes.contains( node ) )
186 continue;
187 inactiveNodes << node;
188 }
189
190 updateNodes( inactiveNodes, mUpdateJobFactory.get() );
191
192 setNeedsUpdate( true );
193}
194
195void QgsTerrainEntity::onLayersChanged()
196{
197 connectToLayersRepaintRequest();
198 invalidateMapImages();
199}
200
201void QgsTerrainEntity::connectToLayersRepaintRequest()
202{
203 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
204 {
205 disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
206 }
207
208 mLayers = mMapSettings->layers();
209
210 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
211 {
212 connect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
213 }
214}
215
216void QgsTerrainEntity::onTerrainElevationOffsetChanged( float newOffset )
217{
218 mTerrainTransform->setTranslation( QVector3D( 0.0f, 0.0f, newOffset ) );
219}
220
221float QgsTerrainEntity::terrainElevationOffset() const
222{
223 return mMapSettings->terrainElevationOffset();
224}
225
226
227// -----------
228
229
230TerrainMapUpdateJob::TerrainMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
231 : QgsChunkQueueJob( node )
232 , mTextureGenerator( textureGenerator )
233{
234 QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( node->entity() );
235 connect( textureGenerator, &QgsTerrainTextureGenerator::tileReady, this, &TerrainMapUpdateJob::onTileReady );
236 mJobId = textureGenerator->render( entity->textureImage()->imageExtent(), node->tileId(), entity->textureImage()->imageDebugText() );
237}
238
239void TerrainMapUpdateJob::cancel()
240{
241 if ( mJobId != -1 )
242 mTextureGenerator->cancelJob( mJobId );
243}
244
245
246void TerrainMapUpdateJob::onTileReady( int jobId, const QImage &image )
247{
248 if ( mJobId == jobId )
249 {
250 QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( mNode->entity() );
251 entity->textureImage()->setImage( image );
252 mJobId = -1;
253 emit finished();
254 }
255}
256
void backgroundColorChanged()
Emitted when the background color has changed.
void showTerrainBoundingBoxesChanged()
Emitted when the flag whether terrain's bounding boxes are shown has changed.
void terrainMapThemeChanged()
Emitted when terrain's map theme has changed.
float terrainElevationOffset() const
Returns the elevation offset of the terrain (used to move the terrain up or down)
QgsTerrainGenerator * terrainGenerator() const
Returns the terrain generator.
void showLabelsChanged()
Emitted when the flag whether labels are displayed on terrain tiles has changed.
void terrainElevationOffsetChanged(float newElevation)
Emitted when the terrain elevation offset is changed.
void layersChanged()
Emitted when the list of map layers for 3d rendering has changed.
void showTerrainTilesInfoChanged()
Emitted when the flag whether terrain's tile info is shown has changed.
static QgsAABB mapToWorldExtent(const QgsRectangle &extent, double zMin, double zMax, const QgsVector3D &mapOrigin)
Converts map extent to axis aligned bounding box in 3D world coordinates.
static QgsVector3D worldToMapCoordinates(const QgsVector3D &worldCoords, const QgsVector3D &origin)
Converts 3D world coordinates to map coordinates (applies offset)
float distanceFromPoint(float x, float y, float z) const
Returns shortest distance from the box to a point.
Definition qgsaabb.cpp:50
Base class for all map layer types.
Definition qgsmaplayer.h:76
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
@ QuantizedMesh
Terrain is built from quantized mesh tiles.
@ Dem
Terrain is built from raster layer with digital elevation model.
@ Online
Terrain is built from downloaded tiles with digital elevation model.
@ Mesh
Terrain is built from mesh layer with z value on vertices.
@ Flat
The whole terrain is flat area.
virtual void setTerrain(QgsTerrainEntity *t)
Sets terrain entity for the generator (does not transfer ownership)
bool isValid() const
Returns whether the terrain generator is valid.
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition qgsvector3d.h:31
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:50
double x() const
Returns X coordinate.
Definition qgsvector3d.h:48
Helper struct to store ray casting parameters.
Helper struct to store ray casting results.