mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-11 00:04:27 -04:00
[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.
This commit is contained in:
parent
24c1c860a9
commit
057f2b324c
@ -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,30 +118,35 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Culling Mode</string>
|
||||
<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">
|
||||
<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="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>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="chkInvertNormals">
|
||||
<property name="text">
|
||||
<string>Invert Normals (Experimental)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
Loading…
x
Reference in New Issue
Block a user