32#include <Qt3DCore/QTransform>
33#include <Qt3DRender/QGeometryRenderer>
39class TerrainMapUpdateJobFactory :
public QgsChunkQueueJobFactory
42 TerrainMapUpdateJobFactory( QgsTerrainTextureGenerator *textureGenerator )
43 : mTextureGenerator( textureGenerator )
47 QgsChunkQueueJob *createJob( QgsChunkNode *chunk )
override
49 return new TerrainMapUpdateJob( mTextureGenerator, chunk );
53 QgsTerrainTextureGenerator *mTextureGenerator =
nullptr;
60QgsTerrainEntity::QgsTerrainEntity(
const Qgs3DMapSettings &map, Qt3DCore::QNode *parent )
61 : QgsChunkedEntity( map.maxTerrainScreenError(), map.terrainGenerator(), false, std::numeric_limits<int>::max(), parent )
75 connectToLayersRepaintRequest();
77 mTextureGenerator =
new QgsTerrainTextureGenerator( map );
79 mUpdateJobFactory.reset(
new TerrainMapUpdateJobFactory( mTextureGenerator ) );
81 mTerrainTransform =
new Qt3DCore::QTransform;
82 mTerrainTransform->setScale( 1.0f );
84 addComponent( mTerrainTransform );
87QgsTerrainEntity::~QgsTerrainEntity()
92 delete mTextureGenerator;
95QVector<QgsRayCastingUtils::RayHit> QgsTerrainEntity::rayIntersection(
const QgsRayCastingUtils::Ray3D &ray,
const QgsRayCastingUtils::RayCastContext &context )
const
98 QVector<QgsRayCastingUtils::RayHit> result;
101 QVector3D intersectionPoint;
102 switch ( mMap.terrainGenerator()->type() )
106 if ( ray.direction().y() == 0 )
109 const float dist =
static_cast<float>( mMap.terrainElevationOffset() - ray.origin().y() ) / ray.direction().y();
110 const QVector3D terrainPlanePoint = ray.origin() + ray.direction() * dist;
112 if ( mMap.extent().contains( mapCoords.
x(), mapCoords.
y() ) )
115 intersectionPoint = terrainPlanePoint;
121 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
122 for ( QgsChunkNode *node : activeNodes )
124 if ( node->entity() &&
125 ( minDist < 0 || node->bbox().distanceFromPoint( ray.origin() ) < minDist ) &&
126 QgsRayCastingUtils::rayBoxIntersection( ray, node->bbox() ) )
128 Qt3DRender::QGeometryRenderer *rend = node->entity()->findChild<Qt3DRender::QGeometryRenderer *>();
129 auto *geom = rend->geometry();
130 Qt3DCore::QTransform *tr = node->entity()->findChild<Qt3DCore::QTransform *>();
131 QVector3D nodeIntPoint;
132 DemTerrainTileGeometry *demGeom =
static_cast<DemTerrainTileGeometry *
>( geom );
133 if ( demGeom->rayIntersection( ray, tr->matrix(), nodeIntPoint ) )
135 const float dist = ( ray.origin() - intersectionPoint ).length();
136 if ( minDist < 0 || dist < minDist )
139 intersectionPoint = nodeIntPoint;
151 if ( !intersectionPoint.isNull() )
153 QgsRayCastingUtils::RayHit hit( minDist, intersectionPoint );
154 result.append( hit );
159void QgsTerrainEntity::onShowBoundingBoxesChanged()
161 setShowBoundingBoxes( mMap.showTerrainBoundingBoxes() );
165void QgsTerrainEntity::invalidateMapImages()
167 QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral(
"3D" ), QStringLiteral(
"Invalidate textures" ) );
171 updateNodes( mActiveNodes, mUpdateJobFactory.get() );
175 QList<QgsChunkNode *> inactiveNodes;
176 const QList<QgsChunkNode *> descendants = mRootNode->descendants();
177 for ( QgsChunkNode *node : descendants )
179 if ( !node->entity() )
181 if ( mActiveNodes.contains( node ) )
183 inactiveNodes << node;
186 updateNodes( inactiveNodes, mUpdateJobFactory.get() );
188 setNeedsUpdate(
true );
191void QgsTerrainEntity::onLayersChanged()
193 connectToLayersRepaintRequest();
194 invalidateMapImages();
197void QgsTerrainEntity::connectToLayersRepaintRequest()
199 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
204 mLayers = mMap.layers();
206 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
212void QgsTerrainEntity::onTerrainElevationOffsetChanged(
float newOffset )
214 mTerrainTransform->setTranslation( QVector3D( 0.0f, newOffset, 0.0f ) );
217float QgsTerrainEntity::terrainElevationOffset()
const
219 return mMap.terrainElevationOffset();
226TerrainMapUpdateJob::TerrainMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
227 : QgsChunkQueueJob( node )
228 , mTextureGenerator( textureGenerator )
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() );
235void TerrainMapUpdateJob::cancel()
238 mTextureGenerator->cancelJob( mJobId );
242void TerrainMapUpdateJob::onTileReady(
int jobId,
const QImage &image )
244 if ( mJobId == jobId )
246 QgsTerrainTileEntity *entity = qobject_cast<QgsTerrainTileEntity *>( mNode->entity() );
247 entity->textureImage()->setImage( image );
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.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
@ 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.
void setTerrain(QgsTerrainEntity *t)
Sets terrain entity for the generator (does not transfer ownership)
bool isValid() const
Returns whether the terrain generator is valid.
double y() const
Returns Y coordinate.
double x() const
Returns X coordinate.