QGIS API Documentation 4.3.0-Master (52aa6503bed)
Loading...
Searching...
No Matches
qgsdemterraingenerator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsdemterraingenerator.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
17
18#include <functional>
19#include <limits>
20
21#include "qgs3drendercontext.h"
22#include "qgs3dutils.h"
26#include "qgsfutureutils.h"
27#include "qgsgeotransform.h"
28#include "qgsrasterlayer.h"
29#include "qgsterrainentity.h"
31#include "qgsthreadingutils.h"
32
33#include <QFuture>
34#include <QGeometryRenderer>
35#include <Qt3DCore/QEntity>
36
37#include "moc_qgsdemterraingenerator.cpp"
38
43
45
48
54
59
66
68{
70 cloned->setTerrain( mTerrain );
71 cloned->mCrs = mCrs;
72 cloned->mLayer = mLayer;
73 cloned->mResolution = mResolution;
74 cloned->mSkirtHeight = mSkirtHeight;
75 cloned->mExtent = mExtent;
76 cloned->updateGenerator();
77 return cloned;
78}
79
84
86{
87 return mTerrainTilingScheme.tileToExtent( 0, 0, 0 );
88}
89
90float QgsDemTerrainGenerator::heightAt( double x, double y, const Qgs3DRenderContext &context ) const
91{
92 Q_UNUSED( context )
94 return mHeightMapGenerator->heightAt( x, y );
95 else
96 return std::numeric_limits<float>::quiet_NaN();
97}
98
99static void heightMapMinMax( const QByteArray &heightMap, float &zMin, float &zMax )
100{
101 const float *zBits = ( const float * ) heightMap.constData();
102 int zCount = heightMap.count() / sizeof( float );
103 bool first = true;
104
105 zMin = zMax = std::numeric_limits<float>::quiet_NaN();
106 for ( int i = 0; i < zCount; ++i )
107 {
108 float z = zBits[i];
109 if ( std::isnan( z ) )
110 continue;
111 if ( first )
112 {
113 zMin = zMax = z;
114 first = false;
115 }
116 zMin = std::min( zMin, z );
117 zMax = std::max( zMax, z );
118 }
119}
120
121QFuture<QgsChunkLoaderResult> QgsDemTerrainGenerator::loadChunk( QgsChunkNode *node )
122{
123 QFuture<QByteArray> heightMapFuture = mHeightMapGenerator->render( node->tileId() );
124 QFuture<TerrainTextureResources> textureFuture = loadTextureResources( node );
125 return QgsFutureUtils::combine( heightMapFuture, textureFuture ).then( this, [this, node]( std::tuple<QByteArray, QgsTerrainGenerator::TerrainTextureResources> results ) {
126 QByteArray heightMap = std::get<0>( results );
127 TerrainTextureResources textureResources = std::get<1>( results );
128 return QgsChunkLoaderResult { std::bind_front( &QgsDemTerrainGenerator::createEntity, this, heightMap, textureResources, node ) };
129 } );
130}
131
132Qt3DCore::QEntity *QgsDemTerrainGenerator::createEntity( QByteArray heightMap, TerrainTextureResources textureResources, QgsChunkNode *node, Qt3DCore::QEntity *parent )
133{
135 float zMin, zMax;
136 heightMapMinMax( heightMap, zMin, zMax );
137
138 if ( std::isnan( zMin ) || std::isnan( zMax ) )
139 {
140 // no data available for this tile
141 return nullptr;
142 }
143
144 Qgs3DMapSettings *map = mTerrain->mapSettings();
145 Qgs3DRenderContext context = Qgs3DRenderContext::fromMapSettings( map );
146 QgsChunkNodeId nodeId = node->tileId();
147 QgsRectangle extent = map->terrainGenerator()->tilingScheme().tileToExtent( nodeId );
148 double side = extent.width();
149
150 QgsTerrainTileEntity *entity = new QgsTerrainTileEntity( nodeId );
151
152 // create geometry renderer
153
154 Qt3DRender::QGeometryRenderer *mesh = new Qt3DRender::QGeometryRenderer;
155 mesh->setGeometry( new DemTerrainTileGeometry( mResolution, side, map->terrainSettings()->verticalScale(), mSkirtHeight, heightMap, mesh ) );
156 entity->addComponent( mesh ); // takes ownership if the component has no parent
157
158 // create material
159
160 applyMaterial( textureResources, entity, map->isTerrainShadingEnabled(), map->terrainShadingMaterial(), !map->layers().empty(), context );
161
162 // create transform
163 QgsGeoTransform *transform = new QgsGeoTransform;
164 transform->setGeoTranslation( QgsVector3D( extent.xMinimum(), extent.yMinimum(), 0 ) );
165 entity->addComponent( transform );
166
167 // clang-format off
168 node->setExactBox3D(
169 QgsBox3D( extent.xMinimum(), extent.yMinimum(), zMin * map->terrainSettings()->verticalScale(),
170 extent.xMinimum() + side, extent.yMinimum() + side, zMax * map->terrainSettings()->verticalScale() )
171 );
172 // clang-format on
173 node->updateParentBoundingBoxesRecursively();
174
175 entity->setParent( parent );
176 return entity;
177}
178
180{
181 if ( mExtent == extent )
182 return;
183
184 mExtent = extent;
186}
187
189{
190 QgsRasterLayer *dem = layer();
191 if ( dem && mCrs.isValid() )
192 {
194 // no need to have an mExtent larger than the actual layer's extent
195 const QgsRectangle intersectExtent = mExtent.intersect( layerExtent );
196
197 mTerrainTilingScheme = QgsTilingScheme( intersectExtent, mCrs );
198 mHeightMapGenerator = std::make_unique<QgsDemHeightMapGenerator>( dem, mTerrainTilingScheme, mResolution, mTransformContext );
199
200 mIsValid = true;
201 }
202 else
203 {
205 mHeightMapGenerator.reset();
206 mIsValid = false;
207 }
208}
209
const QgsAbstractTerrainSettings * terrainSettings() const
Returns the terrain settings.
QgsTerrainGenerator * terrainGenerator() const
Returns the terrain generator.
bool isTerrainShadingEnabled() const
Returns whether terrain shading is enabled.
QgsPhongMaterialSettings terrainShadingMaterial() const
Returns terrain shading material.
QList< QgsMapLayer * > layers() const
Returns the list of 3D map layers to be rendered in the scene.
Rendering context for preparation of 3D entities.
static Qgs3DRenderContext fromMapSettings(const Qgs3DMapSettings *mapSettings)
Creates an initialized Qgs3DRenderContext instance from given Qgs3DMapSettings.
static QgsRectangle tryReprojectExtent2D(const QgsRectangle &extent, const QgsCoordinateReferenceSystem &crs1, const QgsCoordinateReferenceSystem &crs2, const QgsCoordinateTransformContext &context)
Reprojects extent from crs1 to crs2 coordinate reference system with context context.
double verticalScale() const
Returns the vertical scale (exaggeration) for terrain.
Represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
std::unique_ptr< QgsDemHeightMapGenerator > mHeightMapGenerator
QgsRectangle rootChunkExtent() const override
extent of the terrain's root chunk in terrain's CRS
static QgsTerrainGenerator * create()
Creates a new instance of a QgsDemTerrainGenerator object.
float heightAt(double x, double y, const Qgs3DRenderContext &context) const override
Returns height at (x,y) in map's CRS.
Type type() const override
What texture generator implementation is this.
QgsTerrainGenerator * clone() const override
Makes a copy of the current instance.
float mSkirtHeight
height of the "skirts" at the edges of tiles to hide cracks between adjacent cracks
QgsCoordinateTransformContext mTransformContext
QgsTerrainGenerator::Capabilities capabilities() const override
Returns flags containing the supported capabilities.
void setCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context) override
Sets the CRS associated with the terrain.
QgsRasterLayer * layer() const
Returns raster layer with elevation model to be used for terrain generation.
void setExtent(const QgsRectangle &extent) override
sets the extent of the terrain in terrain's CRS
QFuture< QgsChunkLoaderResult > loadChunk(QgsChunkNode *node) override
QPointer< QgsRasterLayer > mLayer
source layer for heights
virtual void updateGenerator()
(Re-)creates mHeightMapGenerator based on current params
QgsCoordinateReferenceSystem crs() const override
Returns CRS of the terrain.
QgsCoordinateReferenceSystem mCrs
int mResolution
how many vertices to place on one side of the tile
void setLayer(QgsRasterLayer *layer)
Sets raster layer with elevation model to be used for terrain generation.
static QFuture< std::tuple< Ts... > > combine(QFuture< Ts >... futures)
Converts multiple futures into a single future that will finish when all its parts have finished.
Represents a raster layer.
A rectangle specified with double values.
double xMinimum
double yMinimum
Type
Enumeration of the available terrain generators.
@ Dem
Terrain is built from raster layer with digital elevation model.
QgsTilingScheme mTerrainTilingScheme
Tiling scheme of the terrain.
QgsTerrainGenerator()
Create new terrain generator.
void applyMaterial(const TerrainTextureResources &resources, QgsTerrainTileEntity *entity, bool isShadingEnabled, const QgsPhongMaterialSettings &shadingMaterial, bool useTexture, const Qgs3DRenderContext &context)
Creates material component for the entity with the rendered map as a texture.
QgsTerrainEntity * mTerrain
QFuture< TerrainTextureResources > loadTextureResources(QgsChunkNode *node)
Asynchronously loads resources for creating texture.
QFlags< Capability > Capabilities
@ SupportsTileResolution
Supports tile resolution.
virtual void setTerrain(QgsTerrainEntity *t)
Sets terrain entity for the generator (does not transfer ownership).
const QgsTilingScheme & tilingScheme() const
Returns tiling scheme of the terrain.
Encapsulates tiling schemes (just like with WMTS / TMS / XYZ layers).
QgsRectangle tileToExtent(int x, int y, int z) const
Returns map coordinates of the extent of a tile.
#define QGIS_CHECK_MAIN_THREAD_ACCESS