Skip to content

Commit

Permalink
[3d] Allow the user to invert calculated normals of faces
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wonder-sk committed Dec 12, 2017
1 parent 24c1c86 commit 057f2b3
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 60 deletions.
2 changes: 1 addition & 1 deletion src/3d/qgstessellatedpolygongeometry.cpp
Expand Up @@ -62,7 +62,7 @@ QgsTessellatedPolygonGeometry::~QgsTessellatedPolygonGeometry()


void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygon *> &polygons, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon ) void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygon *> &polygons, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon )
{ {
QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals ); QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals, mInvertNormals );
for ( int i = 0; i < polygons.count(); ++i ) for ( int i = 0; i < polygons.count(); ++i )
{ {
QgsPolygon *polygon = polygons.at( i ); QgsPolygon *polygon = polygons.at( i );
Expand Down
6 changes: 6 additions & 0 deletions src/3d/qgstessellatedpolygongeometry.h
Expand Up @@ -41,6 +41,11 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
QgsTessellatedPolygonGeometry( QNode *parent = nullptr ); QgsTessellatedPolygonGeometry( QNode *parent = nullptr );
~QgsTessellatedPolygonGeometry(); ~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 //! Initializes vertex buffer from given polygons. Takes ownership of passed polygon geometries
void setPolygons( const QList<QgsPolygon *> &polygons, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon = QList<float>() ); void setPolygons( const QList<QgsPolygon *> &polygons, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon = QList<float>() );


Expand All @@ -51,6 +56,7 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
Qt3DRender::QBuffer *mVertexBuffer = nullptr; Qt3DRender::QBuffer *mVertexBuffer = nullptr;


bool mWithNormals = true; bool mWithNormals = true;
bool mInvertNormals = false;
}; };


#endif // QGSTESSELLATEDPOLYGONGEOMETRY_H #endif // QGSTESSELLATEDPOLYGONGEOMETRY_H
11 changes: 7 additions & 4 deletions src/3d/qgstessellator.cpp
Expand Up @@ -31,6 +31,7 @@
#include <QVector3D> #include <QVector3D>
#include <algorithm> #include <algorithm>



