Skip to content

Commit

Permalink
[FEATURE] Optionally add back faces of polygons in tessellator
Browse files Browse the repository at this point in the history
Often the polygonZ/multipatch data do not have consistent ordering of vertices
(e.g. all clock-wise or counter clock-wise). Disabling culling helps to avoid
seemingly missing surfaces, but the shading is still not correct due to reversed
normals. This new option to add back faces fixes the problem: for each triangle
we create both front and back face with correct normals - at the expense of increased
number of vertex data.
  • Loading branch information
wonder-sk committed May 8, 2018
1 parent 00d7ab6 commit 875da3f
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 64 deletions.
2 changes: 1 addition & 1 deletion src/3d/qgstessellatedpolygongeometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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, mInvertNormals );
QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals, mInvertNormals, mAddBackFaces );
for ( int i = 0; i < polygons.count(); ++i )
{
QgsPolygon *polygon = polygons.at( i );
Expand Down
6 changes: 6 additions & 0 deletions src/3d/qgstessellatedpolygongeometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
//! Sets whether the normals of triangles will be inverted (useful for fixing clockwise / counter-clockwise face vertex orders)
void setInvertNormals( bool invert ) { mInvertNormals = invert; }

//! Returns whether also triangles facing the other side will be created. Useful if input data have inconsistent order of vertices
bool addBackFaces() const { return mAddBackFaces; }
//! Sets whether also triangles facing the other side will be created. Useful if input data have inconsistent order of vertices
void setAddBackFaces( bool add ) { mAddBackFaces = add; }

//! 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>() );

Expand All @@ -57,6 +62,7 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry

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

#endif // QGSTESSELLATEDPOLYGONGEOMETRY_H
33 changes: 32 additions & 1 deletion src/3d/qgstessellator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,12 @@ static void make_quad( float x0, float y0, float z0, float x1, float y1, float z
}


QgsTessellator::QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals )
QgsTessellator::QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals, bool addBackFaces )
: mOriginX( originX )
, mOriginY( originY )
, mAddNormals( addNormals )
, mInvertNormals( invertNormals )
, mAddBackFaces( addBackFaces )
{
mStride = 3 * sizeof( float );
if ( addNormals )
Expand Down Expand Up @@ -368,6 +369,18 @@ void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeigh
if ( mAddNormals )
mData << pNormal.x() << pNormal.z() << - pNormal.y();
}

if ( mAddBackFaces )
{
// the same triangle with reversed order of coordinates and inverted normal
for ( int i = 2; i >= 0; i-- )
{
exterior->pointAt( i, pt, vt );
mData << pt.x() - mOriginX << pt.z() << - pt.y() + mOriginY;
if ( mAddNormals )
mData << -pNormal.x() << -pNormal.z() << pNormal.y();
}
}
}
else
{
Expand Down Expand Up @@ -476,6 +489,24 @@ void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeigh
if ( mAddNormals )
mData << pNormal.x() << pNormal.z() << - pNormal.y();
}

if ( mAddBackFaces )
{
// the same triangle with reversed order of coordinates and inverted normal
for ( int j = 2; j >= 0; --j )
{
p2t::Point *p = t->GetPoint( j );
QVector4D pt( p->x, p->y, z[p], 0 );
if ( toOldBase )
pt = *toOldBase * pt;
const double fx = pt.x() - mOriginX + pt0.x();
const double fy = pt.y() - mOriginY + pt0.y();
const double fz = pt.z() + extrusionHeight + pt0.z();
mData << fx << fz << -fy;
if ( mAddNormals )
mData << -pNormal.x() << -pNormal.z() << pNormal.y();
}
}
}
}
catch ( ... )
Expand Down
3 changes: 2 additions & 1 deletion src/3d/qgstessellator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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, bool invertNormals = false );
QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals = false, bool addBackFaces = false );

