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/QSceneLoader>
68#include <Qt3DRender/QTexture>
69#include <Qt3DRender/QTextureImage>
71#include "moc_qgs3dsceneexporter.cpp"
73using namespace Qt::StringLiterals;
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 );
106 QVector<uint> result;
107 const char *pData = data.constData();
108 for (
int i = 0; i < data.size(); i +=
sizeof( T ) )
111 memcpy( &v, pData + i,
sizeof( T ) );
112 result.push_back( ( uint ) v );
117QVector<uint>
getIndexData( Qt3DCore::QAttribute *indexAttribute,
const QByteArray &data )
119 switch ( indexAttribute->vertexBaseType() )
121 case Qt3DCore::QAttribute::VertexBaseType::Int:
123 case Qt3DCore::QAttribute::VertexBaseType::UnsignedInt:
125 case Qt3DCore::QAttribute::VertexBaseType::Short:
127 case Qt3DCore::QAttribute::VertexBaseType::UnsignedShort:
129 case Qt3DCore::QAttribute::VertexBaseType::Byte:
131 case Qt3DCore::QAttribute::VertexBaseType::UnsignedByte:
134 QgsDebugError(
"Probably trying to get index data using an attribute that has vertex data" );
137 return QVector<uint>();
140QByteArray
getData( Qt3DCore::QBuffer *buffer )
142 QByteArray bytes = buffer->data();
143 if ( bytes.isNull() )
150Qt3DCore::QAttribute *
findAttribute( Qt3DCore::QGeometry *geometry,
const QString &name, Qt3DCore::QAttribute::AttributeType type )
152 QVector<Qt3DCore::QAttribute *> attributes = geometry->attributes();
153 for ( Qt3DCore::QAttribute *attribute : attributes )
155 if ( attribute->attributeType() != type )
157 if ( name.isEmpty() || attribute->name() == name )
163template<
typename Component>
168 QVector<Qt3DCore::QComponent *> components = entity->components();
169 for ( Qt3DCore::QComponent *component : components )
171 Component *typedComponent = qobject_cast<Component *>( component );
172 if ( typedComponent )
173 return typedComponent;
181 const QString rendererType = abstractRenderer->
type();
183 if ( rendererType ==
"rulebased" )
185 int prevSize = mObjects.size();
187 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
188 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
190 Qt3DCore::QEntity *parentEntity = qobject_cast<Qt3DCore::QEntity *>( renderer->parent() );
196 if ( mExportTextures )
197 processEntityMaterial( parentEntity,
object );
198 mObjects.push_back(
object );
200 return mObjects.size() > prevSize;
203 else if ( rendererType ==
"vector" )
206 if ( vectorLayerRenderer )
218 QgsDebugMsgLevel( u
"Type '%1' of layer '%2' is not exportable."_s.arg( layer->
name(), rendererType ), 2 );
225void Qgs3DSceneExporter::processEntityMaterial( Qt3DCore::QEntity *entity,
Qgs3DExportObject *
object )
const
231 object->setupMaterial( &material );
235 if ( diffuseMapMaterial )
237 const Qt3DRender::QTexture2D *diffuseTexture = diffuseMapMaterial->diffuse().value<Qt3DRender::QTexture2D *>();
238 if ( diffuseTexture )
240 const QVector<Qt3DRender::QAbstractTextureImage *> textureImages = diffuseTexture->textureImages();
241 for (
const Qt3DRender::QAbstractTextureImage *tex : textureImages )
243 const QgsImageTexture *imageTexture =
dynamic_cast<const QgsImageTexture *
>( tex );
246 const QImage image = imageTexture->
getImage();
247 object->setTextureImage( image );
261 QgsChunkNode *node = terrain->rootNode();
266 QgsTerrainTileEntity *terrainTile =
nullptr;
267 QgsTerrainTextureGenerator *textureGenerator = terrain->textureGenerator();
268 textureGenerator->waitForFinished();
269 const QSize oldResolution = textureGenerator->textureSize();
270 textureGenerator->setTextureSize( QSize( mTerrainTextureResolution, mTerrainTextureResolution ) );
271 switch ( generator->
type() )
274 terrainTile = getDemTerrainEntity( terrain, node, settings->
origin() );
275 parseDemTile( terrainTile, layerName + u
"_"_s );
278 terrainTile = getFlatTerrainEntity( terrain, node, settings->
origin() );
279 parseFlatTile( terrainTile, layerName + u
"_"_s );
282 terrainTile = getMeshTerrainEntity( terrain, node, settings->
origin() );
283 parseMeshTile( terrainTile, layerName + u
"_"_s );
290 textureGenerator->setTextureSize( oldResolution );
293QgsTerrainTileEntity *Qgs3DSceneExporter::getFlatTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
295 QgsFlatTerrainGenerator *generator = qgis::down_cast<QgsFlatTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
296 FlatTerrainChunkLoader *flatTerrainLoader = qobject_cast<FlatTerrainChunkLoader *>( generator->
createChunkLoader( node ) );
297 flatTerrainLoader->start();
298 if ( mExportTextures )
299 terrain->textureGenerator()->waitForFinished();
301 Qt3DCore::QEntity *entity = flatTerrainLoader->createEntity(
this );
302 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( entity );
304 const QList<QgsGeoTransform *> transforms = entity->findChildren<QgsGeoTransform *>();
305 for ( QgsGeoTransform *transform : transforms )
307 transform->setOrigin( mapOrigin );
313QgsTerrainTileEntity *Qgs3DSceneExporter::getDemTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
317 QgsDemTerrainGenerator *generator = qgis::down_cast<QgsDemTerrainGenerator *>( terrain->mapSettings()->terrainGenerator()->clone() );
319 QgsDemTerrainTileLoader *loader = qobject_cast<QgsDemTerrainTileLoader *>( generator->
createChunkLoader( node ) );
322 if ( mExportTextures )
323 terrain->textureGenerator()->waitForFinished();
324 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity(
this ) );
326 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
327 for ( QgsGeoTransform *transform : transforms )
329 transform->setOrigin( mapOrigin );
336QgsTerrainTileEntity *Qgs3DSceneExporter::getMeshTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
338 QgsMeshTerrainGenerator *generator = qgis::down_cast<QgsMeshTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
339 QgsMeshTerrainTileLoader *loader = qobject_cast<QgsMeshTerrainTileLoader *>( generator->createChunkLoader( node ) );
342 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity(
this ) );
344 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
345 for ( QgsGeoTransform *transform : transforms )
347 transform->setOrigin( mapOrigin );
353void Qgs3DSceneExporter::parseFlatTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
358 Qt3DCore::QGeometry *geometry = mesh->geometry();
359 Qt3DExtras::QPlaneGeometry *tileGeometry = qobject_cast<Qt3DExtras::QPlaneGeometry *>( geometry );
362 QgsDebugError(
"Qt3DExtras::QPlaneGeometry* is expected but something else was given" );
367 Qt3DCore::QAttribute *positionAttribute = tileGeometry->positionAttribute();
368 if ( !positionAttribute )
370 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( layerName ) );
373 const QByteArray verticesBytes =
getData( positionAttribute->buffer() );
374 if ( verticesBytes.isNull() )
376 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( layerName ) );
382 Qt3DCore::QAttribute *indexAttribute = tileGeometry->indexAttribute();
383 if ( !indexAttribute )
385 QgsDebugError( QString(
"Cannot export '%1' - geometry has no index attribute!" ).arg( layerName ) );
388 const QByteArray indexBytes =
getData( indexAttribute->buffer() );
389 if ( indexBytes.isNull() )
391 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( layerName ) );
394 const QVector<uint> indexesBuffer =
getIndexData( indexAttribute, indexBytes );
396 QString objectNamePrefix = layerName;
397 if ( objectNamePrefix != QString() )
398 objectNamePrefix += QString();
400 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"Flat_tile"_s ) );
401 mObjects.push_back(
object );
403 object->setSmoothEdges( mSmoothEdges );
404 object->setupTriangle( positionBuffer, indexesBuffer, transform->matrix() );
406 if ( mExportNormals )
409 QVector<float> normalsBuffer;
410 for (
int i = 0; i < positionBuffer.size(); i += 3 )
411 normalsBuffer << 0.0f << 1.0f << 0.0f;
412 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
415 Qt3DCore::QAttribute *texCoordsAttribute = tileGeometry->texCoordAttribute();
416 if ( mExportTextures && texCoordsAttribute )
420 object->setupTextureCoordinates( texCoords );
422 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
423 const QImage img = textureImage->getImage();
424 object->setTextureImage( img );
428void Qgs3DSceneExporter::parseDemTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
433 Qt3DCore::QGeometry *geometry = mesh->geometry();
434 DemTerrainTileGeometry *tileGeometry = qobject_cast<DemTerrainTileGeometry *>( geometry );
437 QgsDebugError(
"DemTerrainTileGeometry* is expected but something else was given" );
441 Qt3DCore::QAttribute *positionAttribute = tileGeometry->positionAttribute();
442 const QByteArray positionBytes = positionAttribute->buffer()->data();
445 Qt3DCore::QAttribute *indexAttribute = tileGeometry->indexAttribute();
446 const QByteArray indexBytes = indexAttribute->buffer()->data();
447 const QVector<unsigned int> indexBuffer =
getIndexData( indexAttribute, indexBytes );
449 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( layerName + u
"DEM_tile"_s ) );
450 mObjects.push_back(
object );
452 object->setSmoothEdges( mSmoothEdges );
453 object->setupTriangle( positionBuffer, indexBuffer, transform->matrix() );
455 Qt3DCore::QAttribute *normalsAttributes = tileGeometry->normalAttribute();
456 if ( mExportNormals && normalsAttributes )
458 const QByteArray normalsBytes = normalsAttributes->buffer()->data();
460 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
463 Qt3DCore::QAttribute *texCoordsAttribute = tileGeometry->texCoordsAttribute();
464 if ( mExportTextures && texCoordsAttribute )
466 const QByteArray texCoordsBytes = texCoordsAttribute->buffer()->data();
468 object->setupTextureCoordinates( texCoordsBuffer );
470 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
471 const QImage img = textureImage->getImage();
472 object->setTextureImage( img );
476void Qgs3DSceneExporter::parseMeshTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
478 QString objectNamePrefix = layerName;
479 if ( objectNamePrefix != QString() )
480 objectNamePrefix +=
'_'_L1;
482 const QList<Qt3DRender::QGeometryRenderer *> renderers = tileEntity->findChildren<Qt3DRender::QGeometryRenderer *>();
483 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
485 Qgs3DExportObject *obj = processGeometryRenderer( renderer, objectNamePrefix );
492QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processInstancedPointGeometry( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
494 QVector<Qgs3DExportObject *> objects;
495 const QList<Qt3DCore::QGeometry *> geometriesList = entity->findChildren<Qt3DCore::QGeometry *>();
496 for ( Qt3DCore::QGeometry *geometry : geometriesList )
498 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
499 Qt3DCore::QAttribute *indexAttribute =
findAttribute( geometry, QString(), Qt3DCore::QAttribute::IndexAttribute );
500 if ( !positionAttribute || !indexAttribute )
502 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position or index attribute!" ).arg( objectNamePrefix ) );
506 const QByteArray vertexBytes = positionAttribute->buffer()->data();
507 const QByteArray indexBytes = indexAttribute->buffer()->data();
508 if ( vertexBytes.isNull() || indexBytes.isNull() )
510 QgsDebugError( QString(
"Geometry for '%1' has position or index attribute with empty data!" ).arg( objectNamePrefix ) );
515 const QVector<uint> indexData =
getIndexData( indexAttribute, indexBytes );
517 Qt3DCore::QAttribute *instanceDataAttribute =
findAttribute( geometry, u
"pos"_s, Qt3DCore::QAttribute::VertexAttribute );
518 if ( !instanceDataAttribute )
520 QgsDebugError( QString(
"Cannot export '%1' - geometry has no instanceData attribute!" ).arg( objectNamePrefix ) );
523 const QByteArray instancePositionBytes =
getData( instanceDataAttribute->buffer() );
524 if ( instancePositionBytes.isNull() )
526 QgsDebugError( QString(
"Geometry for '%1' has instanceData attribute with empty data!" ).arg( objectNamePrefix ) );
531 for (
int i = 0; i < instancePosition.size(); i += 3 )
533 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"instance_point"_s ) );
534 objects.push_back(
object );
535 QMatrix4x4 instanceTransform;
536 instanceTransform.translate( instancePosition[i], instancePosition[i + 1], instancePosition[i + 2] );
537 object->setupTriangle( positionData, indexData, instanceTransform );
539 object->setSmoothEdges( mSmoothEdges );
541 Qt3DCore::QAttribute *normalsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
542 if ( mExportNormals && normalsAttribute )
546 object->setupNormalCoordinates( normalsData, instanceTransform );
554QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processSceneLoaderGeometries( Qt3DRender::QSceneLoader *sceneLoader,
const QString &objectNamePrefix )
556 QVector<Qgs3DExportObject *> objects;
557 Qt3DCore::QEntity *sceneLoaderParent = qobject_cast<Qt3DCore::QEntity *>( sceneLoader->parent() );
559 QMatrix4x4 sceneTransform;
560 if ( entityTransform )
562 sceneTransform = entityTransform->matrix();
564 QStringList entityNames = sceneLoader->entityNames();
565 for (
const QString &entityName : entityNames )
567 Qt3DRender::QGeometryRenderer *mesh = qobject_cast<Qt3DRender::QGeometryRenderer *>( sceneLoader->component( entityName, Qt3DRender::QSceneLoader::GeometryRendererComponent ) );
568 Qgs3DExportObject *
object = processGeometryRenderer( mesh, objectNamePrefix, sceneTransform );
571 objects.push_back(
object );
576Qgs3DExportObject *Qgs3DSceneExporter::processGeometryRenderer( Qt3DRender::QGeometryRenderer *geomRenderer,
const QString &objectNamePrefix,
const QMatrix4x4 &sceneTransform )
579 if ( geomRenderer->primitiveType() != Qt3DRender::QGeometryRenderer::Triangles )
582 Qt3DCore::QGeometry *geometry = geomRenderer->geometry();
594 QVector<std::pair<uint, uint>> triangleIndexStartingIndiceToKeep;
595 QgsTessellatedPolygonGeometry *tessGeom =
dynamic_cast<QgsTessellatedPolygonGeometry *
>( geometry );
598 QVector<QgsFeatureId> featureIds = tessGeom->
featureIds();
600 QSet<QgsFeatureId> tempFeatToAdd;
603 for (
int idx = 0; idx < featureIds.size(); idx++ )
606 if ( !mExportedFeatureIds.contains( feat ) )
609 tempFeatToAdd += feat;
612 const uint startIdx = triangleIndex[idx];
613 const uint endIdx = idx < triangleIndex.size() - 1 ? triangleIndex[idx + 1] : std::numeric_limits<uint>::max();
615 if ( startIdx < endIdx )
616 triangleIndexStartingIndiceToKeep.append( std::pair<uint, uint>( startIdx, endIdx ) );
619 mExportedFeatureIds += tempFeatToAdd;
621 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
628 QMatrix4x4 transformMatrix = sceneTransform;
629 QObject *parent = geomRenderer->parent();
632 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( parent );
636 transformMatrix = transform->matrix() * transformMatrix;
638 parent = parent->parent();
641 Qt3DCore::QAttribute *positionAttribute =
nullptr;
642 Qt3DCore::QAttribute *indexAttribute =
nullptr;
643 QByteArray indexBytes, vertexBytes;
644 QVector<uint> indexDataTmp;
645 QVector<uint> indexData;
646 QVector<float> positionData;
649 positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
650 if ( !positionAttribute )
652 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
656 vertexBytes =
getData( positionAttribute->buffer() );
657 if ( vertexBytes.isNull() )
659 QgsDebugError( QString(
"Will not export '%1' as geometry has empty position data!" ).arg( objectNamePrefix ) );
666 QVector<Qt3DCore::QAttribute *> attributes = geometry->attributes();
667 for ( Qt3DCore::QAttribute *attribute : attributes )
669 if ( attribute->attributeType() == Qt3DCore::QAttribute::IndexAttribute )
671 indexAttribute = attribute;
672 indexBytes =
getData( indexAttribute->buffer() );
673 if ( indexBytes.isNull() )
675 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( objectNamePrefix ) );
679 indexDataTmp =
getIndexData( indexAttribute, indexBytes );
686 if ( !indexAttribute )
688 for ( uint i = 0; i < static_cast<uint>( positionData.size() / 3 ); ++i )
690 indexDataTmp.push_back( i );
695 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
698 indexData.append( indexDataTmp );
704 const int triangleIndexStartingIndiceToKeepSize = triangleIndexStartingIndiceToKeep.size();
705 const uint indexDataTmpSize =
static_cast<uint
>( indexDataTmp.size() );
706 for ( uint i = 0; i + 2 < indexDataTmpSize; i += 3 )
708 const uint triangleIdx = i / 3;
711 while ( intervalIdx < triangleIndexStartingIndiceToKeepSize
712 && triangleIdx >= triangleIndexStartingIndiceToKeep[intervalIdx].second )
718 if ( intervalIdx < triangleIndexStartingIndiceToKeepSize
719 && triangleIdx >= triangleIndexStartingIndiceToKeep[intervalIdx].first
720 && triangleIdx < triangleIndexStartingIndiceToKeep[intervalIdx].second )
722 indexData.push_back( indexDataTmp[
static_cast<int>( i )] );
723 indexData.push_back( indexDataTmp[
static_cast<int>( i + 1 )] );
724 indexData.push_back( indexDataTmp[
static_cast<int>( i + 2 )] );
730 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"mesh_geometry"_s ) );
731 object->setupTriangle( positionData, indexData, transformMatrix );
733 Qt3DCore::QAttribute *normalsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
734 if ( mExportNormals && normalsAttribute )
738 object->setupNormalCoordinates( normalsData, transformMatrix );
741 Qt3DCore::QAttribute *texCoordsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultTextureCoordinateAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
742 if ( mExportTextures && texCoordsAttribute )
746 object->setupTextureCoordinates( texCoordsData );
752QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processLines( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
754 QVector<Qgs3DExportObject *> objs;
755 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
756 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
758 if ( renderer->primitiveType() != Qt3DRender::QGeometryRenderer::LineStripAdjacency )
760 Qt3DCore::QGeometry *geom = renderer->geometry();
761 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geom, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
762 Qt3DCore::QAttribute *indexAttribute =
findAttribute( geom, QString(), Qt3DCore::QAttribute::IndexAttribute );
763 if ( !positionAttribute || !indexAttribute )
765 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position or index attribute!" ).arg( objectNamePrefix ) );
769 const QByteArray vertexBytes =
getData( positionAttribute->buffer() );
770 const QByteArray indexBytes =
getData( indexAttribute->buffer() );
771 if ( vertexBytes.isNull() || indexBytes.isNull() )
773 QgsDebugError( QString(
"Geometry for '%1' has position or index attribute with empty data!" ).arg( objectNamePrefix ) );
778 Qgs3DExportObject *exportObject =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"line"_s ) );
781 objs.push_back( exportObject );
786Qgs3DExportObject *Qgs3DSceneExporter::processPoints( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
788 QVector<float> points;
789 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
790 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
792 Qt3DCore::QGeometry *geometry = qobject_cast<QgsBillboardGeometry *>( renderer->geometry() );
795 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
796 if ( !positionAttribute )
798 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
801 const QByteArray positionBytes =
getData( positionAttribute->buffer() );
802 if ( positionBytes.isNull() )
804 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( objectNamePrefix ) );
810 Qgs3DExportObject *obj =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"points"_s ) );
817 if ( mObjects.isEmpty() )
822 const QString objFilePath = QDir( sceneFolderPath ).filePath( sceneName + u
".obj"_s );
823 const QString mtlFilePath = QDir( sceneFolderPath ).filePath( sceneName + u
".mtl"_s );
825 QFile file( objFilePath );
826 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
828 QgsDebugError( u
"Scene can not be exported to '%1'. File access error."_s.arg( objFilePath ) );
831 QFile mtlFile( mtlFilePath );
832 if ( !mtlFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
834 QgsDebugError( u
"Scene can not be exported to '%1'. File access error."_s.arg( mtlFilePath ) );
838 float maxfloat = std::numeric_limits<float>::max(), minFloat = std::numeric_limits<float>::lowest();
839 float minX = maxfloat, minY = maxfloat, minZ = maxfloat, maxX = minFloat, maxY = minFloat, maxZ = minFloat;
842 obj->
objectBounds( minX, minY, minZ, maxX, maxY, maxZ );
845 float diffX = 1.0f, diffY = 1.0f, diffZ = 1.0f;
850 const float centerX = ( minX + maxX ) / 2.0f;
851 const float centerY = ( minY + maxY ) / 2.0f;
852 const float centerZ = ( minZ + maxZ ) / 2.0f;
854 const float scale = std::max( diffX, std::max( diffY, diffZ ) );
856 QTextStream out( &file );
858 const QString mtlLibName = sceneName +
".mtl";
859 out <<
"mtllib " << mtlLibName <<
"\n";
861 QTextStream mtlOut( &mtlFile );
867 const QString material = obj->
saveMaterial( mtlOut, sceneFolderPath );
868 out <<
"o " << obj->
name() <<
"\n";
869 if ( material != QString() )
870 out <<
"usemtl " << material <<
"\n";
871 obj->
saveTo( out,
scale / mScale, QVector3D( centerX, centerY, centerZ ), precision );
878QString Qgs3DSceneExporter::getObjectName(
const QString &name )
881 if ( mUsedObjectNamesCounter.contains( name ) )
883 ret = u
"%1%2"_s.arg( name ).arg( mUsedObjectNamesCounter[name] );
884 mUsedObjectNamesCounter[name]++;
887 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)