From 057f2b324c55e0dc1e077686f86776b3b7364bb2 Mon Sep 17 00:00:00 2001 From: Martin Dobias Date: Sun, 10 Dec 2017 16:17:49 +0100 Subject: [PATCH] [3d] Allow the user to invert calculated normals of faces It seems that some data sources / formats with 3D polygons order vertices in clockwise order for the front side of the polygons, while others use counter-clockwise order of vertices. While culling mode configuration fixes some problems with rendering (e.g. only back walls are rendered instead of front walls), there still may be issues with shading if the normals are pointing the other way than the polygon was supposed to. --- src/3d/qgstessellatedpolygongeometry.cpp | 2 +- src/3d/qgstessellatedpolygongeometry.h | 6 ++ src/3d/qgstessellator.cpp | 11 ++- src/3d/qgstessellator.h | 3 +- src/3d/symbols/qgspolygon3dsymbol.cpp | 2 + src/3d/symbols/qgspolygon3dsymbol.h | 6 ++ src/3d/symbols/qgspolygon3dsymbol_p.cpp | 1 + src/app/3d/qgspolygon3dsymbolwidget.cpp | 3 + src/ui/3d/polygon3dsymbolwidget.ui | 115 ++++++++++++----------- 9 files changed, 89 insertions(+), 60 deletions(-) diff --git a/src/3d/qgstessellatedpolygongeometry.cpp b/src/3d/qgstessellatedpolygongeometry.cpp index 502729262284..1441d820637a 100644 --- a/src/3d/qgstessellatedpolygongeometry.cpp +++ b/src/3d/qgstessellatedpolygongeometry.cpp @@ -62,7 +62,7 @@ QgsTessellatedPolygonGeometry::~QgsTessellatedPolygonGeometry() void QgsTessellatedPolygonGeometry::setPolygons( const QList &polygons, const QgsPointXY &origin, float extrusionHeight, const QList &extrusionHeightPerPolygon ) { - QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals ); + QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals, mInvertNormals ); for ( int i = 0; i < polygons.count(); ++i ) { QgsPolygon *polygon = polygons.at( i ); diff --git a/src/3d/qgstessellatedpolygongeometry.h b/src/3d/qgstessellatedpolygongeometry.h index 8c49c38668f5..16a6ae4c4197 100644 --- a/src/3d/qgstessellatedpolygongeometry.h +++ b/src/3d/qgstessellatedpolygongeometry.h @@ -41,6 +41,11 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry QgsTessellatedPolygonGeometry( QNode *parent = nullptr ); ~QgsTessellatedPolygonGeometry(); + //! Returns whether the normals of triangles will be inverted (useful for fixing clockwise / counter-clockwise face vertex orders) + bool invertNormals() const { return mInvertNormals; } + //! Sets whether the normals of triangles will be inverted (useful for fixing clockwise / counter-clockwise face vertex orders) + void setInvertNormals( bool invert ) { mInvertNormals = invert; } + //! Initializes vertex buffer from given polygons. Takes ownership of passed polygon geometries void setPolygons( const QList &polygons, const QgsPointXY &origin, float extrusionHeight, const QList &extrusionHeightPerPolygon = QList() ); @@ -51,6 +56,7 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry Qt3DRender::QBuffer *mVertexBuffer = nullptr; bool mWithNormals = true; + bool mInvertNormals = false; }; #endif // QGSTESSELLATEDPOLYGONGEOMETRY_H diff --git a/src/3d/qgstessellator.cpp b/src/3d/qgstessellator.cpp index 0ba19fca2bae..11bc8ca6ab9e 100644 --- a/src/3d/qgstessellator.cpp +++ b/src/3d/qgstessellator.cpp @@ -31,6 +31,7 @@ #include #include + static void make_quad( float x0, float y0, float z0, float x1, float y1, float z1, float height, QVector &data, bool addNormals ) { float dx = x1 - x0; @@ -65,10 +66,11 @@ static void make_quad( float x0, float y0, float z0, float x1, float y1, float z } -QgsTessellator::QgsTessellator( double originX, double originY, bool addNormals ) +QgsTessellator::QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals ) : mOriginX( originX ) , mOriginY( originY ) , mAddNormals( addNormals ) + , mInvertNormals( invertNormals ) { mStride = 3 * sizeof( float ); if ( addNormals ) @@ -118,7 +120,7 @@ static void _makeWalls( const QgsCurve &ring, bool ccw, float extrusionHeight, Q } } -static QVector3D _calculateNormal( const QgsCurve *curve, double originX, double originY ) +static QVector3D _calculateNormal( const QgsCurve *curve, double originX, double originY, bool invertNormal ) { QgsVertexId::VertexType vt; QgsPoint pt1, pt2; @@ -171,7 +173,8 @@ static QVector3D _calculateNormal( const QgsCurve *curve, double originX, double } QVector3D normal( nx, ny, nz ); - //normal = -normal; // TODO: some datasets seem to work better with, others without inversion + if ( invertNormal ) + normal = -normal; normal.normalize(); return normal; } @@ -320,7 +323,7 @@ void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeigh { const QgsCurve *exterior = polygon.exteriorRing(); - const QVector3D pNormal = _calculateNormal( exterior, mOriginX, mOriginY ); + const QVector3D pNormal = _calculateNormal( exterior, mOriginX, mOriginY, mInvertNormals ); const int pCount = exterior->numPoints(); if ( pCount == 4 && polygon.numInteriorRings() == 0 ) diff --git a/src/3d/qgstessellator.h b/src/3d/qgstessellator.h index b10c4c78c734..a72376dcabb7 100644 --- a/src/3d/qgstessellator.h +++ b/src/3d/qgstessellator.h @@ -39,7 +39,7 @@ class _3D_EXPORT QgsTessellator { public: //! Creates tessellator with a specified origin point of the world (in map coordinates) - QgsTessellator( double originX, double originY, bool addNormals ); + QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals = false ); //! Tessellates a triangle and adds its vertex entries to the output data array void addPolygon( const QgsPolygon &polygon, float extrusionHeight ); @@ -57,6 +57,7 @@ class _3D_EXPORT QgsTessellator private: double mOriginX, mOriginY; bool mAddNormals; + bool mInvertNormals; QVector mData; int mStride; }; diff --git a/src/3d/symbols/qgspolygon3dsymbol.cpp b/src/3d/symbols/qgspolygon3dsymbol.cpp index 3ddca251d10a..38fe0555b7d3 100644 --- a/src/3d/symbols/qgspolygon3dsymbol.cpp +++ b/src/3d/symbols/qgspolygon3dsymbol.cpp @@ -32,6 +32,7 @@ void QgsPolygon3DSymbol::writeXml( QDomElement &elem, const QgsReadWriteContext elemDataProperties.setAttribute( QStringLiteral( "height" ), mHeight ); elemDataProperties.setAttribute( QStringLiteral( "extrusion-height" ), mExtrusionHeight ); elemDataProperties.setAttribute( QStringLiteral( "culling-mode" ), Qgs3DUtils::cullingModeToString( mCullingMode ) ); + elemDataProperties.setAttribute( QStringLiteral( "invert-normals" ), mInvertNormals ? "1" : "0" ); elem.appendChild( elemDataProperties ); QDomElement elemMaterial = doc.createElement( QStringLiteral( "material" ) ); @@ -53,6 +54,7 @@ void QgsPolygon3DSymbol::readXml( const QDomElement &elem, const QgsReadWriteCon mHeight = elemDataProperties.attribute( QStringLiteral( "height" ) ).toFloat(); mExtrusionHeight = elemDataProperties.attribute( QStringLiteral( "extrusion-height" ) ).toFloat(); mCullingMode = Qgs3DUtils::cullingModeFromString( elemDataProperties.attribute( QStringLiteral( "culling-mode" ) ) ); + mInvertNormals = elemDataProperties.attribute( QStringLiteral( "invert-normals" ) ).toInt(); QDomElement elemMaterial = elem.firstChildElement( QStringLiteral( "material" ) ); mMaterial.readXml( elemMaterial ); diff --git a/src/3d/symbols/qgspolygon3dsymbol.h b/src/3d/symbols/qgspolygon3dsymbol.h index 0b32d3514760..8c23faaabd78 100644 --- a/src/3d/symbols/qgspolygon3dsymbol.h +++ b/src/3d/symbols/qgspolygon3dsymbol.h @@ -71,6 +71,11 @@ class _3D_EXPORT QgsPolygon3DSymbol : public QgsAbstract3DSymbol //! Sets front/back culling mode void setCullingMode( Qt3DRender::QCullFace::CullingMode mode ) { mCullingMode = mode; } + //! Returns whether the normals of triangles will be inverted (useful for fixing clockwise / counter-clockwise face vertex orders) + bool invertNormals() const { return mInvertNormals; } + //! Sets whether the normals of triangles will be inverted (useful for fixing clockwise / counter-clockwise face vertex orders) + void setInvertNormals( bool invert ) { mInvertNormals = invert; } + private: //! how to handle altitude of vector features AltitudeClamping mAltClamping = AltClampRelative; @@ -81,6 +86,7 @@ class _3D_EXPORT QgsPolygon3DSymbol : public QgsAbstract3DSymbol float mExtrusionHeight = 0.0f; //!< How much to extrude (0 means no walls) QgsPhongMaterialSettings mMaterial; //!< Defines appearance of objects Qt3DRender::QCullFace::CullingMode mCullingMode = Qt3DRender::QCullFace::NoCulling; //!< Front/back culling mode + bool mInvertNormals = false; }; diff --git a/src/3d/symbols/qgspolygon3dsymbol_p.cpp b/src/3d/symbols/qgspolygon3dsymbol_p.cpp index 2204a12f74ec..d5894c8eb01f 100644 --- a/src/3d/symbols/qgspolygon3dsymbol_p.cpp +++ b/src/3d/symbols/qgspolygon3dsymbol_p.cpp @@ -199,6 +199,7 @@ Qt3DRender::QGeometryRenderer *QgsPolygon3DSymbolEntityNode::renderer( const Qgs } mGeometry = new QgsTessellatedPolygonGeometry; + mGeometry->setInvertNormals( symbol.invertNormals() ); mGeometry->setPolygons( polygons, origin, symbol.extrusionHeight(), extrusionHeightPerPolygon ); Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer; diff --git a/src/app/3d/qgspolygon3dsymbolwidget.cpp b/src/app/3d/qgspolygon3dsymbolwidget.cpp index 29ed24033014..6d5facc25503 100644 --- a/src/app/3d/qgspolygon3dsymbolwidget.cpp +++ b/src/app/3d/qgspolygon3dsymbolwidget.cpp @@ -32,6 +32,7 @@ QgsPolygon3DSymbolWidget::QgsPolygon3DSymbolWidget( QWidget *parent ) connect( cboAltClamping, static_cast( &QComboBox::currentIndexChanged ), this, &QgsPolygon3DSymbolWidget::changed ); connect( cboAltBinding, static_cast( &QComboBox::currentIndexChanged ), this, &QgsPolygon3DSymbolWidget::changed ); connect( cboCullingMode, static_cast( &QComboBox::currentIndexChanged ), this, &QgsPolygon3DSymbolWidget::changed ); + connect( chkInvertNormals, &QCheckBox::clicked, this, &QgsPolygon3DSymbolWidget::changed ); connect( widgetMaterial, &QgsPhongMaterialWidget::changed, this, &QgsPolygon3DSymbolWidget::changed ); connect( btnHeightDD, &QgsPropertyOverrideButton::changed, this, &QgsPolygon3DSymbolWidget::changed ); connect( btnExtrusionDD, &QgsPropertyOverrideButton::changed, this, &QgsPolygon3DSymbolWidget::changed ); @@ -70,6 +71,7 @@ void QgsPolygon3DSymbolWidget::setSymbol( const QgsPolygon3DSymbol &symbol, QgsV cboAltClamping->setCurrentIndex( ( int ) symbol.altitudeClamping() ); cboAltBinding->setCurrentIndex( ( int ) symbol.altitudeBinding() ); cboCullingMode->setCurrentIndex( _cullingModeToIndex( symbol.cullingMode() ) ); + chkInvertNormals->setChecked( symbol.invertNormals() ); widgetMaterial->setMaterial( symbol.material() ); btnHeightDD->init( QgsAbstract3DSymbol::PropertyHeight, symbol.dataDefinedProperties(), QgsAbstract3DSymbol::propertyDefinitions(), layer, true ); @@ -84,6 +86,7 @@ QgsPolygon3DSymbol QgsPolygon3DSymbolWidget::symbol() const sym.setAltitudeClamping( ( AltitudeClamping ) cboAltClamping->currentIndex() ); sym.setAltitudeBinding( ( AltitudeBinding ) cboAltBinding->currentIndex() ); sym.setCullingMode( _cullingModeFromIndex( cboCullingMode->currentIndex() ) ); + sym.setInvertNormals( chkInvertNormals->isChecked() ); sym.setMaterial( widgetMaterial->material() ); QgsPropertyCollection ddp; diff --git a/src/ui/3d/polygon3dsymbolwidget.ui b/src/ui/3d/polygon3dsymbolwidget.ui index 5ddee6e73352..f7ad21ea8c6f 100644 --- a/src/ui/3d/polygon3dsymbolwidget.ui +++ b/src/ui/3d/polygon3dsymbolwidget.ui @@ -14,6 +14,46 @@ Form + + + + Culling Mode + + + + + + + + No culling + + + + + Front + + + + + Back + + + + + + + + ... + + + + + + + Height + + + @@ -45,44 +85,6 @@ - - - - ... - - - - - - - Height - - - - - - - - Vertex - - - - - Centroid - - - - - - - - - - - Qt::Horizontal - - - @@ -116,32 +118,37 @@ - - - - Culling Mode - - - - - - - - No culling - - + + - Front + Vertex - Back + Centroid + + + + + + + Qt::Horizontal + + + + + + + Invert Normals (Experimental) + + +