GUI for configuration of 3D polygon symbols for vector layers

In the style dock there is a new "3D View" tab - so far working just for polygon layers.
It is possible to select a polygon layer, enable 3D renderer and adjust its properties.
If a 3D Map View is open, it will be immediately updated (if auto-apply is enabled)

Very exciting! :-)
This commit is contained in:
Martin Dobias 2017-07-26 17:01:12 +02:00
parent b9dd6bc622
commit ba7573a94e
37 changed files with 850 additions and 60 deletions

View File

@ -284,6 +284,7 @@ IF(WITH_CORE)
FIND_PACKAGE(Qt53DInput REQUIRED)
FIND_PACKAGE(Qt53DLogic REQUIRED)
FIND_PACKAGE(Qt53DExtras REQUIRED)
SET(HAVE_3D TRUE) # used in qgsconfig.h
ENDIF (WITH_3D)
INCLUDE("cmake/modules/ECMQt4To5Porting.cmake")
MESSAGE(STATUS "Found Qt version: ${Qt5Core_VERSION_STRING}")

View File

@ -58,5 +58,7 @@
#cmakedefine ENABLE_MODELTEST
#cmakedefine HAVE_3D
#endif

View File

@ -570,6 +570,7 @@
<file>themes/default/mGeoPackage.svg</file>
<file>themes/default/mActionAddGeoPackageLayer.svg</file>
<file>icons/qgis_icon.svg</file>
<file>themes/default/3d.svg</file>
</qresource>
<qresource prefix="/images/tips">
<file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg12"
version="1.1"
width="16"
height="16">
<metadata
id="metadata18">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs16" />
<path
d="M 2.3348556,4.5414044 8.0212426,8.3776059 13.466994,4.7037083 7.7806076,0.86750671 Z"
style="fill:#f79191;fill-opacity:1;fill-rule:evenodd;stroke:#a40000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4535" />
<path
d="M 8.6635732,9.3611399 V 14.964189 L 14.109324,11.290292 V 5.6872423 Z"
style="fill:#fce94f;fill-opacity:1;fill-rule:evenodd;stroke:#c4a000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4537" />
<path
d="m 1.6730308,5.5249384 5.686387,3.8362015 v 5.6030491 l -5.686387,-3.836201 z"
style="fill:#8ae234;fill-opacity:1;fill-rule:evenodd;stroke:#4e9a06;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4539" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -704,6 +704,7 @@ Returns path to the build output directory. Valid only when running from build d
:rtype: QgsFieldFormatterRegistry
%End
static QString nullRepresentation();
%Docstring
This string is used to represent the value `NULL` throughout QGIS.

View File

