Cleanup light source handling in API

This commit is contained in:
Nyall Dawson 2022-04-25 07:39:37 +10:00
parent 8520d7ed27
commit 5e831971cc
22 changed files with 425 additions and 148 deletions

View File

@ -26,6 +26,14 @@ Definition of a directional light in a 3D map scene
Construct a directional light with default values
%End
virtual Qgis::LightSourceType type() const;
virtual QgsDirectionalLightSettings *clone() const /Factory/;
virtual QDomElement writeXml( QDomDocument &doc, const QgsReadWriteContext &context = QgsReadWriteContext() ) const;
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context = QgsReadWriteContext() );
QgsVector3D direction() const;
%Docstring
@ -52,15 +60,6 @@ Returns intensity of the light
void setIntensity( float intensity );
%Docstring
Sets intensity of the light
%End
QDomElement writeXml( QDomDocument &doc ) const;
%Docstring
Writes configuration to a new DOM element and returns it
%End
void readXml( const QDomElement &elem );
%Docstring
Reads configuration from a DOM element previously written using :py:func:`~QgsDirectionalLightSettings.writeXml`
%End
bool operator==( const QgsDirectionalLightSettings &other );

View File

@ -17,7 +17,7 @@ class QgsLightSource /Abstract/
%Docstring(signature="appended")
Base class for light sources in 3d scenes.
.. versionadded:: 3.16
.. versionadded:: 3.26
%End
%TypeHeaderCode
@ -27,19 +27,39 @@ Base class for light sources in 3d scenes.
virtual ~QgsLightSource();
virtual Qgis::LightSourceType type() const = 0;
%Docstring
Returns the light source type.
%End
virtual QDomElement writeXml( QDomDocument &doc ) const = 0;
virtual QgsLightSource *clone() const = 0 /Factory/;
%Docstring
Returns a copy of the light source.
%End
virtual QDomElement writeXml( QDomDocument &doc, const QgsReadWriteContext &context = QgsReadWriteContext() ) const = 0;
%Docstring
Writes the light source's configuration to a new DOM element and returns it.
.. seealso:: :py:func:`readXml`
%End
virtual void readXml( const QDomElement &elem ) = 0;
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context = QgsReadWriteContext() ) = 0;
%Docstring
Reads configuration from a DOM element previously written using :py:func:`~QgsLightSource.writeXml`.
.. seealso:: :py:func:`writeXml`
%End
virtual void resolveReferences( const QgsProject &project );
%Docstring
After reading from XML, resolve references to any layers that have been read as layer IDs.
%End
static QgsLightSource *createFromXml( const QDomElement &element, const QgsReadWriteContext &context ) /Factory/;
%Docstring
Creates a new light source from an XML element.
%End
};

View File

@ -32,6 +32,14 @@ constant, linear and quadratic attenuation.
Construct a point light with default values
%End
virtual Qgis::LightSourceType type() const;
virtual QgsPointLightSettings *clone() const /Factory/;
virtual QDomElement writeXml( QDomDocument &doc, const QgsReadWriteContext &context = QgsReadWriteContext() ) const;
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context = QgsReadWriteContext() );
QgsVector3D position() const;
%Docstring
@ -85,15 +93,6 @@ Returns quadratic attenuation (A_2)
void setQuadraticAttenuation( float value );
%Docstring
Sets quadratic attenuation (A_2)
%End
QDomElement writeXml( QDomDocument &doc ) const;
%Docstring
Writes configuration to a new DOM element and returns it
%End
void readXml( const QDomElement &elem );
%Docstring
Reads configuration from a DOM element previously written using :py:func:`~QgsPointLightSettings.writeXml`
%End
bool operator==( const QgsPointLightSettings &other );

View File

@ -469,32 +469,56 @@ Returns the corner where the shadow map preview is displayed
Returns the size of the shadow map preview
%End
QList<QgsPointLightSettings> pointLights() const;
QList<QgsPointLightSettings> pointLights() const /Deprecated/;
%Docstring
Returns list of point lights defined in the scene
.. versionadded:: 3.6
.. deprecated::
use :py:func:`~Qgs3DMapSettings.lightSources` instead
%End
QList<QgsDirectionalLightSettings> directionalLights() const;
QList<QgsDirectionalLightSettings> directionalLights() const /Deprecated/;
%Docstring
Returns list of directional lights defined in the scene
.. versionadded:: 3.16
.. deprecated::
use :py:func:`~Qgs3DMapSettings.lightSources` instead
%End
void setPointLights( const QList<QgsPointLightSettings> &pointLights );
void setPointLights( const QList<QgsPointLightSettings> &pointLights ) /Deprecated/;
%Docstring
Sets list of point lights defined in the scene
.. versionadded:: 3.6
.. deprecated::
use :py:func:`~Qgs3DMapSettings.setLightSources` instead
%End
void setDirectionalLights( const QList<QgsDirectionalLightSettings> &directionalLights );
void setDirectionalLights( const QList<QgsDirectionalLightSettings> &directionalLights ) /Deprecated/;
%Docstring
Sets list of directional lights defined in the scene
.. versionadded:: 3.16
.. deprecated::
use :py:func:`~Qgs3DMapSettings.setLightSources` instead
%End
QList<QgsLightSource *> lightSources() const;
%Docstring
Returns list of directional light sources defined in the scene.
.. seealso:: :py:func:`setLightSources`
.. versionadded:: 3.26
%End
void setLightSources( const QList<QgsLightSource *> &lights /Transfer/ );
%Docstring
Sets the list of ``light`` sources defined in the scene.
Ownership of the lights is transferred to the settings.
.. seealso:: :py:func:`lightSources`
.. versionadded:: 3.26
%End
float fieldOfView() const;
@ -801,6 +825,13 @@ Emitted when depth map debugging has changed
Emitted when the list of point lights changes
.. versionadded:: 3.6
%End
void lightSourcesChanged();
%Docstring
Emitted when any of the light source settings in the map changes.
.. versionadded:: 3.26
%End
void directionalLightsChanged();

