QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgs3dsceneexporter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgs3dsceneexporter.cpp
3  --------------------------------------
4  Date : June 2020
5  Copyright : (C) 2020 by Belgacem Nedjima
6  Email : gb underscore nedjima at esi dot dz
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 "qgs3dsceneexporter.h"
17 
18 #include <QVector>
19 #include <Qt3DCore/QEntity>
20 #include <Qt3DCore/QComponent>
21 #include <Qt3DCore/QNode>
22 #include <Qt3DRender/QGeometry>
23 #include <Qt3DRender/QAttribute>
24 #include <Qt3DRender/QBuffer>
25 #include <Qt3DRender/QGeometryRenderer>
26 #include <Qt3DExtras/QPlaneGeometry>
27 #include <Qt3DCore/QTransform>
28 #include <Qt3DRender/QMaterial>
29 #include <Qt3DExtras/QDiffuseMapMaterial>
30 #include <Qt3DExtras/QTextureMaterial>
31 #include <Qt3DRender/QTextureImage>
32 #include <Qt3DRender/QTexture>
33 #include <Qt3DRender/QBufferDataGenerator>
34 #include <Qt3DRender/QBufferDataGeneratorPtr>
35 #include <Qt3DRender/QMesh>
36 #include <Qt3DRender/QSceneLoader>
37 #include <Qt3DRender/QAbstractTexture>
38 #include <Qt3DExtras/QCylinderGeometry>
39 #include <Qt3DExtras/QConeGeometry>
40 #include <Qt3DExtras/QSphereGeometry>
41 #include <Qt3DExtras/QCuboidGeometry>
42 #include <Qt3DExtras/QTorusGeometry>
43 #include <Qt3DExtras/QExtrudedTextMesh>
44 #include <Qt3DExtras/QPhongMaterial>
45 #include <Qt3DRender/QAbstractTextureImage>
46 
47 #include <QByteArray>
48 #include <QFile>
49 #include <QTextStream>
50 
52 #include "qgsterraintileentity_p.h"
53 #include "qgsterrainentity_p.h"
54 #include "qgschunknode_p.h"
55 #include "qgsterraingenerator.h"
56 #include "qgs3dmapsettings.h"
58 #include "qgsdemterraingenerator.h"
61 #include "qgs3dexportobject.h"
64 #include "qgsmesh3dgeometry_p.h"
65 #include "qgsmeshlayer.h"
66 #include "qgsmesh3dentity_p.h"
67 #include "qgsmeshterraingenerator.h"
68 #include "qgsvectorlayer.h"
69 #include "qgsabstract3drenderer.h"
72 #include "qgspolygon3dsymbol.h"
73 #include "qgsline3dsymbol.h"
74 #include "qgspoint3dsymbol.h"
75 #include "qgsrulebased3drenderer.h"
76 #include "qgs3dutils.h"
77 #include "qgsbillboardgeometry.h"
78 #include "qgsimagetexture.h"
79 
80 #include <numeric>
81 
82 template<typename T>
83 QVector<T> getAttributeData( Qt3DRender::QAttribute *attribute, const QByteArray &data )
84 {
85  uint bytesOffset = attribute->byteOffset();
86  uint bytesStride = attribute->byteStride();
87  uint vertexSize = attribute->vertexSize();
88  QVector<T> result;
89 
90  if ( bytesStride == 0 )
91  {
92  QgsDebugMsg( "bytesStride==0, the attribute probably was not set properly" );
93  return result;
94  }
95 
96  const char *pData = data.constData();
97  for ( int i = bytesOffset; i < data.size(); i += bytesStride )
98  {
99  for ( unsigned int j = 0; j < vertexSize * sizeof( T ); j += sizeof( T ) )
100  {
101  T v;
102  memcpy( &v, pData + i + j, sizeof( T ) );
103  result.push_back( v );
104  }
105  }
106  return result;
107 }
108 
109 template<typename T>
110 QVector<uint> _getIndexDataImplementation( const QByteArray &data )
111 {
112  QVector<uint> result;
113  const char *pData = data.constData();
114  for ( int i = 0; i < data.size(); i += sizeof( T ) )
115  {
116  T v;
117  memcpy( &v, pData + i, sizeof( T ) );
118  result.push_back( ( uint ) v );
119  }
120  return result;
121 }
122 
123 QVector<uint> getIndexData( Qt3DRender::QAttribute *indexAttribute, const QByteArray &data )
124 {
125  switch ( indexAttribute->vertexBaseType() )
126  {
127  case Qt3DRender::QAttribute::VertexBaseType::Int:
128  return _getIndexDataImplementation<int>( data );
129  case Qt3DRender::QAttribute::VertexBaseType::UnsignedInt:
130  return _getIndexDataImplementation<uint>( data );
131  case Qt3DRender::QAttribute::VertexBaseType::Short:
132  return _getIndexDataImplementation<short>( data );
133  case Qt3DRender::QAttribute::VertexBaseType::UnsignedShort:
134  return _getIndexDataImplementation<ushort>( data );
135  case Qt3DRender::QAttribute::VertexBaseType::Byte:
136  return _getIndexDataImplementation<char>( data );
137  case Qt3DRender::QAttribute::VertexBaseType::UnsignedByte:
138  return _getIndexDataImplementation<uchar>( data );
139  default:
140  QgsDebugMsg( "Probably trying to get index data using an attribute that has vertex data" );
141  break;
142  }
143  return QVector<uint>();
144 }
145 
146 QByteArray getData( Qt3DRender::QBuffer *buffer )
147 {
148  QByteArray bytes = buffer->data();
149  Qt3DRender::QBufferDataGeneratorPtr dataGenerator = buffer->dataGenerator();
150  if ( bytes.isNull() )
151  {
152  if ( !dataGenerator.isNull() )
153  bytes = dataGenerator->operator()();
154  else
155  QgsDebugMsg( "QBuffer is null" );
156  }
157  return bytes;
158 }
159 
160 Qt3DRender::QAttribute *findAttribute( Qt3DRender::QGeometry *geometry, const QString &name, Qt3DRender::QAttribute::AttributeType type )
161 {
162  for ( Qt3DRender::QAttribute *attribute : geometry->attributes() )
163  {
164  if ( attribute->attributeType() != type ) continue;
165  if ( attribute->name() == name ) return attribute;
166  }
167  return nullptr;
168 }
169 
170 template<typename Component>
171 Component *findTypedComponent( Qt3DCore::QEntity *entity )
172 {
173  if ( entity == nullptr ) return nullptr;
174  for ( Qt3DCore::QComponent *component : entity->components() )
175  {
176  Component *typedComponent = qobject_cast<Component *>( component );
177  if ( typedComponent != nullptr )
178  return typedComponent;
179  }
180  return nullptr;
181 }
182 
183 bool Qgs3DSceneExporter::parseVectorLayerEntity( Qt3DCore::QEntity *entity, QgsVectorLayer *layer )
184 {
185  QgsAbstract3DRenderer *abstractRenderer = layer->renderer3D();
186  QString rendererType = abstractRenderer->type();
187 
188  if ( rendererType == "mesh" )
189  {
190  // TODO: handle mesh layers
191  }
192  else
193  {
194  QgsAbstractVectorLayer3DRenderer *abstractVectorRenderer = dynamic_cast< QgsAbstractVectorLayer3DRenderer *>( abstractRenderer );
195  if ( rendererType == "rulebased" )
196  {
197  // Potential bug: meshes loaded using Qt3DRender::QSceneLoader will probably have wrong scale and translation
198  QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
199  for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
200  {
201  Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( renderer->parent() );
202  if ( entity == nullptr ) continue;
203  Qgs3DExportObject *object = processGeometryRenderer( renderer, layer->name() + QStringLiteral( "_" ) );
204  if ( object == nullptr ) continue;
205  if ( mExportTextures )
206  processEntityMaterial( entity, object );
207  mObjects.push_back( object );
208  }
209  return true;
210  }
211  else
212  {
213  QgsVectorLayer3DRenderer *vectorLayerRenderer = dynamic_cast< QgsVectorLayer3DRenderer *>( abstractVectorRenderer );
214  const QgsAbstract3DSymbol *symbol = vectorLayerRenderer->symbol();
215  bool exported = symbol->exportGeometries( this, entity, layer->name() + QStringLiteral( "_" ) );
216  return exported;
217  }
218  }
219  return false;
220 }
221 
222 void Qgs3DSceneExporter::processEntityMaterial( Qt3DCore::QEntity *entity, Qgs3DExportObject *object )
223 {
224  Qt3DExtras::QPhongMaterial *phongMaterial = findTypedComponent<Qt3DExtras::QPhongMaterial>( entity );
225  if ( phongMaterial != nullptr )
226  {
228  object->setupMaterial( &material );
229  }
230  Qt3DExtras::QDiffuseMapMaterial *diffuseMapMaterial = findTypedComponent<Qt3DExtras::QDiffuseMapMaterial>( entity );
231  if ( diffuseMapMaterial != nullptr )
232  {
233  QVector<Qt3DRender::QAbstractTextureImage *> textureImages = diffuseMapMaterial->diffuse()->textureImages();
234  QgsImageTexture *imageTexture = nullptr;
235  for ( Qt3DRender::QAbstractTextureImage *tex : textureImages )
236  {
237  imageTexture = dynamic_cast<QgsImageTexture *>( tex );
238  if ( imageTexture != nullptr ) break;
239  }
240  if ( imageTexture != nullptr )
241  {
242  QImage image = imageTexture->getImage();
243  object->setTextureImage( image );
244  }
245  }
246 }
247 
248 void Qgs3DSceneExporter::parseTerrain( QgsTerrainEntity *terrain, const QString &layerName )
249 {
250  const Qgs3DMapSettings &settings = terrain->map3D();
251  QgsChunkNode *node = terrain->rootNode();
252 
253  QgsTerrainGenerator *generator = settings.terrainGenerator();
254  QgsTerrainTileEntity *terrainTile = nullptr;
255  QgsTerrainTextureGenerator *textureGenerator = terrain->textureGenerator();
256  textureGenerator->waitForFinished();
257  QSize oldResolution = textureGenerator->textureSize();
258  textureGenerator->setTextureSize( QSize( mTerrainTextureResolution, mTerrainTextureResolution ) );
259  switch ( generator->type() )
260  {
262  terrainTile = getDemTerrainEntity( terrain, node );
263  parseDemTile( terrainTile, layerName + QStringLiteral( "_" ) );
264  break;
266  terrainTile = getFlatTerrainEntity( terrain, node );
267  parseFlatTile( terrainTile, layerName + QStringLiteral( "_" ) );
268  break;
269  // TODO: implement other terrain types
271  terrainTile = getMeshTerrainEntity( terrain, node );
272  parseMeshTile( terrainTile, layerName + QStringLiteral( "_" ) );
273  break;
275  break;
276  }
277  textureGenerator->setTextureSize( oldResolution );
278 }
279 
280 QgsTerrainTileEntity *Qgs3DSceneExporter::getFlatTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node )
281 {
282  QgsFlatTerrainGenerator *generator = dynamic_cast<QgsFlatTerrainGenerator *>( terrain->map3D().terrainGenerator() );
283  FlatTerrainChunkLoader *flatTerrainLoader = qobject_cast<FlatTerrainChunkLoader *>( generator->createChunkLoader( node ) );
284  if ( mExportTextures )
285  terrain->textureGenerator()->waitForFinished();
286  // the entity we created will be deallocated once the scene exporter is deallocated
287  Qt3DCore::QEntity *entity = flatTerrainLoader->createEntity( this );
288  QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( entity );
289  return tileEntity;
290 }
291 
292 QgsTerrainTileEntity *Qgs3DSceneExporter::getDemTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node )
293 {
294  // Just create a new tile (we don't need to export exact level of details as in the scene)
295  // create the entity synchronously and then it will be deleted once our scene exporter instance is deallocated
296  QgsDemTerrainGenerator *generator = dynamic_cast<QgsDemTerrainGenerator *>( terrain->map3D().terrainGenerator()->clone() );
297  generator->setResolution( mTerrainResolution );
298  QgsDemTerrainTileLoader *loader = qobject_cast<QgsDemTerrainTileLoader *>( generator->createChunkLoader( node ) );
299  generator->heightMapGenerator()->waitForFinished();
300  if ( mExportTextures )
301  terrain->textureGenerator()->waitForFinished();
302  QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity( this ) );
303  delete generator;
304  return tileEntity;
305 }
306 
307 QgsTerrainTileEntity *Qgs3DSceneExporter::getMeshTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node )
308 {
309  QgsMeshTerrainGenerator *generator = dynamic_cast<QgsMeshTerrainGenerator *>( terrain->map3D().terrainGenerator() );
310  QgsMeshTerrainTileLoader *loader = qobject_cast<QgsMeshTerrainTileLoader *>( generator->createChunkLoader( node ) );
311  // TODO: export textures
312  QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity( this ) );
313  return tileEntity;
314 }
315 
316 void Qgs3DSceneExporter::parseFlatTile( QgsTerrainTileEntity *tileEntity, const QString &layerName )
317 {
318  Qt3DRender::QGeometryRenderer *mesh = findTypedComponent<Qt3DRender::QGeometryRenderer>( tileEntity );
319  Qt3DCore::QTransform *transform = findTypedComponent<Qt3DCore::QTransform>( tileEntity );
320 
321  Qt3DRender::QGeometry *geometry = mesh->geometry();
322  Qt3DExtras::QPlaneGeometry *tileGeometry = qobject_cast<Qt3DExtras::QPlaneGeometry *>( geometry );
323  if ( tileGeometry == nullptr )
324  {
325  QgsDebugMsg( "Qt3DExtras::QPlaneGeometry* is expected but something else was given" );
326  return;
327  }
328 
329  float scale = transform->scale();
330  QVector3D translation = transform->translation();
331 
332  // Generate vertice data
333  Qt3DRender::QAttribute *positionAttribute = tileGeometry->positionAttribute();
334  QByteArray verticesBytes = getData( positionAttribute->buffer() );
335  QVector<float> positionBuffer = getAttributeData<float>( positionAttribute, verticesBytes );
336 
337  // Generate index data
338  Qt3DRender::QAttribute *indexAttribute = tileGeometry->indexAttribute();
339  QByteArray indexBytes = getData( indexAttribute->buffer() );
340  QVector<uint> indexesBuffer = getIndexData( indexAttribute, indexBytes );
341 
342  QString objectNamePrefix = layerName;
343  if ( objectNamePrefix != QString() ) objectNamePrefix += QString();
344 
345  Qgs3DExportObject *object = new Qgs3DExportObject( getObjectName( objectNamePrefix + QStringLiteral( "Flat_tile" ) ) );
346  mObjects.push_back( object );
347 
348  object->setSmoothEdges( mSmoothEdges );
349  object->setupPositionCoordinates( positionBuffer, scale, translation );
350  object->setupFaces( indexesBuffer );
351 
352  if ( mExportNormals )
353  {
354  // Everts
355  QVector<float> normalsBuffer;
356  for ( int i = 0; i < positionBuffer.size(); i += 3 ) normalsBuffer << 0.0f << 1.0f << 0.0f;
357  object->setupNormalCoordinates( normalsBuffer );
358  }
359 
360  Qt3DRender::QAttribute *texCoordsAttribute = tileGeometry->texCoordAttribute();
361  if ( mExportTextures && texCoordsAttribute != nullptr )
362  {
363  // Reuse vertex buffer data for texture coordinates
364  QVector<float> texCoords = getAttributeData<float>( texCoordsAttribute, verticesBytes );
365  object->setupTextureCoordinates( texCoords );
366 
367  QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
368  QImage img = textureImage->getImage();
369  object->setTextureImage( img );
370  }
371 }
372 
373 void Qgs3DSceneExporter::parseDemTile( QgsTerrainTileEntity *tileEntity, const QString &layerName )
374 {
375  Qt3DRender::QGeometryRenderer *mesh = findTypedComponent<Qt3DRender::QGeometryRenderer>( tileEntity );
376  Qt3DCore::QTransform *transform = findTypedComponent<Qt3DCore::QTransform>( tileEntity );
377 
378  Qt3DRender::QGeometry *geometry = mesh->geometry();
379  DemTerrainTileGeometry *tileGeometry = qobject_cast<DemTerrainTileGeometry *>( geometry );
380  if ( tileGeometry == nullptr )
381  {
382  QgsDebugMsg( "DemTerrainTileGeometry* is expected but something else was given" );
383  return;
384  }
385 
386  float scale = transform->scale();
387  QVector3D translation = transform->translation();
388 
389  Qt3DRender::QAttribute *positionAttribute = tileGeometry->positionAttribute();
390  QByteArray positionBytes = positionAttribute->buffer()->data();
391  QVector<float> positionBuffer = getAttributeData<float>( positionAttribute, positionBytes );
392 
393  Qt3DRender::QAttribute *indexAttribute = tileGeometry->indexAttribute();
394  QByteArray indexBytes = indexAttribute->buffer()->data();
395  QVector<unsigned int> indexBuffer = getIndexData( indexAttribute, indexBytes );
396 
397  QString objectNamePrefix = layerName;
398  if ( objectNamePrefix != QString() ) objectNamePrefix += QStringLiteral( "_" );
399 
400  Qgs3DExportObject *object = new Qgs3DExportObject( getObjectName( layerName + QStringLiteral( "DEM_tile" ) ) );
401  mObjects.push_back( object );
402 
403  object->setSmoothEdges( mSmoothEdges );
404  object->setupPositionCoordinates( positionBuffer, scale, translation );
405  object->setupFaces( indexBuffer );
406 
407  Qt3DRender::QAttribute *normalsAttributes = tileGeometry->normalAttribute();
408  if ( mExportNormals && normalsAttributes != nullptr )
409  {
410  QByteArray normalsBytes = normalsAttributes->buffer()->data();
411  QVector<float> normalsBuffer = getAttributeData<float>( normalsAttributes, normalsBytes );
412  object->setupNormalCoordinates( normalsBuffer );
413  }
414 
415  Qt3DRender::QAttribute *texCoordsAttribute = tileGeometry->texCoordsAttribute();
416  if ( mExportTextures && texCoordsAttribute != nullptr )
417  {
418  QByteArray texCoordsBytes = texCoordsAttribute->buffer()->data();
419  QVector<float> texCoordsBuffer = getAttributeData<float>( texCoordsAttribute, texCoordsBytes );
420  object->setupTextureCoordinates( texCoordsBuffer );
421 
422  QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
423  QImage img = textureImage->getImage();
424  object->setTextureImage( img );
425  }
426 }
427 
428 void Qgs3DSceneExporter::parseMeshTile( QgsTerrainTileEntity *tileEntity, const QString &layerName )
429 {
430  QString objectNamePrefix = layerName;
431  if ( objectNamePrefix != QString() ) objectNamePrefix += QStringLiteral( "_" );
432 
433  QList<Qt3DRender::QGeometryRenderer *> renderers = tileEntity->findChildren<Qt3DRender::QGeometryRenderer *>();
434  for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
435  {
436  Qgs3DExportObject *obj = processGeometryRenderer( renderer, objectNamePrefix );
437  if ( obj == nullptr ) continue;
438  mObjects << obj;
439  }
440 }
441 
442 QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processInstancedPointGeometry( Qt3DCore::QEntity *entity, const QString &objectNamePrefix )
443 {
444  QVector<Qgs3DExportObject *> objects;
445  QList<Qt3DRender::QGeometry *> geometriesList = entity->findChildren<Qt3DRender::QGeometry *>();
446  for ( Qt3DRender::QGeometry *geometry : geometriesList )
447  {
448  Qt3DRender::QAttribute *positionAttribute = findAttribute( geometry, Qt3DRender::QAttribute::defaultPositionAttributeName(), Qt3DRender::QAttribute::VertexAttribute );
449  Qt3DRender::QAttribute *indexAttribute = nullptr;
450  for ( Qt3DRender::QAttribute *attribute : geometry->attributes() )
451  {
452  if ( attribute->attributeType() == Qt3DRender::QAttribute::IndexAttribute )
453  indexAttribute = attribute;
454  }
455  if ( positionAttribute == nullptr || indexAttribute == nullptr )
456  continue;
457  QByteArray vertexBytes = getData( positionAttribute->buffer() );
458  QByteArray indexBytes = getData( indexAttribute->buffer() );
459  QVector<float> positionData = getAttributeData<float>( positionAttribute, vertexBytes );
460  QVector<uint> indexData = getIndexData( indexAttribute, indexBytes );
461 
462  Qt3DRender::QAttribute *instanceDataAttribute = findAttribute( geometry, QStringLiteral( "pos" ), Qt3DRender::QAttribute::VertexAttribute );
463  QByteArray instancePositionBytes = getData( instanceDataAttribute->buffer() );
464  QVector<float> instancePosition = getAttributeData<float>( instanceDataAttribute, instancePositionBytes );
465  for ( int i = 0; i < instancePosition.size(); i += 3 )
466  {
467  Qgs3DExportObject *object = new Qgs3DExportObject( getObjectName( objectNamePrefix + QStringLiteral( "shape_geometry" ) ) );
468  objects.push_back( object );
469  object->setupPositionCoordinates( positionData, 1.0f, QVector3D( instancePosition[i], instancePosition[i + 1], instancePosition[i + 2] ) );
470  object->setupFaces( indexData );
471 
472  object->setSmoothEdges( mSmoothEdges );
473 
474  Qt3DRender::QAttribute *normalsAttribute = findAttribute( geometry, Qt3DRender::QAttribute::defaultNormalAttributeName(), Qt3DRender::QAttribute::VertexAttribute );
475  if ( mExportNormals && normalsAttribute != nullptr )
476  {
477  // Reuse vertex bytes
478  QVector<float> normalsData = getAttributeData<float>( normalsAttribute, vertexBytes );
479  object->setupNormalCoordinates( normalsData );
480  }
481  }
482  }
483  return objects;
484 }
485 
486 QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processSceneLoaderGeometries( Qt3DRender::QSceneLoader *sceneLoader, const QString &objectNamePrefix )
487 {
488  QVector<Qgs3DExportObject *> objects;
489  Qt3DCore::QEntity *sceneLoaderParent = qobject_cast<Qt3DCore::QEntity *>( sceneLoader->parent() );
490  Qt3DCore::QTransform *entityTransform = findTypedComponent<Qt3DCore::QTransform>( sceneLoaderParent );
491  float sceneScale = 1.0f;
492  QVector3D sceneTranslation( 0.0f, 0.0f, 0.0f );
493  if ( entityTransform != nullptr )
494  {
495  sceneScale = entityTransform->scale();
496  sceneTranslation = entityTransform->translation();
497  }
498  for ( QString entityName : sceneLoader->entityNames() )
499  {
500  Qt3DRender::QGeometryRenderer *mesh = qobject_cast<Qt3DRender::QGeometryRenderer *>( sceneLoader->component( entityName, Qt3DRender::QSceneLoader::GeometryRendererComponent ) );
501  Qgs3DExportObject *object = processGeometryRenderer( mesh, objectNamePrefix, sceneScale, sceneTranslation );
502  if ( object == nullptr ) continue;
503  objects.push_back( object );
504  }
505  return objects;
506 }
507 
508 Qgs3DExportObject *Qgs3DSceneExporter::processGeometryRenderer( Qt3DRender::QGeometryRenderer *mesh, const QString &objectNamePrefix, float sceneScale, QVector3D sceneTranslation )
509 {
510  // We only export triangles for now
511  if ( mesh->primitiveType() != Qt3DRender::QGeometryRenderer::Triangles ) return nullptr;
512 
513  float scale = 1.0f;
514  QVector3D translation( 0.0f, 0.0f, 0.0f );
515  QObject *parent = mesh->parent();
516  while ( parent != nullptr )
517  {
518  Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( parent );
519  Qt3DCore::QTransform *transform = findTypedComponent<Qt3DCore::QTransform>( entity );
520  if ( transform != nullptr )
521  {
522  scale *= transform->scale();
523  translation += transform->translation();
524  }
525  parent = parent->parent();
526  }
527 
528  Qt3DRender::QGeometry *geometry = mesh->geometry();
529 
530  Qt3DRender::QAttribute *positionAttribute = findAttribute( geometry, Qt3DRender::QAttribute::defaultPositionAttributeName(), Qt3DRender::QAttribute::VertexAttribute );
531  Qt3DRender::QAttribute *indexAttribute = nullptr;
532  QByteArray indexBytes, vertexBytes;
533  QVector<uint> indexData;
534  QVector<float> positionData;
535  for ( Qt3DRender::QAttribute *attribute : geometry->attributes() )
536  {
537  if ( attribute->attributeType() == Qt3DRender::QAttribute::IndexAttribute )
538  indexAttribute = attribute;
539  }
540 
541  if ( indexAttribute != nullptr )
542  {
543  indexBytes = getData( indexAttribute->buffer() );
544  indexData = getIndexData( indexAttribute, indexBytes );
545  }
546 
547  if ( positionAttribute != nullptr )
548  {
549  vertexBytes = getData( positionAttribute->buffer() );
550  positionData = getAttributeData<float>( positionAttribute, vertexBytes );
551  }
552 
553 // For tessellated polygons that don't have index attributes
554  if ( positionAttribute != nullptr && indexAttribute == nullptr )
555  {
556  for ( int i = 0; i < positionData.size() / 3; ++i )
557  indexData.push_back( i );
558  }
559 
560  if ( positionAttribute == nullptr )
561  {
562  QgsDebugMsg( "Geometry renderer with null data was being processed" );
563  return nullptr;
564  }
565 
566  Qgs3DExportObject *object = new Qgs3DExportObject( getObjectName( objectNamePrefix + QStringLiteral( "mesh_geometry" ) ) );
567  object->setupPositionCoordinates( positionData, scale * sceneScale, translation + sceneTranslation );
568  object->setupFaces( indexData );
569 
570  Qt3DRender::QAttribute *normalsAttribute = findAttribute( geometry, Qt3DRender::QAttribute::defaultNormalAttributeName(), Qt3DRender::QAttribute::VertexAttribute );
571  if ( mExportNormals && normalsAttribute != nullptr )
572  {
573  // Reuse vertex bytes
574  QVector<float> normalsData = getAttributeData<float>( normalsAttribute, vertexBytes );
575  object->setupNormalCoordinates( normalsData );
576  }
577 
578  Qt3DRender::QAttribute *texCoordsAttribute = findAttribute( geometry, Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName(), Qt3DRender::QAttribute::VertexAttribute );
579  if ( mExportTextures && texCoordsAttribute != nullptr )
580  {
581  // Reuse vertex bytes
582  QVector<float> texCoordsData = getAttributeData<float>( texCoordsAttribute, vertexBytes );
583  object->setupTextureCoordinates( texCoordsData );
584  }
585 
586  return object;
587 }
588 
589 QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processLines( Qt3DCore::QEntity *entity, const QString &objectNamePrefix )
590 {
591  QVector<Qgs3DExportObject *> objs;
592  QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
593  for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
594  {
595  if ( renderer->primitiveType() != Qt3DRender::QGeometryRenderer::LineStripAdjacency ) continue;
596  Qt3DRender::QGeometry *geom = renderer->geometry();
597  Qt3DRender::QAttribute *positionAttribute = findAttribute( geom, Qt3DRender::QAttribute::defaultPositionAttributeName(), Qt3DRender::QAttribute::VertexAttribute );
598  Qt3DRender::QAttribute *indexAttribute = nullptr;
599  for ( Qt3DRender::QAttribute *attribute : geom->attributes() )
600  {
601  if ( attribute->attributeType() == Qt3DRender::QAttribute::IndexAttribute )
602  {
603  indexAttribute = attribute;
604  break;
605  }
606  }
607  if ( positionAttribute == nullptr || indexAttribute == nullptr )
608  {
609  QgsDebugMsg( "Position or index attribute was not found" );
610  continue;
611  }
612 
613  QByteArray vertexBytes = getData( positionAttribute->buffer() );
614  QByteArray indexBytes = getData( indexAttribute->buffer() );
615  QVector<float> positionData = getAttributeData<float>( positionAttribute, vertexBytes );
616  QVector<uint> indexData = getIndexData( indexAttribute, indexBytes );
617 
618  Qgs3DExportObject *exportObject = new Qgs3DExportObject( getObjectName( objectNamePrefix + QStringLiteral( "line" ) ) );
619  exportObject->setType( Qgs3DExportObject::LineStrip );
620  exportObject->setupPositionCoordinates( positionData );
621  exportObject->setupLine( indexData );
622 
623  objs.push_back( exportObject );
624  }
625  return objs;
626 }
627 
628 Qgs3DExportObject *Qgs3DSceneExporter::processPoints( Qt3DCore::QEntity *entity, const QString &objectNamePrefix )
629 {
630  QVector<float> points;
631  QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
632  for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
633  {
634  Qt3DRender::QGeometry *geometry = qobject_cast<QgsBillboardGeometry *>( renderer->geometry() );
635  if ( geometry == nullptr )
636  continue;
637  Qt3DRender::QAttribute *positionAttribute = findAttribute( geometry, Qt3DRender::QAttribute::defaultPositionAttributeName(), Qt3DRender::QAttribute::VertexAttribute );
638  QByteArray positionBytes = getData( positionAttribute->buffer() );
639  if ( positionBytes.size() == 0 )
640  continue;
641  QVector<float> positions = getAttributeData<float>( positionAttribute, positionBytes );
642  points << positions;
643  }
644  Qgs3DExportObject *obj = new Qgs3DExportObject( getObjectName( objectNamePrefix + QStringLiteral( "points" ) ) );
646  obj->setupPositionCoordinates( points );
647  return obj;
648 }
649 
650 void Qgs3DSceneExporter::save( const QString &sceneName, const QString &sceneFolderPath )
651 {
652  QString objFilePath = QDir( sceneFolderPath ).filePath( sceneName + QStringLiteral( ".obj" ) );
653  QString mtlFilePath = QDir( sceneFolderPath ).filePath( sceneName + QStringLiteral( ".mtl" ) );
654 
655  QFile file( objFilePath );
656  if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
657  return;
658  QFile mtlFile( mtlFilePath );
659  if ( !mtlFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
660  return;
661 
662  float maxfloat = std::numeric_limits<float>::max(), minFloat = std::numeric_limits<float>::lowest();
663  float minX = maxfloat, minY = maxfloat, minZ = maxfloat, maxX = minFloat, maxY = minFloat, maxZ = minFloat;
664  for ( Qgs3DExportObject *obj : mObjects ) obj->objectBounds( minX, minY, minZ, maxX, maxY, maxZ );
665 
666  float diffX = 1.0f, diffY = 1.0f, diffZ = 1.0f;
667  diffX = maxX - minX;
668  diffY = maxY - minY;
669  diffZ = maxZ - minZ;
670 
671  float centerX = ( minX + maxX ) / 2.0f;
672  float centerY = ( minY + maxY ) / 2.0f;
673  float centerZ = ( minZ + maxZ ) / 2.0f;
674 
675  float scale = std::max( diffX, std::max( diffY, diffZ ) );
676 
677  QTextStream out( &file );
678  // set material library name
679  QString mtlLibName = sceneName + ".mtl";
680  out << "mtllib " << mtlLibName << "\n";
681 
682  QTextStream mtlOut( &mtlFile );
683  for ( Qgs3DExportObject *obj : mObjects )
684  {
685  if ( obj == nullptr ) continue;
686  // Set object name
687  QString material = obj->saveMaterial( mtlOut, sceneFolderPath );
688  out << "o " << obj->name() << "\n";
689  if ( material != QString() )
690  out << "usemtl " << material << "\n";
691  obj->saveTo( out, scale / mScale, QVector3D( centerX, centerY, centerZ ) );
692  }
693 }
694 
695 QString Qgs3DSceneExporter::getObjectName( const QString &name )
696 {
697  QString ret = name;
698  if ( usedObjectNamesCounter.contains( name ) )
699  {
700  ret = QStringLiteral( "%1%2" ).arg( name ).arg( usedObjectNamesCounter[name] );
701  usedObjectNamesCounter[name]++;
702  }
703  else
704  usedObjectNamesCounter[name] = 2;
705  return ret;
706 }
qgs3dexportobject.h
qgstessellatedpolygongeometry.h
QgsTerrainGenerator::Dem
@ Dem
Terrain is built from raster layer with digital elevation model.
Definition: qgsterraingenerator.h:55
findTypedComponent
Component * findTypedComponent(Qt3DCore::QEntity *entity)
Definition: qgs3dsceneexporter.cpp:171
QgsVectorLayer3DRenderer
3D renderer that renders all features of a vector layer with the same 3D symbol.
Definition: qgsvectorlayer3drenderer.h:61
Qgs3DSceneExporter::save
void save(const QString &sceneName, const QString &sceneFolderPath)
Saves the scene to a .obj file.
Definition: qgs3dsceneexporter.cpp:650
QgsTerrainGenerator::type
virtual Type type() const =0
What texture generator implementation is this.
QgsDemTerrainGenerator::setResolution
void setResolution(int resolution)
Sets resolution of the generator (how many elevation samples on one side of a terrain tile)
Definition: qgsdemterraingenerator.h:57
qgsline3dsymbol.h
QgsDemTerrainGenerator::createChunkLoader
QgsChunkLoader * createChunkLoader(QgsChunkNode *node) const override
Definition: qgsdemterraingenerator.cpp:100
QgsAbstract3DRenderer::type
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass)
QgsAbstract3DRenderer
Base class for all renderers that may to participate in 3D view.
Definition: qgsabstract3drenderer.h:49
qgs3dsceneexporter.h
Qgs3DExportObject
The Qgs3DExportObject class Manages the data of each object of the scene (positions,...
Definition: qgs3dexportobject.h:42
qgschunknode_p.h
Qgs3DSceneExporter::parseTerrain
void parseTerrain(QgsTerrainEntity *terrain, const QString &layer)
Creates terrain export objects from the terrain entity.
Definition: qgs3dsceneexporter.cpp:248
_getIndexDataImplementation
QVector< uint > _getIndexDataImplementation(const QByteArray &data)
Definition: qgs3dsceneexporter.cpp:110
qgsdemterraingenerator.h
Qgs3DUtils::phongMaterialFromQt3DComponent
static QgsPhongMaterialSettings phongMaterialFromQt3DComponent(Qt3DExtras::QPhongMaterial *material)
Returns phong material settings object based on the Qt3D material.
Definition: qgs3dutils.cpp:560
Qgs3DSceneExporter::scale
float scale() const
Returns the scale of the exported 3D model.
Definition: qgs3dsceneexporter.h:107
qgsabstract3drenderer.h
qgsterraintileentity_p.h
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsAbstractVectorLayer3DRenderer
3 Base class for 3D renderers that are based on vector layers.
Definition: qgsabstractvectorlayer3drenderer.h:78
findAttribute
Qt3DRender::QAttribute * findAttribute(Qt3DRender::QGeometry *geometry, const QString &name, Qt3DRender::QAttribute::AttributeType type)
Definition: qgs3dsceneexporter.cpp:160
QgsImageTexture::getImage
QImage getImage() const
Returns the image.
Definition: qgsimagetexture.h:50
Qgs3DExportObject::objectBounds
void objectBounds(float &minX, float &minY, float &minZ, float &maxX, float &maxY, float &maxZ)
Updates the box bounds explained with the current object bounds This expands the bounding box if the ...
Definition: qgs3dexportobject.cpp:87
Qgs3DExportObject::name
QString name() const
Returns the object name.
Definition: qgs3dexportobject.h:63
Qgs3DExportObject::setType
void setType(ObjectType type)
Sets the object type.
Definition: qgs3dexportobject.h:70
QgsPhongMaterialSettings
3 Basic shading material used for rendering based on the Phong shading model with three color compone...
Definition: qgsphongmaterialsettings.h:37
QgsDemTerrainGenerator::heightMapGenerator
QgsDemHeightMapGenerator * heightMapGenerator()
Returns height map generator object - takes care of extraction of elevations from the layer)
Definition: qgsdemterraingenerator.h:67
QgsAbstract3DSymbol
3 Abstract base class for 3D symbols that are used by VectorLayer3DRenderer objects.
Definition: qgsabstract3dsymbol.h:46
getData
QByteArray getData(Qt3DRender::QBuffer *buffer)
Definition: qgs3dsceneexporter.cpp:146
QgsTerrainGenerator
3 Base class for generators of terrain.
Definition: qgsterraingenerator.h:48
QgsMapLayer::renderer3D
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
Definition: qgsmaplayer.cpp:1821
qgs3dutils.h
QgsImageTexture
The QgsImageTexture class Holds an image that can be used as a texture in the 3D view.
Definition: qgsimagetexture.h:38
qgsterraingenerator.h
qgsabstractvectorlayer3drenderer.h
qgsterraintextureimage_p.h
Qgs3DExportObject::Points
@ Points
Definition: qgs3dexportobject.h:49
QgsTerrainGenerator::Flat
@ Flat
The whole terrain is flat area.
Definition: qgsterraingenerator.h:54
qgsdemterraintilegeometry_p.h
qgsdemterraintileloader_p.h
Qgs3DMapSettings
3 Definition of the world
Definition: qgs3dmapsettings.h:54
Qgs3DExportObject::setupLine
void setupLine(const QVector< uint > &facesIndexes)
sets line vertex indexes
Definition: qgs3dexportobject.cpp:62
getAttributeData
QVector< T > getAttributeData(Qt3DRender::QAttribute *attribute, const QByteArray &data)
Definition: qgs3dsceneexporter.cpp:83
QgsFlatTerrainGenerator
3 Terrain generator that creates a simple square flat area.
Definition: qgsflatterraingenerator.h:54
QgsFlatTerrainGenerator::createChunkLoader
QgsChunkLoader * createChunkLoader(QgsChunkNode *node) const override SIP_FACTORY
Definition: qgsflatterraingenerator.cpp:81
qgsbillboardgeometry.h
QgsAbstract3DSymbol::exportGeometries
virtual bool exportGeometries(Qgs3DSceneExporter *exporter, Qt3DCore::QEntity *entity, const QString &objectNamePrefix) const
Exports the geometries contained within the hierarchy of entity.
Definition: qgsabstract3dsymbol.cpp:53
QgsDemTerrainGenerator
3 Implementation of terrain generator that uses a raster layer with DEM to build terrain.
Definition: qgsdemterraingenerator.h:42
Qgs3DMapSettings::terrainGenerator
QgsTerrainGenerator * terrainGenerator() const
Returns terrain generator. It takes care of producing terrain tiles from the input data.
Definition: qgs3dmapsettings.h:272
qgsmeshlayer.h
qgsvectorlayer.h
qgsterrainentity_p.h
qgs3dmapsettings.h
Qgs3DExportObject::LineStrip
@ LineStrip
Definition: qgs3dexportobject.h:48
qgspolygon3dsymbol.h
QgsTerrainGenerator::Online
@ Online
Terrain is built from downloaded tiles with digital elevation model.
Definition: qgsterraingenerator.h:56
qgsrulebased3drenderer.h
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:387
qgspoint3dsymbol.h
QgsMapLayer::name
QString name
Definition: qgsmaplayer.h:86
qgsvectorlayer3drenderer.h
getIndexData
QVector< uint > getIndexData(Qt3DRender::QAttribute *indexAttribute, const QByteArray &data)
Definition: qgs3dsceneexporter.cpp:123
qgsimagetexture.h
qgsterraintexturegenerator_p.h
Qgs3DSceneExporter::parseVectorLayerEntity
bool parseVectorLayerEntity(Qt3DCore::QEntity *entity, QgsVectorLayer *layer)
Creates necessary export objects from entity if it represents valid vector layer entity Returns false...
Definition: qgs3dsceneexporter.cpp:183
qgsflatterraingenerator.h
Qgs3DExportObject::saveMaterial
QString saveMaterial(QTextStream &mtlOut, const QString &folder)
saves the texture of the object and material information
Definition: qgs3dexportobject.cpp:183
Qgs3DExportObject::setupPositionCoordinates
void setupPositionCoordinates(const QVector< float > &positionsBuffer, float scale=1.0f, const QVector3D &translation=QVector3D(0, 0, 0))
Sets positions coordinates and does the translation and scaling.
Definition: qgs3dexportobject.cpp:46
QgsTerrainGenerator::Mesh
@ Mesh
Terrain is built from mesh layer with z value on vertices.
Definition: qgsterraingenerator.h:57
Qgs3DExportObject::saveTo
void saveTo(QTextStream &out, float scale, const QVector3D &center)
Saves the current object to the output stream while scaling the object and centering it to be visible...
Definition: qgs3dexportobject.cpp:102
QgsVectorLayer3DRenderer::symbol
const QgsAbstract3DSymbol * symbol() const
Returns 3D symbol associated with the renderer.
Definition: qgsvectorlayer3drenderer.cpp:61