Skip to content
Permalink
Browse files

[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.
  • Loading branch information
wonder-sk committed Dec 10, 2017
1 parent 24c1c86 commit 057f2b324c55e0dc1e077686f86776b3b7364bb2
@@ -62,7 +62,7 @@ QgsTessellatedPolygonGeometry::~QgsTessellatedPolygonGeometry()

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 )
{
QgsPolygon *polygon = polygons.at( i );
@@ -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<QgsPolygon *> &polygons, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon = QList<float>() );

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

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

#endif // QGSTESSELLATEDPOLYGONGEOMETRY_H
@@ -31,6 +31,7 @@
#include <QVector3D>
#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 )
{
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 )
@@ -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<float> mData;
int mStride;
};
@@ -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 );
@@ -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;
};


@@ -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;
@@ -32,6 +32,7 @@ QgsPolygon3DSymbolWidget::QgsPolygon3DSymbolWidget( QWidget *parent )
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( 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( 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;
@@ -14,6 +14,46 @@
<string>Form</string>
</property>
<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">
<widget class="QLabel" name="label_3">
<property name="text">
@@ -45,44 +85,6 @@
</property>
</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="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">
<widget class="QComboBox" name="cboAltClamping">
<item>
@@ -116,32 +118,37 @@
</property>
</widget>
</item>
<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 row="3" column="1">
<widget class="QComboBox" name="cboAltBinding">
<item>
<property name="text">
<string>Front</string>
<string>Vertex</string>
</property>
</item>
<item>
<property name="text">
<string>Back</string>
<string>Centroid</string>
</property>
</item>
</widget>
</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>
</widget>
<customwidgets>

0 comments on commit 057f2b3

Please sign in to comment.
You can’t perform that action at this time.