30#include "qgsmeshterraingenerator.h"
31#include "qgsmeshterraintileloader_p.h"
46#include <Qt3DCore/QAttribute>
47#include <Qt3DCore/QBuffer>
48#include <Qt3DCore/QComponent>
49#include <Qt3DCore/QEntity>
50#include <Qt3DCore/QGeometry>
51#include <Qt3DCore/QNode>
52#include <Qt3DCore/QTransform>
53#include <Qt3DExtras/QConeGeometry>
54#include <Qt3DExtras/QCuboidGeometry>
55#include <Qt3DExtras/QCylinderGeometry>
56#include <Qt3DExtras/QDiffuseSpecularMaterial>
57#include <Qt3DExtras/QExtrudedTextMesh>
58#include <Qt3DExtras/QPhongMaterial>
59#include <Qt3DExtras/QPlaneGeometry>
60#include <Qt3DExtras/QSphereGeometry>
61#include <Qt3DExtras/QTextureMaterial>
62#include <Qt3DExtras/QTorusGeometry>
63#include <Qt3DRender/QAbstractTexture>
64#include <Qt3DRender/QAbstractTextureImage>
65#include <Qt3DRender/QGeometryRenderer>
66#include <Qt3DRender/QMesh>
67#include <Qt3DRender/QParameter>
68#include <Qt3DRender/QSceneLoader>
69#include <Qt3DRender/QTexture>
70#include <Qt3DRender/QTextureImage>
72#include "moc_qgs3dsceneexporter.cpp"
74using namespace Qt::StringLiterals;
76template<
typename T> QVector<T>
getAttributeData( Qt3DCore::QAttribute *attribute,
const QByteArray &data )
78 const uint bytesOffset = attribute->byteOffset();
79 const uint bytesStride = attribute->byteStride();
80 const uint vertexSize = attribute->vertexSize();
81 const uint dataSize =
static_cast<uint
>( data.size() );
84 if ( bytesStride == 0 )
86 QgsDebugError(
"bytesStride==0, the attribute probably was not set properly" );
90 const char *pData = data.constData();
91 for (
unsigned int i = bytesOffset; i < dataSize; i += bytesStride )
93 for (
unsigned int j = 0; j < vertexSize *
sizeof( T ); j +=
sizeof( T ) )
96 memcpy( &v, pData + i + j,
sizeof( T ) );
97 result.push_back( v );
105 QVector<uint> result;
106 const char *pData = data.constData();
107 for (
int i = 0; i < data.size(); i +=
sizeof( T ) )
110 memcpy( &v, pData + i,
sizeof( T ) );
111 result.push_back( ( uint ) v );
116QVector<uint>
getIndexData( Qt3DCore::QAttribute *indexAttribute,
const QByteArray &data )
118 switch ( indexAttribute->vertexBaseType() )
120 case Qt3DCore::QAttribute::VertexBaseType::Int:
122 case Qt3DCore::QAttribute::VertexBaseType::UnsignedInt:
124 case Qt3DCore::QAttribute::VertexBaseType::Short:
126 case Qt3DCore::QAttribute::VertexBaseType::UnsignedShort:
128 case Qt3DCore::QAttribute::VertexBaseType::Byte:
130 case Qt3DCore::QAttribute::VertexBaseType::UnsignedByte:
133 QgsDebugError(
"Probably trying to get index data using an attribute that has vertex data" );
136 return QVector<uint>();
139QByteArray
getData( Qt3DCore::QBuffer *buffer )
141 QByteArray bytes = buffer->data();
142 if ( bytes.isNull() )
149Qt3DCore::QAttribute *
findAttribute( Qt3DCore::QGeometry *geometry,
const QString &name, Qt3DCore::QAttribute::AttributeType type )
151 QVector<Qt3DCore::QAttribute *> attributes = geometry->attributes();
152 for ( Qt3DCore::QAttribute *attribute : attributes )
154 if ( attribute->attributeType() != type )
156 if ( name.isEmpty() || attribute->name() == name )
166 QVector<Qt3DCore::QComponent *> components = entity->components();
167 for ( Qt3DCore::QComponent *component : components )
169 Component *typedComponent = qobject_cast<Component *>( component );
170 if ( typedComponent )
171 return typedComponent;
179 const QString rendererType = abstractRenderer->
type();
181 if ( rendererType ==
"rulebased" )
183 int prevSize = mObjects.size();
185 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
186 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
188 Qt3DCore::QEntity *parentEntity = qobject_cast<Qt3DCore::QEntity *>( renderer->parent() );
194 if ( mExportTextures )
195 processEntityMaterial( parentEntity,
object );
196 mObjects.push_back(
object );
198 return mObjects.size() > prevSize;
201 else if ( rendererType ==
"vector" )
204 if ( vectorLayerRenderer )
216 QgsDebugMsgLevel( u
"Type '%1' of layer '%2' is not exportable."_s.arg( layer->
name(), rendererType ), 2 );
223void Qgs3DSceneExporter::processEntityMaterial( Qt3DCore::QEntity *entity,
Qgs3DExportObject *
object )
const
229 object->setupMaterial( &material );
233 if ( diffuseMapMaterial )
235 const Qt3DRender::QTexture2D *diffuseTexture = diffuseMapMaterial->diffuse().value<Qt3DRender::QTexture2D *>();
236 if ( diffuseTexture )
238 const QVector<Qt3DRender::QAbstractTextureImage *> textureImages = diffuseTexture->textureImages();
239 for (
const Qt3DRender::QAbstractTextureImage *tex : textureImages )
241 const QgsImageTexture *imageTexture =
dynamic_cast<const QgsImageTexture *
>( tex );
244 const QImage image = imageTexture->
getImage();
245 object->setTextureImage( image );
259 QgsChunkNode *node = terrain->rootNode();
264 QgsTerrainTileEntity *terrainTile =
nullptr;
265 QgsTerrainTextureGenerator *textureGenerator = terrain->textureGenerator();
266 textureGenerator->waitForFinished();
267 const QSize oldResolution = textureGenerator->textureSize();
268 textureGenerator->setTextureSize( QSize( mTerrainTextureResolution, mTerrainTextureResolution ) );
269 switch ( generator->
type() )
272 terrainTile = getDemTerrainEntity( terrain, node, settings->
origin() );
273 parseDemTile( terrainTile, layerName + u
"_"_s );
276 terrainTile = getFlatTerrainEntity( terrain, node, settings->
origin() );
277 parseFlatTile( terrainTile, layerName + u
"_"_s );
280 terrainTile = getMeshTerrainEntity( terrain, node, settings->
origin() );
281 parseMeshTile( terrainTile, layerName + u
"_"_s );
288 textureGenerator->setTextureSize( oldResolution );
291QgsTerrainTileEntity *Qgs3DSceneExporter::getFlatTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
293 QgsFlatTerrainGenerator *generator = qgis::down_cast<QgsFlatTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
294 FlatTerrainChunkLoader *flatTerrainLoader = qobject_cast<FlatTerrainChunkLoader *>( generator->
createChunkLoader( node ) );
295 flatTerrainLoader->start();
296 if ( mExportTextures )
297 terrain->textureGenerator()->waitForFinished();
299 Qt3DCore::QEntity *entity = flatTerrainLoader->createEntity(
this );
300 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( entity );
302 const QList<QgsGeoTransform *> transforms = entity->findChildren<QgsGeoTransform *>();
303 for ( QgsGeoTransform *transform : transforms )
305 transform->setOrigin( mapOrigin );
311QgsTerrainTileEntity *Qgs3DSceneExporter::getDemTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
315 QgsDemTerrainGenerator *generator = qgis::down_cast<QgsDemTerrainGenerator *>( terrain->mapSettings()->terrainGenerator()->clone() );
317 QgsDemTerrainTileLoader *loader = qobject_cast<QgsDemTerrainTileLoader *>( generator->
createChunkLoader( node ) );
320 if ( mExportTextures )
321 terrain->textureGenerator()->waitForFinished();
322 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity(
this ) );
324 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
325 for ( QgsGeoTransform *transform : transforms )
327 transform->setOrigin( mapOrigin );
334QgsTerrainTileEntity *Qgs3DSceneExporter::getMeshTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
336 QgsMeshTerrainGenerator *generator = qgis::down_cast<QgsMeshTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
337 QgsMeshTerrainTileLoader *loader = qobject_cast<QgsMeshTerrainTileLoader *>( generator->createChunkLoader( node ) );
340 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity(
this ) );
342 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
343 for ( QgsGeoTransform *transform : transforms )
345 transform->setOrigin( mapOrigin );
351void Qgs3DSceneExporter::parseFlatTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
356 Qt3DCore::QGeometry *geometry = mesh->geometry();
357 Qt3DExtras::QPlaneGeometry *tileGeometry = qobject_cast<Qt3DExtras::QPlaneGeometry *>( geometry );
360 QgsDebugError(
"Qt3DExtras::QPlaneGeometry* is expected but something else was given" );
365 Qt3DCore::QAttribute *positionAttribute = tileGeometry->positionAttribute();
366 if ( !positionAttribute )
368 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( layerName ) );
371 const QByteArray verticesBytes =
getData( positionAttribute->buffer() );
372 if ( verticesBytes.isNull() )
374 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( layerName ) );
380 Qt3DCore::QAttribute *indexAttribute = tileGeometry->indexAttribute();
381 if ( !indexAttribute )
383 QgsDebugError( QString(
"Cannot export '%1' - geometry has no index attribute!" ).arg( layerName ) );
386 const QByteArray indexBytes =
getData( indexAttribute->buffer() );
387 if ( indexBytes.isNull() )
389 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( layerName ) );
392 const QVector<uint> indexesBuffer =
getIndexData( indexAttribute, indexBytes );
394 QString objectNamePrefix = layerName;
395 if ( objectNamePrefix != QString() )
396 objectNamePrefix += QString();
398 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"Flat_tile"_s ) );
399 mObjects.push_back(
object );
401 object->setSmoothEdges( mSmoothEdges );
402 object->setupTriangle( positionBuffer, indexesBuffer, transform->matrix() );
404 if ( mExportNormals )
407 QVector<float> normalsBuffer;
408 for (
int i = 0; i < positionBuffer.size(); i += 3 )
409 normalsBuffer << 0.0f << 1.0f << 0.0f;
410 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
413 Qt3DCore::QAttribute *texCoordsAttribute = tileGeometry->texCoordAttribute();
414 if ( mExportTextures && texCoordsAttribute )
418 object->setupTextureCoordinates( texCoords );
420 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
421 const QImage img = textureImage->getImage();
422 object->setTextureImage( img );
426void Qgs3DSceneExporter::parseDemTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
431 Qt3DCore::QGeometry *geometry = mesh->geometry();
432 DemTerrainTileGeometry *tileGeometry = qobject_cast<DemTerrainTileGeometry *>( geometry );
435 QgsDebugError(
"DemTerrainTileGeometry* is expected but something else was given" );
439 Qt3DCore::QAttribute *positionAttribute = tileGeometry->positionAttribute();
440 const QByteArray positionBytes = positionAttribute->buffer()->data();
443 Qt3DCore::QAttribute *indexAttribute = tileGeometry->indexAttribute();
444 const QByteArray indexBytes = indexAttribute->buffer()->data();
445 const QVector<unsigned int> indexBuffer =
getIndexData( indexAttribute, indexBytes );
447 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( layerName + u
"DEM_tile"_s ) );
448 mObjects.push_back(
object );
450 object->setSmoothEdges( mSmoothEdges );
451 object->setupTriangle( positionBuffer, indexBuffer, transform->matrix() );
453 Qt3DCore::QAttribute *normalsAttributes = tileGeometry->normalAttribute();
454 if ( mExportNormals && normalsAttributes )
456 const QByteArray normalsBytes = normalsAttributes->buffer()->data();
458 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
461 Qt3DCore::QAttribute *texCoordsAttribute = tileGeometry->texCoordsAttribute();
462 if ( mExportTextures && texCoordsAttribute )
464 const QByteArray texCoordsBytes = texCoordsAttribute->buffer()->data();
466 object->setupTextureCoordinates( texCoordsBuffer );
468 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
469 const QImage img = textureImage->getImage();
470 object->setTextureImage( img );
474void Qgs3DSceneExporter::parseMeshTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
476 QString objectNamePrefix = layerName;
477 if ( objectNamePrefix != QString() )
478 objectNamePrefix +=
'_'_L1;
480 const QList<Qt3DRender::QGeometryRenderer *> renderers = tileEntity->findChildren<Qt3DRender::QGeometryRenderer *>();
481 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
483 Qgs3DExportObject *obj = processGeometryRenderer( renderer, objectNamePrefix );
490QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processInstancedPointGeometry( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
495 QMatrix4x4 instanceMaterialTransform;
496 const QList<Qt3DRender::QMaterial *> materials = entity->findChildren<Qt3DRender::QMaterial *>();
497 if ( !materials.isEmpty() )
499 for (
const Qt3DRender::QParameter *parameter : materials[0]->parameters() )
501 if ( parameter->name() ==
"inst"_L1 )
503 instanceMaterialTransform = parameter->value().value<QMatrix4x4>();
509 QVector<Qgs3DExportObject *> objects;
510 const QList<Qt3DCore::QGeometry *> geometriesList = entity->findChildren<Qt3DCore::QGeometry *>();
511 for ( Qt3DCore::QGeometry *geometry : geometriesList )
513 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
514 Qt3DCore::QAttribute *indexAttribute =
findAttribute( geometry, QString(), Qt3DCore::QAttribute::IndexAttribute );
515 if ( !positionAttribute || !indexAttribute )
517 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position or index attribute!" ).arg( objectNamePrefix ) );
521 const QByteArray vertexBytes = positionAttribute->buffer()->data();
522 const QByteArray indexBytes = indexAttribute->buffer()->data();
523 if ( vertexBytes.isNull() || indexBytes.isNull() )
525 QgsDebugError( QString(
"Geometry for '%1' has position or index attribute with empty data!" ).arg( objectNamePrefix ) );
530 const QVector<uint> indexData =
getIndexData( indexAttribute, indexBytes );
532 Qt3DCore::QAttribute *instanceDataAttribute =
findAttribute( geometry, u
"pos"_s, Qt3DCore::QAttribute::VertexAttribute );
533 if ( !instanceDataAttribute )
535 QgsDebugError( QString(
"Cannot export '%1' - geometry has no instanceData attribute!" ).arg( objectNamePrefix ) );
538 const QByteArray instancePositionBytes =
getData( instanceDataAttribute->buffer() );
539 if ( instancePositionBytes.isNull() )
541 QgsDebugError( QString(
"Geometry for '%1' has instanceData attribute with empty data!" ).arg( objectNamePrefix ) );
546 for (
int i = 0; i < instancePosition.size(); i += 3 )
548 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"instance_point"_s ) );
549 objects.push_back(
object );
550 QMatrix4x4 instanceTransform;
551 instanceTransform.translate( instancePosition[i], instancePosition[i + 1], instancePosition[i + 2] );
552 instanceTransform *= instanceMaterialTransform;
553 object->setupTriangle( positionData, indexData, instanceTransform );
555 object->setSmoothEdges( mSmoothEdges );
557 Qt3DCore::QAttribute *normalsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
558 if ( mExportNormals && normalsAttribute )
562 object->setupNormalCoordinates( normalsData, instanceTransform );
570QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processSceneLoaderGeometries( Qt3DRender::QSceneLoader *sceneLoader,
const QString &objectNamePrefix )
572 QVector<Qgs3DExportObject *> objects;
573 Qt3DCore::QEntity *sceneLoaderParent = qobject_cast<Qt3DCore::QEntity *>( sceneLoader->parent() );
575 QMatrix4x4 sceneTransform;
576 if ( entityTransform )
578 sceneTransform = entityTransform->matrix();
580 QStringList entityNames = sceneLoader->entityNames();
581 for (
const QString &entityName : entityNames )
583 Qt3DRender::QGeometryRenderer *mesh = qobject_cast<Qt3DRender::QGeometryRenderer *>( sceneLoader->component( entityName, Qt3DRender::QSceneLoader::GeometryRendererComponent ) );
584 Qgs3DExportObject *
object = processGeometryRenderer( mesh, objectNamePrefix, sceneTransform );
587 objects.push_back(
object );
592Qgs3DExportObject *Qgs3DSceneExporter::processGeometryRenderer( Qt3DRender::QGeometryRenderer *geomRenderer,
const QString &objectNamePrefix,
const QMatrix4x4 &sceneTransform )
595 if ( geomRenderer->primitiveType() != Qt3DRender::QGeometryRenderer::Triangles )
598 Qt3DCore::QGeometry *geometry = geomRenderer->geometry();
610 QVector<std::pair<uint, uint>> triangleIndexStartingIndiceToKeep;
611 QgsTessellatedPolygonGeometry *tessGeom =
dynamic_cast<QgsTessellatedPolygonGeometry *
>( geometry );
614 QVector<QgsFeatureId> featureIds = tessGeom->
featureIds();
616 QSet<QgsFeatureId> tempFeatToAdd;
619 for (
int idx = 0; idx < featureIds.size(); idx++ )
622 if ( !mExportedFeatureIds.contains( feat ) )
625 tempFeatToAdd += feat;
628 const uint startIdx = triangleIndex[idx];
629 const uint endIdx = idx < triangleIndex.size() - 1 ? triangleIndex[idx + 1] : std::numeric_limits<uint>::max();
631 if ( startIdx < endIdx )
632 triangleIndexStartingIndiceToKeep.append( std::pair<uint, uint>( startIdx, endIdx ) );
635 mExportedFeatureIds += tempFeatToAdd;
637 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
644 QMatrix4x4 transformMatrix = sceneTransform;
645 QObject *parent = geomRenderer->parent();
648 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( parent );
652 transformMatrix = transform->matrix() * transformMatrix;
654 parent = parent->parent();
657 Qt3DCore::QAttribute *positionAttribute =
nullptr;
658 Qt3DCore::QAttribute *indexAttribute =
nullptr;
659 QByteArray indexBytes, vertexBytes;
660 QVector<uint> indexDataTmp;
661 QVector<uint> indexData;
662 QVector<float> positionData;
665 positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
666 if ( !positionAttribute )
668 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
672 vertexBytes =
getData( positionAttribute->buffer() );
673 if ( vertexBytes.isNull() )
675 QgsDebugError( QString(
"Will not export '%1' as geometry has empty position data!" ).arg( objectNamePrefix ) );
682 QVector<Qt3DCore::QAttribute *> attributes = geometry->attributes();
683 for ( Qt3DCore::QAttribute *attribute : attributes )
685 if ( attribute->attributeType() == Qt3DCore::QAttribute::IndexAttribute )
687 indexAttribute = attribute;
688 indexBytes =
getData( indexAttribute->buffer() );
689 if ( indexBytes.isNull() )
691 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( objectNamePrefix ) );
695 indexDataTmp =
getIndexData( indexAttribute, indexBytes );
702 if ( !indexAttribute )
704 for ( uint i = 0; i < static_cast<uint>( positionData.size() / 3 ); ++i )
706 indexDataTmp.push_back( i );
711 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
714 indexData.append( indexDataTmp );
720 const int triangleIndexStartingIndiceToKeepSize = triangleIndexStartingIndiceToKeep.size();
721 const uint indexDataTmpSize =
static_cast<uint
>( indexDataTmp.size() );
722 for ( uint i = 0; i + 2 < indexDataTmpSize; i += 3 )
724 const uint triangleIdx = i / 3;
727 while ( intervalIdx < triangleIndexStartingIndiceToKeepSize && triangleIdx >= triangleIndexStartingIndiceToKeep[intervalIdx].second )
733 if ( intervalIdx < triangleIndexStartingIndiceToKeepSize && triangleIdx >= triangleIndexStartingIndiceToKeep[intervalIdx].first && triangleIdx < triangleIndexStartingIndiceToKeep[intervalIdx].second )
735 indexData.push_back( indexDataTmp[
static_cast<int>( i )] );
736 indexData.push_back( indexDataTmp[
static_cast<int>( i + 1 )] );
737 indexData.push_back( indexDataTmp[
static_cast<int>( i + 2 )] );
743 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"mesh_geometry"_s ) );
744 object->setupTriangle( positionData, indexData, transformMatrix );
746 Qt3DCore::QAttribute *normalsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
747 if ( mExportNormals && normalsAttribute )
751 object->setupNormalCoordinates( normalsData, transformMatrix );
754 Qt3DCore::QAttribute *texCoordsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultTextureCoordinateAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
755 if ( mExportTextures && texCoordsAttribute )
759 object->setupTextureCoordinates( texCoordsData );
765QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processLines( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
767 QVector<Qgs3DExportObject *> objs;
768 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
769 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
771 if ( renderer->primitiveType() != Qt3DRender::QGeometryRenderer::LineStripAdjacency )
773 Qt3DCore::QGeometry *geom = renderer->geometry();
774 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geom, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
775 Qt3DCore::QAttribute *indexAttribute =
findAttribute( geom, QString(), Qt3DCore::QAttribute::IndexAttribute );
776 if ( !positionAttribute || !indexAttribute )
778 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position or index attribute!" ).arg( objectNamePrefix ) );
782 const QByteArray vertexBytes =
getData( positionAttribute->buffer() );
783 const QByteArray indexBytes =
getData( indexAttribute->buffer() );
784 if ( vertexBytes.isNull() || indexBytes.isNull() )
786 QgsDebugError( QString(
"Geometry for '%1' has position or index attribute with empty data!" ).arg( objectNamePrefix ) );
791 Qgs3DExportObject *exportObject =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"line"_s ) );
794 objs.push_back( exportObject );
799Qgs3DExportObject *Qgs3DSceneExporter::processPoints( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
801 QVector<float> points;
802 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
803 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
805 Qt3DCore::QGeometry *geometry = qobject_cast<QgsBillboardGeometry *>( renderer->geometry() );
808 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
809 if ( !positionAttribute )
811 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
814 const QByteArray positionBytes =
getData( positionAttribute->buffer() );
815 if ( positionBytes.isNull() )
817 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( objectNamePrefix ) );
823 Qgs3DExportObject *obj =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"points"_s ) );
830 if ( mObjects.isEmpty() )
835 const QString objFilePath = QDir( sceneFolderPath ).filePath( sceneName + u
".obj"_s );
836 const QString mtlFilePath = QDir( sceneFolderPath ).filePath( sceneName + u
".mtl"_s );
838 QFile file( objFilePath );
839 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
841 QgsDebugError( u
"Scene can not be exported to '%1'. File access error."_s.arg( objFilePath ) );
844 QFile mtlFile( mtlFilePath );
845 if ( !mtlFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
847 QgsDebugError( u
"Scene can not be exported to '%1'. File access error."_s.arg( mtlFilePath ) );
851 float maxfloat = std::numeric_limits<float>::max(), minFloat = std::numeric_limits<float>::lowest();
852 float minX = maxfloat, minY = maxfloat, minZ = maxfloat, maxX = minFloat, maxY = minFloat, maxZ = minFloat;
855 obj->
objectBounds( minX, minY, minZ, maxX, maxY, maxZ );
858 float diffX = 1.0f, diffY = 1.0f, diffZ = 1.0f;
863 const float centerX = ( minX + maxX ) / 2.0f;
864 const float centerY = ( minY + maxY ) / 2.0f;
865 const float centerZ = ( minZ + maxZ ) / 2.0f;
867 const float scale = std::max( diffX, std::max( diffY, diffZ ) );
869 QTextStream out( &file );
871 const QString mtlLibName = sceneName +
".mtl";
872 out <<
"mtllib " << mtlLibName <<
"\n";
874 QTextStream mtlOut( &mtlFile );
880 const QString material = obj->
saveMaterial( mtlOut, sceneFolderPath );
881 out <<
"o " << obj->
name() <<
"\n";
882 if ( material != QString() )
883 out <<
"usemtl " << material <<
"\n";
884 obj->
saveTo( out,
scale / mScale, QVector3D( centerX, centerY, centerZ ), precision );
891QString Qgs3DSceneExporter::getObjectName(
const QString &name )
894 if ( mUsedObjectNamesCounter.contains( name ) )
896 ret = u
"%1%2"_s.arg( name ).arg( mUsedObjectNamesCounter[name] );
897 mUsedObjectNamesCounter[name]++;
900 mUsedObjectNamesCounter[name] = 2;
Manages the data of each object of the scene (positions, normals, texture coordinates ....
void setupPoint(const QVector< float > &positionsBuffer)
sets point positions coordinates
QString saveMaterial(QTextStream &mtlOut, const QString &folder) const
saves the texture of the object and material information
QString name() const
Returns the object name.
void saveTo(QTextStream &out, float scale, const QVector3D ¢er, int precision=6) const
Saves the current object to the output stream while scaling the object and centering it to be visible...
void objectBounds(float &minX, float &minY, float &minZ, float &maxX, float &maxY, float &maxZ) const
Updates the box bounds explained with the current object bounds This expands the bounding box if the ...
void setupLine(const QVector< float > &positionsBuffer)
sets line indexes and positions coordinates
QgsTerrainGenerator * terrainGenerator() const
Returns the terrain generator.
bool terrainRenderingEnabled() const
Returns whether the 2D terrain surface will be rendered.
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0).
bool save(const QString &sceneName, const QString &sceneFolderPath, int precision=6) const
Saves the scene to a .obj file Returns false if the operation failed.
void parseTerrain(QgsTerrainEntity *terrain, const QString &layer)
Creates terrain export objects from the terrain entity.
float scale() const
Returns the scale of the exported 3D model.
bool parseVectorLayerEntity(Qt3DCore::QEntity *entity, QgsVectorLayer *layer)
Creates necessary export objects from entity if it represents valid vector layer entity Returns false...
static QgsPhongMaterialSettings phongMaterialFromQt3DComponent(Qt3DExtras::QPhongMaterial *material)
Returns phong material settings object based on the Qt3D material.
Base class for all renderers that participate in 3D views.
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass).
Abstract base class for 3D symbols that are used by VectorLayer3DRenderer objects.
virtual bool exportGeometries(Qgs3DSceneExporter *exporter, Qt3DCore::QEntity *entity, const QString &objectNamePrefix) const
Exports the geometries contained within the hierarchy of entity.
QgsDemHeightMapGenerator * heightMapGenerator()
Returns height map generator object - takes care of extraction of elevations from the layer).
QgsChunkLoader * createChunkLoader(QgsChunkNode *node) const override
void setResolution(int resolution)
Sets resolution of the generator (how many elevation samples on one side of a terrain tile).
Terrain generator that creates a simple square flat area.
QgsChunkLoader * createChunkLoader(QgsChunkNode *node) const override
QImage getImage() const
Returns the image.
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
Basic shading material used for rendering based on the Phong shading model with three color component...
Base class for generators of terrain.
@ QuantizedMesh
Terrain is built from quantized mesh tiles.
@ Dem
Terrain is built from raster layer with digital elevation model.
@ Online
Terrain is built from downloaded tiles with digital elevation model.
@ Mesh
Terrain is built from mesh layer with z value on vertices.
@ Flat
The whole terrain is flat area.
virtual Type type() const =0
What texture generator implementation is this.
QVector< QgsFeatureId > featureIds() const
Returns included feature ids.
QVector< uint > triangleIndexStartingIndices() const
Returns triangle index for features. For a feature featureIds()[i], matching triangles start at trian...
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
3D renderer that renders all features of a vector layer with the same 3D symbol.
const QgsAbstract3DSymbol * symbol() const
Returns 3D symbol associated with the renderer.
Represents a vector layer which manages a vector based dataset.
QVector< uint > getIndexData(Qt3DCore::QAttribute *indexAttribute, const QByteArray &data)
Qt3DCore::QAttribute * findAttribute(Qt3DCore::QGeometry *geometry, const QString &name, Qt3DCore::QAttribute::AttributeType type)
QVector< uint > _getIndexDataImplementation(const QByteArray &data)
Component * findTypedComponent(Qt3DCore::QEntity *entity)
QVector< T > getAttributeData(Qt3DCore::QAttribute *attribute, const QByteArray &data)
QByteArray getData(Qt3DCore::QBuffer *buffer)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)