Skip to content
Permalink
Browse files

[Feature] texturing support for vector layer (#36981)

* [Feature] texturing support for vector layer

* Fixed tesselation test not passing issue

* Fixed diffuse_texture_path typo

* Refactoring according to code review

* [Bugfix] the texture coordinates are not setup properly for certain walls

* Added seperation between roofs and walls

* [Feature] texture coordinates rotation

* took back the seperation of texture rotation between walls and roofs because the user will be specifying them with different symbols anyway

* Fixed docs test not passing issue

* Fixed compilation problem

* Fixed compilation problem

* Executed sipify_all trying to fix test not passing issue

* [ui] Harmonize play button across the board

* [ui] Better network logger icon

* [ui] Harmonize loop widget of 3D map view

* [ui] Add a record icon and use it in the network logger panel

* [ui] Move icon-less network logger toolbar actions under settings menu

* [ui] Reflect playback stay in the 3D map view's play button

* Fix azure

* Use prefix increment operator to fix cppcheck warnings related to postfixOperator

* [afs] Read field aliases from layer definition

* Fix ui build warning

* [FEATURE][processing] Add modeler algorithm to set a project expression variable

Allows a model to set Project-level expression variables during execution. Especially
useful with the new Export Print Layout algorithms to allow models which dynamically set variables
used in a layout prior to export.

* Add test

* Refactoring according to code review

Co-authored-by: nirvn <nirvn.asia@gmail.com>
Co-authored-by: Even Rouault <even.rouault@spatialys.com>
Co-authored-by: Nyall Dawson <nyall.dawson@gmail.com>
  • Loading branch information
4 people committed Jul 9, 2020
1 parent 1219f55 commit cc96e9e1c113dbae7c917f82805a652cd2143aea
@@ -45,6 +45,26 @@ Returns specular color component
float shininess() const;
%Docstring
Returns shininess of the surface
%End
bool isUsingDiffuseTexture() const;
%Docstring
Returns whether the diffuse texture is used
%End
QString texturePath() const;
%Docstring
Returns the diffuse texture path
%End

float textureScale() const;
%Docstring
Returns the texture scale
The texture scale changes the size of the displayed texture in the 3D scene
If the texture scale is less than 1 the texture will be stretched
%End

float textureRotation() const;
%Docstring
Returns the texture's rotation in degrees
%End

void setAmbient( const QColor &ambient );
@@ -62,6 +82,26 @@ Sets specular color component
void setShininess( float shininess );
%Docstring
Sets shininess of the surface
%End
void useTexture( bool used );
%Docstring
Sets whether the diffuse texture will be used
%End
void setTexturePath( QString texturePath );
%Docstring
Sets the path of the texture
%End

void setTextureScale( float scale );
%Docstring
Sets the texture scale
The texture scale changes the size of the displayed texture in the 3D scene
If the texture scale is less than 1 the texture will be stretched
%End

void setTextureRotation( float rotation );
%Docstring
Sets the texture rotation in degrees
%End

void readXml( const QDomElement &elem );
@@ -158,6 +158,20 @@ Returns edge lines color
Sets edge lines color

.. versionadded:: 3.8
%End

void setRenderedFacade( int side );
%Docstring
Sets which facade of the buildings is rendered (0 for None, 1 for Walls, 2 for Roofs, 3 for WallsAndRoofs)

.. versionadded:: 3.16
%End

int renderedFacade() const;
%Docstring
Returns which facade of the buildings is rendered (0 for None, 1 for Walls, 2 for Roofs, 3 for WallsAndRoofs)

.. versionadded:: 3.16
%End

};
@@ -27,12 +27,14 @@ Optionally provides extrusion by adding triangles that serve as walls when extru
#include "qgstessellator.h"
%End
public:
QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals = false, bool addBackFaces = false );
QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals = false, bool addBackFaces = false, bool noZ = false,
bool addTextureCoords = false, int facade = 3, float textureRotation = 0.0f );
%Docstring
Creates tessellator with a specified origin point of the world (in map coordinates)
%End

