QGIS API Documentation 3.40.0-Bratislava (b56115d8743)
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
18#include "qgsaabb.h"
19#include "qgs3dmapsettings.h"
20#include "qgschunknode.h"
22#include "qgseventtracing.h"
24#include "qgsterraingenerator.h"
28#include "qgs3dutils.h"
29
31
32#include <Qt3DCore/QTransform>
33#include <Qt3DRender/QGeometryRenderer>
34
35
37
39class 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
60QgsTerrainEntity::QgsTerrainEntity( Qgs3DMapSettings *map, Qt3DCore::QNode *parent )
61 : QgsChunkedEntity( map, map->maxTerrainScreenError(), map->terrainGenerator(), false, std::numeric_limits<int>::max(), parent )
62{
63 map->terrainGenerator()->setTerrain( this );
64 mIsValid = map->terrainGenerator()->isValid();
65
66 connect( map, &Qgs3DMapSettings::showTerrainBoundingBoxesChanged, this, &QgsTerrainEntity::onShowBoundingBoxesChanged );
67 connect( map, &Qgs3DMapSettings::showTerrainTilesInfoChanged, this, &QgsTerrainEntity::invalidateMapImages );
68 connect( map, &Qgs3DMapSettings::showLabelsChanged, this, &QgsTerrainEntity::invalidateMapImages );
69 connect( map, &Qgs3DMapSettings::layersChanged, this, &QgsTerrainEntity::onLayersChanged );
70 connect( map, &Qgs3DMapSettings::backgroundColorChanged, this, &QgsTerrainEntity::invalidateMapImages );
71 connect( map, &Qgs3DMapSettings::terrainMapThemeChanged, this, &QgsTerrainEntity::invalidateMapImages );
72 connect( map, &Qgs3DMapSettings::terrainElevationOffsetChanged, this, &QgsTerrainEntity::onTerrainElevationOffsetChanged );
73
74 connectToLayersRepaintRequest();
75
76 mTextureGenerator = new QgsTerrainTextureGenerator( *map );
77
78 mUpdateJobFactory.reset( new TerrainMapUpdateJobFactory( mTextureGenerator ) );
79
80 mTerrainTransform = new Qt3DCore::QTransform;
81 mTerrainTransform->setScale( 1.0f );
82 mTerrainTransform->setTranslation( QVector3D( 0.0f, map->terrainElevationOffset(), 0.0f ) );
83 addComponent( mTerrainTransform );
84}
85
86QgsTerrainEntity::~QgsTerrainEntity()
87{
88 // cancel / wait for jobs
89 cancelActiveJobs();
90
91 delete mTextureGenerator;
92}
93
94QVector<QgsRayCastingUtils::RayHit> QgsTerrainEntity::rayIntersection( const QgsRayCastingUtils::Ray3D &ray, const QgsRayCastingUtils::RayCastContext &context ) const
95{
96 Q_UNUSED( context )
97 QVector<QgsRayCastingUtils::RayHit> result;
98
99 float minDist = -1;
100 QVector3D intersectionPoint;
101 switch ( mMapSettings->terrainGenerator()->type() )
102 {
104 {
105 if ( ray.direction().y() == 0 )
106 break; // the ray is parallel to the flat terrain
107
108 const float dist = static_cast<float>( mMapSettings->terrainElevationOffset() - ray.origin().y() ) / ray.direction().y();
109 const QVector3D terrainPlanePoint = ray.origin() + ray.direction() * dist;
110 const QgsVector3D mapCoords = Qgs3DUtils::worldToMapCoordinates( terrainPlanePoint, mMapSettings->origin() );
111 if ( mMapSettings->extent().contains( mapCoords.x(), mapCoords.y() ) )
112 {
113 minDist = dist;
114 intersectionPoint = terrainPlanePoint;
115 }
116 break;
117 }
119 {
120 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
121 for ( QgsChunkNode *node : activeNodes )
122 {
123 if ( node->entity() &&
124 ( minDist < 0 || node->bbox().distanceFromPoint( ray.origin() ) < minDist ) &&
125 QgsRayCastingUtils::rayBoxIntersection( ray, node->bbox() ) )
126 {
127 Qt3DRender::QGeometryRenderer *rend = node->entity()->findChild<Qt3DRender::QGeometryRenderer *>();
128 auto *geom = rend->geometry();
129 Qt3DCore::QTransform *tr = node->entity()->findChild<Qt3DCore::QTransform *>();
130 QVector3D nodeIntPoint;
131 DemTerrainTileGeometry *demGeom = static_cast<DemTerrainTileGeometry *>( geom );
132 if ( demGeom->rayIntersection( ray, tr->matrix(), nodeIntPoint ) )
133 {
134 const float dist = ( ray.origin() - intersectionPoint ).length();
135 if ( minDist < 0 || dist < minDist )
136 {
137 minDist = dist;
138 intersectionPoint = nodeIntPoint;
139 }
140 }
141 }
142 }
143 break;
144 }
148 // not supported
149 break;
150 }
151 if ( !intersectionPoint.isNull() )
152 {
153 QgsRayCastingUtils::RayHit hit( minDist, intersectionPoint );
154 result.append( hit );
155 }
156 return result;
157}
158
159void QgsTerrainEntity::onShowBoundingBoxesChanged()
160{
161 setShowBoundingBoxes( mMapSettings->showTerrainBoundingBoxes() );
162}
163
164
165void QgsTerrainEntity::invalidateMapImages()
166{
167 QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral( "3D" ), QStringLiteral( "Invalidate textures" ) );
168
169 // handle active nodes
170
171 updateNodes( mActiveNodes, mUpdateJobFactory.get() );
172
173 // handle inactive nodes afterwards
174
175 QList<QgsChunkNode *> inactiveNodes;
176 const QList<QgsChunkNode *> descendants = mRootNode->descendants();
177 for ( QgsChunkNode *node : descendants )
178 {
179 if ( !node->entity() )
180 continue;
181 if ( mActiveNodes.contains( node ) )
182 continue;
183 inactiveNodes << node;
184 }
185
186 updateNodes( inactiveNodes, mUpdateJobFactory.get() );
187
188 setNeedsUpdate( true );
189}
190
191void QgsTerrainEntity::onLayersChanged()
192{
193 connectToLayersRepaintRequest();
194 invalidateMapImages();
195}
196
197void QgsTerrainEntity::connectToLayersRepaintRequest()
198{
199 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
200 {
201 disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
202 }
203
204 mLayers = mMapSettings->layers();
205
206 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
207 {
208 connect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
209 }
210}
211
212void QgsTerrainEntity::onTerrainElevationOffsetChanged( float newOffset )
213{
214 mTerrainTransform->setTranslation( QVector3D( 0.0f, newOffset, 0.0f ) );
215}
216
217float QgsTerrainEntity::terrainElevationOffset() const
218{
219 return mMapSettings->terrainElevationOffset();
220}
221
222
223// -----------
224
225
226TerrainMapUpdateJob::TerrainMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
227 : QgsChunkQueueJob( node )
228 , mTextureGenerator( textureGenerator )
229{
230 QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( node->entity() );
231 connect( textureGenerator, &QgsTerrainTextureGenerator::tileReady, this, &TerrainMapUpdateJob::onTileReady );
232 mJobId = textureGenerator->render( entity->textureImage()->imageExtent(), node->tileId(), entity->textureImage()->imageDebugText() );
233}
234
235void TerrainMapUpdateJob::cancel()
236{
237 if ( mJobId != -1 )
238 mTextureGenerator->cancelJob( mJobId );
239}
240
241
242void TerrainMapUpdateJob::onTileReady( int jobId, const QImage &image )
243{
244 if ( mJobId == jobId )
245 {
246 QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( mNode->entity() );
247 entity->textureImage()->setImage( image );
248 mJobId = -1;
249 emit finished();
250 }
251}
252
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 QgsVector3D worldToMapCoordinates(const QgsVector3D &worldCoords, const QgsVector3D &origin)
Converts 3D world coordinates to map coordinates (applies offset and turns (x,y,z) into (x,...
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.