Skip to content
Permalink
Browse files

Decide number of zoom levels from max. allowed ground error

  • Loading branch information
wonder-sk committed Jul 29, 2017
1 parent 6df6681 commit 905222d2c5c47fca9411e8c9cc8bf255a56cf22b
@@ -17,10 +17,11 @@ Map3D::Map3D()
, originY( 0 )
, originZ( 0 )
, backgroundColor( Qt::black )
, tileTextureSize( 512 )
, maxTerrainError( 3.f )
, skybox( false )
, mTerrainVerticalScale( 1 )
, mMapTileResolution( 512 )
, mMaxTerrainScreenError( 3.f )
, mMaxTerrainGroundError( 1.f )
, mShowTerrainBoundingBoxes( false )
, mShowTerrainTileInfo( false )
{
@@ -33,13 +34,14 @@ Map3D::Map3D( const Map3D &other )
, originZ( other.originZ )
, crs( other.crs )
, backgroundColor( other.backgroundColor )
, tileTextureSize( other.tileTextureSize )
, maxTerrainError( other.maxTerrainError )
, skybox( other.skybox )
, skyboxFileBase( other.skyboxFileBase )
, skyboxFileExtension( other.skyboxFileExtension )
, mTerrainVerticalScale( other.mTerrainVerticalScale )
, mTerrainGenerator( other.mTerrainGenerator ? other.mTerrainGenerator->clone() : nullptr )
, mMapTileResolution( other.mMapTileResolution )
, mMaxTerrainScreenError( other.mMaxTerrainScreenError )
, mMaxTerrainGroundError( other.mMaxTerrainGroundError )
, mShowTerrainBoundingBoxes( other.mShowTerrainBoundingBoxes )
, mShowTerrainTileInfo( other.mShowTerrainTileInfo )
, mLayers( other.mLayers )
@@ -67,8 +69,9 @@ void Map3D::readXml( const QDomElement &elem, const QgsReadWriteContext &context

QDomElement elemTerrain = elem.firstChildElement( "terrain" );
mTerrainVerticalScale = elemTerrain.attribute( "exaggeration", "1" ).toFloat();
tileTextureSize = elemTerrain.attribute( "texture-size", "512" ).toInt();
maxTerrainError = elemTerrain.attribute( "max-terrain-error", "3" ).toFloat();
mMapTileResolution = elemTerrain.attribute( "texture-size", "512" ).toInt();
mMaxTerrainScreenError = elemTerrain.attribute( "max-terrain-error", "3" ).toFloat();
mMaxTerrainGroundError = elemTerrain.attribute( "max-ground-error", "1" ).toFloat();
QDomElement elemMapLayers = elemTerrain.firstChildElement( "layers" );
QDomElement elemMapLayer = elemMapLayers.firstChildElement( "layer" );
QList<QgsMapLayerRef> mapLayers;
@@ -147,8 +150,9 @@ QDomElement Map3D::writeXml( QDomDocument &doc, const QgsReadWriteContext &conte

QDomElement elemTerrain = doc.createElement( "terrain" );
elemTerrain.setAttribute( "exaggeration", QString::number( mTerrainVerticalScale ) );
elemTerrain.setAttribute( "texture-size", tileTextureSize );
elemTerrain.setAttribute( "max-terrain-error", QString::number( maxTerrainError ) );
elemTerrain.setAttribute( "texture-size", mMapTileResolution );
elemTerrain.setAttribute( "max-terrain-error", QString::number( mMaxTerrainScreenError ) );
elemTerrain.setAttribute( "max-ground-error", QString::number( mMaxTerrainGroundError ) );
QDomElement elemMapLayers = doc.createElement( "layers" );
Q_FOREACH ( const QgsMapLayerRef &layerRef, mLayers )
{
@@ -247,6 +251,48 @@ QList<QgsMapLayer *> Map3D::layers() const
return lst;
}

void Map3D::setMapTileResolution( int res )
{
if ( mMapTileResolution == res )
return;

mMapTileResolution = res;
emit mapTileResolutionChanged();
}

int Map3D::mapTileResolution() const
{
return mMapTileResolution;
}

void Map3D::setMaxTerrainScreenError( float error )
{
if ( mMaxTerrainScreenError == error )
return;

mMaxTerrainScreenError = error;
emit maxTerrainScreenErrorChanged();
}

float Map3D::maxTerrainScreenError() const
{
return mMaxTerrainScreenError;
}

void Map3D::setMaxTerrainGroundError( float error )
{
if ( mMaxTerrainGroundError == error )
return;

mMaxTerrainGroundError = error;
emit maxTerrainGroundErrorChanged();
}

float Map3D::maxTerrainGroundError() const
{
return mMaxTerrainGroundError;
}

void Map3D::setTerrainGenerator( TerrainGenerator *gen )
{
mTerrainGenerator.reset( gen );
@@ -51,8 +51,14 @@ class _3D_EXPORT Map3D : public QObject
void setLayers( const QList<QgsMapLayer *> &layers );
QList<QgsMapLayer *> layers() const;

int tileTextureSize; //!< Size of map textures of tiles in pixels (width/height)
int maxTerrainError; //!< Maximum allowed terrain error in pixels
void setMapTileResolution( int res );
int mapTileResolution() const;

void setMaxTerrainScreenError( float error );
float maxTerrainScreenError() const;

void setMaxTerrainGroundError( float error );
float maxTerrainGroundError() const;

//! Takes ownership of the generator
void setTerrainGenerator( TerrainGenerator *gen );
@@ -77,12 +83,18 @@ class _3D_EXPORT Map3D : public QObject
void layersChanged();
void terrainGeneratorChanged();
void terrainVerticalScaleChanged();
void mapTileResolutionChanged();
void maxTerrainScreenErrorChanged();
void maxTerrainGroundErrorChanged();
void showTerrainBoundingBoxesChanged();
void showTerrainTilesInfoChanged();

private:
double mTerrainVerticalScale; //!< Multiplier of terrain heights to make the terrain shape more pronounced
std::unique_ptr<TerrainGenerator> mTerrainGenerator; //!< Implementation of the terrain generation
int mMapTileResolution; //!< Size of map textures of tiles in pixels (width/height)
float mMaxTerrainScreenError; //!< Maximum allowed terrain error in pixels (determines when tiles are switched to more detailed ones)
float mMaxTerrainGroundError; //!< Maximum allowed horizontal map error in map units (determines how many zoom levels will be used)
bool mShowTerrainBoundingBoxes; //!< Whether to show bounding boxes of entities - useful for debugging
bool mShowTerrainTileInfo; //!< Whether to draw extra information about terrain tiles to the textures - useful for debugging
QList<QgsMapLayerRef> mLayers; //!< Layers to be rendered
@@ -7,6 +7,8 @@
#include <Qt3DExtras/QSkyboxEntity>
#include <Qt3DLogic/QFrameAction>

#include <QTimer>

#include "aabb.h"
#include "qgsabstract3drenderer.h"
#include "cameracontroller.h"
@@ -15,6 +17,7 @@
#include "terraingenerator.h"
//#include "testchunkloader.h"
#include "chunkedentity.h"
#include "utils.h"

#include <Qt3DRender/QMesh>
#include <Qt3DRender/QSceneLoader>
@@ -57,6 +60,9 @@ Scene::Scene( const Map3D &map, Qt3DExtras::QForwardRenderer *defaultFrameGraph,
createTerrain();
connect( &map, &Map3D::terrainGeneratorChanged, this, &Scene::createTerrain );
connect( &map, &Map3D::terrainVerticalScaleChanged, this, &Scene::createTerrain );
connect( &map, &Map3D::mapTileResolutionChanged, this, &Scene::createTerrain );
connect( &map, &Map3D::maxTerrainScreenErrorChanged, this, &Scene::createTerrain );
connect( &map, &Map3D::maxTerrainGroundErrorChanged, this, &Scene::createTerrain );

// create entities of renderers

@@ -181,7 +187,20 @@ void Scene::createTerrain()
mTerrain = nullptr;
}

mTerrain = new Terrain( 3, mMap );
if ( !mTerrainUpdateScheduled )
{
// defer re-creation of terrain: there may be multiple invokations of this slot, so create the new entity just once
QTimer::singleShot( 0, this, &Scene::createTerrainDeferred );
mTerrainUpdateScheduled = true;
}
}

void Scene::createTerrainDeferred()
{
double tile0width = mMap.terrainGenerator()->extent().width();
int maxZoomLevel = Utils::maxZoomLevel( tile0width, mMap.mapTileResolution(), mMap.maxTerrainGroundError() );

mTerrain = new Terrain( maxZoomLevel, mMap );
//mTerrain->setEnabled(false);
mTerrain->setParent( this );

@@ -203,6 +222,8 @@ void Scene::createTerrain()
// add new entity - if any 3D renderer
addLayerEntity( layer );
}

mTerrainUpdateScheduled = false;
}

void Scene::onLayerRenderer3DChanged()
@@ -45,6 +45,7 @@ class _3D_EXPORT Scene : public Qt3DCore::QEntity
void createTerrain();
void onLayerRenderer3DChanged();
void onLayersChanged();
void createTerrainDeferred();

private:
void addLayerEntity( QgsMapLayer *layer );
@@ -59,6 +60,7 @@ class _3D_EXPORT Scene : public Qt3DCore::QEntity
QList<ChunkedEntity *> chunkEntities;
//! Keeps track of entities that belong to a particular layer
QMap<QgsMapLayer *, Qt3DCore::QEntity *> mLayerEntities;
bool mTerrainUpdateScheduled = false;
};

#endif // SCENE_H
@@ -13,22 +13,21 @@
class FlatTerrainChunkLoader : public TerrainChunkLoader
{
public:
FlatTerrainChunkLoader( Terrain *terrain, Qt3DExtras::QPlaneGeometry *tileGeometry, ChunkNode *node );
FlatTerrainChunkLoader( Terrain *terrain, ChunkNode *node );

virtual void load() override;
virtual Qt3DCore::QEntity *createEntity( Qt3DCore::QEntity *parent ) override;

private:
Qt3DExtras::QPlaneGeometry *mTileGeometry;
Qt3DExtras::QPlaneGeometry *mTileGeometry = nullptr;
};


//---------------


FlatTerrainChunkLoader::FlatTerrainChunkLoader( Terrain *terrain, Qt3DExtras::QPlaneGeometry *tileGeometry, ChunkNode *node )
FlatTerrainChunkLoader::FlatTerrainChunkLoader( Terrain *terrain, ChunkNode *node )
: TerrainChunkLoader( terrain, node )
, mTileGeometry( tileGeometry )
{
}

@@ -43,8 +42,13 @@ Qt3DCore::QEntity *FlatTerrainChunkLoader::createEntity( Qt3DCore::QEntity *pare

// make geometry renderer

// simple quad geometry shared by all tiles
// QPlaneGeometry by default is 1x1 with mesh resultion QSize(2,2), centered at 0
// TODO: the geometry could be shared inside Terrain instance (within terrain-generator specific data?)
mTileGeometry = new Qt3DExtras::QPlaneGeometry;

Qt3DRender::QGeometryRenderer *mesh = new Qt3DRender::QGeometryRenderer;
mesh->setGeometry( mTileGeometry ); // does not take ownership - geometry is already owned by FlatTerrain entity
mesh->setGeometry( mTileGeometry ); // takes ownership if the component has no parent
entity->addComponent( mesh ); // takes ownership if the component has no parent

// create material
@@ -76,14 +80,11 @@ Qt3DCore::QEntity *FlatTerrainChunkLoader::createEntity( Qt3DCore::QEntity *pare

FlatTerrainGenerator::FlatTerrainGenerator()
{
// simple quad geometry shared by all tiles
// QPlaneGeometry by default is 1x1 with mesh resultion QSize(2,2), centered at 0
tileGeometry = new Qt3DExtras::QPlaneGeometry; // TODO: parent to a node...
}

ChunkLoader *FlatTerrainGenerator::createChunkLoader( ChunkNode *node ) const
{
return new FlatTerrainChunkLoader( mTerrain, tileGeometry, node );
return new FlatTerrainChunkLoader( mTerrain, node );
}

TerrainGenerator *FlatTerrainGenerator::clone() const
@@ -9,10 +9,6 @@

#include "chunkloader.h"

namespace Qt3DExtras
{
class QPlaneGeometry;
}

class _3D_EXPORT FlatTerrainGenerator : public TerrainGenerator
{
@@ -34,7 +30,6 @@ class _3D_EXPORT FlatTerrainGenerator : public TerrainGenerator
QgsCoordinateReferenceSystem crs() const { return mCrs; }

private:
Qt3DExtras::QPlaneGeometry *tileGeometry;

void updateTilingScheme();

@@ -111,7 +111,7 @@ QgsMapSettings MapTextureGenerator::baseMapSettings()
{
QgsMapSettings mapSettings;
mapSettings.setLayers( map.layers() );
mapSettings.setOutputSize( QSize( map.tileTextureSize, map.tileTextureSize ) );
mapSettings.setOutputSize( QSize( map.mapTileResolution(), map.mapTileResolution() ) );
mapSettings.setDestinationCrs( map.crs );
mapSettings.setBackgroundColor( Qt::gray );
return mapSettings;
@@ -13,7 +13,7 @@
Terrain::Terrain( int maxLevel, const Map3D &map, Qt3DCore::QNode *parent )
: ChunkedEntity( map.terrainGenerator()->rootChunkBbox( map ),
map.terrainGenerator()->rootChunkError( map ),
map.maxTerrainError, maxLevel, map.terrainGenerator(), parent )
map.maxTerrainScreenError(), maxLevel, map.terrainGenerator(), parent )
, map( map )
, mTerrainPicker( nullptr )
{
@@ -23,7 +23,7 @@ float TerrainGenerator::rootChunkError( const Map3D &map ) const
te = terrainToMapTransform.transformBoundingBox( te );

// use texel size as the error
return te.width() / map.tileTextureSize;
return te.width() / map.mapTileResolution();
}

void TerrainGenerator::rootChunkHeightRange( float &hMin, float &hMax ) const
@@ -7,6 +7,19 @@



int Utils::maxZoomLevel( double tile0width, double tileResolution, double maxError )
{
if ( maxError <= 0 || tileResolution <= 0 || tile0width <= 0 )
return 0; // invalid input

// derived from:
// tile width [map units] = tile0width / 2^zoomlevel
// tile error [map units] = tile width / tile resolution
// + re-arranging to get zoom level if we know tile error we want to get
double zoomLevel = -log( tileResolution * maxError / tile0width ) / log( 2 );
return round( zoomLevel ); // we could use ceil() here if we wanted to always get to the desired error
}

QString Utils::altClampingToString( AltitudeClamping altClamp )
{
switch ( altClamp )
@@ -24,10 +24,16 @@ enum AltitudeBinding
};


class Utils
class _3D_EXPORT Utils
{
public:

/**
* Calculates the highest needed zoom level for tiles in quad-tree given width of the base tile (zoom level 0)
* in map units, resolution of the tile (e.g. tile's texture width) and desired maximum error in map units.
*/
static int maxZoomLevel( double tile0width, double tileResolution, double maxError );

static QString altClampingToString( AltitudeClamping altClamp );
static AltitudeClamping altClampingFromString( const QString &str );

0 comments on commit 905222d

Please sign in to comment.
You can’t perform that action at this time.