Skip to content
Permalink
Browse files

[FEATURE] Simple rendering of 3D linestrings

This mode of 3D line rendering will use OpenGL line rendering
instead of buffering lines into polygons and rendering them as meshes.

The advantage is that the 3D lines do not loose their Z coordinate
which is the case currently with "ordinary" 3D rendering after buffering.

The disadvantage is that the lines cannot be wide (supported in Qt3D only
since 5.10, but even then their rendering won't have nice joins/caps)
and only ambient color is used from the material.
  • Loading branch information
wonder-sk committed Jul 29, 2018
1 parent 2e97b7b commit 6482a3b4d0f716915f0cecfb73c2692da582d7f4
@@ -149,6 +149,20 @@ Qt3DRender::QCullFace::CullingMode Qgs3DUtils::cullingModeFromString( const QStr
return Qt3DRender::QCullFace::NoCulling;
}

float Qgs3DUtils::clampAltitude( const QgsPoint &p, AltitudeClamping altClamp, AltitudeBinding altBind, float height, const QgsPoint &centroid, const Qgs3DMapSettings &map )
{
float terrainZ = 0;
if ( altClamp == AltClampRelative || altClamp == AltClampTerrain )
{
QgsPointXY pt = altBind == AltBindVertex ? p : centroid;
terrainZ = map.terrainGenerator()->heightAt( pt.x(), pt.y(), map );
}

float geomZ = altClamp == AltClampAbsolute || altClamp == AltClampRelative ? p.z() : 0;

float z = ( terrainZ + geomZ ) * map.terrainVerticalScale() + height;
return z;
}

void Qgs3DUtils::clampAltitudes( QgsLineString *lineString, AltitudeClamping altClamp, AltitudeBinding altBind, const QgsPoint &centroid, float height, const Qgs3DMapSettings &map )
{
@@ -81,6 +81,8 @@ class _3D_EXPORT Qgs3DUtils
//! Converts a string to a value from CullingMode enum
static Qt3DRender::QCullFace::CullingMode cullingModeFromString( const QString &str );

//! Clamps altitude of a vertex according to the settings, returns Z value
static float clampAltitude( const QgsPoint &p, AltitudeClamping altClamp, AltitudeBinding altBind, float height, const QgsPoint &centroid, const Qgs3DMapSettings &map );
//! Clamps altitude of vertices of a linestring according to the settings
static void clampAltitudes( QgsLineString *lineString, AltitudeClamping altClamp, AltitudeBinding altBind, const QgsPoint &centroid, float height, const Qgs3DMapSettings &map );
//! Clamps altitude of vertices of a polygon according to the settings
@@ -31,6 +31,7 @@ void QgsLine3DSymbol::writeXml( QDomElement &elem, const QgsReadWriteContext &co
elemDataProperties.setAttribute( QStringLiteral( "alt-binding" ), Qgs3DUtils::altBindingToString( mAltBinding ) );
elemDataProperties.setAttribute( QStringLiteral( "height" ), mHeight );
elemDataProperties.setAttribute( QStringLiteral( "extrusion-height" ), mExtrusionHeight );
elemDataProperties.setAttribute( QStringLiteral( "simple-lines" ), mRenderAsSimpleLines ? "1" : "0" );
elemDataProperties.setAttribute( QStringLiteral( "width" ), mWidth );
elem.appendChild( elemDataProperties );

@@ -49,6 +50,7 @@ void QgsLine3DSymbol::readXml( const QDomElement &elem, const QgsReadWriteContex
mHeight = elemDataProperties.attribute( QStringLiteral( "height" ) ).toFloat();
mExtrusionHeight = elemDataProperties.attribute( QStringLiteral( "extrusion-height" ) ).toFloat();
mWidth = elemDataProperties.attribute( QStringLiteral( "width" ) ).toFloat();
mRenderAsSimpleLines = elemDataProperties.attribute( QStringLiteral( "simple-lines" ), "0" ).toInt();

QDomElement elemMaterial = elem.firstChildElement( QStringLiteral( "material" ) );
mMaterial.readXml( elemMaterial );
@@ -65,6 +65,11 @@ class _3D_EXPORT QgsLine3DSymbol : public QgsAbstract3DSymbol
//! Sets extrusion height (in map units)
void setExtrusionHeight( float extrusionHeight ) { mExtrusionHeight = extrusionHeight; }

//! Returns whether the renderer will render data with simple lines (otherwise it uses buffer)
bool renderAsSimpleLines() const { return mRenderAsSimpleLines; }
//! Sets whether the renderer will render data with simple lines (otherwise it uses buffer)
void setRenderAsSimpleLines( bool enabled ) { mRenderAsSimpleLines = enabled; }

//! Returns material used for shading of the symbol
QgsPhongMaterialSettings material() const { return mMaterial; }
//! Sets material used for shading of the symbol
@@ -79,6 +84,7 @@ class _3D_EXPORT QgsLine3DSymbol : public QgsAbstract3DSymbol
float mWidth = 2.0f; //!< Line width (horizontally)
float mHeight = 0.0f; //!< Base height of polygons
float mExtrusionHeight = 0.0f; //!< How much to extrude (0 means no walls)
bool mRenderAsSimpleLines = false; //!< Whether to render data with simple lines (otherwise it uses buffer)
QgsPhongMaterialSettings mMaterial; //!< Defines appearance of objects
};

@@ -22,9 +22,13 @@
#include "qgs3dutils.h"

#include "qgsvectorlayer.h"
#include "qgsmultilinestring.h"
#include "qgsmultipolygon.h"
#include "qgsgeos.h"

#include <Qt3DRender/QAttribute>
#include <Qt3DRender/QBuffer>

/// @cond PRIVATE

QgsLine3DSymbolEntity::QgsLine3DSymbolEntity( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsLine3DSymbol &symbol, Qt3DCore::QNode *parent )
@@ -88,7 +92,7 @@ void QgsLine3DSymbolEntity::addEntityForNotSelectedLines( const Qgs3DMapSettings
QgsLine3DSymbolEntityNode::QgsLine3DSymbolEntityNode( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsLine3DSymbol &symbol, const QgsFeatureRequest &req, Qt3DCore::QNode *parent )
: Qt3DCore::QEntity( parent )
{
addComponent( renderer( map, symbol, layer, req ) );
addComponent( symbol.renderAsSimpleLines() ? rendererSimple( map, symbol, layer, req ) : renderer( map, symbol, layer, req ) );
}

Qt3DRender::QGeometryRenderer *QgsLine3DSymbolEntityNode::renderer( const Qgs3DMapSettings &map, const QgsLine3DSymbol &symbol, const QgsVectorLayer *layer, const QgsFeatureRequest &request )
@@ -150,4 +154,105 @@ Qt3DRender::QGeometryRenderer *QgsLine3DSymbolEntityNode::renderer( const Qgs3DM
return renderer;
}


Qt3DRender::QGeometryRenderer *QgsLine3DSymbolEntityNode::rendererSimple( const Qgs3DMapSettings &map, const QgsLine3DSymbol &symbol, const QgsVectorLayer *layer, const QgsFeatureRequest &request )
{
QVector<QVector3D> vertices;
vertices << QVector3D(); // the first index is invalid, we use it for primitive restart
QVector<unsigned int> indexes;

QgsPoint centroid;
QgsPointXY origin( map.origin().x(), map.origin().y() );
QgsFeature f;
QgsFeatureIterator fi = layer->getFeatures( request );
while ( fi.nextFeature( f ) )
{
if ( f.geometry().isNull() )
continue;

if ( symbol.altitudeBinding() == AltBindCentroid )
centroid = QgsPoint( f.geometry().centroid().asPoint() );

QgsGeometry geom = f.geometry();
const QgsAbstractGeometry *g = geom.constGet();
if ( QgsLineString *ls = qgsgeometry_cast<QgsLineString *>( g ) )
{
for ( int i = 0; i < ls->vertexCount(); ++i )
{
QgsPoint p = ls->vertexAt( QgsVertexId( 0, 0, i ) );
float z = Qgs3DUtils::clampAltitude( p, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), centroid, map );
vertices << QVector3D( p.x() - map.origin().x(), z, p.y() - map.origin().y() );
indexes << vertices.count() - 1;
}
}
else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
{
for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
{
const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) );
for ( int i = 0; i < ls->vertexCount(); ++i )
{
QgsPoint p = ls->vertexAt( QgsVertexId( 0, 0, i ) );
float z = Qgs3DUtils::clampAltitude( p, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), centroid, map );
vertices << QVector3D( p.x() - map.origin().x(), z, p.y() - map.origin().y() );
indexes << vertices.count() - 1;
}
indexes << 0; // add primitive restart
}
}

indexes << 0; // add primitive restart
}

QByteArray vertexBufferData;
vertexBufferData.resize( vertices.size() * 3 * sizeof( float ) );
float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
int idx = 0;
for ( const auto &v : vertices )
{
rawVertexArray[idx++] = v.x();
rawVertexArray[idx++] = v.y();
rawVertexArray[idx++] = v.z();
}

QByteArray indexBufferData;
indexBufferData.resize( indexes.size() * sizeof( int ) );
unsigned int *rawIndexArray = reinterpret_cast<unsigned int *>( indexBufferData.data() );
idx = 0;
for ( unsigned int indexVal : indexes )
{
rawIndexArray[idx++] = indexVal;
}

Qt3DRender::QBuffer *vertexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, this );
vertexBuffer->setData( vertexBufferData );

