1 /***************************************************************************
2 qgsmesh3dmaterial.cpp
3 -------------------------
4 begin : january 2020
5 copyright : (C) 2020 by Vincent Cloarec
6 email : vcloarec at gmail dot com
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "qgsmesh3dmaterial_p.h"
19
20 #include <Qt3DRender/QEffect>
21 #include <Qt3DRender/QGraphicsApiFilter>
22 #include <Qt3DRender/QParameter>
23 #include <Qt3DRender/QTexture>
24
25 #include <QUrl>
26 #include <QVector2D>
27 #include <QVector3D>
28 #include <QVector4D>
29 #include <Qt3DRender/QBuffer>
30 #include <QByteArray>
31
32 #include "qgsmeshlayer.h"
33 #include "qgsmeshlayerutils.h"
34 #include "qgstriangularmesh.h"
35
36 class ColorRampTextureGenerator: public Qt3DRender::QTextureImageDataGenerator
37 {
38
39 public:
ColorRampTextureGenerator(const QgsColorRampShader & colorRampShader,double verticalScale=1)40 ColorRampTextureGenerator( const QgsColorRampShader &colorRampShader, double verticalScale = 1 ):
41 mColorRampShader( colorRampShader ),
42 mVerticalScale( verticalScale )
43 {}
44
45 public:
operator ()()46 Qt3DRender::QTextureImageDataPtr operator()() override
47 {
48 Qt3DRender::QTextureImageDataPtr dataPtr = Qt3DRender::QTextureImageDataPtr::create();
49 dataPtr->setFormat( QOpenGLTexture::RGBA32F );
50 dataPtr->setTarget( QOpenGLTexture::Target1D );
51 dataPtr->setPixelFormat( QOpenGLTexture::RGBA );
52 dataPtr->setPixelType( QOpenGLTexture::Float32 );
53
54 QByteArray data;
55 QList<QgsColorRampShader::ColorRampItem> colorItemList = mColorRampShader.colorRampItemList();
56 int size = colorItemList.count() ;
57
58 dataPtr->setWidth( size );
59 dataPtr->setHeight( 1 );
60 dataPtr->setDepth( 1 );
61 dataPtr->setFaces( 1 );
62 dataPtr->setLayers( 1 );
63 dataPtr->setMipLevels( 1 );
64
65 for ( int i = 0; i < colorItemList.count(); ++i )
66 {
67 float mag = float( colorItemList.at( i ).value * mVerticalScale );
68
69 QColor color = colorItemList.at( i ).color;
70 float rf = float( color.redF() );
71 float gf = float( color.greenF() );
72 float bf = float( color.blueF() );
73
74 data.append( reinterpret_cast<const char *>( &mag ), sizeof( float ) );
75 data.append( reinterpret_cast<const char *>( &rf ), sizeof( float ) );
76 data.append( reinterpret_cast<const char *>( &gf ), sizeof( float ) );
77 data.append( reinterpret_cast<const char *>( &bf ), sizeof( float ) );
78
79 }
80
81 dataPtr->setData( data, sizeof( float ) ); //size is the size of the type, here float
82
83 return dataPtr;
84 }
85
operator ==(const Qt3DRender::QTextureImageDataGenerator & other) const86 bool operator ==( const Qt3DRender::QTextureImageDataGenerator &other ) const override
87 {
88 const ColorRampTextureGenerator *otherFunctor = functor_cast<ColorRampTextureGenerator>( &other );
89 if ( !otherFunctor )
90 return false;
91
92 QgsColorRampShader otherColorRampShader = otherFunctor->mColorRampShader;
93
94 if ( mColorRampShader.colorRampItemList().count() != otherColorRampShader.colorRampItemList().count() ||
95 mColorRampShader.classificationMode() != otherColorRampShader.classificationMode() ||
96 mColorRampShader.colorRampType() != otherColorRampShader.colorRampType() )
97 {
98 return false;
99 }
100
101 QList<QgsColorRampShader::ColorRampItem> colorItemList = mColorRampShader.colorRampItemList();
102 QList<QgsColorRampShader::ColorRampItem> otherColorItemList = otherColorRampShader.colorRampItemList();
103 for ( int i = 0; i < colorItemList.count(); ++i )
104 {
105 const QColor color = colorItemList.at( i ).color;
106 const QColor otherColor = otherColorItemList.at( i ).color;
107 double value = colorItemList.at( i ).value;
108 double otherValue = otherColorItemList.at( i ).value;
109 if ( color != otherColor ||
110 ( !std::isnan( value ) && !std::isnan( otherValue ) && colorItemList.at( i ).value != otherColorItemList.at( i ).value ) ||
111 ( std::isnan( value ) != std::isnan( otherValue ) ) )
112 return false;
113 }
114
115 return true;
116 }
117
118 QT3D_FUNCTOR( ColorRampTextureGenerator )
119
120 private:
121 QgsColorRampShader mColorRampShader;
122 double mVerticalScale = 1;
123 };
124
125
126 class ColorRampTexture: public Qt3DRender::QAbstractTextureImage
127 {
128 public:
ColorRampTexture(const QgsColorRampShader & colorRampShader,double verticalScale=1,Qt3DCore::QNode * parent=nullptr)129 ColorRampTexture( const QgsColorRampShader &colorRampShader, double verticalScale = 1, Qt3DCore::QNode *parent = nullptr ):
130 Qt3DRender::QAbstractTextureImage( parent ),
131 mColorRampShader( colorRampShader ),
132 mVerticalScale( verticalScale )
133 {
134
135 }
136 // QAbstractTextureImage interface
137 protected:
dataGenerator() const138 Qt3DRender::QTextureImageDataGeneratorPtr dataGenerator() const override
139 {
140 return Qt3DRender::QTextureImageDataGeneratorPtr( new ColorRampTextureGenerator( mColorRampShader, mVerticalScale ) );
141 }
142
143 private:
144 QgsColorRampShader mColorRampShader;
145 double mVerticalScale = 1;
146 };
147
148
149 class ArrowsTextureGenerator: public Qt3DRender::QTextureImageDataGenerator
150 {
151 public:
ArrowsTextureGenerator(const QVector<QgsVector> & vectors,const QSize & size,bool fixedSize,double maxVectorLength)152 ArrowsTextureGenerator( const QVector<QgsVector> &vectors, const QSize &size, bool fixedSize, double maxVectorLength ):
153 mVectors( vectors ), mSize( size ), mFixedSize( fixedSize ), mMaxVectorLength( maxVectorLength )
154 {}
155
operator ()()156 Qt3DRender::QTextureImageDataPtr operator()() override
157 {
158 Qt3DRender::QTextureImageDataPtr dataPtr = Qt3DRender::QTextureImageDataPtr::create();
159 dataPtr->setFormat( QOpenGLTexture::RG32F );
160 dataPtr->setTarget( QOpenGLTexture::Target2D );
161 dataPtr->setPixelFormat( QOpenGLTexture::RG );
162 dataPtr->setPixelType( QOpenGLTexture::Float32 );
163
164 QByteArray data;
165
166 dataPtr->setWidth( mSize.width() );
167 dataPtr->setHeight( mSize.height() );
168 dataPtr->setDepth( 1 );
169 dataPtr->setFaces( 1 );
170 dataPtr->setLayers( 1 );
171 dataPtr->setMipLevels( 1 );
172
173 if ( mSize.isValid() )
174 {
175 data.resize( 2 * mSize.width()*mSize.height()*sizeof( float ) );
176 float *fptr = reinterpret_cast<float *>( data.data() );
177 for ( int i = 0; i < mSize.width()*mSize.height(); ++i )
178 {
179 if ( mFixedSize )
180 *fptr++ = 1;
181 else
182 *fptr++ = mVectors.at( i ).length() / mMaxVectorLength;
183
184 *fptr++ = mVectors.at( i ).angle();
185 }
186 }
187
188 dataPtr->setData( data, sizeof( float ) ); //size is the size of the type, here float
189 return dataPtr;
190 }
191
operator ==(const Qt3DRender::QTextureImageDataGenerator & other) const192 bool operator ==( const Qt3DRender::QTextureImageDataGenerator &other ) const override
193 {
194 const ArrowsTextureGenerator *otherFunctor = functor_cast<ArrowsTextureGenerator>( &other );
195 if ( !otherFunctor )
196 return false;
197
198 return ( otherFunctor->mVectors == mVectors &&
199 otherFunctor->mSize == mSize &&
200 otherFunctor->mFixedSize == mFixedSize );
201 }
202
203 private:
204 const QVector<QgsVector> mVectors;
205 const QSize mSize;
206 const bool mFixedSize;
207 const double mMaxVectorLength;
208
209 QT3D_FUNCTOR( ArrowsTextureGenerator )
210 };
211
212
213 class ArrowsGridTexture: public Qt3DRender::QAbstractTextureImage
214 {
215 public:
ArrowsGridTexture(const QVector<QgsVector> & vectors,const QSize & size,bool fixedSize,double maxVectorLength)216 ArrowsGridTexture( const QVector<QgsVector> &vectors, const QSize &size, bool fixedSize, double maxVectorLength ):
217 mVectors( vectors ), mSize( size ), mFixedSize( fixedSize ), mMaxVectorLength( maxVectorLength )
218 {}
219
220 protected:
dataGenerator() const221 Qt3DRender::QTextureImageDataGeneratorPtr dataGenerator() const override
222 {
223 return Qt3DRender::QTextureImageDataGeneratorPtr( new ArrowsTextureGenerator( mVectors, mSize, mFixedSize, mMaxVectorLength ) );
224 }
225
226 private:
227 const QVector<QgsVector> mVectors;
228 const QSize mSize;
229 const bool mFixedSize;
230 const double mMaxVectorLength;
231 };
232
233
QgsMesh3dMaterial(QgsMeshLayer * layer,const QgsDateTimeRange & timeRange,const QgsVector3D & origin,const QgsMesh3DSymbol * symbol,MagnitudeType magnitudeType)234 QgsMesh3dMaterial::QgsMesh3dMaterial( QgsMeshLayer *layer,
235 const QgsDateTimeRange &timeRange,
236 const QgsVector3D &origin,
237 const QgsMesh3DSymbol *symbol,
238 MagnitudeType magnitudeType )
239 : mSymbol( symbol->clone() )
240 , mMagnitudeType( magnitudeType )
241 , mOrigin( origin )
242 {
243 Qt3DRender::QEffect *eff = new Qt3DRender::QEffect( this );
244
245 configure();
246
247 // this method has to be called even if there isn't arrows (terrain) because it configures the parameter of shaders
248 // If al the parameters ("uniform" in shaders) are not defined in QGis, the shaders it happens the sahder doesn't work (depend on hardware?)
249 configureArrows( layer, timeRange );
250
251 eff->addTechnique( mTechnique );
252 setEffect( eff );
253 }
254
configure()255 void QgsMesh3dMaterial::configure()
256 {
257 // Create the texture to pass the color ramp
258 Qt3DRender::QTexture1D *colorRampTexture = nullptr;
259 if ( mSymbol->colorRampShader().colorRampItemList().count() > 0 )
260 {
261 colorRampTexture = new Qt3DRender::QTexture1D( this );
262 switch ( mMagnitudeType )
263 {
264 case QgsMesh3dMaterial::ZValue:
265 // if the color shading is done with the Z value of vertices, the color ramp has to be adapted with vertical scale
266 colorRampTexture->addTextureImage( new ColorRampTexture( mSymbol->colorRampShader(), mSymbol->verticalScale() ) );
267 break;
268 case QgsMesh3dMaterial::ScalarDataSet:
269 // if the color shading is done with scalar dataset, no vertical scale to use
270 colorRampTexture->addTextureImage( new ColorRampTexture( mSymbol->colorRampShader(), 1 ) );
271 break;
272 }
273
274 colorRampTexture->setMinificationFilter( Qt3DRender::QTexture1D::Linear );
275 colorRampTexture->setMagnificationFilter( Qt3DRender::QTexture1D::Linear );
276 }
277
278 // Create and configure technique
279 mTechnique = new Qt3DRender::QTechnique();
280 mTechnique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL );
281 mTechnique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile );
282 mTechnique->graphicsApiFilter()->setMajorVersion( 3 );
283 mTechnique->graphicsApiFilter()->setMinorVersion( 3 );
284 Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey();
285 filterKey->setName( QStringLiteral( "renderingStyle" ) );
286 filterKey->setValue( QStringLiteral( "forward" ) );
287 mTechnique->addFilterKey( filterKey );
288
289 Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass();
290 Qt3DRender::QShaderProgram *shaderProgram = new Qt3DRender::QShaderProgram();
291
292 //Load shader programs
293 QUrl urlVert( QStringLiteral( "qrc:/shaders/mesh/mesh.vert" ) );
294 shaderProgram->setShaderCode( Qt3DRender::QShaderProgram::Vertex, shaderProgram->loadSource( urlVert ) );
295 QUrl urlGeom( QStringLiteral( "qrc:/shaders/mesh/mesh.geom" ) );
296 shaderProgram->setShaderCode( Qt3DRender::QShaderProgram::Geometry, shaderProgram->loadSource( urlGeom ) );
297 QUrl urlFrag( QStringLiteral( "qrc:/shaders/mesh/mesh.frag" ) );
298 shaderProgram->setShaderCode( Qt3DRender::QShaderProgram::Fragment, shaderProgram->loadSource( urlFrag ) );
299
300 renderPass->setShaderProgram( shaderProgram );
301 mTechnique->addRenderPass( renderPass );
302
303 // Parameters
304 mTechnique->addParameter( new Qt3DRender::QParameter( "flatTriangles", ( !mSymbol->smoothedTriangles() ) ) );
305 QColor wireframecolor = mSymbol->wireframeLineColor();
306 mTechnique->addParameter( new Qt3DRender::QParameter( "lineWidth", float( mSymbol->wireframeLineWidth() ) ) );
307 mTechnique->addParameter( new Qt3DRender::QParameter( "lineColor", QVector4D( wireframecolor.redF(), wireframecolor.greenF(), wireframecolor.blueF(), 1.0f ) ) );
308 mTechnique->addParameter( new Qt3DRender::QParameter( "wireframeEnabled", mSymbol->wireframeEnabled() ) );
309 mTechnique->addParameter( new Qt3DRender::QParameter( "textureType", int( mSymbol->renderingStyle() ) ) );
310 if ( colorRampTexture )
311 mTechnique->addParameter( new Qt3DRender::QParameter( "colorRampTexture", colorRampTexture ) ) ;
312 mTechnique->addParameter( new Qt3DRender::QParameter( "colorRampCount", mSymbol->colorRampShader().colorRampItemList().count() ) );
313 int colorRampType = mSymbol->colorRampShader().colorRampType();
314 mTechnique->addParameter( new Qt3DRender::QParameter( "colorRampType", colorRampType ) );
315 QColor meshColor = mSymbol->singleMeshColor();
316 mTechnique->addParameter( new Qt3DRender::QParameter( "meshColor", QVector4D( meshColor.redF(), meshColor.greenF(), meshColor.blueF(), 1.0f ) ) );
317 mTechnique->addParameter( new Qt3DRender::QParameter( "isScalarMagnitude", ( mMagnitudeType == QgsMesh3dMaterial::ScalarDataSet ) ) );
318 }
319
configureArrows(QgsMeshLayer * layer,const QgsDateTimeRange & timeRange)320 void QgsMesh3dMaterial::configureArrows( QgsMeshLayer *layer, const QgsDateTimeRange &timeRange )
321 {
322 QgsMeshDatasetIndex datasetIndex;
323 QColor arrowsColor;
324 QgsMeshDatasetGroupMetadata meta;
325
326 if ( layer )
327 datasetIndex = layer->activeVectorDatasetAtTime( timeRange );
328
329 QVector<QgsVector> vectors;
330 QSize gridSize;
331 QgsPointXY minCorner;
332 std::unique_ptr< Qt3DRender::QParameter > arrowsEnabledParameter = qgis::make_unique< Qt3DRender::QParameter >( "arrowsEnabled", nullptr );
333 if ( !layer || mMagnitudeType != MagnitudeType::ScalarDataSet || !mSymbol->arrowsEnabled() || meta.isScalar() || !datasetIndex.isValid() )
334 arrowsEnabledParameter->setValue( false );
335 else
336 {
337 meta = layer->datasetGroupMetadata( datasetIndex );
338 arrowsColor = layer->rendererSettings().vectorSettings( datasetIndex.group() ).color();
339 arrowsEnabledParameter->setValue( true );
340 int maxSize = mSymbol->maximumTextureSize();
341 // construct grid
342 QgsRectangle gridExtent = layer->triangularMesh()->extent();
343 gridSize = QSize( maxSize, maxSize );
344 double xSpacing = mSymbol->arrowsSpacing();
345 double ySpacing = mSymbol->arrowsSpacing();
346 // check the size of the grid and adjust the spacing if needed
347 int desiredXSize = int( gridExtent.width() / xSpacing );
348 if ( desiredXSize > maxSize )
349 xSpacing = gridExtent.width() / maxSize;
350 else
351 gridSize.setWidth( desiredXSize );
352
353 int desiredYSize = int( gridExtent.height() / ySpacing );
354 if ( desiredYSize > maxSize )
355 ySpacing = gridExtent.height() / maxSize;
356 else
357 gridSize.setHeight( desiredYSize );
358
359 double xMin = gridExtent.xMinimum() + xSpacing / 2;
360 double yMin = gridExtent.yMinimum() + ySpacing / 2;
361 minCorner = QgsPointXY( xMin, yMin );
362
363 vectors = QgsMeshLayerUtils::griddedVectorValues(
364 layer,
365 datasetIndex,
366 xSpacing,
367 ySpacing,
368 gridSize,
369 minCorner );
370
371 if ( vectors.isEmpty() )
372 return;
373 }
374
375 mTechnique->addParameter( arrowsEnabledParameter.release() ) ;
376
377 Qt3DRender::QTexture2D *arrowsGridTexture = new Qt3DRender::QTexture2D( this );
378 arrowsGridTexture->addTextureImage( new ArrowsGridTexture( vectors, gridSize, mSymbol->arrowsFixedSize(), meta.maximum() ) );
379 arrowsGridTexture->setMinificationFilter( Qt3DRender::QTexture2D::Nearest );
380 arrowsGridTexture->setMagnificationFilter( Qt3DRender::QTexture2D::Nearest );
381
382 Qt3DRender::QTexture2D *arrowTexture = new Qt3DRender::QTexture2D( this );
383 Qt3DRender::QTextureImage *arrowTextureImage = new Qt3DRender::QTextureImage();
384 arrowTextureImage->setSource( QStringLiteral( "qrc:/textures/arrow.png" ) );
385 arrowTexture->addTextureImage( arrowTextureImage );
386 arrowTexture->setMinificationFilter( Qt3DRender::QTexture2D::Nearest );
387 arrowTexture->setMagnificationFilter( Qt3DRender::QTexture2D::Nearest );
388 mTechnique->addParameter( new Qt3DRender::QParameter( "arrowsColor", QVector4D( arrowsColor.redF(), arrowsColor.greenF(), arrowsColor.blueF(), 1.0f ) ) ) ;
389 mTechnique->addParameter( new Qt3DRender::QParameter( "arrowsSpacing", float( mSymbol->arrowsSpacing() ) ) ) ;
390 mTechnique->addParameter( new Qt3DRender::QParameter( "arrowTexture", arrowTexture ) );
391 mTechnique->addParameter( new Qt3DRender::QParameter( "arrowsGridTexture", arrowsGridTexture ) ) ;
392 mTechnique->addParameter( new Qt3DRender::QParameter( "arrowsMinCorner", QVector2D( minCorner.x() - mOrigin.x(), -minCorner.y() + mOrigin.y() ) ) ) ;
393 }
394