@ -754,6 +754,8 @@ Return pointer to layer's undo stack
:rtype: QgsMapLayerStyleManager
%End
bool isInScaleRange( double scale ) const;
%Docstring
Tests whether the layer should be visible at the specified ``scale``.
@ -1014,6 +1016,12 @@ Signal emitted when the blend mode is changed, through QgsMapLayer.setBlendMode(
%Docstring
Signal emitted when legend of the layer has changed
.. versionadded:: 2.6
%End
void renderer3DChanged();
%Docstring
Signal emitted when 3D renderer associated with the layer has changed.
.. versionadded:: 3.0
%End
void configChanged();

View File

@ -2,7 +2,6 @@
# sources
SET(QGIS_3D_SRCS
abstract3drenderer.cpp
abstract3dsymbol.cpp
cameracontroller.cpp
lineentity.cpp
@ -15,6 +14,7 @@ SET(QGIS_3D_SRCS
tessellator.cpp
tilingscheme.cpp
utils.cpp
vectorlayer3drenderer.cpp
chunks/chunkboundsentity.cpp
chunks/chunkedentity.cpp
@ -63,7 +63,6 @@ QT5_ADD_RESOURCES(QGIS_3D_RCC_SRCS shaders.qrc)
SET(QGIS_3D_HDRS
aabb.h
abstract3drenderer.h
abstract3dsymbol.h
cameracontroller.h
lineentity.h
@ -76,6 +75,7 @@ SET(QGIS_3D_HDRS
tessellator.h
tilingscheme.h
utils.h
vectorlayer3drenderer.h
chunks/chunkboundsentity.h
chunks/chunkedentity.h
@ -105,6 +105,7 @@ INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/src/core/symbology-ng
${CMAKE_SOURCE_DIR}/src/core/metadata
${CMAKE_SOURCE_DIR}/src/core/expression
${CMAKE_SOURCE_DIR}/src/core/3d
${CMAKE_BINARY_DIR}/src/core
${CMAKE_BINARY_DIR}/src/3d
)

View File

@ -1,9 +1,9 @@
#include "map3d.h"
#include "abstract3drenderer.h"
#include "flatterraingenerator.h"
#include "demterraingenerator.h"
//#include "quantizedmeshterraingenerator.h"
#include "vectorlayer3drenderer.h"
#include <QDomDocument>
#include <QDomElement>
@ -44,7 +44,7 @@ Map3D::Map3D( const Map3D &other )
, mShowTerrainTileInfo( other.mShowTerrainTileInfo )
, mLayers( other.mLayers )
{
Q_FOREACH ( Abstract3DRenderer *renderer, other.renderers )
Q_FOREACH ( QgsAbstract3DRenderer *renderer, other.renderers )
{
renderers << renderer->clone();
}
@ -108,7 +108,7 @@ void Map3D::readXml( const QDomElement &elem, const QgsReadWriteContext &context
QDomElement elemRenderer = elemRenderers.firstChildElement( "renderer" );
while ( !elemRenderer.isNull() )
{
Abstract3DRenderer *renderer = nullptr;
QgsAbstract3DRenderer *renderer = nullptr;
QString type = elemRenderer.attribute( "type" );
if ( type == "vector" )
{
@ -167,7 +167,7 @@ QDomElement Map3D::writeXml( QDomDocument &doc, const QgsReadWriteContext &conte
elem.appendChild( elemTerrain );
QDomElement elemRenderers = doc.createElement( "renderers" );
Q_FOREACH ( const Abstract3DRenderer *renderer, renderers )
Q_FOREACH ( const QgsAbstract3DRenderer *renderer, renderers )
{
QDomElement elemRenderer = doc.createElement( "renderer" );
elemRenderer.setAttribute( "type", renderer->type() );
@ -203,7 +203,7 @@ void Map3D::resolveReferences( const QgsProject &project )
for ( int i = 0; i < renderers.count(); ++i )
{
Abstract3DRenderer *renderer = renderers[i];
QgsAbstract3DRenderer *renderer = renderers[i];
renderer->resolveReferences( project );
}
}

View File

@ -13,7 +13,7 @@
class QgsMapLayer;
class QgsRasterLayer;
class Abstract3DRenderer;
class QgsAbstract3DRenderer;
class TerrainGenerator;
@ -61,7 +61,7 @@ class _3D_EXPORT Map3D : public QObject
// 3D renderers
//
QList<Abstract3DRenderer *> renderers; //!< Stuff to render as 3D object
QList<QgsAbstract3DRenderer *> renderers; //!< Stuff to render as 3D object
bool skybox; //!< Whether to render skybox
QString skyboxFileBase;

View File

@ -12,10 +12,10 @@ class _3D_EXPORT PhongMaterialSettings
{
public:
PhongMaterialSettings()
: mAmbient( QColor::fromRgbF( 0.05f, 0.05f, 0.05f, 1.0f ) )
: mAmbient( QColor::fromRgbF( 0.1f, 0.1f, 0.1f, 1.0f ) )
, mDiffuse( QColor::fromRgbF( 0.7f, 0.7f, 0.7f, 1.0f ) )
, mSpecular( QColor::fromRgbF( 0.01f, 0.01f, 0.01f, 1.0f ) )
, mShininess( 150.0f )
, mSpecular( QColor::fromRgbF( 1.0f, 1.0f, 1.0f, 1.0f ) )
, mShininess( 0.0f )
{
}

View File

@ -8,7 +8,7 @@
#include <Qt3DLogic/QFrameAction>
#include "aabb.h"
#include "abstract3drenderer.h"
#include "qgsabstract3drenderer.h"
#include "cameracontroller.h"
#include "map3d.h"
#include "terrain.h"
@ -59,12 +59,26 @@ Scene::Scene( const Map3D &map, Qt3DExtras::QForwardRenderer *defaultFrameGraph,
// create entities of renderers
Q_FOREACH ( const Abstract3DRenderer *renderer, map.renderers )
Q_FOREACH ( const QgsAbstract3DRenderer *renderer, map.renderers )
{
Qt3DCore::QEntity *p = renderer->createEntity( map );
p->setParent( this );
}
// create entities of renderers of layers
Q_FOREACH ( QgsMapLayer *layer, map.layers() )
{
if ( layer->renderer3D() )
{
Qt3DCore::QEntity *p = layer->renderer3D()->createEntity( map );
p->setParent( this );
mLayerEntities.insert( layer, p );
}
connect( layer, &QgsMapLayer::renderer3DChanged, this, &Scene::onLayerRenderer3DChanged );
// TODO: connect( layer, &QgsMapLayer::willBeDeleted, this, &Scene::onLayerWillBeDeleted );
}
Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity;
Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform;
lightTransform->setTranslation( QVector3D( 0, 1000, 0 ) );
@ -83,7 +97,7 @@ Scene::Scene( const Map3D &map, Qt3DExtras::QForwardRenderer *defaultFrameGraph,
ChunkedEntity *testChunkEntity = new ChunkedEntity( AABB( -500, 0, -500, 500, 100, 500 ), 2.f, 3.f, 7, new TestChunkLoaderFactory );
testChunkEntity->setEnabled( false );
testChunkEntity->setParent( this );
chunkEntities << testChunkEntity
chunkEntities << testChunkEntity;
#endif
connect( mCameraController, &CameraController::cameraChanged, this, &Scene::onCameraChanged );
@ -190,3 +204,23 @@ void Scene::createTerrain()
onCameraChanged(); // force update of the new terrain
}
void Scene::onLayerRenderer3DChanged()
{
QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
Q_ASSERT( layer );
// remove old entity - if any
Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
if ( entity )
entity->deleteLater();
// add new entity - if any 3D renderer
QgsAbstract3DRenderer *renderer = layer->renderer3D();
if ( renderer )
{
Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
newEntity->setParent( this );
mLayerEntities.insert( layer, newEntity );
}
}

View File

@ -21,6 +21,7 @@ namespace Qt3DExtras
class QForwardRenderer;
}
class QgsMapLayer;
class CameraController;
class Map3D;
class Terrain;
@ -42,6 +43,7 @@ class _3D_EXPORT Scene : public Qt3DCore::QEntity
void onCameraChanged();
void onFrameTriggered( float dt );
void createTerrain();
void onLayerRenderer3DChanged();
private:
const Map3D &mMap;
@ -50,6 +52,8 @@ class _3D_EXPORT Scene : public Qt3DCore::QEntity
CameraController *mCameraController;
Terrain *mTerrain;
QList<ChunkedEntity *> chunkEntities;
//! Keeps track of entities that belong to a particular layer
QMap<QgsMapLayer *, Qt3DCore::QEntity *> mLayerEntities;
};
#endif // SCENE_H

View File

@ -5,10 +5,10 @@
#include <Qt3DRender>
#include <Qt3DExtras>
#include "abstract3drenderer.h"
#include "abstract3dsymbol.h"
#include "maptexturegenerator.h"
#include "sidepanel.h"
#include "vectorlayer3drenderer.h"
#include "window3d.h"
#include "map3d.h"
#include "flatterraingenerator.h"
@ -239,6 +239,7 @@ int main( int argc, char *argv[] )
hLayout->addWidget( container, 1 );
hLayout->addWidget( sidePanel );
widget.setWindowTitle( "QGIS 3D" );
widget.resize( 800, 600 );
widget.show();

View File

@ -1,4 +1,4 @@
#include "abstract3drenderer.h"
#include "vectorlayer3drenderer.h"
#include "abstract3dsymbol.h"
#include "lineentity.h"
@ -18,7 +18,7 @@ VectorLayer3DRenderer::~VectorLayer3DRenderer()
{
}
Abstract3DRenderer *VectorLayer3DRenderer::clone() const
VectorLayer3DRenderer *VectorLayer3DRenderer::clone() const
{
VectorLayer3DRenderer *r = new VectorLayer3DRenderer( mSymbol ? mSymbol->clone() : nullptr );
r->layerRef = layerRef;

View File

@ -1,8 +1,10 @@
#ifndef ABSTRACT3DRENDERER_H
#define ABSTRACT3DRENDERER_H
#ifndef VECTORLAYER3DRENDERER_H
#define VECTORLAYER3DRENDERER_H
#include "qgis_3d.h"
#include "qgsabstract3drenderer.h"
#include "phongmaterialsettings.h"
#include "utils.h"
@ -13,34 +15,11 @@
class QgsVectorLayer;
class Abstract3DSymbol;
class Map3D;
namespace Qt3DCore
{
class QEntity;
}
class _3D_EXPORT Abstract3DRenderer //: public QObject
{
//Q_OBJECT
public:
virtual ~Abstract3DRenderer() {}
virtual QString type() const = 0;
virtual Abstract3DRenderer *clone() const = 0;
virtual Qt3DCore::QEntity *createEntity( const Map3D &map ) const = 0;
virtual void writeXml( QDomElement &elem ) const = 0;
virtual void readXml( const QDomElement &elem ) = 0;
virtual void resolveReferences( const QgsProject &project ) { Q_UNUSED( project ); }
};
/** 3D renderer that renders all features of a vector layer with the same 3D symbol.
* The appearance if completely defined by the symbol.
* The appearance is completely defined by the symbol.
*/
class _3D_EXPORT VectorLayer3DRenderer : public Abstract3DRenderer
class _3D_EXPORT VectorLayer3DRenderer : public QgsAbstract3DRenderer
{
public:
//! Takes ownership of the symbol object
@ -55,7 +34,7 @@ class _3D_EXPORT VectorLayer3DRenderer : public Abstract3DRenderer
const Abstract3DSymbol *symbol() const;
QString type() const override { return "vector"; }
Abstract3DRenderer *clone() const override;
VectorLayer3DRenderer *clone() const override;
Qt3DCore::QEntity *createEntity( const Map3D &map ) const override;
void writeXml( QDomElement &elem ) const override;
@ -68,4 +47,4 @@ class _3D_EXPORT VectorLayer3DRenderer : public Abstract3DRenderer
};
#endif // ABSTRACT3DRENDERER_H
#endif // VECTORLAYER3DRENDERER_H

View File

@ -0,0 +1,35 @@
#include "qgsphongmaterialwidget.h"
#include "phongmaterialsettings.h"
QgsPhongMaterialWidget::QgsPhongMaterialWidget( QWidget *parent )
: QWidget( parent )
{
setupUi( this );
setMaterial( PhongMaterialSettings() );
connect( btnDiffuse, &QgsColorButton::colorChanged, this, &QgsPhongMaterialWidget::changed );
connect( btnAmbient, &QgsColorButton::colorChanged, this, &QgsPhongMaterialWidget::changed );
connect( btnSpecular, &QgsColorButton::colorChanged, this, &QgsPhongMaterialWidget::changed );
connect( spinShininess, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsPhongMaterialWidget::changed );
}
void QgsPhongMaterialWidget::setMaterial( const PhongMaterialSettings &material )
{
btnDiffuse->setColor( material.diffuse() );
btnAmbient->setColor( material.ambient() );
btnSpecular->setColor( material.specular() );
spinShininess->setValue( material.shininess() );
}
PhongMaterialSettings QgsPhongMaterialWidget::material() const
{
PhongMaterialSettings m;
m.setDiffuse( btnDiffuse->color() );
m.setAmbient( btnAmbient->color() );
m.setSpecular( btnSpecular->color() );
m.setShininess( spinShininess->value() );
return m;
}

View File

@ -0,0 +1,27 @@
#ifndef QGSPHONGMATERIALWIDGET_H
#define QGSPHONGMATERIALWIDGET_H
#include <QWidget>
#include <ui_phongmaterialwidget.h>
class PhongMaterialSettings;
//! Widget for configuration of Phong material settings
class QgsPhongMaterialWidget : public QWidget, private Ui::PhongMaterialWidget
{
Q_OBJECT
public:
explicit QgsPhongMaterialWidget( QWidget *parent = nullptr );
void setMaterial( const PhongMaterialSettings &material );
PhongMaterialSettings material() const;
signals:
void changed();
public slots:
};
#endif // QGSPHONGMATERIALWIDGET_H

View File

@ -0,0 +1,38 @@
#include "qgspolygon3dsymbolwidget.h"
#include "abstract3dsymbol.h"
QgsPolygon3DSymbolWidget::QgsPolygon3DSymbolWidget( QWidget *parent )
: QWidget( parent )
{
setupUi( this );
setSymbol( Polygon3DSymbol() );
connect( spinHeight, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsPolygon3DSymbolWidget::changed );
connect( spinExtrusion, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsPolygon3DSymbolWidget::changed );
connect( cboAltClamping, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPolygon3DSymbolWidget::changed );
connect( cboAltBinding, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPolygon3DSymbolWidget::changed );
connect( widgetMaterial, &QgsPhongMaterialWidget::changed, this, &QgsPolygon3DSymbolWidget::changed );
}
void QgsPolygon3DSymbolWidget::setSymbol( const Polygon3DSymbol &symbol )
{
spinHeight->setValue( symbol.height );
spinExtrusion->setValue( symbol.extrusionHeight );
cboAltClamping->setCurrentIndex( ( int ) symbol.altClamping );
cboAltBinding->setCurrentIndex( ( int ) symbol.altBinding );
widgetMaterial->setMaterial( symbol.material );
}
Polygon3DSymbol QgsPolygon3DSymbolWidget::symbol() const
{
Polygon3DSymbol sym;
sym.height = spinHeight->value();
sym.extrusionHeight = spinExtrusion->value();
sym.altClamping = ( AltitudeClamping ) cboAltClamping->currentIndex();
sym.altBinding = ( AltitudeBinding ) cboAltBinding->currentIndex();
sym.material = widgetMaterial->material();
return sym;
}

View File

@ -0,0 +1,26 @@
#ifndef QGSPOLYGON3DSYMBOLWIDGET_H
#define QGSPOLYGON3DSYMBOLWIDGET_H
#include <QWidget>
#include "ui_polygon3dsymbolwidget.h"
class Polygon3DSymbol;
//! A widget for configuration of 3D symbol for polygons
class QgsPolygon3DSymbolWidget : public QWidget, private Ui::Polygon3DSymbolWidget
{
Q_OBJECT
public:
explicit QgsPolygon3DSymbolWidget( QWidget *parent = nullptr );
void setSymbol( const Polygon3DSymbol &symbol );
Polygon3DSymbol symbol() const;
signals:
void changed();
public slots:
};
#endif // QGSPOLYGON3DSYMBOLWIDGET_H

View File

@ -0,0 +1,88 @@
#include "qgsvectorlayer3drendererwidget.h"
#include "abstract3dsymbol.h"
#include "qgspolygon3dsymbolwidget.h"
#include "vectorlayer3drenderer.h"
#include "qgsvectorlayer.h"
#include <QBoxLayout>
#include <QCheckBox>
QgsVectorLayer3DRendererWidget::QgsVectorLayer3DRendererWidget( QgsVectorLayer *layer, QgsMapCanvas *canvas, QWidget *parent )
: QgsMapLayerConfigWidget( layer, canvas, parent )
{
setPanelTitle( tr( "3D View" ) );
QVBoxLayout *layout = new QVBoxLayout( this );
chkEnabled = new QCheckBox( "Enable 3D renderer", this );
widgetPolygon = new QgsPolygon3DSymbolWidget( this );
layout->addWidget( chkEnabled );
layout->addWidget( widgetPolygon );
widgetPolygon->setEnabled( false );
connect( chkEnabled, &QCheckBox::clicked, this, &QgsVectorLayer3DRendererWidget::onEnabledClicked );
connect( widgetPolygon, &QgsPolygon3DSymbolWidget::changed, this, &QgsVectorLayer3DRendererWidget::widgetChanged );
}
QgsVectorLayer3DRendererWidget::~QgsVectorLayer3DRendererWidget()
{
}
void QgsVectorLayer3DRendererWidget::setLayer( QgsVectorLayer *layer )
{
mLayer = layer;
QgsAbstract3DRenderer *r = layer->renderer3D();
if ( r && r->type() == "vector" )
{
VectorLayer3DRenderer *vectorRenderer = static_cast<VectorLayer3DRenderer *>( r );
setRenderer( vectorRenderer );
}
else
{
setRenderer( nullptr );
}
}
void QgsVectorLayer3DRendererWidget::setRenderer( const VectorLayer3DRenderer *renderer )
{
mRenderer.reset( renderer ? renderer->clone() : nullptr );
whileBlocking( chkEnabled )->setChecked( ( bool )mRenderer );
widgetPolygon->setEnabled( chkEnabled->isChecked() );
if ( mRenderer && mRenderer->symbol() && mRenderer->symbol()->type() == "polygon" )
{
whileBlocking( widgetPolygon )->setSymbol( *static_cast<const Polygon3DSymbol *>( mRenderer->symbol() ) );
}
}
VectorLayer3DRenderer *QgsVectorLayer3DRendererWidget::renderer()
{
if ( chkEnabled->isChecked() )
{
VectorLayer3DRenderer *r = new VectorLayer3DRenderer( new Polygon3DSymbol( widgetPolygon->symbol() ) );
r->setLayer( qobject_cast<QgsVectorLayer *>( mLayer ) );
mRenderer.reset( r );
}
else
{
mRenderer.reset();
}
return mRenderer.get();
}
void QgsVectorLayer3DRendererWidget::apply()
{
VectorLayer3DRenderer *r = renderer();
mLayer->setRenderer3D( r ? r->clone() : nullptr );
}
void QgsVectorLayer3DRendererWidget::onEnabledClicked()
{
widgetPolygon->setEnabled( chkEnabled->isChecked() );
emit widgetChanged();
}

View File

@ -0,0 +1,43 @@
#ifndef QGSVECTORLAYER3DRENDERERWIDGET_H
#define QGSVECTORLAYER3DRENDERERWIDGET_H
#include <memory>
#include "qgsmaplayerconfigwidget.h"
class QCheckBox;
class QgsPolygon3DSymbolWidget;
class QgsVectorLayer;
class QgsMapCanvas;
class VectorLayer3DRenderer;
//! Widget for configuration of 3D renderer of a vector layer
class QgsVectorLayer3DRendererWidget : public QgsMapLayerConfigWidget
{
Q_OBJECT
public:
explicit QgsVectorLayer3DRendererWidget( QgsVectorLayer *layer, QgsMapCanvas *canvas, QWidget *parent = nullptr );
~QgsVectorLayer3DRendererWidget();
void setLayer( QgsVectorLayer *layer );
//! no transfer of ownership
void setRenderer( const VectorLayer3DRenderer *renderer );
//! no transfer of ownership
VectorLayer3DRenderer *renderer();
public slots:
virtual void apply() override;
private slots:
void onEnabledClicked();
private:
QCheckBox *chkEnabled;
QgsPolygon3DSymbolWidget *widgetPolygon;
std::unique_ptr<VectorLayer3DRenderer> mRenderer;
};
#endif // QGSVECTORLAYER3DRENDERERWIDGET_H

View File

@ -370,6 +370,9 @@ IF (WITH_3D)
3d/qgs3dmapcanvas.cpp
3d/qgs3dmapcanvasdockwidget.cpp
3d/qgs3dmapconfigwidget.cpp
3d/qgspolygon3dsymbolwidget.cpp
3d/qgsphongmaterialwidget.cpp
3d/qgsvectorlayer3drendererwidget.cpp
)
SET (QGIS_APP_MOC_HDRS
@ -377,6 +380,9 @@ IF (WITH_3D)
3d/qgs3dmapcanvas.h
3d/qgs3dmapcanvasdockwidget.h
3d/qgs3dmapconfigwidget.h
3d/qgspolygon3dsymbolwidget.h
3d/qgsphongmaterialwidget.h
3d/qgsvectorlayer3drendererwidget.h
)
ENDIF (WITH_3D)
@ -561,6 +567,7 @@ INCLUDE_DIRECTORIES(
IF (WITH_3D)
INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/src/app/3d
${CMAKE_SOURCE_DIR}/src/core/3d
${CMAKE_SOURCE_DIR}/src/3d
${CMAKE_SOURCE_DIR}/src/3d/terrain
${CMAKE_SOURCE_DIR}/src/3d/chunks

View File

@ -9879,6 +9879,7 @@ void QgisApp::newMapCanvas()
}
}
#include "qgsabstract3drenderer.h"
#include "qgs3dmapcanvasdockwidget.h"
#include "map3d.h"
#include "flatterraingenerator.h"
@ -9890,8 +9891,6 @@ void QgisApp::new3DMapCanvas()
QgsRectangle fullExtent = mMapCanvas->fullExtent();
Map3D *map = new Map3D;
map->setShowTerrainBoundingBoxes( true );
map->setShowTerrainTilesInfo( true );
map->crs = prj->crs();
map->originX = fullExtent.center().x();
map->originY = fullExtent.center().y();
@ -9910,7 +9909,7 @@ void QgisApp::new3DMapCanvas()
map3DWidget->setGeometry( QRect( rect().width() * 0.75, rect().height() * 0.5, 400, 400 ) );
map3DWidget->setMap( map );
map3DWidget->setMainCanvas( mMapCanvas );
addDockWidget( Qt::RightDockWidgetArea, map3DWidget );
addDockWidget( Qt::BottomDockWidgetArea, map3DWidget );
}
void QgisApp::setExtent( const QgsRectangle &rect )

View File

@ -45,6 +45,10 @@
#include "qgsruntimeprofiler.h"
#include "qgsrasterminmaxwidget.h"
#ifdef HAVE_3D
#include "qgsvectorlayer3drendererwidget.h"
#endif
QgsLayerStylingWidget::QgsLayerStylingWidget( QgsMapCanvas *canvas, const QList<QgsMapLayerConfigWidgetFactory *> &pages, QWidget *parent )
: QWidget( parent )
@ -158,6 +162,13 @@ void QgsLayerStylingWidget::setLayer( QgsMapLayer *layer )
labelItem->setData( Qt::UserRole, VectorLabeling );
labelItem->setToolTip( tr( "Labels" ) );
mOptionsListWidget->addItem( labelItem );
#ifdef HAVE_3D
QListWidgetItem *symbol3DItem = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "3d.svg" ) ), QString() );
symbol3DItem->setData( Qt::UserRole, Symbology3D );
symbol3DItem->setToolTip( tr( "3D View" ) );
mOptionsListWidget->addItem( symbol3DItem );
#endif
}
else if ( layer->type() == QgsMapLayer::RasterLayer )
{
@ -318,6 +329,12 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer()
{
mRasterStyleWidget = widget;
}
#ifdef HAVE_3D
else if ( QgsVectorLayer3DRendererWidget *widget = qobject_cast<QgsVectorLayer3DRendererWidget *>( current ) )
{
mVector3DWidget = widget;
}
#endif
}
@ -369,6 +386,20 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer()
mWidgetStack->setMainPanel( mLabelingWidget );
break;
}
#ifdef HAVE_3D
case 2: // 3D View
{
if ( !mVector3DWidget )
{
mVector3DWidget = new QgsVectorLayer3DRendererWidget( nullptr, mMapCanvas, mWidgetStack );
mVector3DWidget->setDockMode( true );
connect( mVector3DWidget, &QgsVectorLayer3DRendererWidget::widgetChanged, this, &QgsLayerStylingWidget::autoApply );
}
mVector3DWidget->setLayer( vlayer );
mWidgetStack->setMainPanel( mVector3DWidget );
break;
}
#endif
default:
break;
}

View File

@ -39,6 +39,7 @@ class QgsRendererRasterPropertiesWidget;
class QgsUndoWidget;
class QgsRasterHistogramWidget;
class QgsMapLayerStyleManagerWidget;
class QgsVectorLayer3DRendererWidget;
class APP_EXPORT QgsLayerStyleManagerWidgetFactory : public QgsMapLayerConfigWidgetFactory
{
@ -84,6 +85,7 @@ class APP_EXPORT QgsLayerStylingWidget : public QWidget, private Ui::QgsLayerSty
RasterTransparency,
RasterHistogram,
History,
Symbology3D,
};
QgsLayerStylingWidget( QgsMapCanvas *canvas, const QList<QgsMapLayerConfigWidgetFactory *> &pages, QWidget *parent = 0 );
@ -131,6 +133,9 @@ class APP_EXPORT QgsLayerStylingWidget : public QWidget, private Ui::QgsLayerSty
QgsUndoWidget *mUndoWidget = nullptr;
QgsMapLayer *mCurrentLayer = nullptr;
QgsLabelingWidget *mLabelingWidget = nullptr;
#ifdef HAVE_3D
QgsVectorLayer3DRendererWidget *mVector3DWidget = nullptr;
#endif
QgsRendererRasterPropertiesWidget *mRasterStyleWidget = nullptr;
QList<QgsMapLayerConfigWidgetFactory *> mPageFactories;
QMap<int, QgsMapLayerConfigWidgetFactory *> mUserPages;

View File

@ -0,0 +1,45 @@
#include "qgs3drendererregistry.h"
Qgs3DRendererAbstractMetadata::Qgs3DRendererAbstractMetadata( const QString &name )
: mName( name )
{
}
QString Qgs3DRendererAbstractMetadata::name() const
{
return mName;
}
// ----------
Qgs3DRendererRegistry::Qgs3DRendererRegistry()
{
}
Qgs3DRendererRegistry::~Qgs3DRendererRegistry()
{
qDeleteAll( mRenderers );
}
void Qgs3DRendererRegistry::addRenderer( Qgs3DRendererAbstractMetadata *metadata )
{
mRenderers.insert( metadata->name(), metadata );
}
void Qgs3DRendererRegistry::removeRenderer( const QString &name )
{
delete mRenderers.take( name );
}
Qgs3DRendererAbstractMetadata *Qgs3DRendererRegistry::rendererMetadata( const QString &name ) const
{
return mRenderers.value( name );
}
QStringList Qgs3DRendererRegistry::renderersList() const
{
return mRenderers.keys();
}

View File

@ -0,0 +1,61 @@
#ifndef QGS3DRENDERERREGISTRY_H
#define QGS3DRENDERERREGISTRY_H
#include "qgis_core.h"
#include <QMap>
class QDomElement;
class QgsAbstract3DRenderer;
class QgsReadWriteContext;
/**
* Base metadata class for 3D renderers. Instances of derived classes may be registered in Qgs3DRendererRegistry.
* \since QGIS 3.0
* \ingroup core
*/
class CORE_EXPORT Qgs3DRendererAbstractMetadata
{
public:
Qgs3DRendererAbstractMetadata( const QString &name );
QString name() const;
/** Return new instance of the renderer given the DOM element. Returns NULL on error.
* Pure virtual function: must be implemented in derived classes. */
virtual QgsAbstract3DRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) = 0;
protected:
//! name used within QGIS for identification (the same what renderer's type() returns)
QString mName;
};
/**
* Keeps track of available 3D renderers. Should be accessed through QgsApplication::renderer3DRegistry() singleton.
* \since QGIS 3.0
* \ingroup core
*/
class CORE_EXPORT Qgs3DRendererRegistry
{
public:
Qgs3DRendererRegistry();
~Qgs3DRendererRegistry();
//! takes ownership
void addRenderer( Qgs3DRendererAbstractMetadata *metadata );
void removeRenderer( const QString &name );
Qgs3DRendererAbstractMetadata *rendererMetadata( const QString &name ) const;
QStringList renderersList() const;
private:
QMap<QString, Qgs3DRendererAbstractMetadata *> mRenderers;
};
#endif // QGS3DRENDERERREGISTRY_H