QgsTessellator( const QgsRectangle &bounds, bool addNormals, bool invertNormals = false, bool addBackFaces = false, bool noZ = false );
QgsTessellator( const QgsRectangle &bounds, bool addNormals, bool invertNormals = false, bool addBackFaces = false, bool noZ = false,
bool addTextureCoords = false, int facade = 3, float textureRotation = 0.0f );
%Docstring
Creates tessellator with a specified ``bounds`` of input geometry coordinates.
This constructor allows the tessellator to map input coordinates to a desirable range for numerical
@@ -24,6 +24,10 @@ void QgsPhongMaterialSettings::readXml( const QDomElement &elem )
mDiffuse = QgsSymbolLayerUtils::decodeColor( elem.attribute( QStringLiteral( "diffuse" ) ) );
mSpecular = QgsSymbolLayerUtils::decodeColor( elem.attribute( QStringLiteral( "specular" ) ) );
mShininess = elem.attribute( QStringLiteral( "shininess" ) ).toFloat();
mIsUsingDiffuseTexture = elem.attribute( QStringLiteral( "is_using_diffuse_texture" ), QStringLiteral( "0" ) ).toInt();
mTexturePath = elem.attribute( QStringLiteral( "diffuse_texture_path" ), QString() );
mTextureScale = elem.attribute( QStringLiteral( "texture_scale" ), QString( "1.0" ) ).toFloat();
mTextureRotation = elem.attribute( QStringLiteral( "texture-rotation" ), QString( "0.0" ) ).toFloat();
}

void QgsPhongMaterialSettings::writeXml( QDomElement &elem ) const
@@ -32,4 +36,8 @@ void QgsPhongMaterialSettings::writeXml( QDomElement &elem ) const
elem.setAttribute( QStringLiteral( "diffuse" ), QgsSymbolLayerUtils::encodeColor( mDiffuse ) );
elem.setAttribute( QStringLiteral( "specular" ), QgsSymbolLayerUtils::encodeColor( mSpecular ) );
elem.setAttribute( QStringLiteral( "shininess" ), mShininess );
elem.setAttribute( QStringLiteral( "is_using_diffuse_texture" ), mIsUsingDiffuseTexture );
elem.setAttribute( QStringLiteral( "diffuse_texture_path" ), mTexturePath );
elem.setAttribute( QStringLiteral( "texture_scale" ), mTextureScale );
elem.setAttribute( QStringLiteral( "texture-rotation" ), mTextureRotation );
}
@@ -39,6 +39,10 @@ class _3D_EXPORT QgsPhongMaterialSettings
: mAmbient( QColor::fromRgbF( 0.1f, 0.1f, 0.1f, 1.0f ) )
, mDiffuse( QColor::fromRgbF( 0.7f, 0.7f, 0.7f, 1.0f ) )
, mSpecular( QColor::fromRgbF( 1.0f, 1.0f, 1.0f, 1.0f ) )
, mIsUsingDiffuseTexture( false )
, mTexturePath( QString() )
, mTextureScale( 1.0f )
, mTextureRotation( 0.0f )
{
}

@@ -50,6 +54,20 @@ class _3D_EXPORT QgsPhongMaterialSettings
QColor specular() const { return mSpecular; }
//! Returns shininess of the surface
float shininess() const { return mShininess; }
//! Returns whether the diffuse texture is used
bool isUsingDiffuseTexture() const { return mIsUsingDiffuseTexture; }
//! Returns the diffuse texture path
QString texturePath() const { return mTexturePath; }

/**
* Returns the texture scale
* The texture scale changes the size of the displayed texture in the 3D scene
* If the texture scale is less than 1 the texture will be stretched
*/
float textureScale() const { return mTextureScale; }

//! Returns the texture's rotation in degrees
float textureRotation() const { return mTextureRotation; }

//! Sets ambient color component
void setAmbient( const QColor &ambient ) { mAmbient = ambient; }
@@ -59,6 +77,20 @@ class _3D_EXPORT QgsPhongMaterialSettings
void setSpecular( const QColor &specular ) { mSpecular = specular; }
//! Sets shininess of the surface
void setShininess( float shininess ) { mShininess = shininess; }
//! Sets whether the diffuse texture will be used
void useTexture( bool used ) { mIsUsingDiffuseTexture = used; }
//! Sets the path of the texture
void setTexturePath( QString texturePath ) { mTexturePath = texturePath; }

/**
* Sets the texture scale
* The texture scale changes the size of the displayed texture in the 3D scene
* If the texture scale is less than 1 the texture will be stretched
*/
void setTextureScale( float scale ) { mTextureScale = scale; }

//! Sets the texture rotation in degrees
void setTextureRotation( float rotation ) { mTextureRotation = rotation; }

//! Reads settings from a DOM element
void readXml( const QDomElement &elem );
@@ -70,14 +102,22 @@ class _3D_EXPORT QgsPhongMaterialSettings
return mAmbient == other.mAmbient &&
mDiffuse == other.mDiffuse &&
mSpecular == other.mSpecular &&
mShininess == other.mShininess;
mShininess == other.mShininess &&
mIsUsingDiffuseTexture == other.mIsUsingDiffuseTexture &&
mTexturePath == other.mTexturePath &&
mTextureScale == other.mTextureScale &&
mTextureRotation == other.mTextureRotation;
}

private:
QColor mAmbient;
QColor mDiffuse;
QColor mSpecular;
float mShininess = 0.0f;
bool mIsUsingDiffuseTexture;
QString mTexturePath;
float mTextureScale;
float mTextureRotation;
};


