mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-27 00:33:48 -05:00
[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.
This commit is contained in:
parent
2e97b7bcd4
commit
6482a3b4d0
@ -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 ¢roid, 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 ¢roid, 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 ¢roid, 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 ¢roid, 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">
|
||||
|
Loading…
x
Reference in New Issue
Block a user