Qt3DRender::QBuffer *indexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::IndexBuffer, this );
indexBuffer->setData( indexBufferData );

Qt3DRender::QAttribute *positionAttribute = new Qt3DRender::QAttribute( this );
positionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
positionAttribute->setBuffer( vertexBuffer );
positionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
positionAttribute->setVertexSize( 3 );
positionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() );

Qt3DRender::QAttribute *indexAttribute = new Qt3DRender::QAttribute( this );
indexAttribute->setAttributeType( Qt3DRender::QAttribute::IndexAttribute );
indexAttribute->setBuffer( indexBuffer );
indexAttribute->setVertexBaseType( Qt3DRender::QAttribute::UnsignedInt );

Qt3DRender::QGeometry *geom = new Qt3DRender::QGeometry;
geom->addAttribute( positionAttribute );
geom->addAttribute( indexAttribute );

Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStrip );
renderer->setGeometry( geom );
renderer->setVertexCount( vertices.count() );
renderer->setPrimitiveRestartEnabled( true );
renderer->setRestartIndexValue( 0 );
return renderer;
}

/// @endcond
@@ -59,6 +59,7 @@ class QgsLine3DSymbolEntityNode : public Qt3DCore::QEntity

private:
Qt3DRender::QGeometryRenderer *renderer( const Qgs3DMapSettings &map, const QgsLine3DSymbol &symbol, const QgsVectorLayer *layer, const QgsFeatureRequest &req );
Qt3DRender::QGeometryRenderer *rendererSimple( const Qgs3DMapSettings &map, const QgsLine3DSymbol &symbol, const QgsVectorLayer *layer, const QgsFeatureRequest &request );

