Skip to content

Commit d695c4b

Browse files
committedJan 16, 2019
[feature] [3d] [mesh] Display mesh faces in 3D view as terrain
1 parent 83340d0 commit d695c4b

22 files changed

+1040
-5
lines changed
 

‎src/3d/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ SET(QGIS_3D_SRCS
1717
qgstessellatedpolygongeometry.cpp
1818
qgstilingscheme.cpp
1919
qgsvectorlayer3drenderer.cpp
20+
qgsmeshlayer3drenderer.cpp
2021
qgswindow3dengine.cpp
2122

2223
chunks/qgschunkboundsentity_p.cpp
@@ -31,6 +32,8 @@ SET(QGIS_3D_SRCS
3132
symbols/qgsabstract3dsymbol.cpp
3233
symbols/qgsline3dsymbol.cpp
3334
symbols/qgsline3dsymbol_p.cpp
35+
symbols/qgsmesh3dsymbol.cpp
36+
symbols/qgsmesh3dsymbol_p.cpp
3437
symbols/qgspoint3dsymbol.cpp
3538
symbols/qgspoint3dsymbol_p.cpp
3639
symbols/qgspolygon3dsymbol.cpp
@@ -99,6 +102,7 @@ SET(QGIS_3D_HDRS
99102
qgstessellatedpolygongeometry.h
100103
qgstilingscheme.h
101104
qgsvectorlayer3drenderer.h
105+
qgsmeshlayer3drenderer.h
102106
qgswindow3dengine.h
103107

104108
chunks/qgschunkedentity_p.h
@@ -110,6 +114,8 @@ SET(QGIS_3D_HDRS
110114
symbols/qgsabstract3dsymbol.h
111115
symbols/qgsline3dsymbol.h
112116
symbols/qgsline3dsymbol_p.h
117+
symbols/qgsmesh3dsymbol.h
118+
symbols/qgsmesh3dsymbol_p.h
113119
symbols/qgspoint3dsymbol.h
114120
symbols/qgspoint3dsymbol_p.h
115121
symbols/qgspolygon3dsymbol.h
@@ -135,6 +141,7 @@ INCLUDE_DIRECTORIES(
135141
${CMAKE_SOURCE_DIR}/src/core/
136142
${CMAKE_SOURCE_DIR}/src/core/geometry
137143
${CMAKE_SOURCE_DIR}/src/core/raster
144+
${CMAKE_SOURCE_DIR}/src/core/mesh
138145
${CMAKE_SOURCE_DIR}/src/core/symbology
139146
${CMAKE_SOURCE_DIR}/src/core/metadata
140147
${CMAKE_SOURCE_DIR}/src/core/expression

‎src/3d/qgs3dmapscene.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,14 @@
4141
#include "qgscameracontroller.h"
4242
#include "qgschunkedentity_p.h"
4343
#include "qgschunknode_p.h"
44+
#include "qgsmeshlayer.h"
45+
#include "qgsmeshlayer3drenderer.h"
4446
#include "qgsterrainentity_p.h"
4547
#include "qgsterraingenerator.h"
4648
#include "qgstessellatedpolygongeometry.h"
4749
#include "qgsvectorlayer.h"
4850
#include "qgsvectorlayer3drenderer.h"
4951

50-
5152
Qgs3DMapScene::Qgs3DMapScene( const Qgs3DMapSettings &map, QgsAbstract3DEngine *engine )
5253
: mMap( map )
5354
, mEngine( engine )
@@ -538,6 +539,10 @@ void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
538539
{
539540
static_cast<QgsVectorLayer3DRenderer *>( renderer )->setLayer( static_cast<QgsVectorLayer *>( layer ) );
540541
}
542+
else if ( layer->type() == QgsMapLayer::MeshLayer && renderer->type() == QLatin1String( "mesh" ) )
543+
{
544+
static_cast<QgsMeshLayer3DRenderer *>( renderer )->setLayer( static_cast<QgsMeshLayer *>( layer ) );
545+
}
541546

542547
Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
543548
if ( newEntity )

‎src/3d/qgs3dmapsettings.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "qgsdemterraingenerator.h"
2121
//#include "quantizedmeshterraingenerator.h"
2222
#include "qgsvectorlayer3drenderer.h"
23+
#include "qgsmeshlayer3drenderer.h"
2324

2425
#include <QDomDocument>
2526
#include <QDomElement>
@@ -161,6 +162,10 @@ void Qgs3DMapSettings::readXml( const QDomElement &elem, const QgsReadWriteConte
161162
{
162163
renderer = new QgsVectorLayer3DRenderer;
163164
}
165+
else if ( type == QLatin1String( "mesh" ) )
166+
{
167+
renderer = new QgsMeshLayer3DRenderer;
168+
}
164169

165170
if ( renderer )
166171
{

‎src/3d/qgsmeshlayer3drenderer.cpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/***************************************************************************
2+
qgsmeshlayer3drenderer.cpp
3+
--------------------------
4+
Date : January 2019
5+
Copyright : (C) 2019 by Peter Petrik
6+
Email : zilolv at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsmeshlayer3drenderer.h"
17+
18+
#include "qgsmeshlayer3drenderer.h"
19+
#include "qgsmesh3dsymbol.h"
20+
#include "qgsmesh3dsymbol_p.h"
21+
22+
#include "qgsmeshlayer.h"
23+
#include "qgsxmlutils.h"
24+
25+
26+
QgsMeshLayer3DRendererMetadata::QgsMeshLayer3DRendererMetadata()
27+
: Qgs3DRendererAbstractMetadata( QStringLiteral( "mesh" ) )
28+
{
29+
}
30+
31+
QgsAbstract3DRenderer *QgsMeshLayer3DRendererMetadata::createRenderer( QDomElement &elem, const QgsReadWriteContext &context )
32+
{
33+
QgsMeshLayer3DRenderer *r = new QgsMeshLayer3DRenderer;
34+
r->readXml( elem, context );
35+
return r;
36+
}
37+
38+
39+
// ---------
40+
41+
42+
QgsMeshLayer3DRenderer::QgsMeshLayer3DRenderer( QgsMesh3DSymbol *s )
43+
: mSymbol( s )
44+
{
45+
}
46+
47+
QgsMeshLayer3DRenderer *QgsMeshLayer3DRenderer::clone() const
48+
{
49+
QgsMeshLayer3DRenderer *r = new QgsMeshLayer3DRenderer( mSymbol ? ( QgsMesh3DSymbol * )mSymbol->clone() : nullptr );
50+
r->mLayerRef = mLayerRef;
51+
return r;
52+
}
53+
54+
void QgsMeshLayer3DRenderer::setLayer( QgsMeshLayer *layer )
55+
{
56+
mLayerRef = QgsMapLayerRef( layer );
57+
}
58+
59+
QgsMeshLayer *QgsMeshLayer3DRenderer::layer() const
60+
{
61+
return qobject_cast<QgsMeshLayer *>( mLayerRef.layer );
62+
}
63+
64+
void QgsMeshLayer3DRenderer::setSymbol( QgsMesh3DSymbol *symbol )
65+
{
66+
mSymbol.reset( symbol );
67+
}
68+
69+
const QgsMesh3DSymbol *QgsMeshLayer3DRenderer::symbol() const
70+
{
71+
return mSymbol.get();
72+
}
73+
74+
Qt3DCore::QEntity *QgsMeshLayer3DRenderer::createEntity( const Qgs3DMapSettings &map ) const
75+
{
76+
QgsMeshLayer *vl = layer();
77+
78+
if ( !mSymbol || !vl )
79+
return nullptr;
80+
81+
return new QgsMesh3DSymbolEntity( map, vl, *static_cast<QgsMesh3DSymbol *>( mSymbol.get() ) );
82+
}
83+
84+
void QgsMeshLayer3DRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
85+
{
86+
QDomDocument doc = elem.ownerDocument();
87+
88+
elem.setAttribute( QStringLiteral( "layer" ), mLayerRef.layerId );
89+
90+
QDomElement elemSymbol = doc.createElement( QStringLiteral( "symbol" ) );
91+
if ( mSymbol )
92+
{
93+
elemSymbol.setAttribute( QStringLiteral( "type" ), mSymbol->type() );
94+
mSymbol->writeXml( elemSymbol, context );
95+
}
96+
elem.appendChild( elemSymbol );
97+
}
98+
99+
void QgsMeshLayer3DRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
100+
{
101+
mLayerRef = QgsMapLayerRef( elem.attribute( QStringLiteral( "layer" ) ) );
102+
103+
QDomElement elemSymbol = elem.firstChildElement( QStringLiteral( "symbol" ) );
104+
QgsMesh3DSymbol *symbol = new QgsMesh3DSymbol;
105+
symbol->readXml( elemSymbol, context );
106+
mSymbol.reset( symbol );
107+
}
108+
109+
void QgsMeshLayer3DRenderer::resolveReferences( const QgsProject &project )
110+
{
111+
mLayerRef.setLayer( project.mapLayer( mLayerRef.layerId ) );
112+
}

‎src/3d/qgsmeshlayer3drenderer.h

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/***************************************************************************
2+
qgsmeshlayer3drenderer.h
3+
------------------------
4+
Date : January 2019
5+
Copyright : (C) 2019 by Peter Petrik
6+
Email : zilolv at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSMESHLAYER3DRENDERER_H
17+
#define QGSMESHLAYER3DRENDERER_H
18+
19+
#include "qgis_3d.h"
20+
21+
#include "qgs3drendererregistry.h"
22+
#include "qgsabstract3drenderer.h"
23+
#include "qgsmesh3dsymbol.h"
24+
25+
#include "qgsphongmaterialsettings.h"
26+
#include "qgsmaplayerref.h"
27+
28+
#include <QObject>
29+
30+
class QgsMeshLayer;
31+
32+
33+
/**
34+
* \ingroup core
35+
* Metadata for mesh layer 3D renderer to allow creation of its instances from XML
36+
*
37+
* \warning This is not considered stable API, and may change in future QGIS releases
38+
*
39+
* \since QGIS 3.6
40+
*/
41+
class _3D_EXPORT QgsMeshLayer3DRendererMetadata : public Qgs3DRendererAbstractMetadata
42+
{
43+
public:
44+
QgsMeshLayer3DRendererMetadata();
45+
46+
//! Creates an instance of a 3D renderer based on a DOM element with renderer configuration
47+
QgsAbstract3DRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) override SIP_FACTORY;
48+
};
49+
50+
51+
/**
52+
* \ingroup core
53+
* 3D renderer that renders all mesh triangles of a mesh layer.
54+
* \since QGIS 3.6
55+
*/
56+
class _3D_EXPORT QgsMeshLayer3DRenderer : public QgsAbstract3DRenderer
57+
{
58+
public:
59+
//! Takes ownership of the symbol object
60+
explicit QgsMeshLayer3DRenderer( QgsMesh3DSymbol *s SIP_TRANSFER = nullptr );
61+
62+
//! Sets vector layer associated with the renderer
63+
void setLayer( QgsMeshLayer *layer );
64+
//! Returns mesh layer associated with the renderer
65+
QgsMeshLayer *layer() const;
66+
67+
//! Sets 3D symbol associated with the renderer
68+
void setSymbol( QgsMesh3DSymbol *symbol SIP_TRANSFER );
69+
//! Returns 3D symbol associated with the renderer
70+
const QgsMesh3DSymbol *symbol() const;
71+
72+
QString type() const override { return "mesh"; }
73+
QgsMeshLayer3DRenderer *clone() const override SIP_FACTORY;
74+
Qt3DCore::QEntity *createEntity( const Qgs3DMapSettings &map ) const override SIP_SKIP;
75+
76+
void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const override;
77+
void readXml( const QDomElement &elem, const QgsReadWriteContext &context ) override;
78+
void resolveReferences( const QgsProject &project ) override;
79+
80+
81+
private:
82+
QgsMapLayerRef mLayerRef; //!< Layer used to extract polygons from
83+
std::unique_ptr<QgsMesh3DSymbol> mSymbol; //!< 3D symbol that defines appearance
84+
85+
private:
86+
#ifdef SIP_RUN
87+
QgsMeshLayer3DRenderer( const QgsMeshLayer3DRenderer & );
88+
QgsMeshLayer3DRenderer &operator=( const QgsMeshLayer3DRenderer & );
89+
#endif
90+
};
91+
92+
#endif // QGSMESHLAYER3DRENDERER_H

‎src/3d/symbols/qgsmesh3dsymbol.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/***************************************************************************
2+
qgsmesh3dsymbol.cpp
3+
-------------------
4+
Date : January 2019
5+
Copyright : (C) 2019 by Peter Petrik
6+
Email : zilolv at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsmesh3dsymbol.h"
17+
18+
#include "qgs3dutils.h"
19+
20+
QgsAbstract3DSymbol *QgsMesh3DSymbol::clone() const
21+
{
22+
return new QgsMesh3DSymbol( *this );
23+
}
24+
25+
void QgsMesh3DSymbol::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
26+
{
27+
Q_UNUSED( context );
28+
29+
QDomDocument doc = elem.ownerDocument();
30+
31+
QDomElement elemDataProperties = doc.createElement( QStringLiteral( "data" ) );
32+
elemDataProperties.setAttribute( QStringLiteral( "alt-clamping" ), Qgs3DUtils::altClampingToString( mAltClamping ) );
33+
elemDataProperties.setAttribute( QStringLiteral( "height" ), mHeight );
34+
elemDataProperties.setAttribute( QStringLiteral( "add-back-faces" ), mAddBackFaces ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
35+
elem.appendChild( elemDataProperties );
36+
37+
QDomElement elemMaterial = doc.createElement( QStringLiteral( "material" ) );
38+
mMaterial.writeXml( elemMaterial );
39+
elem.appendChild( elemMaterial );
40+
41+
QDomElement elemDDP = doc.createElement( QStringLiteral( "data-defined-properties" ) );
42+
mDataDefinedProperties.writeXml( elemDDP, propertyDefinitions() );
43+
elem.appendChild( elemDDP );
44+
}
45+
46+
void QgsMesh3DSymbol::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
47+
{
48+
Q_UNUSED( context );
49+
50+
QDomElement elemDataProperties = elem.firstChildElement( QStringLiteral( "data" ) );
51+
mAltClamping = Qgs3DUtils::altClampingFromString( elemDataProperties.attribute( QStringLiteral( "alt-clamping" ) ) );
52+
mHeight = elemDataProperties.attribute( QStringLiteral( "height" ) ).toFloat();
53+
mAddBackFaces = elemDataProperties.attribute( QStringLiteral( "add-back-faces" ) ).toInt();
54+
55+
QDomElement elemMaterial = elem.firstChildElement( QStringLiteral( "material" ) );
56+
mMaterial.readXml( elemMaterial );
57+
58+
QDomElement elemDDP = elem.firstChildElement( QStringLiteral( "data-defined-properties" ) );
59+
if ( !elemDDP.isNull() )
60+
mDataDefinedProperties.readXml( elemDDP, propertyDefinitions() );
61+
}

‎src/3d/symbols/qgsmesh3dsymbol.h

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/***************************************************************************
2+
qgsmesh3dsymbol.h
3+
-----------------
4+
Date : January 2019
5+
Copyright : (C) 2019 by Peter Petrik
6+
Email : zilolv at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSMESH3DSYMBOL_H
17+
#define QGSMESH3DSYMBOL_H
18+
19+
#include "qgis_3d.h"
20+
21+
#include "qgsabstract3dsymbol.h"
22+
#include "qgsphongmaterialsettings.h"
23+
#include "qgs3dtypes.h"
24+
25+
#include <Qt3DRender/QCullFace>
26+
27+
/**
28+
* \ingroup 3d
29+
* 3D symbol that draws mesh geometry as planar triangles.
30+
*
31+
* \warning This is not considered stable API, and may change in future QGIS releases. It is
32+
* exposed to the Python bindings as a tech preview only.
33+
*
34+
* \since QGIS 3.6
35+
*/
36+
class _3D_EXPORT QgsMesh3DSymbol : public QgsAbstract3DSymbol
37+
{
38+
public:
39+
//! Constructor for QgsMesh3DSymbol
40+
QgsMesh3DSymbol() = default;
41+
42+
QString type() const override { return "mesh"; }
43+
QgsAbstract3DSymbol *clone() const override SIP_FACTORY;
44+
45+
void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const override;
46+
void readXml( const QDomElement &elem, const QgsReadWriteContext &context ) override;
47+
48+
//! Returns method that determines altitude (whether to clamp to feature to terrain)
49+
Qgs3DTypes::AltitudeClamping altitudeClamping() const { return mAltClamping; }
50+
//! Sets method that determines altitude (whether to clamp to feature to terrain)
51+
void setAltitudeClamping( Qgs3DTypes::AltitudeClamping altClamping ) { mAltClamping = altClamping; }
52+
53+
//! Returns height (altitude) of the symbol (in map units)
54+
float height() const { return mHeight; }
55+
//! Sets height (altitude) of the symbol (in map units)
56+
void setHeight( float height ) { mHeight = height; }
57+
58+
//! Returns material used for shading of the symbol
59+
QgsPhongMaterialSettings material() const { return mMaterial; }
60+
//! Sets material used for shading of the symbol
61+
void setMaterial( const QgsPhongMaterialSettings &material ) { mMaterial = material; }
62+
63+
/**
64+
* Returns whether also triangles facing the other side will be created. Useful if input data have inconsistent order of vertices
65+
*/
66+
bool addBackFaces() const { return mAddBackFaces; }
67+
68+
/**
69+
* Sets whether also triangles facing the other side will be created. Useful if input data have inconsistent order of vertices
70+
*/
71+
void setAddBackFaces( bool add ) { mAddBackFaces = add; }
72+
73+
private:
74+
//! how to handle altitude of vector features
75+
Qgs3DTypes::AltitudeClamping mAltClamping = Qgs3DTypes::AltClampRelative;
76+
float mHeight = 0.0f; //!< Base height of triangles
77+
QgsPhongMaterialSettings mMaterial; //!< Defines appearance of objects
78+
bool mAddBackFaces = false;
79+
};
80+
81+
82+
#endif // QGSMESH3DSYMBOL_H

‎src/3d/symbols/qgsmesh3dsymbol_p.cpp

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/***************************************************************************
2+
qgsmesh3dsymbol_p.cpp
3+
---------------------
4+
Date : January 2019
5+
Copyright : (C) 2019 by Peter Petrik
6+
Email : zilolv at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsmesh3dsymbol_p.h"
17+
18+
#include "qgsmesh3dsymbol.h"
19+
#include "qgstessellatedpolygongeometry.h"
20+
#include "qgs3dmapsettings.h"
21+
#include "qgs3dutils.h"
22+
23+
#include <Qt3DCore/QTransform>
24+
#include <Qt3DRender/QEffect>
25+
#include <Qt3DRender/QTechnique>
26+
#include <Qt3DRender/QCullFace>
27+
28+
#include "qgsmultipolygon.h"
29+
#include "qgsmeshlayer.h"
30+
#include "qgstriangularmesh.h"
31+
32+
static QgsExpressionContext _expressionContext3D()
33+
{
34+
QgsExpressionContext ctx;
35+
ctx << QgsExpressionContextUtils::globalScope()
36+
<< QgsExpressionContextUtils::projectScope( QgsProject::instance() );
37+
return ctx;
38+
}
39+
40+
/// @cond PRIVATE
41+
42+
QgsMesh3DSymbolEntity::QgsMesh3DSymbolEntity( const Qgs3DMapSettings &map,
43+
QgsMeshLayer *layer,
44+
const QgsMesh3DSymbol &symbol,
45+
Qt3DCore::QNode *parent )
46+
: Qt3DCore::QEntity( parent )
47+
{
48+
// build the default material
49+
Qt3DExtras::QPhongMaterial *mat = material( symbol );
50+
51+
// build a transform function
52+
Qt3DCore::QTransform *tform = new Qt3DCore::QTransform;
53+
tform->setTranslation( QVector3D( 0, 0, 0 ) );
54+
55+
// build the entity
56+
QgsMesh3DSymbolEntityNode *entity = new QgsMesh3DSymbolEntityNode( map, layer, symbol );
57+
entity->findChild<Qt3DRender::QGeometryRenderer *>()->setObjectName( QStringLiteral( "main" ) ); // temporary measure to distinguish between "selected" and "main"
58+
entity->addComponent( mat );
59+
entity->addComponent( tform );
60+
entity->setParent( this );
61+
}
62+
63+
Qt3DExtras::QPhongMaterial *QgsMesh3DSymbolEntity::material( const QgsMesh3DSymbol &symbol ) const
64+
{
65+
Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
66+
67+
// front/back side culling
68+
auto techniques = material->effect()->techniques();
69+
for ( auto tit = techniques.constBegin(); tit != techniques.constEnd(); ++tit )
70+
{
71+
auto renderPasses = ( *tit )->renderPasses();
72+
for ( auto rpit = renderPasses.begin(); rpit != renderPasses.end(); ++rpit )
73+
{
74+
Qt3DRender::QCullFace *cullFace = new Qt3DRender::QCullFace;
75+
cullFace->setMode( Qt3DRender::QCullFace::Back );
76+
( *rpit )->addRenderState( cullFace );
77+
}
78+
}
79+
80+
material->setAmbient( symbol.material().ambient() );
81+
material->setDiffuse( symbol.material().diffuse() );
82+
material->setSpecular( symbol.material().specular() );
83+
material->setShininess( symbol.material().shininess() );
84+
return material;
85+
}
86+
87+
QgsMesh3DSymbolEntityNode::QgsMesh3DSymbolEntityNode( const Qgs3DMapSettings &map,
88+
QgsMeshLayer *layer,
89+
const QgsMesh3DSymbol &symbol,
90+
Qt3DCore::QNode *parent ) : Qt3DCore::QEntity( parent )
91+
{
92+
addComponent( renderer( map, symbol, layer ) );
93+
}
94+
95+
Qt3DRender::QGeometryRenderer *QgsMesh3DSymbolEntityNode::renderer( const Qgs3DMapSettings &map,
96+
const QgsMesh3DSymbol &symbol,
97+
const QgsMeshLayer *layer )
98+
{
99+
QgsPointXY origin( map.origin().x(), map.origin().y() );
100+
QList<QgsPolygon *> polygons;
101+
QList<QgsFeatureId> fids;
102+
103+
QgsExpressionContext ctx( _expressionContext3D() );
104+
const QgsPropertyCollection &ddp = symbol.dataDefinedProperties();
105+
bool hasDDHeight = ddp.isActive( QgsAbstract3DSymbol::PropertyHeight );
106+
float height = symbol.height();
107+
if ( hasDDHeight )
108+
{
109+
height = static_cast<float>( ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyHeight,
110+
ctx,
111+
static_cast<double>( height )
112+
)
113+
);
114+
}
115+
116+
const QgsTriangularMesh *mesh = layer->triangularMesh();
117+
if ( layer && mesh )
118+
{
119+
const QVector<QgsMeshFace> &triangles = mesh->triangles();
120+
const QVector<QgsMeshVertex> &vertices = mesh->vertices();
121+
for ( int i = 0; i < triangles.size(); ++i )
122+
{
123+
const QgsMeshFace &triangle = triangles.at( i );
124+
Q_ASSERT( triangle.size() == 3 );
125+
std::unique_ptr< QgsPolygon > polygon = QgsMeshUtils::toPolygon( triangle, vertices );
126+
Qgs3DUtils::clampAltitudes( polygon.get(),
127+
symbol.altitudeClamping(),
128+
Qgs3DTypes::AltitudeBinding::AltBindVertex,
129+
height,
130+
map );
131+
polygons.append( polygon.release() );
132+
fids.append( i );
133+
}
134+
}
135+
136+
// Polygons from mesh are already triangles, but
137+
// call QgsTessellatedPolygonGeometry to
138+
// use symbol settings for back faces, normals, etc
139+
mGeometry = new QgsTessellatedPolygonGeometry;
140+
mGeometry->setInvertNormals( false );
141+
mGeometry->setAddBackFaces( symbol.addBackFaces() );
142+
QList<float> extrusionHeightPerPolygon;
143+
mGeometry->setPolygons( polygons, fids, origin, 0.0, extrusionHeightPerPolygon );
144+
145+
Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
146+
renderer->setGeometry( mGeometry );
147+
148+
return renderer;
149+
}
150+
/// @endcond

‎src/3d/symbols/qgsmesh3dsymbol_p.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/***************************************************************************
2+
qgsmesh3dsymbol_p.h
3+
-------------------
4+
Date : January 2019
5+
Copyright : (C) 2019 by Peter Petrik
6+
Email : zilolv at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSMESH3DSYMBOL_P_H
17+
#define QGSMESH3DSYMBOL_P_H
18+
19+
/// @cond PRIVATE
20+
21+
//
22+
// W A R N I N G
23+
// -------------
24+
//
25+
// This file is not part of the QGIS API. It exists purely as an
26+
// implementation detail. This header file may change from version to
27+
// version without notice, or even be removed.
28+
//
29+
30+
#include <Qt3DCore/QEntity>
31+
#include <Qt3DExtras/QPhongMaterial>
32+
#include <Qt3DRender/QGeometryRenderer>
33+
34+
class Qgs3DMapSettings;
35+
class QgsTessellatedPolygonGeometry;
36+
class QgsMesh3DSymbol;
37+
38+
class QgsPointXY;
39+
class QgsMeshLayer;
40+
41+
//! Entity that handles rendering of polygons
42+
class QgsMesh3DSymbolEntity : public Qt3DCore::QEntity
43+
{
44+
public:
45+
QgsMesh3DSymbolEntity( const Qgs3DMapSettings &map, QgsMeshLayer *layer, const QgsMesh3DSymbol &symbol, Qt3DCore::QNode *parent = nullptr );
46+
47+
private:
48+
Qt3DExtras::QPhongMaterial *material( const QgsMesh3DSymbol &symbol ) const;
49+
};
50+
51+
class QgsMesh3DSymbolEntityNode : public Qt3DCore::QEntity
52+
{
53+
public:
54+
QgsMesh3DSymbolEntityNode( const Qgs3DMapSettings &map, QgsMeshLayer *layer, const QgsMesh3DSymbol &symbol, Qt3DCore::QNode *parent = nullptr );
55+
56+
private:
57+
Qt3DRender::QGeometryRenderer *renderer( const Qgs3DMapSettings &map, const QgsMesh3DSymbol &symbol, const QgsMeshLayer *layer );
58+
59+
QgsTessellatedPolygonGeometry *mGeometry = nullptr;
60+
};
61+
62+
/// @endcond
63+
64+
#endif // QGSMESH3DSYMBOL_P_H

‎src/app/3d/qgsmesh3dsymbolwidget.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/***************************************************************************
2+
qgsmesh3dsymbolwidget.cpp
3+
-------------------------
4+
Date : January 2019
5+
Copyright : (C) 2019 by Peter Petrik
6+
Email : zilolv at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsmesh3dsymbolwidget.h"
17+
#include "qgsmeshlayer.h"
18+
#include "qgsmesh3dsymbol.h"
19+
20+
21+
QgsMesh3DSymbolWidget::QgsMesh3DSymbolWidget( QWidget *parent )
22+
: QWidget( parent )
23+
{
24+
setupUi( this );
25+
spinHeight->setClearValue( 0.0 );
26+
27+
setSymbol( QgsMesh3DSymbol(), nullptr );
28+
29+
connect( spinHeight, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsMesh3DSymbolWidget::changed );
30+
connect( cboAltClamping, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsMesh3DSymbolWidget::changed );
31+
connect( chkAddBackFaces, &QCheckBox::clicked, this, &QgsMesh3DSymbolWidget::changed );
32+
connect( widgetMaterial, &QgsPhongMaterialWidget::changed, this, &QgsMesh3DSymbolWidget::changed );
33+
connect( btnHeightDD, &QgsPropertyOverrideButton::changed, this, &QgsMesh3DSymbolWidget::changed );
34+
}
35+
36+
void QgsMesh3DSymbolWidget::setSymbol( const QgsMesh3DSymbol &symbol, QgsMeshLayer *layer )
37+
{
38+
Q_UNUSED( layer );
39+
40+
spinHeight->setValue( symbol.height() );
41+
cboAltClamping->setCurrentIndex( static_cast<int>( symbol.altitudeClamping() ) );
42+
chkAddBackFaces->setChecked( symbol.addBackFaces() );
43+
widgetMaterial->setMaterial( symbol.material() );
44+
45+
btnHeightDD->init( QgsAbstract3DSymbol::PropertyHeight, symbol.dataDefinedProperties(), QgsAbstract3DSymbol::propertyDefinitions(), nullptr, true );
46+
}
47+
48+
QgsMesh3DSymbol QgsMesh3DSymbolWidget::symbol() const
49+
{
50+
QgsMesh3DSymbol sym;
51+
sym.setHeight( spinHeight->value() );
52+
sym.setAltitudeClamping( static_cast<Qgs3DTypes::AltitudeClamping>( cboAltClamping->currentIndex() ) );
53+
sym.setAddBackFaces( chkAddBackFaces->isChecked() );
54+
sym.setMaterial( widgetMaterial->material() );
55+
56+
QgsPropertyCollection ddp;
57+
ddp.setProperty( QgsAbstract3DSymbol::PropertyHeight, btnHeightDD->toProperty() );
58+
sym.setDataDefinedProperties( ddp );
59+
60+
return sym;
61+
}

‎src/app/3d/qgsmesh3dsymbolwidget.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/***************************************************************************
2+
qgsmesh3dsymbolwidget.h
3+
-----------------------
4+
Date : January 2019
5+
Copyright : (C) 2019 by Peter Petrik
6+
Email : zilolv at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSMESH3DSYMBOLWIDGET_H
17+
#define QGSMESH3DSYMBOLWIDGET_H
18+
19+
#include <QWidget>
20+
21+
#include "ui_mesh3dsymbolwidget.h"
22+
23+
class QgsMesh3DSymbol;
24+
class QgsMeshLayer;
25+
26+
//! A widget for configuration of 3D symbol for polygons
27+
class QgsMesh3DSymbolWidget : public QWidget, private Ui::Mesh3DSymbolWidget
28+
{
29+
Q_OBJECT
30+
public:
31+
explicit QgsMesh3DSymbolWidget( QWidget *parent = nullptr );
32+
33+
void setSymbol( const QgsMesh3DSymbol &symbol, QgsMeshLayer *layer );
34+
QgsMesh3DSymbol symbol() const;
35+
36+
signals:
37+
void changed();
38+
39+
public slots:
40+
};
41+
42+
#endif // QGSMESH3DSYMBOLWIDGET_H
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/***************************************************************************
2+
qgsmeshlayer3drendererwidget.cpp
3+
--------------------------------
4+
Date : January 2019
5+
Copyright : (C) 2019 by Peter Petrik
6+
Email : zilolv at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsmeshlayer3drendererwidget.h"
17+
18+
#include "qgsmesh3dsymbol.h"
19+
#include "qgsmesh3dsymbolwidget.h"
20+
#include "qgsmeshlayer3drenderer.h"
21+
22+
#include "qgsmeshlayer.h"
23+
24+
#include <QBoxLayout>
25+
#include <QCheckBox>
26+
27+
QgsMeshLayer3DRendererWidget::QgsMeshLayer3DRendererWidget( QgsMeshLayer *layer, QgsMapCanvas *canvas, QWidget *parent )
28+
: QgsMapLayerConfigWidget( layer, canvas, parent )
29+
{
30+
setPanelTitle( tr( "3D View" ) );
31+
32+
QVBoxLayout *layout = new QVBoxLayout( this );
33+
chkEnabled = new QCheckBox( tr( "Enable 3D Renderer" ), this );
34+
layout->addWidget( chkEnabled );
35+
widgetMesh = new QgsMesh3DSymbolWidget( this );
36+
layout->addWidget( widgetMesh );
37+
38+
connect( chkEnabled, &QCheckBox::clicked, this, &QgsMeshLayer3DRendererWidget::onEnabledClicked );
39+
connect( widgetMesh, &QgsMesh3DSymbolWidget::changed, this, &QgsMeshLayer3DRendererWidget::widgetChanged );
40+
}
41+
42+
void QgsMeshLayer3DRendererWidget::setLayer( QgsMeshLayer *layer )
43+
{
44+
mLayer = layer;
45+
46+
QgsAbstract3DRenderer *r = layer->renderer3D();
47+
if ( r && r->type() == QLatin1String( "mesh" ) )
48+
{
49+
QgsMeshLayer3DRenderer *meshRenderer = static_cast<QgsMeshLayer3DRenderer *>( r );
50+
setRenderer( meshRenderer );
51+
}
52+
else
53+
{
54+
setRenderer( nullptr );
55+
}
56+
}
57+
58+
void QgsMeshLayer3DRendererWidget::setRenderer( const QgsMeshLayer3DRenderer *renderer )
59+
{
60+
mRenderer.reset( renderer ? renderer->clone() : nullptr );
61+
62+
whileBlocking( chkEnabled )->setChecked( ( bool )mRenderer );
63+
64+
if ( mRenderer && mRenderer->symbol() && mRenderer->symbol()->type() == QLatin1String( "polygon" ) )
65+
{
66+
whileBlocking( widgetMesh )->setSymbol( *static_cast<const QgsMesh3DSymbol *>( mRenderer->symbol() ), nullptr );
67+
}
68+
else
69+
{
70+
whileBlocking( widgetMesh )->setSymbol( QgsMesh3DSymbol(), nullptr );
71+
}
72+
}
73+
74+
QgsMeshLayer3DRenderer *QgsMeshLayer3DRendererWidget::renderer()
75+
{
76+
if ( chkEnabled->isChecked() )
77+
{
78+
QgsMesh3DSymbol *sym = new QgsMesh3DSymbol( widgetMesh->symbol() );
79+
QgsMeshLayer3DRenderer *r = new QgsMeshLayer3DRenderer( sym );
80+
r->setLayer( qobject_cast<QgsMeshLayer *>( mLayer ) );
81+
mRenderer.reset( r );
82+
}
83+
else
84+
{
85+
mRenderer.reset();
86+
}
87+
88+
return mRenderer.get();
89+
}
90+
91+
void QgsMeshLayer3DRendererWidget::apply()
92+
{
93+
QgsMeshLayer3DRenderer *r = renderer();
94+
mLayer->setRenderer3D( r ? r->clone() : nullptr );
95+
}
96+
97+
void QgsMeshLayer3DRendererWidget::onEnabledClicked()
98+
{
99+
widgetMesh->setEnabled( chkEnabled->isChecked() );
100+
emit widgetChanged();
101+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/***************************************************************************
2+
qgsmeshlayer3drendererwidget.h
3+
------------------------------
4+
Date : January 2019
5+
Copyright : (C) 2019 by Peter Petrik
6+
Email : zilolv at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSMESHLAYER3DRENDERERWIDGET_H
17+
#define QGSMESHLAYER3DRENDERERWIDGET_H
18+
19+
#include <memory>
20+
21+
#include "qgsmaplayerconfigwidget.h"
22+
#include "qgsmeshlayer3drenderer.h"
23+
24+
class QCheckBox;
25+
26+
class QgsMesh3DSymbolWidget;
27+
class QgsMeshLayer;
28+
class QgsMapCanvas;
29+
30+
31+
//! Widget for configuration of 3D renderer of a mesh layer
32+
class QgsMeshLayer3DRendererWidget : public QgsMapLayerConfigWidget
33+
{
34+
Q_OBJECT
35+
public:
36+
explicit QgsMeshLayer3DRendererWidget( QgsMeshLayer *layer, QgsMapCanvas *canvas, QWidget *parent = nullptr );
37+
38+
void setLayer( QgsMeshLayer *layer );
39+
40+
//! no transfer of ownership
41+
void setRenderer( const QgsMeshLayer3DRenderer *renderer );
42+
//! no transfer of ownership
43+
QgsMeshLayer3DRenderer *renderer();
44+
45+
public slots:
46+
void apply() override;
47+
48+
private slots:
49+
void onEnabledClicked();
50+
51+
private:
52+
QCheckBox *chkEnabled = nullptr;
53+
QgsMesh3DSymbolWidget *widgetMesh = nullptr;
54+
std::unique_ptr<QgsMeshLayer3DRenderer> mRenderer;
55+
};
56+
57+
#endif // QGSMESHLAYER3DRENDERERWIDGET_H

‎src/app/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,10 +481,12 @@ IF (WITH_3D)
481481
3d/qgs3dmaptoolidentify.cpp
482482
3d/qgslightswidget.cpp
483483
3d/qgsline3dsymbolwidget.cpp
484+
3d/qgsmesh3dsymbolwidget.cpp
484485
3d/qgspoint3dsymbolwidget.cpp
485486
3d/qgspolygon3dsymbolwidget.cpp
486487
3d/qgsphongmaterialwidget.cpp
487488
3d/qgsvectorlayer3drendererwidget.cpp
489+
3d/qgsmeshlayer3drendererwidget.cpp
488490
layout/qgslayout3dmapwidget.cpp
489491
)
490492

@@ -498,10 +500,12 @@ IF (WITH_3D)
498500
3d/qgs3dmaptoolidentify.h
499501
3d/qgslightswidget.h
500502
3d/qgsline3dsymbolwidget.h
503+
3d/qgsmesh3dsymbolwidget.h
501504
3d/qgspoint3dsymbolwidget.h
502505
3d/qgspolygon3dsymbolwidget.h
503506
3d/qgsphongmaterialwidget.h
504507
3d/qgsvectorlayer3drendererwidget.h
508+
3d/qgsmeshlayer3drendererwidget.h
505509
layout/qgslayout3dmapwidget.h
506510
)
507511
ENDIF (WITH_3D)

‎src/app/qgslayerstylingwidget.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151

5252
#ifdef HAVE_3D
5353
#include "qgsvectorlayer3drendererwidget.h"
54+
#include "qgsmeshlayer3drendererwidget.h"
5455
#endif
5556

5657

@@ -204,6 +205,13 @@ void QgsLayerStylingWidget::setLayer( QgsMapLayer *layer )
204205
symbolItem->setData( Qt::UserRole, Symbology );
205206
symbolItem->setToolTip( tr( "Symbology" ) );
206207
mOptionsListWidget->addItem( symbolItem );
208+
209+
#ifdef HAVE_3D
210+
QListWidgetItem *symbol3DItem = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "3d.svg" ) ), QString() );
211+
symbol3DItem->setData( Qt::UserRole, Symbology3D );
212+
symbol3DItem->setToolTip( tr( "3D View" ) );
213+
mOptionsListWidget->addItem( symbol3DItem );
214+
#endif
207215
}
208216

209217
Q_FOREACH ( QgsMapLayerConfigWidgetFactory *factory, mPageFactories )
@@ -355,6 +363,12 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer()
355363
{
356364
mMeshStyleWidget = widget;
357365
}
366+
#ifdef HAVE_3D
367+
else if ( QgsMeshLayer3DRendererWidget *widget = qobject_cast<QgsMeshLayer3DRendererWidget *>( current ) )
368+
{
369+
mMesh3DWidget = widget;
370+
}
371+
#endif
358372
}
359373

360374
mWidgetStack->clear();
@@ -517,6 +531,20 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer()
517531
mWidgetStack->setMainPanel( mMeshStyleWidget );
518532
break;
519533
}
534+
#ifdef HAVE_3D
535+
case 1: // 3D View
536+
{
537+
if ( !mMesh3DWidget )
538+
{
539+
mMesh3DWidget = new QgsMeshLayer3DRendererWidget( nullptr, mMapCanvas, mWidgetStack );
540+
mMesh3DWidget->setDockMode( true );
541+
connect( mMesh3DWidget, &QgsMeshLayer3DRendererWidget::widgetChanged, this, &QgsLayerStylingWidget::autoApply );
542+
}
543+
mMesh3DWidget->setLayer( meshLayer );
544+
mWidgetStack->setMainPanel( mMesh3DWidget );
545+
break;
546+
}
547+
#endif
520548
default:
521549
break;
522550
}

‎src/app/qgslayerstylingwidget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class QgsUndoWidget;
4141
class QgsRasterHistogramWidget;
4242
class QgsMapLayerStyleManagerWidget;
4343
class QgsVectorLayer3DRendererWidget;
44+
class QgsMeshLayer3DRendererWidget;
4445
class QgsMessageBar;
4546

4647
class APP_EXPORT QgsLayerStyleManagerWidgetFactory : public QgsMapLayerConfigWidgetFactory
@@ -141,6 +142,7 @@ class APP_EXPORT QgsLayerStylingWidget : public QWidget, private Ui::QgsLayerSty
141142
QgsLabelingWidget *mLabelingWidget = nullptr;
142143
#ifdef HAVE_3D
143144
QgsVectorLayer3DRendererWidget *mVector3DWidget = nullptr;
145+
QgsMeshLayer3DRendererWidget *mMesh3DWidget = nullptr;
144146
#endif
145147
QgsRendererRasterPropertiesWidget *mRasterStyleWidget = nullptr;
146148
QgsRendererMeshPropertiesWidget *mMeshStyleWidget = nullptr;

‎src/core/mesh/qgstriangularmesh.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ void QgsTriangularMesh::update( QgsMesh *nativeMesh, QgsRenderContext *context )
138138
{
139139
QgsPointXY mapPoint = mCoordinateTransform.transform( QgsPointXY( vertex.x(), vertex.y() ) );
140140
QgsMeshVertex mapVertex( mapPoint );
141-
mapVertex.setZ( vertex.z() );
141+
mapVertex.addZValue( vertex.z() );
142142
mapVertex.setM( vertex.m() );
143143
mTriangularMesh.vertices[i] = mapVertex;
144144
}
@@ -255,7 +255,7 @@ QList<int> QgsTriangularMesh::faceIndexesForRectangle( const QgsRectangle &recta
255255
return indexes;
256256
}
257257

258-
QgsGeometry QgsMeshUtils::toGeometry( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
258+
std::unique_ptr< QgsPolygon > QgsMeshUtils::toPolygon( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
259259
{
260260
QVector<QgsPoint> ring;
261261
for ( int j = 0; j < face.size(); ++j )
@@ -267,7 +267,12 @@ QgsGeometry QgsMeshUtils::toGeometry( const QgsMeshFace &face, const QVector<Qgs
267267
}
268268
std::unique_ptr< QgsPolygon > polygon = qgis::make_unique< QgsPolygon >();
269269
polygon->setExteriorRing( new QgsLineString( ring ) );
270-
return QgsGeometry( std::move( polygon ) );
270+
return polygon;
271+
}
272+
273+
QgsGeometry QgsMeshUtils::toGeometry( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
274+
{
275+
return QgsGeometry( QgsMeshUtils::toPolygon( face, vertices ) );
271276
}
272277

273278
QList<int> QgsMeshUtils::nativeFacesFromTriangles( const QList<int> &triangleIndexes, const QVector<int> &trianglesToNativeFaces )

‎src/core/mesh/qgstriangularmesh.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#define SIP_NO_FILE
2323

2424
#include <QVector>
25+
#include <memory>
2526
#include "qgis_core.h"
2627
#include "qgsmeshdataprovider.h"
2728
#include "qgsgeometry.h"
@@ -145,6 +146,9 @@ namespace QgsMeshUtils
145146
//! Returns face as polygon geometry
146147
CORE_EXPORT QgsGeometry toGeometry( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices );
147148

149+
//! Returns face as polygon geometry, caller is responsible for delete
150+
CORE_EXPORT std::unique_ptr< QgsPolygon > toPolygon( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices );
151+
148152
/**
149153
* Returns unique native faces indexes from list of triangle indexes
150154
* \since QGIS 3.4

‎src/ui/3d/mesh3dsymbolwidget.ui

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>Mesh3DSymbolWidget</class>
4+
<widget class="QWidget" name="Mesh3DSymbolWidget">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>561</width>
10+
<height>452</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>Form</string>
15+
</property>
16+
<layout class="QGridLayout" name="gridLayout">
17+
<item row="4" column="0" colspan="3">
18+
<widget class="QgsPhongMaterialWidget" name="widgetMaterial" native="true"/>
19+
</item>
20+
<item row="0" column="0">
21+
<widget class="QLabel" name="label">
22+
<property name="text">
23+
<string>Height</string>
24+
</property>
25+
</widget>
26+
</item>
27+
<item row="3" column="0" colspan="3">
28+
<widget class="Line" name="line">
29+
<property name="orientation">
30+
<enum>Qt::Horizontal</enum>
31+
</property>
32+
</widget>
33+
</item>
34+
<item row="1" column="0">
35+
<widget class="QLabel" name="label_3">
36+
<property name="text">
37+
<string>Altitude clamping</string>
38+
</property>
39+
</widget>
40+
</item>
41+
<item row="1" column="1">
42+
<widget class="QComboBox" name="cboAltClamping">
43+
<item>
44+
<property name="text">
45+
<string>Absolute</string>
46+
</property>
47+
</item>
48+
<item>
49+
<property name="text">
50+
<string>Relative</string>
51+
</property>
52+
</item>
53+
<item>
54+
<property name="text">
55+
<string>Terrain</string>
56+
</property>
57+
</item>
58+
</widget>
59+
</item>
60+
<item row="0" column="2">
61+
<widget class="QgsPropertyOverrideButton" name="btnHeightDD">
62+
<property name="text">
63+
<string>…</string>
64+
</property>
65+
</widget>
66+
</item>
67+
<item row="0" column="1">
68+
<widget class="QgsDoubleSpinBox" name="spinHeight">
69+
<property name="minimum">
70+
<double>-99999.000000000000000</double>
71+
</property>
72+
<property name="maximum">
73+
<double>99999.000000000000000</double>
74+
</property>
75+
</widget>
76+
</item>
77+
<item row="2" column="0">
78+
<widget class="QCheckBox" name="chkAddBackFaces">
79+
<property name="text">
80+
<string>Add back faces</string>
81+
</property>
82+
</widget>
83+
</item>
84+
</layout>
85+
</widget>
86+
<customwidgets>
87+
<customwidget>
88+
<class>QgsDoubleSpinBox</class>
89+
<extends>QDoubleSpinBox</extends>
90+
<header>qgsdoublespinbox.h</header>
91+
</customwidget>
92+
<customwidget>
93+
<class>QgsPropertyOverrideButton</class>
94+
<extends>QToolButton</extends>
95+
<header>qgspropertyoverridebutton.h</header>
96+
</customwidget>
97+
<customwidget>
98+
<class>QgsPhongMaterialWidget</class>
99+
<extends>QWidget</extends>
100+
<header>qgsphongmaterialwidget.h</header>
101+
<container>1</container>
102+
</customwidget>
103+
</customwidgets>
104+
<tabstops>
105+
<tabstop>spinHeight</tabstop>
106+
<tabstop>btnHeightDD</tabstop>
107+
<tabstop>cboAltClamping</tabstop>
108+
<tabstop>chkAddBackFaces</tabstop>
109+
</tabstops>
110+
<resources/>
111+
<connections/>
112+
</ui>

‎tests/src/3d/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}
1818
${CMAKE_SOURCE_DIR}/src/core/geometry
1919
${CMAKE_SOURCE_DIR}/src/core/layout
2020
${CMAKE_SOURCE_DIR}/src/core/metadata
21+
${CMAKE_SOURCE_DIR}/src/core/mesh
2122
${CMAKE_SOURCE_DIR}/src/core/raster
2223
${CMAKE_SOURCE_DIR}/src/core/symbology
2324
${CMAKE_SOURCE_DIR}/src/core/fieldformatter

‎tests/src/3d/testqgs3drendering.cpp

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "qgsrastershader.h"
2424
#include "qgssinglebandpseudocolorrenderer.h"
2525
#include "qgsvectorlayer.h"
26+
#include "qgsmeshlayer.h"
2627

2728
#include "qgs3dmapscene.h"
2829
#include "qgs3dmapsettings.h"
@@ -35,7 +36,7 @@
3536
#include "qgspolygon3dsymbol.h"
3637
#include "qgsterrainentity_p.h"
3738
#include "qgsvectorlayer3drenderer.h"
38-
39+
#include "qgsmeshlayer3drenderer.h"
3940

4041
class TestQgs3DRendering : public QObject
4142
{
@@ -48,6 +49,7 @@ class TestQgs3DRendering : public QObject
4849
void testDemTerrain();
4950
void testExtrudedPolygons();
5051
void testMapTheme();
52+
void testMesh();
5153

5254
private:
5355
bool renderCheck( const QString &testName, QImage &image, int mismatchCount = 0 );
@@ -58,6 +60,7 @@ class TestQgs3DRendering : public QObject
5860
QgsRasterLayer *mLayerDtm;
5961
QgsRasterLayer *mLayerRgb;
6062
QgsVectorLayer *mLayerBuildings;
63+
QgsMeshLayer *mLayerMesh;
6164
};
6265

6366
//runs before all tests
@@ -92,6 +95,17 @@ void TestQgs3DRendering::initTestCase()
9295
QgsVectorLayer3DRenderer *renderer3d = new QgsVectorLayer3DRenderer( symbol3d );
9396
mLayerBuildings->setRenderer3D( renderer3d );
9497

98+
mLayerMesh = new QgsMeshLayer( dataDir + "/mesh/quad_and_triangle.2dm", "mesh", "mdal" );
99+
QVERIFY( mLayerMesh->isValid() );
100+
mProject->addMapLayer( mLayerMesh );
101+
102+
QgsPhongMaterialSettings meshMaterial;
103+
meshMaterial.setAmbient( Qt::lightGray );
104+
QgsMesh3DSymbol *symbolMesh3d = new QgsMesh3DSymbol;
105+
symbolMesh3d->setMaterial( meshMaterial );
106+
QgsMeshLayer3DRenderer *meshRenderer3d = new QgsMeshLayer3DRenderer( symbolMesh3d );
107+
mLayerMesh->setRenderer3D( meshRenderer3d );
108+
95109
mProject->setCrs( mLayerDtm->crs() );
96110

97111
//
@@ -276,6 +290,32 @@ void TestQgs3DRendering::testMapTheme()
276290
QVERIFY( renderCheck( "terrain_theme", img, 40 ) );
277291
}
278292

293+
void TestQgs3DRendering::testMesh()
294+
{
295+
QgsRectangle fullExtent = mLayerMesh->extent();
296+
297+
Qgs3DMapSettings *map = new Qgs3DMapSettings;
298+
map->setCrs( mProject->crs() );
299+
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
300+
map->setLayers( QList<QgsMapLayer *>() << mLayerMesh );
301+
QgsPointLightSettings defaultLight;
302+
defaultLight.setPosition( QgsVector3D( 1500, 2500, 0 ) );
303+
map->setPointLights( QList<QgsPointLightSettings>() << defaultLight );
304+
305+
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
306+
flatTerrain->setCrs( map->crs() );
307+
flatTerrain->setExtent( fullExtent );
308+
map->setTerrainGenerator( flatTerrain );
309+
310+
QgsOffscreen3DEngine engine;
311+
Qgs3DMapScene *scene = new Qgs3DMapScene( *map, &engine );
312+
engine.setRootEntity( scene );
313+
314+
scene->cameraController()->setLookingAtPoint( QgsVector3D( 1500, 2500, 20 ), 500, 45, 0 );
315+
QImage img = Qgs3DUtils::captureSceneImage( engine, scene );
316+
317+
QVERIFY( renderCheck( "mesh3d", img, 40 ) );
318+
}
279319

280320
bool TestQgs3DRendering::renderCheck( const QString &testName, QImage &image, int mismatchCount )
281321
{
Loading

0 commit comments

Comments
 (0)
Please sign in to comment.