View File

@ -0,0 +1,11 @@
#include "qgsabstract3drenderer.h"
QgsAbstract3DRenderer::~QgsAbstract3DRenderer()
{
}
void QgsAbstract3DRenderer::resolveReferences( const QgsProject &project )
{
Q_UNUSED( project );
}

View File

@ -0,0 +1,35 @@
#ifndef QGSABSTRACT3DRENDERER_H
#define QGSABSTRACT3DRENDERER_H
#include "qgis_core.h"
#include <QString>
class QDomElement;
class QgsProject;
class Map3D;
namespace Qt3DCore
{
class QEntity;
}
//! Base class for all renderers that may to participate in 3D view.
class CORE_EXPORT QgsAbstract3DRenderer //: public QObject
{
//Q_OBJECT
public:
virtual ~QgsAbstract3DRenderer();
virtual QString type() const = 0;
virtual QgsAbstract3DRenderer *clone() const = 0;
virtual Qt3DCore::QEntity *createEntity( const Map3D &map ) const = 0;
virtual void writeXml( QDomElement &elem ) const = 0;
virtual void readXml( const QDomElement &elem ) = 0;
virtual void resolveReferences( const QgsProject &project );
};
#endif // QGSABSTRACT3DRENDERER_H

View File

@ -463,6 +463,9 @@ SET(QGIS_CORE_SRCS
geometry/qgswkbptr.cpp
geometry/qgswkbtypes.cpp
3d/qgs3drendererregistry.cpp
3d/qgsabstract3drenderer.cpp
fieldformatter/qgsdatetimefieldformatter.cpp
fieldformatter/qgsfallbackfieldformatter.cpp
fieldformatter/qgskeyvaluefieldformatter.cpp
@ -1093,6 +1096,9 @@ SET(QGIS_CORE_HDRS
geometry/qgswkbptr.h
geometry/qgswkbtypes.h
3d/qgs3drendererregistry.h
3d/qgsabstract3drenderer.h
fieldformatter/qgsdatetimefieldformatter.h
fieldformatter/qgsfallbackfieldformatter.h
fieldformatter/qgskeyvaluefieldformatter.h
@ -1118,6 +1124,7 @@ INCLUDE_DIRECTORIES(
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${LIBZIP_INCLUDE_DIR}
3d
annotations
auth
composer

View File

@ -42,6 +42,7 @@
#include "qgsuserprofile.h"
#include "qgsuserprofilemanager.h"
#include "qgsreferencedgeometry.h"
#include "qgs3drendererregistry.h"
#include "gps/qgsgpsconnectionregistry.h"
#include "processing/qgsprocessingregistry.h"
@ -1574,6 +1575,11 @@ QgsFieldFormatterRegistry *QgsApplication::fieldFormatterRegistry()
return members()->mFieldFormatterRegistry;
}
Qgs3DRendererRegistry *QgsApplication::renderer3DRegistry()
{
return members()->m3DRendererRegistry;
}
QgsApplication::ApplicationMembers::ApplicationMembers()
{
// don't use initializer lists or scoped pointers - as more objects are added here we
@ -1598,11 +1604,13 @@ QgsApplication::ApplicationMembers::ApplicationMembers()
mLayoutItemRegistry->populate();
mProcessingRegistry->addProvider( new QgsNativeAlgorithms( mProcessingRegistry ) );
mAnnotationRegistry = new QgsAnnotationRegistry();
m3DRendererRegistry = new Qgs3DRendererRegistry();
}
QgsApplication::ApplicationMembers::~ApplicationMembers()
{
delete mActionScopeRegistry;
delete m3DRendererRegistry;
delete mAnnotationRegistry;
delete mColorSchemeRegistry;
delete mFieldFormatterRegistry;

View File

@ -23,6 +23,7 @@
#include "qgis.h"
#include "qgsconfig.h"
class Qgs3DRendererRegistry;
class QgsActionScopeRegistry;
class QgsRuntimeProfiler;
class QgsTaskManager;
@ -583,6 +584,13 @@ class CORE_EXPORT QgsApplication : public QApplication
*/
static QgsFieldFormatterRegistry *fieldFormatterRegistry();
/**
* Returns registry of available 3D renderers.
* \note not available in Python bindings
* \since QGIS 3.0
*/
static Qgs3DRendererRegistry *renderer3DRegistry() SIP_SKIP;
/**
* This string is used to represent the value `NULL` throughout QGIS.
*
@ -698,6 +706,7 @@ class CORE_EXPORT QgsApplication : public QApplication
struct ApplicationMembers
{
Qgs3DRendererRegistry *m3DRendererRegistry = nullptr;
QgsActionScopeRegistry *mActionScopeRegistry = nullptr;
QgsAnnotationRegistry *mAnnotationRegistry = nullptr;
QgsColorSchemeRegistry *mColorSchemeRegistry = nullptr;

View File

@ -28,6 +28,7 @@
#include <sqlite3.h>
#include "qgsabstract3drenderer.h"
#include "qgsapplication.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsdatasourceuri.h"
@ -88,6 +89,7 @@ QgsMapLayer::QgsMapLayer( QgsMapLayer::LayerType type,
QgsMapLayer::~QgsMapLayer()
{
delete m3DRenderer;
delete mLegend;
delete mStyleManager;
}
@ -1673,6 +1675,21 @@ QgsMapLayerStyleManager *QgsMapLayer::styleManager() const
return mStyleManager;
}
void QgsMapLayer::setRenderer3D( QgsAbstract3DRenderer *renderer )
{
if ( renderer == m3DRenderer )
return;
delete m3DRenderer;
m3DRenderer = renderer;
emit renderer3DChanged();
}
QgsAbstract3DRenderer *QgsMapLayer::renderer3D() const
{
return m3DRenderer;
}
void QgsMapLayer::triggerRepaint( bool deferredUpdate )
{
emit repaintRequested( deferredUpdate );

View File

@ -36,6 +36,7 @@
#include "qgsmaplayerdependency.h"
#include "qgslayermetadata.h"
class QgsAbstract3DRenderer;
class QgsDataProvider;
class QgsMapLayerLegend;
class QgsMapLayerRenderer;
@ -675,6 +676,20 @@ class CORE_EXPORT QgsMapLayer : public QObject
*/
QgsMapLayerStyleManager *styleManager() const;
/**
* Sets 3D renderer for the layer. Takes ownership of the renderer.
* \note not available in Python bindings
* \since QGIS 3.0
*/
void setRenderer3D( QgsAbstract3DRenderer *renderer SIP_TRANSFER ) SIP_SKIP;
/**
* Returns 3D renderer associated with the layer. May be null.
* \note not available in Python bindings
* \since QGIS 3.0
*/
QgsAbstract3DRenderer *renderer3D() const SIP_SKIP;
/** Tests whether the layer should be visible at the specified \a scale.
* The \a scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
* \returns true if the layer is visible at the given scale.
@ -897,6 +912,12 @@ class CORE_EXPORT QgsMapLayer : public QObject
*/
void legendChanged();
/**
* Signal emitted when 3D renderer associated with the layer has changed.
* \since QGIS 3.0
*/
void renderer3DChanged();
/**
* Emitted whenever the configuration is changed. The project listens to this signal
* to be marked as dirty.
@ -1080,6 +1101,9 @@ class CORE_EXPORT QgsMapLayer : public QObject
QgsLayerMetadata mMetadata;
//! Renderer for 3D views
QgsAbstract3DRenderer *m3DRenderer = nullptr;
};
Q_DECLARE_METATYPE( QgsMapLayer * )

View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PhongMaterialWidget</class>
<widget class="QWidget" name="PhongMaterialWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>334</width>
<height>252</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Diffuse</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QgsColorButton" name="btnDiffuse">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Ambient</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QgsColorButton" name="btnAmbient">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Specular</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QgsColorButton" name="btnSpecular">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Shininess</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="spinShininess">
<property name="maximum">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsColorButton</class>
<extends>QToolButton</extends>
<header>qgscolorbutton.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Polygon3DSymbolWidget</class>
<widget class="QWidget" name="Polygon3DSymbolWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>538</width>
<height>452</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Height</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="spinHeight"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Extrusion</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="spinExtrusion"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Altitude Clamping</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cboAltClamping">
<item>
<property name="text">
<string>Absolute</string>
</property>
</item>
<item>
<property name="text">
<string>Relative</string>
</property>
</item>
<item>
<property name="text">
<string>Terrain</string>
</property>
</item>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Altitude Binding</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="cboAltBinding">
<item>
<property name="text">
<string>Vertex</string>
</property>
</item>
<item>
<property name="text">
<string>Centroid</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QgsPhongMaterialWidget" name="widgetMaterial" native="true"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsPhongMaterialWidget</class>
<extends>QWidget</extends>
<header>qgsphongmaterialwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>332</width>
<height>319</height>
<width>573</width>
<height>507</height>
</rect>
</property>
<property name="windowTitle">
@ -336,6 +336,12 @@
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsColorButton</class>
<extends>QToolButton</extends>
<header>qgscolorbutton.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsPropertyOverrideButton</class>
<extends>QToolButton</extends>
@ -352,12 +358,6 @@
<header>qgsunitselectionwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsColorButton</class>
<extends>QToolButton</extends>
<header>qgscolorbutton.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsPenJoinStyleComboBox</class>
<extends>QComboBox</extends>