Skip to content

Commit

Permalink
[FEATURE] Configuration of lights in 3D map scene
Browse files Browse the repository at this point in the history
This adds a section to define point lights in 3D scenes.
Up to 8 lights are supported (limitation by implementation of materials).
For each light one can set the position, intensity, color and attenuation
  • Loading branch information
wonder-sk committed Nov 12, 2018
1 parent 1275708 commit 3c66749
Show file tree
Hide file tree
Showing 14 changed files with 623 additions and 13 deletions.
2 changes: 2 additions & 0 deletions src/3d/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ SET(QGIS_3D_SRCS
qgslayoutitem3dmap.cpp
qgsoffscreen3dengine.cpp
qgsphongmaterialsettings.cpp
qgspointlightsettings.cpp
qgsraycastingutils_p.cpp
qgstessellatedpolygongeometry.cpp
qgstilingscheme.cpp
Expand Down Expand Up @@ -93,6 +94,7 @@ SET(QGIS_3D_HDRS
qgslayoutitem3dmap.h
qgsoffscreen3dengine.h
qgsphongmaterialsettings.h
qgspointlightsettings.h
qgsraycastingutils_p.h
qgstessellatedpolygongeometry.h
qgstilingscheme.h
Expand Down
45 changes: 32 additions & 13 deletions src/3d/qgs3dmapscene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Qgs3DMapScene::Qgs3DMapScene( const Qgs3DMapSettings &map, QgsAbstract3DEngine *
connect( &map, &Qgs3DMapSettings::maxTerrainScreenErrorChanged, this, &Qgs3DMapScene::createTerrain );
connect( &map, &Qgs3DMapSettings::maxTerrainGroundErrorChanged, this, &Qgs3DMapScene::createTerrain );
connect( &map, &Qgs3DMapSettings::terrainShadingChanged, this, &Qgs3DMapScene::createTerrain );
connect( &map, &Qgs3DMapSettings::pointLightsChanged, this, &Qgs3DMapScene::updateLights );

// create entities of renderers

Expand All @@ -105,19 +106,7 @@ Qgs3DMapScene::Qgs3DMapScene( const Qgs3DMapSettings &map, QgsAbstract3DEngine *
// listen to changes of layers in order to add/remove 3D renderer entities
connect( &map, &Qgs3DMapSettings::layersChanged, this, &Qgs3DMapScene::onLayersChanged );

Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity;
Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform;
lightTransform->setTranslation( QVector3D( 0, 1000, 0 ) );
// defaults: white, intensity 0.5
// attenuation: constant 1.0, linear 0.0, quadratic 0.0
Qt3DRender::QPointLight *light = new Qt3DRender::QPointLight;
light->setConstantAttenuation( 0 );
//light->setColor(Qt::white);
//light->setIntensity(0.5);
lightEntity->addComponent( light );
lightEntity->addComponent( lightTransform );
lightEntity->setParent( this );

updateLights();

#if 0
ChunkedEntity *testChunkEntity = new ChunkedEntity( AABB( -500, 0, -500, 500, 100, 500 ), 2.f, 3.f, 7, new TestChunkLoaderFactory );
Expand Down Expand Up @@ -465,6 +454,36 @@ void Qgs3DMapScene::onLayerEntityPickEvent( Qt3DRender::QPickEvent *event )

}

void Qgs3DMapScene::updateLights()
{
for ( Qt3DCore::QEntity *entity : qgis::as_const( mLightEntities ) )
entity->deleteLater();
mLightEntities.clear();

const auto newPointLights = mMap.pointLights();
for ( const QgsPointLightSettings &pointLightSettings : newPointLights )
{
Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity;
Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform;
lightTransform->setTranslation( QVector3D( pointLightSettings.position().x(),
pointLightSettings.position().y(),
pointLightSettings.position().z() ) );

Qt3DRender::QPointLight *light = new Qt3DRender::QPointLight;
light->setColor( pointLightSettings.color() );
light->setIntensity( pointLightSettings.intensity() );

light->setConstantAttenuation( pointLightSettings.constantAttenuation() );
light->setLinearAttenuation( pointLightSettings.linearAttenuation() );
light->setQuadraticAttenuation( pointLightSettings.quadraticAttenuation() );

lightEntity->addComponent( light );
lightEntity->addComponent( lightTransform );
lightEntity->setParent( this );
mLightEntities << lightEntity;
}
}

void Qgs3DMapScene::onLayerRenderer3DChanged()
{
QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
Expand Down
3 changes: 3 additions & 0 deletions src/3d/qgs3dmapscene.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class _3D_EXPORT Qgs3DMapScene : public Qt3DCore::QEntity
void createTerrainDeferred();
void onBackgroundColorChanged();
void onLayerEntityPickEvent( Qt3DRender::QPickEvent *event );
void updateLights();

private:
void addLayerEntity( QgsMapLayer *layer );
Expand All @@ -132,6 +133,8 @@ class _3D_EXPORT Qgs3DMapScene : public Qt3DCore::QEntity
SceneState mSceneState = Ready;
//! List of currently registered pick handlers (used by identify tool)
QList<Qgs3DMapScenePickHandler *> mPickHandlers;
//! List of lights in the scene
QList<Qt3DCore::QEntity *> mLightEntities;
};

#endif // QGS3DMAPSCENE_H
41 changes: 41 additions & 0 deletions src/3d/qgs3dmapsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,28 @@ void Qgs3DMapSettings::readXml( const QDomElement &elem, const QgsReadWriteConte
if ( !elemTerrainShadingMaterial.isNull() )
mTerrainShadingMaterial.readXml( elemTerrainShadingMaterial );
mShowLabels = elemTerrain.attribute( QStringLiteral( "show-labels" ), QStringLiteral( "0" ) ).toInt();

mPointLights.clear();
QDomElement elemPointLights = elem.firstChildElement( QStringLiteral( "point-lights" ) );
if ( !elemPointLights.isNull() )
{
QDomElement elemPointLight = elemPointLights.firstChildElement( QStringLiteral( "point-light" ) );
while ( !elemPointLight.isNull() )
{
QgsPointLightSettings pointLight;
pointLight.readXml( elemPointLight );
mPointLights << pointLight;
elemPointLight = elemPointLight.nextSiblingElement( QStringLiteral( "point-light" ) );
}
}
else
{
// QGIS <= 3.4 did not have light configuration
QgsPointLightSettings defaultLight;
defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) );
mPointLights << defaultLight;
}

QDomElement elemMapLayers = elemTerrain.firstChildElement( QStringLiteral( "layers" ) );
QDomElement elemMapLayer = elemMapLayers.firstChildElement( QStringLiteral( "layer" ) );
QList<QgsMapLayerRef> mapLayers;
Expand All @@ -95,6 +117,7 @@ void Qgs3DMapSettings::readXml( const QDomElement &elem, const QgsReadWriteConte
elemMapLayer = elemMapLayer.nextSiblingElement( QStringLiteral( "layer" ) );
}
mLayers = mapLayers; // needs to resolve refs afterwards

QDomElement elemTerrainGenerator = elemTerrain.firstChildElement( QStringLiteral( "generator" ) );
QString terrainGenType = elemTerrainGenerator.attribute( QStringLiteral( "type" ) );
if ( terrainGenType == QLatin1String( "dem" ) )
Expand Down Expand Up @@ -180,6 +203,15 @@ QDomElement Qgs3DMapSettings::writeXml( QDomDocument &doc, const QgsReadWriteCon
mTerrainShadingMaterial.writeXml( elemTerrainShadingMaterial );
elemTerrain.appendChild( elemTerrainShadingMaterial );
elemTerrain.setAttribute( QStringLiteral( "show-labels" ), mShowLabels ? 1 : 0 );

QDomElement elemPointLights = doc.createElement( QStringLiteral( "point-lights" ) );
for ( const QgsPointLightSettings &pointLight : qgis::as_const( mPointLights ) )
{
QDomElement elemPointLight = pointLight.writeXml( doc );
elemPointLights.appendChild( elemPointLight );
}
elem.appendChild( elemPointLights );

QDomElement elemMapLayers = doc.createElement( QStringLiteral( "layers" ) );
Q_FOREACH ( const QgsMapLayerRef &layerRef, mLayers )
{
Expand Down Expand Up @@ -438,3 +470,12 @@ void Qgs3DMapSettings::setShowLabels( bool enabled )
mShowLabels = enabled;
emit showLabelsChanged();
}

void Qgs3DMapSettings::setPointLights( const QList<QgsPointLightSettings> &pointLights )
{
if ( mPointLights == pointLights )
return;

mPointLights = pointLights;
emit pointLightsChanged();
}
20 changes: 20 additions & 0 deletions src/3d/qgs3dmapsettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "qgscoordinatereferencesystem.h"
#include "qgsmaplayerref.h"
#include "qgsphongmaterialsettings.h"
#include "qgspointlightsettings.h"
#include "qgsterraingenerator.h"
#include "qgsvector3d.h"

Expand Down Expand Up @@ -274,6 +275,18 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
//! Returns whether to display labels on terrain tiles
bool showLabels() const { return mShowLabels; }

/**
* Returns list of point lights defined in the scene
* \since QGIS 3.6
*/
QList<QgsPointLightSettings> pointLights() const { return mPointLights; }

/**
* Sets list of point lights defined in the scene
* \since QGIS 3.6
*/
void setPointLights( const QList<QgsPointLightSettings> &pointLights );

signals:
//! Emitted when the background color has changed
void backgroundColorChanged();
Expand Down Expand Up @@ -310,6 +323,12 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
//! Emitted when the flag whether labels are displayed on terrain tiles has changed
void showLabelsChanged();

/**
* Emitted when the list of point lights changes
* \since QGIS 3.6
*/
void pointLightsChanged();

private:
//! Offset in map CRS coordinates at which our 3D world has origin (0,0,0)
QgsVector3D mOrigin;
Expand All @@ -327,6 +346,7 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
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
bool mShowLabels = false; //!< Whether to display labels on terrain tiles
QList<QgsPointLightSettings> mPointLights; //!< List of lights defined for the scene
QList<QgsMapLayerRef> mLayers; //!< Layers to be rendered
QList<QgsAbstract3DRenderer *> mRenderers; //!< Extra stuff to render as 3D object
bool mSkyboxEnabled = false; //!< Whether to render skybox
Expand Down
54 changes: 54 additions & 0 deletions src/3d/qgspointlightsettings.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/***************************************************************************
qgspointlightsettings.cpp
--------------------------------------
Date : November 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgspointlightsettings.h"

#include <QDomDocument>

#include "qgssymbollayerutils.h"


QDomElement QgsPointLightSettings::writeXml( QDomDocument &doc ) const
{
QDomElement elemLight = doc.createElement( QStringLiteral( "point-light" ) );
elemLight.setAttribute( QStringLiteral( "x" ), mPosition.x() );
elemLight.setAttribute( QStringLiteral( "y" ), mPosition.y() );
elemLight.setAttribute( QStringLiteral( "z" ), mPosition.z() );
elemLight.setAttribute( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
elemLight.setAttribute( QStringLiteral( "intensity" ), mIntensity );
elemLight.setAttribute( QStringLiteral( "attenuation-0" ), mConstantAttenuation );
elemLight.setAttribute( QStringLiteral( "attenuation-1" ), mLinearAttenuation );
elemLight.setAttribute( QStringLiteral( "attenuation-2" ), mQuadraticAttenuation );
return elemLight;
}

void QgsPointLightSettings::readXml( const QDomElement &elem )
{
mPosition.set( elem.attribute( QStringLiteral( "x" ) ).toDouble(),
elem.attribute( QStringLiteral( "y" ) ).toDouble(),
elem.attribute( QStringLiteral( "z" ) ).toDouble() );
mColor = QgsSymbolLayerUtils::decodeColor( elem.attribute( QStringLiteral( "color" ) ) );
mIntensity = elem.attribute( QStringLiteral( "intensity" ) ).toFloat();
mConstantAttenuation = elem.attribute( QStringLiteral( "attenuation-0" ) ).toDouble();
mLinearAttenuation = elem.attribute( QStringLiteral( "attenuation-1" ) ).toDouble();
mQuadraticAttenuation = elem.attribute( QStringLiteral( "attenuation-2" ) ).toDouble();
}

bool QgsPointLightSettings::operator==( const QgsPointLightSettings &other )
{
return mPosition == other.mPosition && mColor == other.mColor && mIntensity == other.mIntensity &&
mConstantAttenuation == other.mConstantAttenuation && mLinearAttenuation == other.mLinearAttenuation &&
mQuadraticAttenuation == other.mQuadraticAttenuation;
}
60 changes: 60 additions & 0 deletions src/3d/qgspointlightsettings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#ifndef QGSPOINTLIGHTSETTINGS_H
#define QGSPOINTLIGHTSETTINGS_H

#include "qgsvector3d.h"
#include <QColor>

class QDomDocument;
class QDomElement;

/**
* \ingroup 3d
* Definition of a point light in a 3D map scene
* \since QGIS 3.6
*/
class QgsPointLightSettings
{
public:
QgsPointLightSettings() = default;

//! Returns position of the light (in 3D world coordinates)
QgsVector3D position() const { return mPosition; }
//! Sets position of the light (in 3D world coordinates)
void setPosition( const QgsVector3D &pos ) { mPosition = pos; }

//! Returns color of the light
QColor color() const { return mColor; }
//! Sets color of the light
void setColor( const QColor &c ) { mColor = c; }

//! Returns intensity of the light
float intensity() const { return mIntensity; }
//! Sets intensity of the light
void setIntensity( float intensity ) { mIntensity = intensity; }

float constantAttenuation() const { return mConstantAttenuation; }
void setConstantAttenuation( float value ) { mConstantAttenuation = value; }

float linearAttenuation() const { return mLinearAttenuation; }
void setLinearAttenuation( float value ) { mLinearAttenuation = value; }

float quadraticAttenuation() const { return mQuadraticAttenuation; }
void setQuadraticAttenuation( float value ) { mQuadraticAttenuation = value; }

//! Writes configuration to a new DOM element and returns it
QDomElement writeXml( QDomDocument &doc ) const;
//! Reads configuration from a DOM element previously written using writeXml()
void readXml( const QDomElement &elem );

bool operator==( const QgsPointLightSettings &other );

private:
QgsVector3D mPosition;
QColor mColor = Qt::white;
float mIntensity = 0.5;
float mConstantAttenuation = 1.0f;
float mLinearAttenuation = 0.0f;
float mQuadraticAttenuation = 0.0f;
};

#endif // QGSPOINTLIGHTSETTINGS_H
4 changes: 4 additions & 0 deletions src/app/3d/qgs3dmapconfigwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ Qgs3DMapConfigWidget::Qgs3DMapConfigWidget( Qgs3DMapSettings *map, QgsMapCanvas
widgetTerrainMaterial->setDiffuseVisible( false );
widgetTerrainMaterial->setMaterial( mMap->terrainShadingMaterial() );

widgetLights->setPointLights( mMap->pointLights() );

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 @@ -141,6 +143,8 @@ void Qgs3DMapConfigWidget::apply()

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

mMap->setPointLights( widgetLights->pointLights() );
}

void Qgs3DMapConfigWidget::onTerrainLayerChanged()
Expand Down
Loading

0 comments on commit 3c66749

Please sign in to comment.