@@ -25,16 +25,20 @@
#include "qgspolygon.h"


QgsTessellatedPolygonGeometry::QgsTessellatedPolygonGeometry( QNode *parent )
: Qt3DRender::QGeometry( parent )
QgsTessellatedPolygonGeometry::QgsTessellatedPolygonGeometry( bool _withNormals, bool _invertNormals, bool _addBackFaces, bool _addTextureCoords, QNode *parent )
: Qt3DRender::QGeometry( parent ),
mWithNormals( _withNormals ),
mInvertNormals( _invertNormals ),
mAddBackFaces( _addBackFaces ),
mAddTextureCoords( _addTextureCoords )
{
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
mVertexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, this );
#else
mVertexBuffer = new Qt3DRender::QBuffer( this );
#endif

QgsTessellator tmpTess( 0, 0, mWithNormals );
QgsTessellator tmpTess( 0, 0, mWithNormals, false, false, false, mAddTextureCoords );
const int stride = tmpTess.stride();

mPositionAttribute = new Qt3DRender::QAttribute( this );
@@ -58,6 +62,18 @@ QgsTessellatedPolygonGeometry::QgsTessellatedPolygonGeometry( QNode *parent )
mNormalAttribute->setByteOffset( 3 * sizeof( float ) );
addAttribute( mNormalAttribute );
}
if ( mAddTextureCoords )
{
mTextureCoordsAttribute = new Qt3DRender::QAttribute( this );
mTextureCoordsAttribute->setName( Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName() );
mTextureCoordsAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
mTextureCoordsAttribute->setVertexSize( 2 );
mTextureCoordsAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
mTextureCoordsAttribute->setBuffer( mVertexBuffer );
mTextureCoordsAttribute->setByteStride( stride );
mTextureCoordsAttribute->setByteOffset( mWithNormals ? 6 * sizeof( float ) : 3 * sizeof( float ) );
addAttribute( mTextureCoordsAttribute );
}
}

void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygon *> &polygons, const QList<QgsFeatureId> &featureIds, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon )
@@ -66,7 +82,7 @@ void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygon *> &poly
mTriangleIndexStartingIndices.reserve( polygons.count() );
mTriangleIndexFids.reserve( polygons.count() );

QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals, mInvertNormals, mAddBackFaces );
QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals, mInvertNormals, mAddBackFaces, false, mAddTextureCoords );
for ( int i = 0; i < polygons.count(); ++i )
{
Q_ASSERT( tessellator.dataVerticesCount() % 3 == 0 );
@@ -88,6 +104,8 @@ void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygon *> &poly
mPositionAttribute->setCount( nVerts );
if ( mNormalAttribute )
mNormalAttribute->setCount( nVerts );
if ( mAddTextureCoords )
mTextureCoordsAttribute->setCount( nVerts );
}

void QgsTessellatedPolygonGeometry::setData( const QByteArray &vertexBufferData, int vertexCount, const QVector<QgsFeatureId> &triangleIndexFids, const QVector<uint> &triangleIndexStartingIndices )
@@ -99,6 +117,8 @@ void QgsTessellatedPolygonGeometry::setData( const QByteArray &vertexBufferData,
mPositionAttribute->setCount( vertexCount );
if ( mNormalAttribute )
mNormalAttribute->setCount( vertexCount );
if ( mTextureCoordsAttribute )
mTextureCoordsAttribute->setCount( vertexCount );
}


@@ -44,7 +44,7 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
Q_OBJECT
public:
//! Constructor
QgsTessellatedPolygonGeometry( QNode *parent = nullptr );
QgsTessellatedPolygonGeometry( bool _withNormals = true, bool invertNormals = false, bool addBackFaces = false, bool addTextureCoords = false, QNode *parent = nullptr );

//! Returns whether the normals of triangles will be inverted (useful for fixing clockwise / counter-clockwise face vertex orders)
bool invertNormals() const { return mInvertNormals; }
@@ -63,6 +63,12 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
*/
void setAddBackFaces( bool add ) { mAddBackFaces = add; }

/**
* Sets whether the texture coordinates will be generated
* \since QGIS 3.16
*/
void setAddTextureCoords( bool add ) { mAddTextureCoords = add; }

//! Initializes vertex buffer from given polygons. Takes ownership of passed polygon geometries
void setPolygons( const QList<QgsPolygon *> &polygons, const QList<QgsFeatureId> &featureIds, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon = QList<float>() );

@@ -83,6 +89,7 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry

Qt3DRender::QAttribute *mPositionAttribute = nullptr;
Qt3DRender::QAttribute *mNormalAttribute = nullptr;
Qt3DRender::QAttribute *mTextureCoordsAttribute = nullptr;
Qt3DRender::QBuffer *mVertexBuffer = nullptr;

