QGIS API Documentation 4.3.0-Master (bf28115e945)
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 <memory>
19
20#include "qgs3dmapsettings.h"
21#include "qgs3dutils.h"
22#include "qgsaabb.h"
24#include "qgschunknode.h"
27#include "qgseventtracing.h"
29#include "qgsraycastingutils.h"
30#include "qgsterraingenerator.h"
34
35#include <QString>
36#include <Qt3DCore/QTransform>
37#include <Qt3DRender/QGeometryRenderer>
38
39#include "moc_qgsterrainentity.cpp"
40
41using namespace Qt::StringLiterals;
42
44
46class TerrainMapUpdateJobFactory : public QgsChunkQueueJobFactory
47{
48 public:
49 TerrainMapUpdateJobFactory( QgsTerrainTextureGenerator *textureGenerator )
50 : mTextureGenerator( textureGenerator )
51 {}
52
53 QgsChunkQueueJob *createJob( QgsChunkNode *chunk ) override { return new TerrainMapUpdateJob( mTextureGenerator, chunk ); }
54
55 private:
56 QgsTerrainTextureGenerator *mTextureGenerator = nullptr;
57};
58
59
60// -----------
61
62
63QgsTerrainEntity::QgsTerrainEntity( Qgs3DMapSettings *map, Qt3DCore::QNode *parent )
64 : QgsChunkedEntity( map, map->terrainSettings()->maximumScreenError(), map->terrainGenerator(), false, std::numeric_limits<int>::max(), parent )
65{
66 map->terrainGenerator()->setTerrain( this );
67 mIsValid = map->terrainGenerator()->isValid();
68
69 mLayerWatcher = make_qobject_unique<QgsLayerStyleWatcher>( map );
70 connect( mLayerWatcher.get(), &QgsLayerStyleWatcher::styleChanged, this, &QgsTerrainEntity::invalidateMapImages );
71
72 connect( map, &Qgs3DMapSettings::showTerrainBoundingBoxesChanged, this, &QgsTerrainEntity::onShowBoundingBoxesChanged );
73 connect( map, &Qgs3DMapSettings::showTerrainTilesInfoChanged, this, &QgsTerrainEntity::invalidateMapImages );
74 connect( map, &Qgs3DMapSettings::showLabelsChanged, this, &QgsTerrainEntity::invalidateMapImages );
75 connect( map, &Qgs3DMapSettings::backgroundColorChanged, this, &QgsTerrainEntity::invalidateMapImages );
76 connect( map, &Qgs3DMapSettings::terrainMapThemeChanged, this, &QgsTerrainEntity::invalidateMapImages );
77 connect( map, &Qgs3DMapSettings::terrainSettingsChanged, this, &QgsTerrainEntity::onTerrainElevationOffsetChanged );
78
79 mTextureGenerator = std::make_unique<QgsTerrainTextureGenerator>( *map );
80
81 mUpdateJobFactory = std::make_unique<TerrainMapUpdateJobFactory>( mTextureGenerator.get() );
82
83 mTerrainTransform = new Qt3DCore::QTransform;
84 mTerrainTransform->setScale( 1.0f );
85 mTerrainTransform->setTranslation( QVector3D( 0.0f, 0.0f, map->terrainSettings()->elevationOffset() ) );
86 addComponent( mTerrainTransform );
87}
88
89QgsTerrainEntity::~QgsTerrainEntity()
90{
91 // cancel / wait for jobs
92 cancelActiveJobs();
93}
94
95QList<QgsRayCastHit> QgsTerrainEntity::rayIntersection( const QgsRay3D &ray, const QgsRayCastContext &context ) const
96{
97 Q_UNUSED( context )
98 QList<QgsRayCastHit> result;
99
100 float minDist = -1;
101 QgsVector3D intersectionPointMapCoords;
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->terrainSettings()->elevationOffset() - ray.origin().z() - mMapSettings->origin().z() ) / ray.direction().z();
110 const QVector3D terrainPlanePoint = ray.point( dist );
111 const QgsVector3D mapCoords = Qgs3DUtils::worldToMapCoordinates( terrainPlanePoint, mMapSettings->origin() );
112 if ( mMapSettings->extent().contains( mapCoords.x(), mapCoords.y() ) )
113 {
114 minDist = dist;
115 intersectionPointMapCoords = mapCoords;
116 }
117 break;
118 }
120 {
121 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
122 QVector3D nearestIntersectionPoint;
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, context, tr->matrix(), nodeIntPoint ) )
135 {
136 const float dist = ( ray.origin() - nodeIntPoint ).length();
137 if ( minDist < 0 || dist < minDist )
138 {
139 minDist = dist;
140 nearestIntersectionPoint = nodeIntPoint;
141 }
142 }
143 }
144 }
145 if ( minDist >= 0 )
146 intersectionPointMapCoords = Qgs3DUtils::worldToMapCoordinates( nearestIntersectionPoint, mMapSettings->origin() );
147 break;
148 }
152 // not supported
153 break;
154 }
155 if ( minDist >= 0 )
156 {
157 QgsRayCastHit hit;
158 hit.setDistance( minDist );
159 hit.setMapCoordinates( intersectionPointMapCoords );
160 result.append( hit );
161 }
162 return result;
163}
164
165void QgsTerrainEntity::onShowBoundingBoxesChanged()
166{
167 setShowBoundingBoxes( mMapSettings->debugFlags().testFlag( Qgis::Map3DDebugFlag::ShowTerrainBoundingBoxes ) );
168}
169
170void QgsTerrainEntity::invalidateMapImages()
171{
172 QgsEventTracing::addEvent( QgsEventTracing::Instant, u"3D"_s, u"Invalidate textures"_s );
173
174 // handle active nodes
175
176 updateNodes( mActiveNodes, mUpdateJobFactory.get() );
177
178 // handle inactive nodes afterwards
179
180 QList<QgsChunkNode *> inactiveNodes;
181 const QList<QgsChunkNode *> descendants = mRootNode->descendants();
182 for ( QgsChunkNode *node : descendants )
183 {
184 if ( !node->entity() )
185 continue;
186 if ( mActiveNodes.contains( node ) )
187 continue;
188 inactiveNodes << node;
189 }
190
191 updateNodes( inactiveNodes, mUpdateJobFactory.get() );
192
193 setNeedsUpdate( true );
194}
195
196void QgsTerrainEntity::onTerrainElevationOffsetChanged()
197{
198 float newOffset = qobject_cast<Qgs3DMapSettings *>( sender() )->terrainSettings()->elevationOffset();
199 mTerrainTransform->setTranslation( QVector3D( 0.0f, 0.0f, newOffset ) );
200}
201
202float QgsTerrainEntity::terrainElevationOffset() const
203{
204 return mMapSettings->terrainSettings()->elevationOffset();
205}
206
207
208// -----------
209
210
211TerrainMapUpdateJob::TerrainMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
212 : QgsChunkQueueJob( node )
213 , mTextureGenerator( textureGenerator )
214{}
215
216void TerrainMapUpdateJob::start()
217{
218 QgsChunkNode *node = chunk();
219
220 QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( node->entity() );
221 connect( mTextureGenerator, &QgsTerrainTextureGenerator::tileReady, this, &TerrainMapUpdateJob::onTileReady );
222 mJobId = mTextureGenerator->render( entity->textureImage()->imageExtent(), node->tileId(), entity->textureImage()->imageDebugText() );
223}
224
225void TerrainMapUpdateJob::cancel()
226{
227 if ( mJobId != -1 )
228 mTextureGenerator->cancelJob( mJobId );
229}
230
231
232void TerrainMapUpdateJob::onTileReady( int jobId, const QImage &image )
233{
234 if ( mJobId == jobId )
235 {
236 QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( mNode->entity() );
237 entity->textureImage()->setImage( image );
238 mJobId = -1;
239 emit finished();
240 }
241}
242
@ ShowTerrainBoundingBoxes
Displays bounding boxes of terrain tiles.
Definition qgis.h:4334
Definition of the world.
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 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).
Axis-aligned bounding box - in world coords.
Definition qgsaabb.h:33
float distanceFromPoint(float x, float y, float z) const
Returns shortest distance from the box to a point.
Definition qgsaabb.cpp:50
double elevationOffset() const
Returns the elevation offset of the terrain (used to move the terrain up or down).
A representation of a ray in 3D.
Definition qgsray3d.h:31
QVector3D origin() const
Returns the origin of the ray.
Definition qgsray3d.h:43
QVector3D direction() const
Returns the direction of the ray see setDirection().
Definition qgsray3d.h:49
QVector3D point(float distance) const
Returns the point along the ray with the specified distance from the ray's origin.
Definition qgsray3d.cpp:62
Responsible for defining parameters of the ray casting operations in 3D map canvases.
Contains details about the ray intersecting entities when ray casting in a 3D map canvas.
void setMapCoordinates(const QgsVector3D &point)
Sets the hit point position in 3d map coordinates.
void setDistance(double distance)
Sets the hit's distance from the ray's origin.
@ 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.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:33
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:60
double x() const
Returns X coordinate.
Definition qgsvector3d.h:58
bool rayBoxIntersection(const QgsRay3D &ray, const QgsAABB &nodeBbox)
Tests whether an axis aligned box is intersected by a ray.
constexpr QObjectUniquePtr< Tp > make_qobject_unique(Args &&...args)
Create an object owned by a QObjectUniquePtr.