//! Tessellates a triangle and adds its vertex entries to the output data array
void addPolygon( const QgsPolygon &polygon, float extrusionHeight );
Expand All @@ -58,6 +58,7 @@ class _3D_EXPORT QgsTessellator
double mOriginX, mOriginY;
bool mAddNormals;
bool mInvertNormals;
bool mAddBackFaces;
QVector<float> mData;
int mStride;
};
Expand Down
2 changes: 2 additions & 0 deletions src/3d/symbols/qgspolygon3dsymbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ void QgsPolygon3DSymbol::writeXml( QDomElement &elem, const QgsReadWriteContext
elemDataProperties.setAttribute( QStringLiteral( "extrusion-height" ), mExtrusionHeight );
elemDataProperties.setAttribute( QStringLiteral( "culling-mode" ), Qgs3DUtils::cullingModeToString( mCullingMode ) );
elemDataProperties.setAttribute( QStringLiteral( "invert-normals" ), mInvertNormals ? "1" : "0" );
elemDataProperties.setAttribute( QStringLiteral( "add-back-faces" ), mAddBackFaces ? "1" : "0" );
elem.appendChild( elemDataProperties );

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

QDomElement elemMaterial = elem.firstChildElement( QStringLiteral( "material" ) );
mMaterial.readXml( elemMaterial );
Expand Down
13 changes: 13 additions & 0 deletions src/3d/symbols/qgspolygon3dsymbol.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@ class _3D_EXPORT QgsPolygon3DSymbol : public QgsAbstract3DSymbol
//! Sets whether the normals of triangles will be inverted (useful for fixing clockwise / counter-clockwise face vertex orders)
void setInvertNormals( bool invert ) { mInvertNormals = invert; }

/**
* Returns whether also triangles facing the other side will be created. Useful if input data have inconsistent order of vertices
* \since QGIS 3.2
*/
bool addBackFaces() const { return mAddBackFaces; }

/**
* Sets whether also triangles facing the other side will be created. Useful if input data have inconsistent order of vertices
* \since QGIS 3.2
*/
void setAddBackFaces( bool add ) { mAddBackFaces = add; }

private:
//! how to handle altitude of vector features
AltitudeClamping mAltClamping = AltClampRelative;
Expand All @@ -87,6 +99,7 @@ class _3D_EXPORT QgsPolygon3DSymbol : public QgsAbstract3DSymbol
QgsPhongMaterialSettings mMaterial; //!< Defines appearance of objects
Qt3DRender::QCullFace::CullingMode mCullingMode = Qt3DRender::QCullFace::NoCulling; //!< Front/back culling mode
bool mInvertNormals = false;
bool mAddBackFaces = false;
};


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

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

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

Expand All @@ -86,6 +88,7 @@ QgsPolygon3DSymbol QgsPolygon3DSymbolWidget::symbol() const
sym.setAltitudeClamping( ( AltitudeClamping ) cboAltClamping->currentIndex() );
sym.setAltitudeBinding( ( AltitudeBinding ) cboAltBinding->currentIndex() );
sym.setCullingMode( _cullingModeFromIndex( cboCullingMode->currentIndex() ) );
sym.setAddBackFaces( chkAddBackFaces->isChecked() );
sym.setInvertNormals( chkInvertNormals->isChecked() );
sym.setMaterial( widgetMaterial->material() );

Expand Down
132 changes: 71 additions & 61 deletions src/ui/3d/polygon3dsymbolwidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,58 @@
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<item row="6" column="0" colspan="2">
<widget class="QCheckBox" name="chkInvertNormals">
<property name="text">
<string>Culling Mode</string>
<string>Invert Normals (Experimental)</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="cboCullingMode">
<item row="0" column="2">
<widget class="QgsPropertyOverrideButton" name="btnHeightDD">
<property name="text">
<string>...</string>
</property>
</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>No culling</string>
<string>Vertex</string>
</property>
</item>
<item>
<property name="text">
<string>Front</string>
<string>Centroid</string>
</property>
</item>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cboAltClamping">
<item>
<property name="text">
<string>Back</string>
<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="0" column="2">
<widget class="QgsPropertyOverrideButton" name="btnHeightDD">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="0" column="0">
Expand All @@ -54,18 +75,8 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Altitude Clamping</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QgsDoubleSpinBox" name="spinHeight">
<property name="minimum">
<double>-99999.000000000000000</double>
</property>
<item row="1" column="1">
<widget class="QgsDoubleSpinBox" name="spinExtrusion">
<property name="maximum">
<double>99999.000000000000000</double>
</property>
Expand All @@ -78,74 +89,70 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Altitude Clamping</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QgsPropertyOverrideButton" name="btnExtrusionDD">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cboAltClamping">
<item row="4" column="1">
<widget class="QComboBox" name="cboCullingMode">
<item>
<property name="text">
<string>Absolute</string>
<string>No culling</string>
</property>
</item>
<item>
<property name="text">
<string>Relative</string>
<string>Front</string>
</property>
</item>
<item>
<property name="text">
<string>Terrain</string>
<string>Back</string>
</property>
</item>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Altitude Binding</string>
<string>Culling Mode</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QgsDoubleSpinBox" name="spinExtrusion">
<property name="maximum">
<double>99999.000000000000000</double>
<item row="7" column="0" colspan="3">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</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="7" column="0" colspan="3">
<item row="8" 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>
<item row="0" column="1">
<widget class="QgsDoubleSpinBox" name="spinHeight">
<property name="minimum">
<double>-99999.000000000000000</double>
</property>
<property name="maximum">
<double>99999.000000000000000</double>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="chkInvertNormals">
<item row="5" column="0">
<widget class="QCheckBox" name="chkAddBackFaces">
<property name="text">
<string>Invert Normals (Experimental)</string>
<string>Add back faces</string>
</property>
</widget>
</item>
Expand Down Expand Up @@ -176,6 +183,9 @@
<tabstop>btnExtrusionDD</tabstop>
<tabstop>cboAltClamping</tabstop>
<tabstop>cboAltBinding</tabstop>
<tabstop>cboCullingMode</tabstop>
<tabstop>chkAddBackFaces</tabstop>
<tabstop>chkInvertNormals</tabstop>
</tabstops>
<resources/>
<connections/>
Expand Down
Loading

0 comments on commit 875da3f

Please sign in to comment.