QGIS API Documentation  3.2.0-Bonn (bc43194)
qgspoint3dsymbol_p.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspoint3dsymbol_p.cpp
3  --------------------------------------
4  Date : July 2017
5  Copyright : (C) 2017 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgspoint3dsymbol_p.h"
17 
18 #include <Qt3DRender/QAttribute>
19 #include <Qt3DRender/QBuffer>
20 #include <Qt3DRender/QEffect>
21 #include <Qt3DRender/QGraphicsApiFilter>
22 #include <Qt3DRender/QParameter>
23 #include <Qt3DRender/QTechnique>
24 
25 #include <Qt3DExtras/QCylinderGeometry>
26 #include <Qt3DExtras/QConeGeometry>
27 #include <Qt3DExtras/QCuboidGeometry>
28 #include <Qt3DExtras/QPlaneGeometry>
29 #include <Qt3DExtras/QSphereGeometry>
30 #include <Qt3DExtras/QTorusGeometry>
31 #include <Qt3DExtras/QPhongMaterial>
32 #include <Qt3DRender/QSceneLoader>
33 
34 #include <Qt3DRender/QMesh>
35 
36 #if QT_VERSION >= 0x050900
37 #include <Qt3DExtras/QExtrudedTextGeometry>
38 #endif
39 
40 #include <QUrl>
41 #include <QVector3D>
42 
43 #include "qgspoint3dsymbol.h"
44 #include "qgs3dmapsettings.h"
45 
46 #include "qgsvectorlayer.h"
47 #include "qgspoint.h"
48 #include "qgs3dutils.h"
49 
51 
52 QgsPoint3DSymbolEntity::QgsPoint3DSymbolEntity( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPoint3DSymbol &symbol, Qt3DCore::QNode *parent )
53  : Qt3DCore::QEntity( parent )
54 {
55  if ( symbol.shape() == QgsPoint3DSymbol::Model )
56  {
57  QgsPoint3DSymbolModelEntityFactory::addEntitiesForSelectedPoints( map, layer, symbol, this );
58  QgsPoint3DSymbolModelEntityFactory::addEntitiesForNotSelectedPoints( map, layer, symbol, this );
59  }
60  else
61  {
62  QgsPoint3DSymbolInstancedEntityFactory::addEntityForNotSelectedPoints( map, layer, symbol, this );
63  QgsPoint3DSymbolInstancedEntityFactory::addEntityForSelectedPoints( map, layer, symbol, this );
64  }
65 }
66 
67 //* INSTANCED RENDERING *//
68 
69 Qt3DRender::QMaterial *QgsPoint3DSymbolInstancedEntityFactory::material( const QgsPoint3DSymbol &symbol )
70 {
71  Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey;
72  filterKey->setName( "renderingStyle" );
73  filterKey->setValue( "forward" );
74 
75  // the fragment shader implements a simplified version of phong shading that uses hardcoded light
76  // (instead of whatever light we have defined in the scene)
77  // TODO: use phong shading that respects lights from the scene
78  Qt3DRender::QShaderProgram *shaderProgram = new Qt3DRender::QShaderProgram;
79  shaderProgram->setVertexShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( "qrc:/shaders/instanced.vert" ) ) );
80  shaderProgram->setFragmentShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( "qrc:/shaders/instanced.frag" ) ) );
81 
82  Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass;
83  renderPass->setShaderProgram( shaderProgram );
84 
85  Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique;
86  technique->addFilterKey( filterKey );
87  technique->addRenderPass( renderPass );
88  technique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL );
89  technique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile );
90  technique->graphicsApiFilter()->setMajorVersion( 3 );
91  technique->graphicsApiFilter()->setMinorVersion( 2 );
92 
93  Qt3DRender::QParameter *ambientParameter = new Qt3DRender::QParameter( QStringLiteral( "ka" ), QColor::fromRgbF( 0.05f, 0.05f, 0.05f, 1.0f ) );
94  Qt3DRender::QParameter *diffuseParameter = new Qt3DRender::QParameter( QStringLiteral( "kd" ), QColor::fromRgbF( 0.7f, 0.7f, 0.7f, 1.0f ) );
95  Qt3DRender::QParameter *specularParameter = new Qt3DRender::QParameter( QStringLiteral( "ks" ), QColor::fromRgbF( 0.01f, 0.01f, 0.01f, 1.0f ) );
96  Qt3DRender::QParameter *shininessParameter = new Qt3DRender::QParameter( QStringLiteral( "shininess" ), 150.0f );
97 
98  diffuseParameter->setValue( symbol.material().diffuse() );
99  ambientParameter->setValue( symbol.material().ambient() );
100  specularParameter->setValue( symbol.material().specular() );
101  shininessParameter->setValue( symbol.material().shininess() );
102 
103  QMatrix4x4 transformMatrix = symbol.transform();
104  QMatrix3x3 normalMatrix = transformMatrix.normalMatrix(); // transponed inverse of 3x3 sub-matrix
105 
106  // QMatrix3x3 is not supported for passing to shaders, so we pass QMatrix4x4
107  float *n = normalMatrix.data();
108  QMatrix4x4 normalMatrix4(
109  n[0], n[3], n[6], 0,
110  n[1], n[4], n[7], 0,
111  n[2], n[5], n[8], 0,
112  0, 0, 0, 0 );
113 
114  Qt3DRender::QParameter *paramInst = new Qt3DRender::QParameter;
115  paramInst->setName( "inst" );
116  paramInst->setValue( transformMatrix );
117 
118  Qt3DRender::QParameter *paramInstNormal = new Qt3DRender::QParameter;
119  paramInstNormal->setName( "instNormal" );
120  paramInstNormal->setValue( normalMatrix4 );
121 
122  Qt3DRender::QEffect *effect = new Qt3DRender::QEffect;
123  effect->addTechnique( technique );
124  effect->addParameter( paramInst );
125  effect->addParameter( paramInstNormal );
126 
127  effect->addParameter( ambientParameter );
128  effect->addParameter( diffuseParameter );
129  effect->addParameter( specularParameter );
130  effect->addParameter( shininessParameter );
131 
132  Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial;
133  material->setEffect( effect );
134 
135  return material;
136 }
137 
138 void QgsPoint3DSymbolInstancedEntityFactory::addEntityForSelectedPoints( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPoint3DSymbol &symbol, QgsPoint3DSymbolEntity *parent )
139 {
140  // build the default material
141  Qt3DRender::QMaterial *mat = material( symbol );
142 
143  // update the material with selection colors
144  Q_FOREACH ( Qt3DRender::QParameter *param, mat->effect()->parameters() )
145  {
146  if ( param->name() == "kd" ) // diffuse
147  param->setValue( map.selectionColor() );
148  else if ( param->name() == "ka" ) // ambient
149  param->setValue( map.selectionColor().darker() );
150  }
151 
152  // build the feature request to select features
153  QgsFeatureRequest req;
154  req.setDestinationCrs( map.crs(), map.transformContext() );
155  req.setFilterFids( layer->selectedFeatureIds() );
157 
158  // build the entity
159  QgsPoint3DSymbolInstancedEntityNode *entity = new QgsPoint3DSymbolInstancedEntityNode( map, layer, symbol, req );
160  entity->addComponent( mat );
161  entity->setParent( parent );
162 }
163 
164 void QgsPoint3DSymbolInstancedEntityFactory::addEntityForNotSelectedPoints( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPoint3DSymbol &symbol, QgsPoint3DSymbolEntity *parent )
165 {
166  // build the default material
167  Qt3DRender::QMaterial *mat = material( symbol );
168 
169  // build the feature request to select features
170  QgsFeatureRequest req;
171  req.setDestinationCrs( map.crs(), map.transformContext() );
173 
174  QgsFeatureIds notSelected = layer->allFeatureIds();
175  notSelected.subtract( layer->selectedFeatureIds() );
176  req.setFilterFids( notSelected );
177 
178  // build the entity
179  QgsPoint3DSymbolInstancedEntityNode *entity = new QgsPoint3DSymbolInstancedEntityNode( map, layer, symbol, req );
180  entity->addComponent( mat );
181  entity->setParent( parent );
182 }
183 
184 QgsPoint3DSymbolInstancedEntityNode::QgsPoint3DSymbolInstancedEntityNode( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPoint3DSymbol &symbol, const QgsFeatureRequest &req, Qt3DCore::QNode *parent )
185  : Qt3DCore::QEntity( parent )
186 {
187  QList<QVector3D> pos = Qgs3DUtils::positions( map, layer, req, symbol.altitudeClamping() );
188  addComponent( renderer( symbol, pos ) );
189 }
190 
191 Qt3DRender::QGeometryRenderer *QgsPoint3DSymbolInstancedEntityNode::renderer( const QgsPoint3DSymbol &symbol, const QList<QVector3D> &positions ) const
192 {
193  int count = positions.count();
194 
195  QByteArray ba;
196  ba.resize( count * sizeof( QVector3D ) );
197  QVector3D *posData = reinterpret_cast<QVector3D *>( ba.data() );
198  for ( int j = 0; j < count; ++j )
199  {
200  *posData = positions[j];
201  ++posData;
202  }
203 
204  Qt3DRender::QBuffer *instanceBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer );
205  instanceBuffer->setData( ba );
206 
207  Qt3DRender::QAttribute *instanceDataAttribute = new Qt3DRender::QAttribute;
208  instanceDataAttribute->setName( "pos" );
209  instanceDataAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
210  instanceDataAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
211  instanceDataAttribute->setVertexSize( 3 );
212  instanceDataAttribute->setDivisor( 1 );
213  instanceDataAttribute->setBuffer( instanceBuffer );
214  instanceDataAttribute->setCount( count );
215  instanceDataAttribute->setByteStride( 3 * sizeof( float ) );
216 
217  Qt3DRender::QGeometry *geometry = symbolGeometry( symbol.shape(), symbol.shapeProperties() );
218  geometry->addAttribute( instanceDataAttribute );
219  geometry->setBoundingVolumePositionAttribute( instanceDataAttribute );
220 
221  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
222  renderer->setGeometry( geometry );
223  renderer->setInstanceCount( count );
224 
225  return renderer;
226 }
227 
228 Qt3DRender::QGeometry *QgsPoint3DSymbolInstancedEntityNode::symbolGeometry( QgsPoint3DSymbol::Shape shape, const QVariantMap &shapeProperties ) const
229 {
230  switch ( shape )
231  {
233  {
234  float radius = shapeProperties["radius"].toFloat();
235  float length = shapeProperties["length"].toFloat();
236  Qt3DExtras::QCylinderGeometry *g = new Qt3DExtras::QCylinderGeometry;
237  //g->setRings(2); // how many vertices vertically
238  //g->setSlices(8); // how many vertices on circumference
239  g->setRadius( radius ? radius : 10 );
240  g->setLength( length ? length : 10 );
241  return g;
242  }
243 
245  {
246  float radius = shapeProperties["radius"].toFloat();
247  Qt3DExtras::QSphereGeometry *g = new Qt3DExtras::QSphereGeometry;
248  g->setRadius( radius ? radius : 10 );
249  return g;
250  }
251 
253  {
254  float length = shapeProperties["length"].toFloat();
255  float bottomRadius = shapeProperties["bottomRadius"].toFloat();
256  float topRadius = shapeProperties["topRadius"].toFloat();
257  Qt3DExtras::QConeGeometry *g = new Qt3DExtras::QConeGeometry;
258  g->setLength( length ? length : 10 );
259  g->setBottomRadius( bottomRadius );
260  g->setTopRadius( topRadius );
261  //g->setHasBottomEndcap(hasBottomEndcap);
262  //g->setHasTopEndcap(hasTopEndcap);
263  return g;
264  }
265 
267  {
268  float size = shapeProperties["size"].toFloat();
269  Qt3DExtras::QCuboidGeometry *g = new Qt3DExtras::QCuboidGeometry;
270  g->setXExtent( size ? size : 10 );
271  g->setYExtent( size ? size : 10 );
272  g->setZExtent( size ? size : 10 );
273  return g;
274  }
275 
277  {
278  float radius = shapeProperties["radius"].toFloat();
279  float minorRadius = shapeProperties["minorRadius"].toFloat();
280  Qt3DExtras::QTorusGeometry *g = new Qt3DExtras::QTorusGeometry;
281  g->setRadius( radius ? radius : 10 );
282  g->setMinorRadius( minorRadius ? minorRadius : 5 );
283  return g;
284  }
285 
287  {
288  float size = shapeProperties["size"].toFloat();
289  Qt3DExtras::QPlaneGeometry *g = new Qt3DExtras::QPlaneGeometry;
290  g->setWidth( size ? size : 10 );
291  g->setHeight( size ? size : 10 );
292  return g;
293  }
294 
295 #if QT_VERSION >= 0x050900
297  {
298  float depth = shapeProperties["depth"].toFloat();
299  QString text = shapeProperties["text"].toString();
300  Qt3DExtras::QExtrudedTextGeometry *g = new Qt3DExtras::QExtrudedTextGeometry;
301  g->setDepth( depth ? depth : 1 );
302  g->setText( text );
303  return g;
304  }
305 #endif
306 
307  default:
308  Q_ASSERT( false );
309  return nullptr;
310  }
311 }
312 
313 //* 3D MODEL RENDERING *//
314 
315 static Qt3DExtras::QPhongMaterial *phongMaterial( const QgsPoint3DSymbol &symbol )
316 {
317  Qt3DExtras::QPhongMaterial *phong = new Qt3DExtras::QPhongMaterial;
318 
319  phong->setAmbient( symbol.material().ambient() );
320  phong->setDiffuse( symbol.material().diffuse() );
321  phong->setSpecular( symbol.material().specular() );
322  phong->setShininess( symbol.material().shininess() );
323 
324  return phong;
325 }
326 
327 void QgsPoint3DSymbolModelEntityFactory::addEntitiesForSelectedPoints( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPoint3DSymbol &symbol, QgsPoint3DSymbolEntity *parent )
328 {
329  QgsFeatureRequest req;
330  req.setDestinationCrs( map.crs(), map.transformContext() );
332  req.setFilterFids( layer->selectedFeatureIds() );
333 
334  addMeshEntities( map, layer, req, symbol, parent, true );
335 }
336 
337 
338 
339 void QgsPoint3DSymbolModelEntityFactory::addEntitiesForNotSelectedPoints( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPoint3DSymbol &symbol, QgsPoint3DSymbolEntity *parent )
340 {
341  // build the feature request to select features
342  QgsFeatureRequest req;
343  req.setDestinationCrs( map.crs(), map.transformContext() );
345  QgsFeatureIds notSelected = layer->allFeatureIds();
346  notSelected.subtract( layer->selectedFeatureIds() );
347  req.setFilterFids( notSelected );
348 
349  if ( symbol.shapeProperties()["overwriteMaterial"].toBool() )
350  {
351  addMeshEntities( map, layer, req, symbol, parent, false );
352  }
353  else
354  {
355  addSceneEntities( map, layer, req, symbol, parent );
356  }
357 }
358 
359 void QgsPoint3DSymbolModelEntityFactory::addSceneEntities( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsFeatureRequest &req, const QgsPoint3DSymbol &symbol, QgsPoint3DSymbolEntity *parent )
360 {
361  QList<QVector3D> positions = Qgs3DUtils::positions( map, layer, req, symbol.altitudeClamping() );
362  Q_FOREACH ( const QVector3D &position, positions )
363  {
364  // build the entity
365  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
366 
367  QUrl url = QUrl::fromLocalFile( symbol.shapeProperties()["model"].toString() );
368  Qt3DRender::QSceneLoader *modelLoader = new Qt3DRender::QSceneLoader;
369  modelLoader->setSource( url );
370 
371  entity->addComponent( modelLoader );
372  entity->addComponent( transform( position, symbol ) );
373  entity->setParent( parent );
374  }
375 }
376 
377 void QgsPoint3DSymbolModelEntityFactory::addMeshEntities( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsFeatureRequest &req, const QgsPoint3DSymbol &symbol, QgsPoint3DSymbolEntity *parent, bool are_selected )
378 {
379  // build the default material
380  Qt3DExtras::QPhongMaterial *mat = phongMaterial( symbol );
381 
382  if ( are_selected )
383  {
384  mat->setDiffuse( map.selectionColor() );
385  mat->setAmbient( map.selectionColor().darker() );
386  }
387 
388  // get nodes
389  QList<QVector3D> positions = Qgs3DUtils::positions( map, layer, req, symbol.altitudeClamping() );
390  Q_FOREACH ( const QVector3D &position, positions )
391  {
392  // build the entity
393  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
394 
395  QUrl url = QUrl::fromLocalFile( symbol.shapeProperties()["model"].toString() );
396  Qt3DRender::QMesh *mesh = new Qt3DRender::QMesh;
397  mesh->setSource( url );
398 
399  entity->addComponent( mesh );
400  entity->addComponent( mat );
401  entity->addComponent( transform( position, symbol ) );
402  entity->setParent( parent );
403  }
404 }
405 
406 Qt3DCore::QTransform *QgsPoint3DSymbolModelEntityFactory::transform( const QVector3D &position, const QgsPoint3DSymbol &symbol )
407 {
408  Qt3DCore::QTransform *tr = new Qt3DCore::QTransform;
409  tr->setMatrix( symbol.transform() );
410  tr->setTranslation( position + tr->translation() );
411  return tr;
412 }
413 
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature&#39;s geometries.
float shininess() const
Returns shininess of the surface.
QColor selectionColor() const
Returns color used for selected features.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used in the 3D scene.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:544
QColor specular() const
Returns specular color component.
Shape shape() const
Returns 3D shape for points.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QVariantMap shapeProperties() const
Returns a key-value dictionary of point shape properties.
Supported in Qt 5.9+.
3 Definition of the world
QMatrix4x4 transform() const
Returns transform for individual objects represented by the symbol.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
3 3D symbol that draws point geometries as 3D objects using one of the predefined shapes...
QgsPhongMaterialSettings material() const
Returns material used for shading of the symbol.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
Shape
3D shape types supported by the symbol
static QList< QVector3D > positions(const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsFeatureRequest &req, AltitudeClamping altClamp)
Calculates (x,y,z) positions of a (multi)point in the Point vector layers.
Definition: qgs3dutils.cpp:188
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets feature IDs that should be fetched.
QColor ambient() const
Returns ambient color component.
QColor diffuse() const
Returns diffuse color component.
QList< int > QgsAttributeList
Definition: qgsfield.h:27
Represents a vector layer which manages a vector based data sets.
virtual QgsFeatureIds allFeatureIds() const
Returns a list of all feature IDs for features present in the source.
AltitudeClamping altitudeClamping() const
Returns method that determines altitude (whether to clamp to feature to terrain)