QgsTessellatedPolygonGeometry *mGeometry = nullptr;
};
@@ -34,6 +34,8 @@ QgsLine3DSymbolWidget::QgsLine3DSymbolWidget( QWidget *parent )
connect( spinExtrusion, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsLine3DSymbolWidget::changed );
connect( cboAltClamping, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLine3DSymbolWidget::changed );
connect( cboAltBinding, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLine3DSymbolWidget::changed );
connect( chkSimpleLines, &QCheckBox::clicked, this, &QgsLine3DSymbolWidget::changed );
connect( chkSimpleLines, &QCheckBox::clicked, this, &QgsLine3DSymbolWidget::updateGuiState );
connect( widgetMaterial, &QgsPhongMaterialWidget::changed, this, &QgsLine3DSymbolWidget::changed );
}

@@ -44,7 +46,9 @@ void QgsLine3DSymbolWidget::setSymbol( const QgsLine3DSymbol &symbol )
spinExtrusion->setValue( symbol.extrusionHeight() );
cboAltClamping->setCurrentIndex( ( int ) symbol.altitudeClamping() );
cboAltBinding->setCurrentIndex( ( int ) symbol.altitudeBinding() );
chkSimpleLines->setChecked( symbol.renderAsSimpleLines() );
widgetMaterial->setMaterial( symbol.material() );
updateGuiState();
}

QgsLine3DSymbol QgsLine3DSymbolWidget::symbol() const
@@ -55,6 +59,14 @@ QgsLine3DSymbol QgsLine3DSymbolWidget::symbol() const
sym.setExtrusionHeight( spinExtrusion->value() );
sym.setAltitudeClamping( ( AltitudeClamping ) cboAltClamping->currentIndex() );
sym.setAltitudeBinding( ( AltitudeBinding ) cboAltBinding->currentIndex() );
sym.setRenderAsSimpleLines( chkSimpleLines->isChecked() );
sym.setMaterial( widgetMaterial->material() );
return sym;
}

void QgsLine3DSymbolWidget::updateGuiState()
{
bool simple = chkSimpleLines->isChecked();
spinWidth->setEnabled( !simple );
spinExtrusion->setEnabled( !simple );
}
@@ -32,6 +32,9 @@ class QgsLine3DSymbolWidget : public QWidget, private Ui::Line3DSymbolWidget
void setSymbol( const QgsLine3DSymbol &symbol );
QgsLine3DSymbol symbol() const;

private slots:
void updateGuiState();

signals:
void changed();

@@ -110,6 +110,13 @@
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="chkSimpleLines">
<property name="text">
<string>Render as simple 3D lines</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">

0 comments on commit 6482a3b

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