View File

@ -1470,3 +1470,9 @@ Qgis.PlotToolFlag.__doc__ = 'Flags that control the way the :py:class:`QgsPlotTo
Qgis.PlotToolFlag.baseClass = Qgis
Qgis.PlotToolFlags.baseClass = Qgis
PlotToolFlags = Qgis # dirty hack since SIP seems to introduce the flags in module
# monkey patching scoped based enum
Qgis.LightSourceType.Point.__doc__ = "Point light source"
Qgis.LightSourceType.Directional.__doc__ = "Directional light source"
Qgis.LightSourceType.__doc__ = 'Light source types for 3D scenes.\n\n.. versionadded:: 3.26\n\n' + '* ``Point``: ' + Qgis.LightSourceType.Point.__doc__ + '\n' + '* ``Directional``: ' + Qgis.LightSourceType.Directional.__doc__
# --
Qgis.LightSourceType.baseClass = Qgis

View File

@ -995,6 +995,12 @@ The development version
typedef QFlags<Qgis::PlotToolFlag> PlotToolFlags;
enum class LightSourceType
{
Point,
Directional,
};
static const double DEFAULT_SEARCH_RADIUS_MM;
static const float DEFAULT_MAPTOPIXEL_THRESHOLD;

View File

@ -20,9 +20,19 @@
#include <Qt3DRender/QDirectionalLight>
#include <Qt3DCore/QEntity>
Qgis::LightSourceType QgsDirectionalLightSettings::type() const
{
return Qgis::LightSourceType::Directional;
}
QgsDirectionalLightSettings *QgsDirectionalLightSettings::clone() const
{
return new QgsDirectionalLightSettings( *this );
}
QList<Qt3DCore::QEntity *> QgsDirectionalLightSettings::createEntities( const Qgs3DMapSettings &, Qt3DCore::QEntity *parent ) const
{
Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity;
Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity( parent );
Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform;
Qt3DRender::QDirectionalLight *light = new Qt3DRender::QDirectionalLight;
@ -33,12 +43,11 @@ QList<Qt3DCore::QEntity *> QgsDirectionalLightSettings::createEntities( const Qg
lightEntity->addComponent( light );
lightEntity->addComponent( lightTransform );
lightEntity->setParent( parent );
return {lightEntity};
}
QDomElement QgsDirectionalLightSettings::writeXml( QDomDocument &doc ) const
QDomElement QgsDirectionalLightSettings::writeXml( QDomDocument &doc, const QgsReadWriteContext & ) const
{
QDomElement elemLight = doc.createElement( QStringLiteral( "directional-light" ) );
elemLight.setAttribute( QStringLiteral( "x" ), mDirection.x() );
@ -49,7 +58,7 @@ QDomElement QgsDirectionalLightSettings::writeXml( QDomDocument &doc ) const
return elemLight;
}
void QgsDirectionalLightSettings::readXml( const QDomElement &elem )
void QgsDirectionalLightSettings::readXml( const QDomElement &elem, const QgsReadWriteContext & )
{
mDirection.set( elem.attribute( QStringLiteral( "x" ) ).toFloat(),
elem.attribute( QStringLiteral( "y" ) ).toFloat(),

View File

@ -36,9 +36,11 @@ class _3D_EXPORT QgsDirectionalLightSettings : public QgsLightSource
//! Construct a directional light with default values
QgsDirectionalLightSettings() = default;
Qgis::LightSourceType type() const override;
QgsDirectionalLightSettings *clone() const override SIP_FACTORY;
QList<Qt3DCore::QEntity *> createEntities( const Qgs3DMapSettings &map, Qt3DCore::QEntity *parent ) const override SIP_SKIP;
QDomElement writeXml( QDomDocument &doc ) const override;
void readXml( const QDomElement &elem ) override;
QDomElement writeXml( QDomDocument &doc, const QgsReadWriteContext &context = QgsReadWriteContext() ) const override;
void readXml( const QDomElement &elem, const QgsReadWriteContext &context = QgsReadWriteContext() ) override;
//! Returns the direction of the light in degrees
QgsVector3D direction() const { return mDirection; }

View File

@ -17,5 +17,30 @@
***************************************************************************/
#include "qgslightsource.h"
#include "qgspointlightsettings.h"
#include "qgsdirectionallightsettings.h"
QgsLightSource::~QgsLightSource() = default;
void QgsLightSource::resolveReferences( const QgsProject & )
{
}
QgsLightSource *QgsLightSource::createFromXml( const QDomElement &element, const QgsReadWriteContext &context )
{
std::unique_ptr< QgsLightSource > res;
if ( element.nodeName() == QLatin1String( "point-light" ) )
{
res = std::make_unique< QgsPointLightSettings >();
}
else if ( element.nodeName() == QLatin1String( "directional-light" ) )
{
res = std::make_unique< QgsDirectionalLightSettings >();
}
if ( res )
res->readXml( element, context );
return res.release();
}

View File

@ -19,11 +19,14 @@
#define QGSLIGHTSOURCE_H
#include "qgis_3d.h"
#include "qgis.h"
#include "qgis_sip.h"
#include "qgsreadwritecontext.h"
#include <QList>
class Qgs3DMapSettings;
class QgsProject;
class QDomElement;
class QDomDocument;
@ -38,7 +41,7 @@ namespace Qt3DCore
* \ingroup 3d
* \brief Base class for light sources in 3d scenes.
*
* \since QGIS 3.16
* \since QGIS 3.26
*/
class _3D_EXPORT QgsLightSource SIP_ABSTRACT
{
@ -47,6 +50,16 @@ class _3D_EXPORT QgsLightSource SIP_ABSTRACT
virtual ~QgsLightSource();
/**
* Returns the light source type.
*/
virtual Qgis::LightSourceType type() const = 0;
/**
* Returns a copy of the light source.
*/
virtual QgsLightSource *clone() const = 0 SIP_FACTORY;
/**
* Creates entities representing the light source.
*/
@ -57,14 +70,24 @@ class _3D_EXPORT QgsLightSource SIP_ABSTRACT
*
* \see readXml()
*/
virtual QDomElement writeXml( QDomDocument &doc ) const = 0;
virtual QDomElement writeXml( QDomDocument &doc, const QgsReadWriteContext &context = QgsReadWriteContext() ) const = 0;
/**
* Reads configuration from a DOM element previously written using writeXml().
*
* \see writeXml()
*/
virtual void readXml( const QDomElement &elem ) = 0;
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context = QgsReadWriteContext() ) = 0;
/**
* After reading from XML, resolve references to any layers that have been read as layer IDs.
*/
virtual void resolveReferences( const QgsProject &project );
/**
* Creates a new light source from an XML element.
*/
static QgsLightSource *createFromXml( const QDomElement &element, const QgsReadWriteContext &context ) SIP_FACTORY;
};

View File

@ -24,9 +24,19 @@
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DExtras/QSphereMesh>
Qgis::LightSourceType QgsPointLightSettings::type() const
{
return Qgis::LightSourceType::Point;
}
QgsPointLightSettings *QgsPointLightSettings::clone() const
{
return new QgsPointLightSettings( *this );
}
QList<Qt3DCore::QEntity *> QgsPointLightSettings::createEntities( const Qgs3DMapSettings &map, Qt3DCore::QEntity *parent ) const
{
Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity;
Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity( parent );
Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform;
lightTransform->setTranslation( QVector3D( position().x(),
position().y(),
@ -42,13 +52,12 @@ QList<Qt3DCore::QEntity *> QgsPointLightSettings::createEntities( const Qgs3DMap
lightEntity->addComponent( light );
lightEntity->addComponent( lightTransform );
lightEntity->setParent( parent );
QList<Qt3DCore::QEntity *> res { lightEntity };
if ( map.showLightSourceOrigins() )
{
Qt3DCore::QEntity *originEntity = new Qt3DCore::QEntity;
Qt3DCore::QEntity *originEntity = new Qt3DCore::QEntity( parent );
Qt3DCore::QTransform *trLightOriginCenter = new Qt3DCore::QTransform;
trLightOriginCenter->setTranslation( lightTransform->translation() );
@ -63,7 +72,6 @@ QList<Qt3DCore::QEntity *> QgsPointLightSettings::createEntities( const Qgs3DMap
originEntity->addComponent( rendererLightOriginCenter );
originEntity->setEnabled( true );
originEntity->setParent( parent );
res << originEntity;
}
@ -71,7 +79,7 @@ QList<Qt3DCore::QEntity *> QgsPointLightSettings::createEntities( const Qgs3DMap
return res;
}
QDomElement QgsPointLightSettings::writeXml( QDomDocument &doc ) const
QDomElement QgsPointLightSettings::writeXml( QDomDocument &doc, const QgsReadWriteContext & ) const
{
QDomElement elemLight = doc.createElement( QStringLiteral( "point-light" ) );
elemLight.setAttribute( QStringLiteral( "x" ), mPosition.x() );
@ -85,7 +93,7 @@ QDomElement QgsPointLightSettings::writeXml( QDomDocument &doc ) const
return elemLight;
}
void QgsPointLightSettings::readXml( const QDomElement &elem )
void QgsPointLightSettings::readXml( const QDomElement &elem, const QgsReadWriteContext & )
{
mPosition.set( elem.attribute( QStringLiteral( "x" ) ).toDouble(),
elem.attribute( QStringLiteral( "y" ) ).toDouble(),

View File

@ -42,9 +42,11 @@ class _3D_EXPORT QgsPointLightSettings : public QgsLightSource
//! Construct a point light with default values
QgsPointLightSettings() = default;
Qgis::LightSourceType type() const override;
QgsPointLightSettings *clone() const override SIP_FACTORY;
QList<Qt3DCore::QEntity *> createEntities( const Qgs3DMapSettings &map, Qt3DCore::QEntity *parent ) const override SIP_SKIP;
QDomElement writeXml( QDomDocument &doc ) const override;
void readXml( const QDomElement &elem ) override;
QDomElement writeXml( QDomDocument &doc, const QgsReadWriteContext &context = QgsReadWriteContext() ) const override;
void readXml( const QDomElement &elem, const QgsReadWriteContext &context = QgsReadWriteContext() ) override;
//! Returns position of the light (in 3D world coordinates)
QgsVector3D position() const { return mPosition; }

View File

@ -128,8 +128,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 );
connect( &map, &Qgs3DMapSettings::directionalLightsChanged, this, &Qgs3DMapScene::updateLights );
connect( &map, &Qgs3DMapSettings::lightSourcesChanged, this, &Qgs3DMapScene::updateLights );
connect( &map, &Qgs3DMapSettings::showLightSourceOriginsChanged, this, &Qgs3DMapScene::updateLights );
connect( &map, &Qgs3DMapSettings::fieldOfViewChanged, this, &Qgs3DMapScene::updateCameraLens );
connect( &map, &Qgs3DMapSettings::projectionTypeChanged, this, &Qgs3DMapScene::updateCameraLens );
@ -657,16 +656,10 @@ void Qgs3DMapScene::updateLights()
entity->deleteLater();
mLightEntities.clear();
const auto newPointLights = mMap.pointLights();
for ( const QgsPointLightSettings &pointLightSettings : newPointLights )
const QList< QgsLightSource * > newLights = mMap.lightSources();
for ( const QgsLightSource *source : newLights )
{
mLightEntities.append( pointLightSettings.createEntities( mMap, this ) );
}
const auto newDirectionalLights = mMap.directionalLights();
for ( const QgsDirectionalLightSettings &directionalLightSettings : newDirectionalLights )
{
mLightEntities.append( directionalLightSettings.createEntities( mMap, this ) );
mLightEntities.append( source->createEntities( mMap, this ) );
}
onShadowSettingsChanged();
@ -1034,15 +1027,24 @@ void Qgs3DMapScene::onShadowSettingsChanged()
{
QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
QList<QgsDirectionalLightSettings> directionalLights = mMap.directionalLights();
const QList< QgsLightSource * > lightSources = mMap.lightSources();
QList< QgsDirectionalLightSettings * > directionalLightSources;
for ( QgsLightSource *source : lightSources )
{
if ( source->type() == Qgis::LightSourceType::Directional )
{
directionalLightSources << qgis::down_cast< QgsDirectionalLightSettings * >( source );
}
}
QgsShadowSettings shadowSettings = mMap.shadowSettings();
int selectedLight = shadowSettings.selectedDirectionalLight();
if ( shadowSettings.renderShadows() && selectedLight >= 0 && selectedLight < directionalLights.count() )
if ( shadowSettings.renderShadows() && selectedLight >= 0 && selectedLight < directionalLightSources.count() )
{
shadowRenderingFrameGraph->setShadowRenderingEnabled( true );
shadowRenderingFrameGraph->setShadowBias( shadowSettings.shadowBias() );
shadowRenderingFrameGraph->setShadowMapResolution( shadowSettings.shadowMapResolution() );
QgsDirectionalLightSettings light = directionalLights[selectedLight];
QgsDirectionalLightSettings light = *directionalLightSources.at( selectedLight );
shadowRenderingFrameGraph->setupDirectionalLight( light, shadowSettings.maximumShadowRenderingDistance() );
}
else

View File

@ -64,8 +64,6 @@ Qgs3DMapSettings::Qgs3DMapSettings( const Qgs3DMapSettings &other )
, mShowCameraRotationCenter( other.mShowCameraRotationCenter )
, mShowLightSources( other.mShowLightSources )
, mShowLabels( other.mShowLabels )
, mPointLights( other.mPointLights )
, mDirectionalLights( other.mDirectionalLights )
, mFieldOfView( other.mFieldOfView )
, mProjectionType( other.mProjectionType )
, mCameraNavigationMode( other.mCameraNavigationMode )
@ -99,6 +97,12 @@ Qgs3DMapSettings::Qgs3DMapSettings( const Qgs3DMapSettings &other )
mRenderers << renderer->clone();
}
for ( QgsLightSource *source : std::as_const( other.mLightSources ) )
{
if ( source )
mLightSources << source->clone();
}
connect( this, &Qgs3DMapSettings::settingsChanged, [&]()
{
QgsProject::instance()->setDirty();
@ -109,6 +113,7 @@ Qgs3DMapSettings::Qgs3DMapSettings( const Qgs3DMapSettings &other )
Qgs3DMapSettings::~Qgs3DMapSettings()
{
qDeleteAll( mRenderers );
qDeleteAll( mLightSources );
}
void Qgs3DMapSettings::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
@ -158,38 +163,53 @@ void Qgs3DMapSettings::readXml( const QDomElement &elem, const QgsReadWriteConte
mTerrainMapTheme = elemTerrain.attribute( QStringLiteral( "map-theme" ) );
mShowLabels = elemTerrain.attribute( QStringLiteral( "show-labels" ), QStringLiteral( "0" ) ).toInt();
mPointLights.clear();
QDomElement elemPointLights = elem.firstChildElement( QStringLiteral( "point-lights" ) );
if ( !elemPointLights.isNull() )
qDeleteAll( mLightSources );
mLightSources.clear();
const QDomElement lightsElem = elem.firstChildElement( QStringLiteral( "lights" ) );
if ( !lightsElem.isNull() )
{
QDomElement elemPointLight = elemPointLights.firstChildElement( QStringLiteral( "point-light" ) );
while ( !elemPointLight.isNull() )
const QDomNodeList lightNodes = lightsElem.childNodes();
for ( int i = 0; i < lightNodes.size(); ++i )
{
QgsPointLightSettings pointLight;
pointLight.readXml( elemPointLight );
mPointLights << pointLight;
elemPointLight = elemPointLight.nextSiblingElement( QStringLiteral( "point-light" ) );
const QDomElement lightElement = lightNodes.at( i ).toElement();
if ( QgsLightSource *light = QgsLightSource::createFromXml( lightElement, context ) )
mLightSources << light;
}
}
else
{
// QGIS <= 3.4 did not have light configuration
QgsPointLightSettings defaultLight;
defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) );
mPointLights << defaultLight;
}
mDirectionalLights.clear();
QDomElement elemDirectionalLights = elem.firstChildElement( QStringLiteral( "directional-lights" ) );
if ( !elemDirectionalLights.isNull() )
{
QDomElement elemDirectionalLight = elemDirectionalLights.firstChildElement( QStringLiteral( "directional-light" ) );
while ( !elemDirectionalLight.isNull() )
// older project format
QDomElement elemPointLights = elem.firstChildElement( QStringLiteral( "point-lights" ) );
if ( !elemPointLights.isNull() )
{
QgsDirectionalLightSettings directionalLight;
directionalLight.readXml( elemDirectionalLight );
mDirectionalLights << directionalLight;
elemDirectionalLight = elemDirectionalLight.nextSiblingElement( QStringLiteral( "directional-light" ) );
QDomElement elemPointLight = elemPointLights.firstChildElement( QStringLiteral( "point-light" ) );
while ( !elemPointLight.isNull() )
{
std::unique_ptr< QgsPointLightSettings > pointLight = std::make_unique< QgsPointLightSettings >();
pointLight->readXml( elemPointLight, context );
mLightSources << pointLight.release();
elemPointLight = elemPointLight.nextSiblingElement( QStringLiteral( "point-light" ) );
}
}
else
{
// QGIS <= 3.4 did not have light configuration
std::unique_ptr< QgsPointLightSettings > defaultLight = std::make_unique< QgsPointLightSettings >();
defaultLight->setPosition( QgsVector3D( 0, 1000, 0 ) );
mLightSources << defaultLight.release();
}
QDomElement elemDirectionalLights = elem.firstChildElement( QStringLiteral( "directional-lights" ) );
if ( !elemDirectionalLights.isNull() )
{
QDomElement elemDirectionalLight = elemDirectionalLights.firstChildElement( QStringLiteral( "directional-light" ) );
while ( !elemDirectionalLight.isNull() )
{
std::unique_ptr< QgsDirectionalLightSettings > directionalLight = std::make_unique< QgsDirectionalLightSettings >();
directionalLight->readXml( elemDirectionalLight, context );
mLightSources << directionalLight.release();
elemDirectionalLight = elemDirectionalLight.nextSiblingElement( QStringLiteral( "directional-light" ) );
}
}
}
@ -349,21 +369,15 @@ QDomElement Qgs3DMapSettings::writeXml( QDomDocument &doc, const QgsReadWriteCon
elemTerrain.setAttribute( QStringLiteral( "map-theme" ), mTerrainMapTheme );
elemTerrain.setAttribute( QStringLiteral( "show-labels" ), mShowLabels ? 1 : 0 );
QDomElement elemPointLights = doc.createElement( QStringLiteral( "point-lights" ) );
for ( const QgsPointLightSettings &pointLight : std::as_const( mPointLights ) )
{
QDomElement elemPointLight = pointLight.writeXml( doc );
elemPointLights.appendChild( elemPointLight );
QDomElement elemLights = doc.createElement( QStringLiteral( "lights" ) );
for ( const QgsLightSource *light : mLightSources )
{
const QDomElement elemLight = light->writeXml( doc, context );
elemLights.appendChild( elemLight );
}
elem.appendChild( elemLights );
}
elem.appendChild( elemPointLights );
QDomElement elemDirectionalLights = doc.createElement( QStringLiteral( "directional-lights" ) );
for ( const QgsDirectionalLightSettings &directionalLight : std::as_const( mDirectionalLights ) )
{
QDomElement elemDirectionalLight = directionalLight.writeXml( doc );
elemDirectionalLights.appendChild( elemDirectionalLight );
}
elem.appendChild( elemDirectionalLights );
QDomElement elemMapLayers = doc.createElement( QStringLiteral( "layers" ) );
for ( const QgsMapLayerRef &layerRef : mLayers )
@ -776,20 +790,65 @@ void Qgs3DMapSettings::setEyeDomeLightingDistance( int distance )
void Qgs3DMapSettings::setPointLights( const QList<QgsPointLightSettings> &pointLights )
{
if ( mPointLights == pointLights )
return;
for ( auto it = mLightSources.begin(); it != mLightSources.end(); )
{
if ( ( *it )->type() == Qgis::LightSourceType::Point )
{
delete *it;
it = mLightSources.erase( it );
}
else
{
++it;
}
}
for ( const QgsPointLightSettings &settings : pointLights )
{
mLightSources.append( settings.clone() );
}
mPointLights = pointLights;
emit pointLightsChanged();
emit lightSourcesChanged();
}
void Qgs3DMapSettings::setDirectionalLights( const QList<QgsDirectionalLightSettings> &directionalLights )
{
if ( mDirectionalLights == directionalLights )
return;
for ( auto it = mLightSources.begin(); it != mLightSources.end(); )
{
if ( ( *it )->type() == Qgis::LightSourceType::Directional )
{
delete *it;
it = mLightSources.erase( it );
}
else
{
++it;
}
}
for ( const QgsDirectionalLightSettings &settings : directionalLights )
{
mLightSources.append( settings.clone() );
}
mDirectionalLights = directionalLights;
emit directionalLightsChanged();
emit lightSourcesChanged();
}
QList<QgsLightSource *> Qgs3DMapSettings::lightSources() const
{
return mLightSources;
}
void Qgs3DMapSettings::setLightSources( const QList<QgsLightSource *> &lights )
{
qDeleteAll( mLightSources );
mLightSources = lights;
emit directionalLightsChanged();
emit pointLightsChanged();
emit lightSourcesChanged();
}
void Qgs3DMapSettings::setFieldOfView( const float fieldOfView )
@ -856,6 +915,28 @@ void Qgs3DMapSettings::setDebugDepthMapSettings( bool enabled, Qt::Corner corner
emit debugDepthMapSettingsChanged();
}
QList<QgsPointLightSettings> Qgs3DMapSettings::pointLights() const
{
QList<QgsPointLightSettings> res;
for ( QgsLightSource *source : mLightSources )
{
if ( source->type() == Qgis::LightSourceType::Point )
res << *qgis::down_cast< QgsPointLightSettings * >( source );
}
return res;
}
QList<QgsDirectionalLightSettings> Qgs3DMapSettings::directionalLights() const
{
QList<QgsDirectionalLightSettings> res;
for ( QgsLightSource *source : mLightSources )
{
if ( source->type() == Qgis::LightSourceType::Directional )
res << *qgis::down_cast< QgsDirectionalLightSettings * >( source );
}
return res;
}
void Qgs3DMapSettings::setIsFpsCounterEnabled( bool fpsCounterEnabled )
{
if ( fpsCounterEnabled == mIsFpsCounterEnabled )
@ -920,8 +1001,7 @@ void Qgs3DMapSettings::connectChangedSignalsToSettingsChanged()
connect( this, &Qgs3DMapSettings::eyeDomeLightingDistanceChanged, this, &Qgs3DMapSettings::settingsChanged );
connect( this, &Qgs3DMapSettings::debugShadowMapSettingsChanged, this, &Qgs3DMapSettings::settingsChanged );
connect( this, &Qgs3DMapSettings::debugDepthMapSettingsChanged, this, &Qgs3DMapSettings::settingsChanged );
connect( this, &Qgs3DMapSettings::pointLightsChanged, this, &Qgs3DMapSettings::settingsChanged );
connect( this, &Qgs3DMapSettings::directionalLightsChanged, this, &Qgs3DMapSettings::settingsChanged );
connect( this, &Qgs3DMapSettings::lightSourcesChanged, this, &Qgs3DMapSettings::settingsChanged );
connect( this, &Qgs3DMapSettings::fieldOfViewChanged, this, &Qgs3DMapSettings::settingsChanged );
connect( this, &Qgs3DMapSettings::projectionTypeChanged, this, &Qgs3DMapSettings::settingsChanged );
connect( this, &Qgs3DMapSettings::cameraNavigationModeChanged, this, &Qgs3DMapSettings::settingsChanged );

View File

@ -436,27 +436,44 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec
/**
* Returns list of point lights defined in the scene
* \since QGIS 3.6
* \deprecated use lightSources() instead
*/
QList<QgsPointLightSettings> pointLights() const { return mPointLights; }
Q_DECL_DEPRECATED QList<QgsPointLightSettings> pointLights() const SIP_DEPRECATED;
/**
* Returns list of directional lights defined in the scene
* \since QGIS 3.16
* \deprecated use lightSources() instead
*/
QList<QgsDirectionalLightSettings> directionalLights() const { return mDirectionalLights; }
Q_DECL_DEPRECATED QList<QgsDirectionalLightSettings> directionalLights() const SIP_DEPRECATED;
/**
* Sets list of point lights defined in the scene
* \since QGIS 3.6
* \deprecated use setLightSources() instead
*/
void setPointLights( const QList<QgsPointLightSettings> &pointLights );
Q_DECL_DEPRECATED void setPointLights( const QList<QgsPointLightSettings> &pointLights ) SIP_DEPRECATED;
/**
* Sets list of directional lights defined in the scene
* \since QGIS 3.16
* \deprecated use setLightSources() instead
*/
void setDirectionalLights( const QList<QgsDirectionalLightSettings> &directionalLights );
Q_DECL_DEPRECATED void setDirectionalLights( const QList<QgsDirectionalLightSettings> &directionalLights ) SIP_DEPRECATED;
/**
* Returns list of directional light sources defined in the scene.
* \see setLightSources()
* \since QGIS 3.26
*/
QList<QgsLightSource *> lightSources() const;
/**
* Sets the list of \a light sources defined in the scene.
*
* Ownership of the lights is transferred to the settings.
*
* \see lightSources()
* \since QGIS 3.26
*/
void setLightSources( const QList<QgsLightSource *> &lights SIP_TRANSFER );
/**
* Returns the camera lens' field of view
@ -753,6 +770,12 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec
*/
void pointLightsChanged();
/**
* Emitted when any of the light source settings in the map changes.
* \since QGIS 3.26
*/
void lightSourcesChanged();
/**
* Emitted when the list of directional lights changes
* \since QGIS 3.16
@ -838,8 +861,7 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec
bool mShowCameraRotationCenter = false; //!< Whether to show camera rotation center as a sphere - useful for debugging
bool mShowLightSources = false; //!< Whether to show the origin of light sources
bool mShowLabels = false; //!< Whether to display labels on terrain tiles
QList<QgsPointLightSettings> mPointLights; //!< List of point lights defined for the scene
QList<QgsDirectionalLightSettings> mDirectionalLights; //!< List of directional lights defined for the scene
QList< QgsLightSource * > mLightSources; //!< List of light sources in the scene (owned by the settings)
float mFieldOfView = 45.0f; //<! Camera lens field of view value
Qt3DRender::QCameraLens::ProjectionType mProjectionType = Qt3DRender::QCameraLens::PerspectiveProjection; //<! Camera lens projection type
QgsCameraController::NavigationMode mCameraNavigationMode = QgsCameraController::NavigationMode::TerrainBasedNavigation;

View File

@ -161,7 +161,7 @@ Qgs3DMapConfigWidget::Qgs3DMapConfigWidget( Qgs3DMapSettings *map, QgsMapCanvas
QgsPhongMaterialSettings terrainShadingMaterial = mMap->terrainShadingMaterial();
widgetTerrainMaterial->setSettings( &terrainShadingMaterial, nullptr );
widgetLights->setLights( mMap->pointLights(), mMap->directionalLights() );
widgetLights->setLights( mMap->lightSources() );
connect( cboTerrainType, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &Qgs3DMapConfigWidget::onTerrainTypeChanged );
connect( cboTerrainLayer, static_cast<void ( QComboBox::* )( int )>( &QgsMapLayerComboBox::currentIndexChanged ), this, &Qgs3DMapConfigWidget::onTerrainLayerChanged );
@ -177,11 +177,11 @@ Qgs3DMapConfigWidget::Qgs3DMapConfigWidget( Qgs3DMapSettings *map, QgsMapCanvas
groupSkyboxSettings->layout()->addWidget( mSkyboxSettingsWidget );
groupSkyboxSettings->setChecked( mMap->isSkyboxEnabled() );
mShadowSetiingsWidget = new QgsShadowRenderingSettingsWidget( this );
mShadowSetiingsWidget->onDirectionalLightsCountChanged( widgetLights->directionalLights().count() );
mShadowSetiingsWidget->setShadowSettings( map->shadowSettings() );
groupShadowRendering->layout()->addWidget( mShadowSetiingsWidget );
connect( widgetLights, &QgsLightsWidget::directionalLightsCountChanged, mShadowSetiingsWidget, &QgsShadowRenderingSettingsWidget::onDirectionalLightsCountChanged );
mShadowSettingsWidget = new QgsShadowRenderingSettingsWidget( this );
mShadowSettingsWidget->onDirectionalLightsCountChanged( widgetLights->directionalLightCount() );
mShadowSettingsWidget->setShadowSettings( map->shadowSettings() );
groupShadowRendering->layout()->addWidget( mShadowSettingsWidget );
connect( widgetLights, &QgsLightsWidget::directionalLightsCountChanged, mShadowSettingsWidget, &QgsShadowRenderingSettingsWidget::onDirectionalLightsCountChanged );
connect( widgetLights, &QgsLightsWidget::lightsAdded, this, &Qgs3DMapConfigWidget::validate );
connect( widgetLights, &QgsLightsWidget::lightsRemoved, this, &Qgs3DMapConfigWidget::validate );
@ -351,11 +351,10 @@ void Qgs3DMapConfigWidget::apply()
if ( QgsPhongMaterialSettings *phongMaterial = dynamic_cast< QgsPhongMaterialSettings * >( terrainMaterial.get() ) )
mMap->setTerrainShadingMaterial( *phongMaterial );
mMap->setPointLights( widgetLights->pointLights() );
mMap->setDirectionalLights( widgetLights->directionalLights() );
mMap->setLightSources( widgetLights->lightSources() );
mMap->setIsSkyboxEnabled( groupSkyboxSettings->isChecked() );
mMap->setSkyboxSettings( mSkyboxSettingsWidget->toSkyboxSettings() );
QgsShadowSettings shadowSettings = mShadowSetiingsWidget->toShadowSettings();
QgsShadowSettings shadowSettings = mShadowSettingsWidget->toShadowSettings();
shadowSettings.setRenderShadows( groupShadowRendering->isChecked() );
mMap->setShadowSettings( shadowSettings );
@ -484,7 +483,7 @@ void Qgs3DMapConfigWidget::validate()
break;
}
if ( valid && widgetLights->directionalLights().empty() && widgetLights->pointLights().empty() )
if ( valid && widgetLights->lightSourceCount() == 0 )
{
mMessageBar->pushMessage( tr( "No lights exist in the scene" ), Qgis::MessageLevel::Warning );
}

View File

@ -55,7 +55,7 @@ class Qgs3DMapConfigWidget : public QWidget, private Ui::Map3DConfigWidget
Qgs3DMapCanvas *m3DMapCanvas = nullptr;
QgsMesh3dSymbolWidget *mMeshSymbolWidget = nullptr;
QgsSkyboxRenderingSettingsWidget *mSkyboxSettingsWidget = nullptr;
QgsShadowRenderingSettingsWidget *mShadowSetiingsWidget = nullptr;
QgsShadowRenderingSettingsWidget *mShadowSettingsWidget = nullptr;
};
#endif // QGS3DMAPCONFIGWIDGET_H

View File

@ -80,22 +80,53 @@ QgsLightsWidget::QgsLightsWidget( QWidget *parent )
selectedLightChanged( mLightsListView->selectionModel()->selection(), QItemSelection() );
}
void QgsLightsWidget::setLights( const QList<QgsPointLightSettings> &pointLights, const QList<QgsDirectionalLightSettings> &directionalLights )
void QgsLightsWidget::setLights( const QList<QgsLightSource *> sources )
{
QList< QgsPointLightSettings > pointLights;
QList< QgsDirectionalLightSettings > directionalLights;
for ( const QgsLightSource *source : sources )
{
switch ( source->type() )
{
case Qgis::LightSourceType::Point:
pointLights.append( *qgis::down_cast< const QgsPointLightSettings *>( source ) );
break;
case Qgis::LightSourceType::Directional:
directionalLights.append( *qgis::down_cast< const QgsDirectionalLightSettings *>( source ) );
break;
}
}
mLightsModel->setPointLights( pointLights );
mLightsModel->setDirectionalLights( directionalLights );
mLightsListView->selectionModel()->select( mLightsModel->index( 0, 0 ), QItemSelectionModel::ClearAndSelect );
selectedLightChanged( mLightsListView->selectionModel()->selection(), QItemSelection() );
}
QList<QgsPointLightSettings> QgsLightsWidget::pointLights()
QList<QgsLightSource *> QgsLightsWidget::lightSources()
{
return mLightsModel->pointLights();
QList<QgsLightSource *> res;
const QList<QgsPointLightSettings> pointLights = mLightsModel->pointLights();
const QList<QgsDirectionalLightSettings> directionalLights = mLightsModel->directionalLights();
for ( const QgsPointLightSettings &light : pointLights )
{
res.append( light.clone() );
}
for ( const QgsDirectionalLightSettings &light : directionalLights )
{
res.append( light.clone() );
}
return res;
}
QList<QgsDirectionalLightSettings> QgsLightsWidget::directionalLights()
int QgsLightsWidget::directionalLightCount() const
{
return mLightsModel->directionalLights();
return mLightsModel->directionalLights().count();
}
int QgsLightsWidget::lightSourceCount() const
{
return mLightsModel->rowCount( QModelIndex() );
}
void QgsLightsWidget::selectedLightChanged( const QItemSelection &selected, const QItemSelection & )

View File

@ -74,11 +74,12 @@ class QgsLightsWidget : public QWidget, private Ui::QgsLightsWidget
public:
explicit QgsLightsWidget( QWidget *parent = nullptr );
void setLights( const QList<QgsPointLightSettings> &pointLights,
const QList<QgsDirectionalLightSettings> &directionalLights );
void setLights( const QList<QgsLightSource *> sources );
QList<QgsPointLightSettings> pointLights();
QList<QgsDirectionalLightSettings> directionalLights();
QList<QgsLightSource *> lightSources();
int directionalLightCount() const;
int lightSourceCount() const;
signals:
void directionalLightsCountChanged( int count );

View File

@ -13965,7 +13965,7 @@ void QgisApp::new3DMapCanvas()
map->configureTerrainFromProject( QgsProject::instance()->elevationProperties(), fullExtent );
// new scenes default to a single directional light
map->setDirectionalLights( QList<QgsDirectionalLightSettings>() << QgsDirectionalLightSettings() );
map->setLightSources( QList<QgsLightSource *>() << new QgsDirectionalLightSettings() );
map->setOutputDpi( QgsApplication::desktop()->logicalDpiX() );
map->setRendererUsage( Qgis::RendererUsage::View );

View File

@ -1636,6 +1636,18 @@ class CORE_EXPORT Qgis
Q_DECLARE_FLAGS( PlotToolFlags, PlotToolFlag )
Q_FLAG( PlotToolFlags )
/**
* Light source types for 3D scenes.
*
* \since QGIS 3.26
*/
enum class LightSourceType : int
{
Point, //!< Point light source
Directional, //!< Directional light source
};
Q_ENUM( LightSourceType )
/**
* Identify search radius in mm
* \since QGIS 2.3

View File

@ -320,7 +320,7 @@ void TestQgs3DRendering::testTerrainShading()
QgsPointLightSettings defaultLight;
defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) );
defaultLight.setIntensity( 0.5 );
map->setPointLights( QList<QgsPointLightSettings>() << defaultLight );
map->setLightSources( {defaultLight.clone() } );
QgsOffscreen3DEngine engine;
Qgs3DMapScene *scene = new Qgs3DMapScene( *map, &engine );
@ -383,7 +383,7 @@ void TestQgs3DRendering::testExtrudedPolygons()
QgsPointLightSettings defaultLight;
defaultLight.setIntensity( 0.5 );
defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) );
map->setPointLights( QList<QgsPointLightSettings>() << defaultLight );
map->setLightSources( {defaultLight.clone() } );
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
flatTerrain->setCrs( map->crs() );
@ -435,7 +435,7 @@ void TestQgs3DRendering::testExtrudedPolygonsDataDefined()
QgsPointLightSettings defaultLight;
defaultLight.setIntensity( 0.5 );
defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) );
map->setPointLights( QList<QgsPointLightSettings>() << defaultLight );
map->setLightSources( { defaultLight.clone() } );
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
flatTerrain->setCrs( map->crs() );
@ -488,7 +488,7 @@ void TestQgs3DRendering::testPolygonsEdges()
QgsPointLightSettings defaultLight;
defaultLight.setIntensity( 0.5 );
defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) );
map->setPointLights( QList<QgsPointLightSettings>() << defaultLight );
map->setLightSources( { defaultLight.clone() } );
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
flatTerrain->setCrs( map->crs() );
@ -646,7 +646,7 @@ void TestQgs3DRendering::testBufferedLineRendering()
QgsPointLightSettings defaultLight;
defaultLight.setIntensity( 0.5 );
defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) );
map->setPointLights( QList<QgsPointLightSettings>() << defaultLight );
map->setLightSources( { defaultLight.clone() } );
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
flatTerrain->setCrs( map->crs() );
@ -693,7 +693,7 @@ void TestQgs3DRendering::testBufferedLineRenderingWidth()
QgsPointLightSettings defaultLight;
defaultLight.setIntensity( 0.5 );
defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) );
map->setPointLights( QList<QgsPointLightSettings>() << defaultLight );
map->setLightSources( { defaultLight.clone() } );
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
flatTerrain->setCrs( map->crs() );
@ -762,7 +762,7 @@ void TestQgs3DRendering::testMesh()
QgsPointLightSettings defaultLight;
defaultLight.setIntensity( 0.5 );
defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) );
map->setPointLights( QList<QgsPointLightSettings>() << defaultLight );
map->setLightSources( { defaultLight.clone() } );
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
flatTerrain->setCrs( map->crs() );
@ -803,7 +803,7 @@ void TestQgs3DRendering::testMesh_datasetOnFaces()
QgsPointLightSettings defaultLight;
defaultLight.setIntensity( 0.5 );
defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) );
map->setPointLights( QList<QgsPointLightSettings>() << defaultLight );
map->setLightSources( { defaultLight.clone() } );
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
flatTerrain->setCrs( map->crs() );
@ -924,7 +924,7 @@ void TestQgs3DRendering::testRuleBasedRenderer()
QgsPointLightSettings defaultLight;
defaultLight.setIntensity( 0.5 );
defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) );
map->setPointLights( QList<QgsPointLightSettings>() << defaultLight );
map->setLightSources( { defaultLight.clone() } );
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
flatTerrain->setCrs( map->crs() );