QVector<QgsFeatureId> mTriangleIndexFids;
@@ -91,6 +98,7 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
bool mWithNormals = true;
bool mInvertNormals = false;
bool mAddBackFaces = false;
bool mAddTextureCoords = false;
};

#endif // QGSTESSELLATEDPOLYGONGEOMETRY_H
@@ -182,7 +182,7 @@ void QgsBufferedLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, cons
QByteArray data( ( const char * )out.tessellator->data().constData(), out.tessellator->data().count() * sizeof( float ) );
int nVerts = data.count() / out.tessellator->stride();

QgsTessellatedPolygonGeometry *geometry = new QgsTessellatedPolygonGeometry;
QgsTessellatedPolygonGeometry *geometry = new QgsTessellatedPolygonGeometry( false, false, false, false );
geometry->setData( data, nVerts, out.triangleIndexFids, out.triangleIndexStartingIndices );

Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
@@ -137,9 +137,7 @@ Qt3DRender::QGeometryRenderer *QgsMesh3DSymbolEntityNode::renderer( const Qgs3DM
// Polygons from mesh are already triangles, but
// call QgsTessellatedPolygonGeometry to
// use symbol settings for back faces, normals, etc
mGeometry = new QgsTessellatedPolygonGeometry;
mGeometry->setInvertNormals( false );
mGeometry->setAddBackFaces( symbol.addBackFaces() );
mGeometry = new QgsTessellatedPolygonGeometry( true, false, symbol.addBackFaces(), symbol.material().isUsingDiffuseTexture() );
QList<float> extrusionHeightPerPolygon;
mGeometry->setPolygons( polygons, fids, origin, 0.0, extrusionHeightPerPolygon );

@@ -37,6 +37,7 @@ void QgsPolygon3DSymbol::writeXml( QDomElement &elem, const QgsReadWriteContext
elemDataProperties.setAttribute( QStringLiteral( "culling-mode" ), Qgs3DUtils::cullingModeToString( mCullingMode ) );
elemDataProperties.setAttribute( QStringLiteral( "invert-normals" ), mInvertNormals ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
elemDataProperties.setAttribute( QStringLiteral( "add-back-faces" ), mAddBackFaces ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
elemDataProperties.setAttribute( QStringLiteral( "rendered-facade" ), mRenderedFacade );
elem.appendChild( elemDataProperties );

QDomElement elemMaterial = doc.createElement( QStringLiteral( "material" ) );
@@ -66,6 +67,7 @@ void QgsPolygon3DSymbol::readXml( const QDomElement &elem, const QgsReadWriteCon
mCullingMode = Qgs3DUtils::cullingModeFromString( elemDataProperties.attribute( QStringLiteral( "culling-mode" ) ) );
mInvertNormals = elemDataProperties.attribute( QStringLiteral( "invert-normals" ) ).toInt();
mAddBackFaces = elemDataProperties.attribute( QStringLiteral( "add-back-faces" ) ).toInt();
mRenderedFacade = elemDataProperties.attribute( QStringLiteral( "rendered-facade" ), "3" ).toInt();

QDomElement elemMaterial = elem.firstChildElement( QStringLiteral( "material" ) );
mMaterial.readXml( elemMaterial );
@@ -128,6 +128,18 @@ class _3D_EXPORT QgsPolygon3DSymbol : public QgsAbstract3DSymbol
*/
void setEdgeColor( const QColor &color ) { mEdgeColor = color; }

/**
* Sets which facade of the buildings is rendered (0 for None, 1 for Walls, 2 for Roofs, 3 for WallsAndRoofs)
* \since QGIS 3.16
*/
void setRenderedFacade( int side ) { mRenderedFacade = side; }

/**
* Returns which facade of the buildings is rendered (0 for None, 1 for Walls, 2 for Roofs, 3 for WallsAndRoofs)
* \since QGIS 3.16
*/
int renderedFacade() const { return mRenderedFacade; }

private:
//! how to handle altitude of vector features
Qgs3DTypes::AltitudeClamping mAltClamping = Qgs3DTypes::AltClampRelative;
@@ -140,6 +152,7 @@ class _3D_EXPORT QgsPolygon3DSymbol : public QgsAbstract3DSymbol
Qgs3DTypes::CullingMode mCullingMode = Qgs3DTypes::NoCulling; //!< Front/back culling mode
bool mInvertNormals = false;
bool mAddBackFaces = false;
int mRenderedFacade = 3;

bool mEdgesEnabled = false; //!< Whether to highlight edges
float mEdgeWidth = 1.f; //!< Width of edges in pixels

0 comments on commit cc96e9e

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