QGIS API Documentation 3.41.0-Master (af5edcb665c)
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"
31
33
34#include <Qt3DCore/QTransform>
35#include <Qt3DRender/QGeometryRenderer>
36
37
39
41class TerrainMapUpdateJobFactory : public QgsChunkQueueJobFactory
42{
43 public:
44 TerrainMapUpdateJobFactory( QgsTerrainTextureGenerator *textureGenerator )
45 : mTextureGenerator( textureGenerator )
46 {
47 }
48
49 QgsChunkQueueJob *createJob( QgsChunkNode *chunk ) override
50 {
51 return new TerrainMapUpdateJob( mTextureGenerator, chunk );
52 }
53
54 private:
55 QgsTerrainTextureGenerator *mTextureGenerator = nullptr;
56};
57
58
59// -----------
60
61
62QgsTerrainEntity::QgsTerrainEntity( Qgs3DMapSettings *map, Qt3DCore::QNode *parent )
63 : QgsChunkedEntity( map, map->terrainSettings()->maximumScreenError(), map->terrainGenerator(), false, std::numeric_limits<int>::max(), parent )
64{
65 map->terrainGenerator()->setTerrain( this );
66 mIsValid = map->terrainGenerator()->isValid();
67
68 connect( map, &Qgs3DMapSettings::showTerrainBoundingBoxesChanged, this, &QgsTerrainEntity::onShowBoundingBoxesChanged );
69 connect( map, &Qgs3DMapSettings::showTerrainTilesInfoChanged, this, &QgsTerrainEntity::invalidateMapImages );
70 connect( map, &Qgs3DMapSettings::showLabelsChanged, this, &QgsTerrainEntity::invalidateMapImages );
71 connect( map, &Qgs3DMapSettings::layersChanged, this, &QgsTerrainEntity::onLayersChanged );
72 connect( map, &Qgs3DMapSettings::backgroundColorChanged, this, &QgsTerrainEntity::invalidateMapImages );
73 connect( map, &Qgs3DMapSettings::terrainMapThemeChanged, this, &QgsTerrainEntity::invalidateMapImages );
74 connect( map, &Qgs3DMapSettings::terrainSettingsChanged, this, &QgsTerrainEntity::onTerrainElevationOffsetChanged );
75
76 connectToLayersRepaintRequest();
77
78 mTextureGenerator = new QgsTerrainTextureGenerator( *map );
79
80 mUpdateJobFactory.reset( new TerrainMapUpdateJobFactory( mTextureGenerator ) );
81
82 mTerrainTransform = new Qt3DCore::QTransform;
83 mTerrainTransform->setScale( 1.0f );
84 mTerrainTransform->setTranslation( QVector3D( 0.0f, 0.0f, map->terrainSettings()->elevationOffset() ) );
85 addComponent( mTerrainTransform );
86}
87
88QgsTerrainEntity::~QgsTerrainEntity()
89{
90 // cancel / wait for jobs
91 cancelActiveJobs();
92
93 delete mTextureGenerator;
94}
95
96QVector<QgsRayCastingUtils::RayHit> QgsTerrainEntity::rayIntersection( const QgsRayCastingUtils::Ray3D &ray, const QgsRayCastingUtils::RayCastContext &context ) const
97{
98 Q_UNUSED( context )
99 QVector<QgsRayCastingUtils::RayHit> result;
100
101 float minDist = -1;
102 QVector3D intersectionPoint;
103 switch ( mMapSettings->terrainGenerator()->type() )
104 {
106 {
107 if ( ray.direction().z() == 0 )
108 break; // the ray is parallel to the flat terrain
109
110 const float dist = static_cast<float>( mMapSettings->terrainSettings()->elevationOffset() - ray.origin().z() ) / ray.direction().z();
111 const QVector3D terrainPlanePoint = ray.origin() + ray.direction() * dist;
112 const QgsVector3D mapCoords = Qgs3DUtils::worldToMapCoordinates( terrainPlanePoint, mMapSettings->origin() );
113 if ( mMapSettings->extent().contains( mapCoords.x(), mapCoords.y() ) )
114 {
115 minDist = dist;
116 intersectionPoint = terrainPlanePoint;
117 }
118 break;
119 }
121 {
122 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
123 for ( QgsChunkNode *node : activeNodes )
124 {
125 QgsAABB nodeBbox = Qgs3DUtils::mapToWorldExtent( node->box3D(), mMapSettings->origin() );
126
127 if ( node->entity() && ( minDist < 0 || nodeBbox.distanceFromPoint( ray.origin() ) < minDist ) && QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
128 {
129 Qt3DRender::QGeometryRenderer *rend = node->entity()->findChild<Qt3DRender::QGeometryRenderer *>();
130 auto *geom = rend->geometry();
131 Qt3DCore::QTransform *tr = node->entity()->findChild<Qt3DCore::QTransform *>();
132 QVector3D nodeIntPoint;
133 DemTerrainTileGeometry *demGeom = static_cast<DemTerrainTileGeometry *>( geom );
134 if ( demGeom->rayIntersection( ray, tr->matrix(), nodeIntPoint ) )
135 {
136 const float dist = ( ray.origin() - intersectionPoint ).length();
137 if ( minDist < 0 || dist < minDist )
138 {
139 minDist = dist;
140 intersectionPoint = nodeIntPoint;
141 }
142 }
143 }
144 }
145 break;
146 }
150 // not supported
151 break;
152 }
153 if ( !intersectionPoint.isNull() )
154 {
155 QgsRayCastingUtils::RayHit hit( minDist, intersectionPoint );
156 result.append( hit );
157 }
158 return result;
159}
160
161void QgsTerrainEntity::onShowBoundingBoxesChanged()
162{
163 setShowBoundingBoxes( mMapSettings->showTerrainBoundingBoxes() );
164}
165
166
167void QgsTerrainEntity::invalidateMapImages()
168{
169 QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral( "3D" ), QStringLiteral( "Invalidate textures" ) );
170
171 // handle active nodes
172
173 updateNodes( mActiveNodes, mUpdateJobFactory.get() );
174
175 // handle inactive nodes afterwards
176
177 QList<QgsChunkNode *> inactiveNodes;
178 const QList<QgsChunkNode *> descendants = mRootNode->descendants();
179 for ( QgsChunkNode *node : descendants )
180 {
181 if ( !node->entity() )
182 continue;
183 if ( mActiveNodes.contains( node ) )
184 continue;
185 inactiveNodes << node;
186 }
187
188 updateNodes( inactiveNodes, mUpdateJobFactory.get() );
189
190 setNeedsUpdate( true );
191}
192
193void QgsTerrainEntity::onLayersChanged()
194{
195 connectToLayersRepaintRequest();
196 invalidateMapImages();
197}
198
199void QgsTerrainEntity::connectToLayersRepaintRequest()
200{
201 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
202 {
203 disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
204 }
205
206 mLayers = mMapSettings->layers();
207
208 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
209 {
210 connect( layer, &QgsMapLayer::repaintRequested, this, &QgsTerrainEntity::invalidateMapImages );
211 }
212}
213
214void QgsTerrainEntity::onTerrainElevationOffsetChanged()
215{
216 float newOffset = qobject_cast<Qgs3DMapSettings *>( sender() )->terrainSettings()->elevationOffset();
217 mTerrainTransform->setTranslation( QVector3D( 0.0f, 0.0f, newOffset ) );
218}
219
220float QgsTerrainEntity::terrainElevationOffset() const
221{
222 return mMapSettings->terrainSettings()->elevationOffset();
223}
224
225
226// -----------
227
228
229TerrainMapUpdateJob::TerrainMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
230 : QgsChunkQueueJob( node )
231 , mTextureGenerator( textureGenerator )
232{
233 QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( node->entity() );
234 connect( textureGenerator, &QgsTerrainTextureGenerator::tileReady, this, &TerrainMapUpdateJob::onTileReady );
235 mJobId = textureGenerator->render( entity->textureImage()->imageExtent(), node->tileId(), entity->textureImage()->imageDebugText() );
236}
237
238void TerrainMapUpdateJob::cancel()
239{
240 if ( mJobId != -1 )
241 mTextureGenerator->cancelJob( mJobId );
242}
243
244
245void TerrainMapUpdateJob::onTileReady( int jobId, const QImage &image )
246{
247 if ( mJobId == jobId )
248 {
249 QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( mNode->entity() );
250 entity->textureImage()->setImage( image );
251 mJobId = -1;
252 emit finished();
253 }
254}
255
void backgroundColorChanged()
Emitted when the background color has changed.
void showTerrainBoundingBoxesChanged()
Emitted when the flag whether terrain's bounding boxes are shown has changed.
const QgsAbstractTerrainSettings * terrainSettings() const
Returns the terrain settings.
void terrainMapThemeChanged()
Emitted when terrain's map theme has changed.
QgsTerrainGenerator * terrainGenerator() const
Returns the terrain generator.
void terrainSettingsChanged()
Emitted when the terrain settings are changed.
void showLabelsChanged()
Emitted when the flag whether labels are displayed on terrain tiles has 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:46
double elevationOffset() const
Returns the elevation offset of the terrain (used to move the terrain up or down).
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.