static void make_quad( float x0, float y0, float z0, float x1, float y1, float z1, float height, QVector<float> &data, bool addNormals ) static void make_quad( float x0, float y0, float z0, float x1, float y1, float z1, float height, QVector<float> &data, bool addNormals )
{ {
float dx = x1 - x0; float dx = x1 - x0;
Expand Down Expand Up @@ -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 ) : mOriginX( originX )
, mOriginY( originY ) , mOriginY( originY )
, mAddNormals( addNormals ) , mAddNormals( addNormals )
, mInvertNormals( invertNormals )
{ {
mStride = 3 * sizeof( float ); mStride = 3 * sizeof( float );
if ( addNormals ) if ( addNormals )
Expand Down Expand Up @@ -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; QgsVertexId::VertexType vt;
QgsPoint pt1, pt2; QgsPoint pt1, pt2;
Expand Down Expand Up @@ -171,7 +173,8 @@ static QVector3D _calculateNormal( const QgsCurve *curve, double originX, double
} }


QVector3D normal( nx, ny, nz ); QVector3D normal( nx, ny, nz );
//normal = -normal; // TODO: some datasets seem to work better with, others without inversion if ( invertNormal )
normal = -normal;
normal.normalize(); normal.normalize();
return normal; return normal;
} }
Expand Down Expand Up @@ -320,7 +323,7 @@ void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeigh
{ {
const QgsCurve *exterior = polygon.exteriorRing(); 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(); const int pCount = exterior->numPoints();


if ( pCount == 4 && polygon.numInteriorRings() == 0 ) if ( pCount == 4 && polygon.numInteriorRings() == 0 )
Expand Down
3 changes: 2 additions & 1 deletion src/3d/qgstessellator.h
Expand Up @@ -39,7 +39,7 @@ class _3D_EXPORT QgsTessellator
{ {
public: public:
//! Creates tessellator with a specified origin point of the world (in map coordinates) //! 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 //! Tessellates a triangle and adds its vertex entries to the output data array
void addPolygon( const QgsPolygon &polygon, float extrusionHeight ); void addPolygon( const QgsPolygon &polygon, float extrusionHeight );
Expand All @@ -57,6 +57,7 @@ class _3D_EXPORT QgsTessellator
private: private:
double mOriginX, mOriginY; double mOriginX, mOriginY;
bool mAddNormals; bool mAddNormals;
bool mInvertNormals;
QVector<float> mData; QVector<float> mData;
int mStride; int mStride;
}; };
Expand Down
2 changes: 2 additions & 0 deletions src/3d/symbols/qgspolygon3dsymbol.cpp
Expand Up @@ -32,6 +32,7 @@ void QgsPolygon3DSymbol::writeXml( QDomElement &elem, const QgsReadWriteContext
elemDataProperties.setAttribute( QStringLiteral( "height" ), mHeight ); elemDataProperties.setAttribute( QStringLiteral( "height" ), mHeight );
elemDataProperties.setAttribute( QStringLiteral( "extrusion-height" ), mExtrusionHeight ); elemDataProperties.setAttribute( QStringLiteral( "extrusion-height" ), mExtrusionHeight );
elemDataProperties.setAttribute( QStringLiteral( "culling-mode" ), Qgs3DUtils::cullingModeToString( mCullingMode ) ); elemDataProperties.setAttribute( QStringLiteral( "culling-mode" ), Qgs3DUtils::cullingModeToString( mCullingMode ) );
elemDataProperties.setAttribute( QStringLiteral( "invert-normals" ), mInvertNormals ? "1" : "0" );
elem.appendChild( elemDataProperties ); elem.appendChild( elemDataProperties );


QDomElement elemMaterial = doc.createElement( QStringLiteral( "material" ) ); QDomElement elemMaterial = doc.createElement( QStringLiteral( "material" ) );
Expand All @@ -53,6 +54,7 @@ void QgsPolygon3DSymbol::readXml( const QDomElement &elem, const QgsReadWriteCon
mHeight = elemDataProperties.attribute( QStringLiteral( "height" ) ).toFloat(); mHeight = elemDataProperties.attribute( QStringLiteral( "height" ) ).toFloat();
mExtrusionHeight = elemDataProperties.attribute( QStringLiteral( "extrusion-height" ) ).toFloat(); mExtrusionHeight = elemDataProperties.attribute( QStringLiteral( "extrusion-height" ) ).toFloat();
mCullingMode = Qgs3DUtils::cullingModeFromString( elemDataProperties.attribute( QStringLiteral( "culling-mode" ) ) ); mCullingMode = Qgs3DUtils::cullingModeFromString( elemDataProperties.attribute( QStringLiteral( "culling-mode" ) ) );
mInvertNormals = elemDataProperties.attribute( QStringLiteral( "invert-normals" ) ).toInt();


QDomElement elemMaterial = elem.firstChildElement( QStringLiteral( "material" ) ); QDomElement elemMaterial = elem.firstChildElement( QStringLiteral( "material" ) );
mMaterial.readXml( elemMaterial ); mMaterial.readXml( elemMaterial );
Expand Down
6 changes: 6 additions & 0 deletions src/3d/symbols/qgspolygon3dsymbol.h
Expand Up @@ -71,6 +71,11 @@ class _3D_EXPORT QgsPolygon3DSymbol : public QgsAbstract3DSymbol
//! Sets front/back culling mode //! Sets front/back culling mode
void setCullingMode( Qt3DRender::QCullFace::CullingMode mode ) { mCullingMode = 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: private:
//! how to handle altitude of vector features //! how to handle altitude of vector features
AltitudeClamping mAltClamping = AltClampRelative; AltitudeClamping mAltClamping = AltClampRelative;
Expand All @@ -81,6 +86,7 @@ class _3D_EXPORT QgsPolygon3DSymbol : public QgsAbstract3DSymbol
float mExtrusionHeight = 0.0f; //!< How much to extrude (0 means no walls) float mExtrusionHeight = 0.0f; //!< How much to extrude (0 means no walls)
QgsPhongMaterialSettings mMaterial; //!< Defines appearance of objects QgsPhongMaterialSettings mMaterial; //!< Defines appearance of objects
Qt3DRender::QCullFace::CullingMode mCullingMode = Qt3DRender::QCullFace::NoCulling; //!< Front/back culling mode Qt3DRender::QCullFace::CullingMode mCullingMode = Qt3DRender::QCullFace::NoCulling; //!< Front/back culling mode
bool mInvertNormals = false;
}; };




Expand Down
1 change: 1 addition & 0 deletions src/3d/symbols/qgspolygon3dsymbol_p.cpp
Expand Up @@ -199,6 +199,7 @@ Qt3DRender::QGeometryRenderer *QgsPolygon3DSymbolEntityNode::renderer( const Qgs
} }


mGeometry = new QgsTessellatedPolygonGeometry; mGeometry = new QgsTessellatedPolygonGeometry;
mGeometry->setInvertNormals( symbol.invertNormals() );
mGeometry->setPolygons( polygons, origin, symbol.extrusionHeight(), extrusionHeightPerPolygon ); mGeometry->setPolygons( polygons, origin, symbol.extrusionHeight(), extrusionHeightPerPolygon );


Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer; Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
Expand Down
3 changes: 3 additions & 0 deletions src/app/3d/qgspolygon3dsymbolwidget.cpp
Expand Up @@ -32,6 +32,7 @@ QgsPolygon3DSymbolWidget::QgsPolygon3DSymbolWidget( QWidget *parent )
connect( cboAltClamping, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), 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( cboAltBinding, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPolygon3DSymbolWidget::changed );
connect( cboCullingMode, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPolygon3DSymbolWidget::changed ); connect( cboCullingMode, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPolygon3DSymbolWidget::changed );
connect( chkInvertNormals, &QCheckBox::clicked, this, &QgsPolygon3DSymbolWidget::changed );
connect( widgetMaterial, &QgsPhongMaterialWidget::changed, this, &QgsPolygon3DSymbolWidget::changed ); connect( widgetMaterial, &QgsPhongMaterialWidget::changed, this, &QgsPolygon3DSymbolWidget::changed );
connect( btnHeightDD, &QgsPropertyOverrideButton::changed, this, &QgsPolygon3DSymbolWidget::changed ); connect( btnHeightDD, &QgsPropertyOverrideButton::changed, this, &QgsPolygon3DSymbolWidget::changed );
connect( btnExtrusionDD, &QgsPropertyOverrideButton::changed, this, &QgsPolygon3DSymbolWidget::changed ); connect( btnExtrusionDD, &QgsPropertyOverrideButton::changed, this, &QgsPolygon3DSymbolWidget::changed );
Expand Down Expand Up @@ -70,6 +71,7 @@ void QgsPolygon3DSymbolWidget::setSymbol( const QgsPolygon3DSymbol &symbol, QgsV
cboAltClamping->setCurrentIndex( ( int ) symbol.altitudeClamping() ); cboAltClamping->setCurrentIndex( ( int ) symbol.altitudeClamping() );
cboAltBinding->setCurrentIndex( ( int ) symbol.altitudeBinding() ); cboAltBinding->setCurrentIndex( ( int ) symbol.altitudeBinding() );
cboCullingMode->setCurrentIndex( _cullingModeToIndex( symbol.cullingMode() ) ); cboCullingMode->setCurrentIndex( _cullingModeToIndex( symbol.cullingMode() ) );
chkInvertNormals->setChecked( symbol.invertNormals() );
widgetMaterial->setMaterial( symbol.material() ); widgetMaterial->setMaterial( symbol.material() );


btnHeightDD->init( QgsAbstract3DSymbol::PropertyHeight, symbol.dataDefinedProperties(), QgsAbstract3DSymbol::propertyDefinitions(), layer, true ); btnHeightDD->init( QgsAbstract3DSymbol::PropertyHeight, symbol.dataDefinedProperties(), QgsAbstract3DSymbol::propertyDefinitions(), layer, true );
Expand All @@ -84,6 +86,7 @@ QgsPolygon3DSymbol QgsPolygon3DSymbolWidget::symbol() const
sym.setAltitudeClamping( ( AltitudeClamping ) cboAltClamping->currentIndex() ); sym.setAltitudeClamping( ( AltitudeClamping ) cboAltClamping->currentIndex() );
sym.setAltitudeBinding( ( AltitudeBinding ) cboAltBinding->currentIndex() ); sym.setAltitudeBinding( ( AltitudeBinding ) cboAltBinding->currentIndex() );
sym.setCullingMode( _cullingModeFromIndex( cboCullingMode->currentIndex() ) ); sym.setCullingMode( _cullingModeFromIndex( cboCullingMode->currentIndex() ) );
sym.setInvertNormals( chkInvertNormals->isChecked() );
sym.setMaterial( widgetMaterial->material() ); sym.setMaterial( widgetMaterial->material() );


QgsPropertyCollection ddp; QgsPropertyCollection ddp;
Expand Down
115 changes: 61 additions & 54 deletions src/ui/3d/polygon3dsymbolwidget.ui
Expand Up @@ -14,6 +14,46 @@
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Culling Mode</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="cboCullingMode">
<item>
<property name="text">
<string>No culling</string>
</property>
</item>
<item>
<property name="text">
<string>Front</string>
</property>
</item>
<item>
<property name="text">
<string>Back</string>
</property>
</item>
</widget>
</item>
<item row="0" column="2">
<widget class="QgsPropertyOverrideButton" name="btnHeightDD">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Height</string>
</property>
</widget>
</item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
<property name="text"> <property name="text">
Expand Down Expand Up @@ -45,44 +85,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="2">
<widget class="QgsPropertyOverrideButton" name="btnHeightDD">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Height</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>
<item row="6" column="0" colspan="3">
<widget class="QgsPhongMaterialWidget" name="widgetMaterial" native="true"/>
</item>
<item row="5" column="0" colspan="3">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QComboBox" name="cboAltClamping"> <widget class="QComboBox" name="cboAltClamping">
<item> <item>
Expand Down Expand Up @@ -116,32 +118,37 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="3" column="1">
<widget class="QLabel" name="label_5"> <widget class="QComboBox" name="cboAltBinding">
<property name="text">
<string>Culling Mode</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="cboCullingMode">
<item>
<property name="text">
<string>No culling</string>
</property>
</item>
<item> <item>
<property name="text"> <property name="text">
<string>Front</string> <string>Vertex</string>
</property> </property>
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>Back</string> <string>Centroid</string>
</property> </property>
</item> </item>
</widget> </widget>
</item> </item>
<item row="7" column="0" colspan="3">
<widget class="QgsPhongMaterialWidget" name="widgetMaterial" native="true"/>
</item>
<item row="6" column="0" colspan="3">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="chkInvertNormals">
<property name="text">
<string>Invert Normals (Experimental)</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>
Expand Down

0 comments on commit 057f2b3

Please sign in to comment.