Skip to content

Commit

Permalink
[FEATURE] Add support for shading of terrain
Browse files Browse the repository at this point in the history
This adds new options for user to choose how the terrain should be rendered:
- shading disabled - color of terrain is determined only from map texture
- shading enabled - color of terrain is determined using Phong's shading model,
  taking into account map texture, terrain normal vector, scene light(s) and
  terrain material's ambient+specular colors and shininess

(configuration of lights is not there yet - now we always have a single point
light with white color at the middle of the scene at 1km above the zero elevation)
  • Loading branch information
wonder-sk committed Nov 11, 2018
1 parent cf24e65 commit d3e81f6
Show file tree
Hide file tree
Showing 13 changed files with 151 additions and 18 deletions.
2 changes: 2 additions & 0 deletions python/3d/auto_generated/qgsphongmaterialsettings.sip.in
Expand Up @@ -73,6 +73,8 @@ Reads settings from a DOM element
Writes settings to a DOM element
%End

bool operator==( const QgsPhongMaterialSettings &other ) const;

};


Expand Down
1 change: 1 addition & 0 deletions src/3d/qgs3dmapscene.cpp
Expand Up @@ -92,6 +92,7 @@ Qgs3DMapScene::Qgs3DMapScene( const Qgs3DMapSettings &map, QgsAbstract3DEngine *
connect( &map, &Qgs3DMapSettings::mapTileResolutionChanged, this, &Qgs3DMapScene::createTerrain );
connect( &map, &Qgs3DMapSettings::maxTerrainScreenErrorChanged, this, &Qgs3DMapScene::createTerrain );
connect( &map, &Qgs3DMapSettings::maxTerrainGroundErrorChanged, this, &Qgs3DMapScene::createTerrain );
connect( &map, &Qgs3DMapSettings::terrainShadingChanged, this, &Qgs3DMapScene::createTerrain );

// create entities of renderers

Expand Down
28 changes: 28 additions & 0 deletions src/3d/qgs3dmapsettings.cpp
Expand Up @@ -37,6 +37,8 @@ Qgs3DMapSettings::Qgs3DMapSettings( const Qgs3DMapSettings &other )
, mMapTileResolution( other.mMapTileResolution )
, mMaxTerrainScreenError( other.mMaxTerrainScreenError )
, mMaxTerrainGroundError( other.mMaxTerrainGroundError )
, mTerrainShadingEnabled( other.mTerrainShadingEnabled )
, mTerrainShadingMaterial( other.mTerrainShadingMaterial )
, mShowTerrainBoundingBoxes( other.mShowTerrainBoundingBoxes )
, mShowTerrainTileInfo( other.mShowTerrainTileInfo )
, mShowCameraViewCenter( other.mShowCameraViewCenter )
Expand Down Expand Up @@ -79,6 +81,10 @@ void Qgs3DMapSettings::readXml( const QDomElement &elem, const QgsReadWriteConte
mMapTileResolution = elemTerrain.attribute( QStringLiteral( "texture-size" ), QStringLiteral( "512" ) ).toInt();
mMaxTerrainScreenError = elemTerrain.attribute( QStringLiteral( "max-terrain-error" ), QStringLiteral( "3" ) ).toFloat();
mMaxTerrainGroundError = elemTerrain.attribute( QStringLiteral( "max-ground-error" ), QStringLiteral( "1" ) ).toFloat();
mTerrainShadingEnabled = elemTerrain.attribute( QStringLiteral( "shading-enabled" ), QStringLiteral( "0" ) ).toInt();
QDomElement elemTerrainShadingMaterial = elemTerrain.firstChildElement( QStringLiteral( "shading-material" ) );
if ( !elemTerrainShadingMaterial.isNull() )
mTerrainShadingMaterial.readXml( elemTerrainShadingMaterial );
mShowLabels = elemTerrain.attribute( QStringLiteral( "show-labels" ), QStringLiteral( "0" ) ).toInt();
QDomElement elemMapLayers = elemTerrain.firstChildElement( QStringLiteral( "layers" ) );
QDomElement elemMapLayer = elemMapLayers.firstChildElement( QStringLiteral( "layer" ) );
Expand Down Expand Up @@ -169,6 +175,10 @@ QDomElement Qgs3DMapSettings::writeXml( QDomDocument &doc, const QgsReadWriteCon
elemTerrain.setAttribute( QStringLiteral( "texture-size" ), mMapTileResolution );
elemTerrain.setAttribute( QStringLiteral( "max-terrain-error" ), QString::number( mMaxTerrainScreenError ) );
elemTerrain.setAttribute( QStringLiteral( "max-ground-error" ), QString::number( mMaxTerrainGroundError ) );
elemTerrain.setAttribute( QStringLiteral( "shading-enabled" ), mTerrainShadingEnabled ? 1 : 0 );
QDomElement elemTerrainShadingMaterial = doc.createElement( QStringLiteral( "shading-material" ) );
mTerrainShadingMaterial.writeXml( elemTerrainShadingMaterial );
elemTerrain.appendChild( elemTerrainShadingMaterial );
elemTerrain.setAttribute( QStringLiteral( "show-labels" ), mShowLabels ? 1 : 0 );
QDomElement elemMapLayers = doc.createElement( QStringLiteral( "layers" ) );
Q_FOREACH ( const QgsMapLayerRef &layerRef, mLayers )
Expand Down Expand Up @@ -370,6 +380,24 @@ void Qgs3DMapSettings::setTerrainGenerator( QgsTerrainGenerator *gen )
emit terrainGeneratorChanged();
}

void Qgs3DMapSettings::setTerrainShadingEnabled( bool enabled )
{
if ( mTerrainShadingEnabled == enabled )
return;

mTerrainShadingEnabled = enabled;
emit terrainShadingChanged();
}

void Qgs3DMapSettings::setTerrainShadingMaterial( const QgsPhongMaterialSettings &material )
{
if ( mTerrainShadingMaterial == material )
return;

mTerrainShadingMaterial = material;
emit terrainShadingChanged();
}

void Qgs3DMapSettings::setRenderers( const QList<QgsAbstract3DRenderer *> &renderers )
{
mRenderers = renderers;
Expand Down
38 changes: 38 additions & 0 deletions src/3d/qgs3dmapsettings.h
Expand Up @@ -24,6 +24,7 @@

#include "qgscoordinatereferencesystem.h"
#include "qgsmaplayerref.h"
#include "qgsphongmaterialsettings.h"
#include "qgsterraingenerator.h"
#include "qgsvector3d.h"

Expand Down Expand Up @@ -200,6 +201,35 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
//! Returns terrain generator. It takes care of producing terrain tiles from the input data.
QgsTerrainGenerator *terrainGenerator() const { return mTerrainGenerator.get(); }

/**
* Sets whether terrain shading is enabled.
* \sa isTerrainShadingEnabled()
* \since QGIS 3.6
*/
void setTerrainShadingEnabled( bool enabled );

/**
* Returns whether terrain shading is enabled. When enabled, in addition to the terrain texture
* generated from the map, the terrain rendering will take into account position of the lights,
* terrain normals and terrain shading material (ambient and specular colors, shininess).
* \since QGIS 3.6
*/
bool isTerrainShadingEnabled() const { return mTerrainShadingEnabled; }

/**
* Sets terrain shading material.
* \sa terrainShadingMaterial()
* \since QGIS 3.6
*/
void setTerrainShadingMaterial( const QgsPhongMaterialSettings &material );

/**
* Returns terrain shading material. Diffuse color component is ignored since the diffuse component
* is provided by 2D rendered map texture. Only used when isTerrainShadingEnabled() is true.
* \since QGIS 3.6
*/
QgsPhongMaterialSettings terrainShadingMaterial() const { return mTerrainShadingMaterial; }

//! Sets list of extra 3D renderers to use in the scene. Takes ownership of the objects.
void setRenderers( const QList<QgsAbstract3DRenderer *> &renderers SIP_TRANSFER );
//! Returns list of extra 3D renderers
Expand Down Expand Up @@ -261,6 +291,12 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
void maxTerrainScreenErrorChanged();
//! Emitted when the maximum terrain ground error has changed
void maxTerrainGroundErrorChanged();

/**
* Emitted when terrain shading enabled flag or terrain shading material has changed
* \since QGIS 3.6
*/
void terrainShadingChanged();
//! Emitted when the flag whether terrain's bounding boxes are shown has changed
void showTerrainBoundingBoxesChanged();
//! Emitted when the flag whether terrain's tile info is shown has changed
Expand All @@ -285,6 +321,8 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
int mMapTileResolution = 512; //!< Size of map textures of tiles in pixels (width/height)
float mMaxTerrainScreenError = 3.f; //!< Maximum allowed terrain error in pixels (determines when tiles are switched to more detailed ones)
float mMaxTerrainGroundError = 1.f; //!< Maximum allowed horizontal map error in map units (determines how many zoom levels will be used)
bool mTerrainShadingEnabled = false; //!< Whether terrain should be shaded taking lights into account
QgsPhongMaterialSettings mTerrainShadingMaterial; //!< Material to use for the terrain (if shading is enabled). Diffuse color is ignored.
bool mShowTerrainBoundingBoxes = false; //!< Whether to show bounding boxes of entities - useful for debugging
bool mShowTerrainTileInfo = false; //!< Whether to draw extra information about terrain tiles to the textures - useful for debugging
bool mShowCameraViewCenter = false; //!< Whether to show camera view center as a sphere - useful for debugging
Expand Down
8 changes: 8 additions & 0 deletions src/3d/qgsphongmaterialsettings.h
Expand Up @@ -65,6 +65,14 @@ class _3D_EXPORT QgsPhongMaterialSettings
//! Writes settings to a DOM element
void writeXml( QDomElement &elem ) const;

bool operator==( const QgsPhongMaterialSettings &other ) const
{
return mAmbient == other.mAmbient &&
mDiffuse == other.mDiffuse &&
mSpecular == other.mSpecular &&
mShininess == other.mShininess;
}

private:
QColor mAmbient;
QColor mDiffuse;
Expand Down
2 changes: 1 addition & 1 deletion src/3d/terrain/qgsdemterraintileloader_p.cpp
Expand Up @@ -94,7 +94,7 @@ Qt3DCore::QEntity *QgsDemTerrainTileLoader::createEntity( Qt3DCore::QEntity *par

// create material

createTextureComponent( entity );
createTextureComponent( entity, map.isTerrainShadingEnabled(), map.terrainShadingMaterial() );

// create transform

Expand Down
3 changes: 2 additions & 1 deletion src/3d/terrain/qgsflatterraingenerator.cpp
Expand Up @@ -52,7 +52,8 @@ Qt3DCore::QEntity *FlatTerrainChunkLoader::createEntity( Qt3DCore::QEntity *pare

// create material

createTextureComponent( entity );
const Qgs3DMapSettings &map = terrain()->map3D();
createTextureComponent( entity, map.isTerrainShadingEnabled(), map.terrainShadingMaterial() );

// create transform

Expand Down
34 changes: 20 additions & 14 deletions src/3d/terrain/qgsterraintileloader_p.cpp
Expand Up @@ -25,11 +25,8 @@

#include <Qt3DRender/QTexture>

#if QT_VERSION >= 0x050900
#include <Qt3DExtras/QTextureMaterial>
#else
#include <Qt3DExtras/QDiffuseMapMaterial>
#endif

#include "quantizedmeshterraingenerator.h"

Expand Down Expand Up @@ -67,23 +64,32 @@ void QgsTerrainTileLoader::loadTexture()
mTextureJobId = mTerrain->textureGenerator()->render( mExtentMapCrs, mTileDebugText );
}

void QgsTerrainTileLoader::createTextureComponent( QgsTerrainTileEntity *entity )
void QgsTerrainTileLoader::createTextureComponent( QgsTerrainTileEntity *entity, bool isShadingEnabled, const QgsPhongMaterialSettings &shadingMaterial )
{
Qt3DRender::QTexture2D *texture = new Qt3DRender::QTexture2D( entity );
QgsTerrainTextureImage *textureImage = new QgsTerrainTextureImage( mTextureImage, mExtentMapCrs, mTileDebugText );
texture->addTextureImage( textureImage );
texture->setMinificationFilter( Qt3DRender::QTexture2D::Linear );
texture->setMagnificationFilter( Qt3DRender::QTexture2D::Linear );
Qt3DExtras::QTextureMaterial *material = nullptr;
#if QT_VERSION >= 0x050900
material = new Qt3DExtras::QTextureMaterial;
material->setTexture( texture );
#else
material = new Qt3DExtras::QDiffuseMapMaterial;
material->setDiffuse( texture );
material->setShininess( 1 );
material->setAmbient( Qt::white );
#endif

Qt3DRender::QMaterial *material = nullptr;
if ( isShadingEnabled )
{
Qt3DExtras::QDiffuseMapMaterial *diffuseMapMaterial;
diffuseMapMaterial = new Qt3DExtras::QDiffuseMapMaterial;
diffuseMapMaterial->setDiffuse( texture );
diffuseMapMaterial->setAmbient( shadingMaterial.ambient() );
diffuseMapMaterial->setSpecular( shadingMaterial.specular() );
diffuseMapMaterial->setShininess( shadingMaterial.shininess() );
material = diffuseMapMaterial;
}
else
{
Qt3DExtras::QTextureMaterial *textureMaterial = new Qt3DExtras::QTextureMaterial;
textureMaterial->setTexture( texture );
material = textureMaterial;
}

entity->setTextureImage( textureImage );
entity->addComponent( material ); // takes ownership if the component has no parent
}
Expand Down
3 changes: 2 additions & 1 deletion src/3d/terrain/qgsterraintileloader_p.h
Expand Up @@ -32,6 +32,7 @@
#include <QImage>
#include "qgsrectangle.h"

class QgsPhongMaterialSettings;
class QgsTerrainEntity;
class QgsTerrainTileEntity;

Expand All @@ -54,7 +55,7 @@ class QgsTerrainTileLoader : public QgsChunkLoader
//! Starts asynchronous rendering of map texture
void loadTexture();
//! Creates material component for the entity with the rendered map as a texture
void createTextureComponent( QgsTerrainTileEntity *entity );
void createTextureComponent( QgsTerrainTileEntity *entity, bool isShadingEnabled, const QgsPhongMaterialSettings &shadingMaterial );
//! Gives access to the terain entity
QgsTerrainEntity *terrain() { return mTerrain; }

Expand Down
7 changes: 7 additions & 0 deletions src/app/3d/qgs3dmapconfigwidget.cpp
Expand Up @@ -69,6 +69,10 @@ Qgs3DMapConfigWidget::Qgs3DMapConfigWidget( Qgs3DMapSettings *map, QgsMapCanvas
chkShowBoundingBoxes->setChecked( mMap->showTerrainBoundingBoxes() );
chkShowCameraViewCenter->setChecked( mMap->showCameraViewCenter() );

groupTerrainShading->setChecked( mMap->isTerrainShadingEnabled() );
widgetTerrainMaterial->setDiffuseVisible( false );
widgetTerrainMaterial->setMaterial( mMap->terrainShadingMaterial() );

connect( cboTerrainLayer, static_cast<void ( QComboBox::* )( int )>( &QgsMapLayerComboBox::currentIndexChanged ), this, &Qgs3DMapConfigWidget::onTerrainLayerChanged );
connect( spinMapResolution, static_cast<void ( QSpinBox::* )( int )>( &QSpinBox::valueChanged ), this, &Qgs3DMapConfigWidget::updateMaxZoomLevel );
connect( spinGroundError, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &Qgs3DMapConfigWidget::updateMaxZoomLevel );
Expand Down Expand Up @@ -134,6 +138,9 @@ void Qgs3DMapConfigWidget::apply()
mMap->setShowTerrainTilesInfo( chkShowTileInfo->isChecked() );
mMap->setShowTerrainBoundingBoxes( chkShowBoundingBoxes->isChecked() );
mMap->setShowCameraViewCenter( chkShowCameraViewCenter->isChecked() );

mMap->setTerrainShadingEnabled( groupTerrainShading->isChecked() );
mMap->setTerrainShadingMaterial( widgetTerrainMaterial->material() );
}

void Qgs3DMapConfigWidget::onTerrainLayerChanged()
Expand Down
11 changes: 11 additions & 0 deletions src/app/3d/qgsphongmaterialwidget.cpp
Expand Up @@ -31,6 +31,17 @@ QgsPhongMaterialWidget::QgsPhongMaterialWidget( QWidget *parent )
connect( spinShininess, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsPhongMaterialWidget::changed );
}

void QgsPhongMaterialWidget::setDiffuseVisible( bool visible )
{
label->setVisible( visible );
btnDiffuse->setVisible( visible );
}

bool QgsPhongMaterialWidget::isDiffuseVisible() const
{
return btnDiffuse->isVisible();
}

void QgsPhongMaterialWidget::setMaterial( const QgsPhongMaterialSettings &material )
{
btnDiffuse->setColor( material.diffuse() );
Expand Down
3 changes: 3 additions & 0 deletions src/app/3d/qgsphongmaterialwidget.h
Expand Up @@ -30,6 +30,9 @@ class QgsPhongMaterialWidget : public QWidget, private Ui::PhongMaterialWidget
public:
explicit QgsPhongMaterialWidget( QWidget *parent = nullptr );

void setDiffuseVisible( bool visible );
bool isDiffuseVisible() const;

void setMaterial( const QgsPhongMaterialSettings &material );
QgsPhongMaterialSettings material() const;

Expand Down
29 changes: 28 additions & 1 deletion src/ui/3d/map3dconfigwidget.ui
Expand Up @@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>691</width>
<height>830</height>
<height>1135</height>
</rect>
</property>
<property name="windowTitle">
Expand Down Expand Up @@ -87,6 +87,21 @@
</layout>
</widget>
</item>
<item>
<widget class="QgsCollapsibleGroupBox" name="groupTerrainShading">
<property name="title">
<string>Terrain shading</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QgsPhongMaterialWidget" name="widgetTerrainMaterial" native="true"/>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
Expand Down Expand Up @@ -224,6 +239,18 @@
<extends>QSpinBox</extends>
<header>qgsspinbox.h</header>
</customwidget>
<customwidget>
<class>QgsPhongMaterialWidget</class>
<extends>QWidget</extends>
<header>qgsphongmaterialwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsCollapsibleGroupBox</class>
<extends>QGroupBox</extends>
<header>qgscollapsiblegroupbox.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>cboTerrainLayer</tabstop>
Expand Down

0 comments on commit d3e81f6

Please sign in to comment.