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;
75template<
typename T> QVector<T>
getAttributeData( Qt3DCore::QAttribute *attribute,
const QByteArray &data )
77 const uint bytesOffset = attribute->byteOffset();
78 const uint bytesStride = attribute->byteStride();
79 const uint vertexSize = attribute->vertexSize();
80 const uint dataSize =
static_cast<uint
>( data.size() );
83 if ( bytesStride == 0 )
85 QgsDebugError(
"bytesStride==0, the attribute probably was not set properly" );
89 const char *pData = data.constData();
90 for (
unsigned int i = bytesOffset; i < dataSize; i += bytesStride )
92 for (
unsigned int j = 0; j < vertexSize *
sizeof( T ); j +=
sizeof( T ) )
95 memcpy( &v, pData + i + j,
sizeof( T ) );
96 result.push_back( v );
104 QVector<uint> result;
105 const char *pData = data.constData();
106 for (
int i = 0; i < data.size(); i +=
sizeof( T ) )
109 memcpy( &v, pData + i,
sizeof( T ) );
110 result.push_back( ( uint ) v );
115QVector<uint>
getIndexData( Qt3DCore::QAttribute *indexAttribute,
const QByteArray &data )
117 switch ( indexAttribute->vertexBaseType() )
119 case Qt3DCore::QAttribute::VertexBaseType::Int:
121 case Qt3DCore::QAttribute::VertexBaseType::UnsignedInt:
123 case Qt3DCore::QAttribute::VertexBaseType::Short:
125 case Qt3DCore::QAttribute::VertexBaseType::UnsignedShort:
127 case Qt3DCore::QAttribute::VertexBaseType::Byte:
129 case Qt3DCore::QAttribute::VertexBaseType::UnsignedByte:
132 QgsDebugError(
"Probably trying to get index data using an attribute that has vertex data" );
135 return QVector<uint>();
138QByteArray
getData( Qt3DCore::QBuffer *buffer )
140 QByteArray bytes = buffer->data();
141 if ( bytes.isNull() )
148Qt3DCore::QAttribute *
findAttribute( Qt3DCore::QGeometry *geometry,
const QString &name, Qt3DCore::QAttribute::AttributeType type )
150 QVector<Qt3DCore::QAttribute *> attributes = geometry->attributes();
151 for ( Qt3DCore::QAttribute *attribute : attributes )
153 if ( attribute->attributeType() != type )
155 if ( name.isEmpty() || attribute->name() == name )
165 QVector<Qt3DCore::QComponent *> components = entity->components();
166 for ( Qt3DCore::QComponent *component : components )
168 Component *typedComponent = qobject_cast<Component *>( component );
169 if ( typedComponent )
170 return typedComponent;
178 const QString rendererType = abstractRenderer->
type();
180 if ( rendererType ==
"rulebased" )
182 int prevSize = mObjects.size();
184 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
185 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
187 Qt3DCore::QEntity *parentEntity = qobject_cast<Qt3DCore::QEntity *>( renderer->parent() );
193 if ( mExportTextures )
194 processEntityMaterial( parentEntity,
object );
195 mObjects.push_back(
object );
197 return mObjects.size() > prevSize;
200 else if ( rendererType ==
"vector" )
203 if ( vectorLayerRenderer )
215 QgsDebugMsgLevel( u
"Type '%1' of layer '%2' is not exportable."_s.arg( layer->
name(), rendererType ), 2 );
222void Qgs3DSceneExporter::processEntityMaterial( Qt3DCore::QEntity *entity,
Qgs3DExportObject *
object )
const
228 object->setupMaterial( &material );
232 if ( diffuseMapMaterial )
234 const Qt3DRender::QTexture2D *diffuseTexture = diffuseMapMaterial->diffuse().value<Qt3DRender::QTexture2D *>();
235 if ( diffuseTexture )
237 const QVector<Qt3DRender::QAbstractTextureImage *> textureImages = diffuseTexture->textureImages();
238 for (
const Qt3DRender::QAbstractTextureImage *tex : textureImages )
240 const QgsImageTexture *imageTexture =
dynamic_cast<const QgsImageTexture *
>( tex );
243 const QImage image = imageTexture->
getImage();
244 object->setTextureImage( image );
258 QgsChunkNode *node = terrain->rootNode();
263 QgsTerrainTileEntity *terrainTile =
nullptr;
264 QgsTerrainTextureGenerator *textureGenerator = terrain->textureGenerator();
265 textureGenerator->waitForFinished();
266 const QSize oldResolution = textureGenerator->textureSize();
267 textureGenerator->setTextureSize( QSize( mTerrainTextureResolution, mTerrainTextureResolution ) );
268 switch ( generator->
type() )
271 terrainTile = getDemTerrainEntity( terrain, node, settings->
origin() );
272 parseDemTile( terrainTile, layerName + u
"_"_s );
275 terrainTile = getFlatTerrainEntity( terrain, node, settings->
origin() );
276 parseFlatTile( terrainTile, layerName + u
"_"_s );
279 terrainTile = getMeshTerrainEntity( terrain, node, settings->
origin() );
280 parseMeshTile( terrainTile, layerName + u
"_"_s );
287 textureGenerator->setTextureSize( oldResolution );
290QgsTerrainTileEntity *Qgs3DSceneExporter::getFlatTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
292 QgsFlatTerrainGenerator *generator = qgis::down_cast<QgsFlatTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
293 FlatTerrainChunkLoader *flatTerrainLoader = qobject_cast<FlatTerrainChunkLoader *>( generator->
createChunkLoader( node ) );
294 flatTerrainLoader->start();
295 if ( mExportTextures )
296 terrain->textureGenerator()->waitForFinished();
298 Qt3DCore::QEntity *entity = flatTerrainLoader->createEntity(
this );
299 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( entity );
301 const QList<QgsGeoTransform *> transforms = entity->findChildren<QgsGeoTransform *>();
302 for ( QgsGeoTransform *transform : transforms )
304 transform->setOrigin( mapOrigin );
310QgsTerrainTileEntity *Qgs3DSceneExporter::getDemTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
314 QgsDemTerrainGenerator *generator = qgis::down_cast<QgsDemTerrainGenerator *>( terrain->mapSettings()->terrainGenerator()->clone() );
316 QgsDemTerrainTileLoader *loader = qobject_cast<QgsDemTerrainTileLoader *>( generator->
createChunkLoader( node ) );
319 if ( mExportTextures )
320 terrain->textureGenerator()->waitForFinished();
321 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity(
this ) );
323 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
324 for ( QgsGeoTransform *transform : transforms )
326 transform->setOrigin( mapOrigin );
333QgsTerrainTileEntity *Qgs3DSceneExporter::getMeshTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
335 QgsMeshTerrainGenerator *generator = qgis::down_cast<QgsMeshTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
336 QgsMeshTerrainTileLoader *loader = qobject_cast<QgsMeshTerrainTileLoader *>( generator->createChunkLoader( node ) );
339 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity(
this ) );
341 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
342 for ( QgsGeoTransform *transform : transforms )
344 transform->setOrigin( mapOrigin );
350void Qgs3DSceneExporter::parseFlatTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
355 Qt3DCore::QGeometry *geometry = mesh->geometry();
356 Qt3DExtras::QPlaneGeometry *tileGeometry = qobject_cast<Qt3DExtras::QPlaneGeometry *>( geometry );
359 QgsDebugError(
"Qt3DExtras::QPlaneGeometry* is expected but something else was given" );
364 Qt3DCore::QAttribute *positionAttribute = tileGeometry->positionAttribute();
365 if ( !positionAttribute )
367 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( layerName ) );
370 const QByteArray verticesBytes =
getData( positionAttribute->buffer() );
371 if ( verticesBytes.isNull() )
373 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( layerName ) );
379 Qt3DCore::QAttribute *indexAttribute = tileGeometry->indexAttribute();
380 if ( !indexAttribute )
382 QgsDebugError( QString(
"Cannot export '%1' - geometry has no index attribute!" ).arg( layerName ) );
385 const QByteArray indexBytes =
getData( indexAttribute->buffer() );
386 if ( indexBytes.isNull() )
388 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( layerName ) );
391 const QVector<uint> indexesBuffer =
getIndexData( indexAttribute, indexBytes );
393 QString objectNamePrefix = layerName;
394 if ( objectNamePrefix != QString() )
395 objectNamePrefix += QString();
397 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"Flat_tile"_s ) );
398 mObjects.push_back(
object );
400 object->setSmoothEdges( mSmoothEdges );
401 object->setupTriangle( positionBuffer, indexesBuffer, transform->matrix() );
403 if ( mExportNormals )
406 QVector<float> normalsBuffer;
407 for (
int i = 0; i < positionBuffer.size(); i += 3 )
408 normalsBuffer << 0.0f << 1.0f << 0.0f;
409 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
412 Qt3DCore::QAttribute *texCoordsAttribute = tileGeometry->texCoordAttribute();
413 if ( mExportTextures && texCoordsAttribute )
417 object->setupTextureCoordinates( texCoords );
419 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
420 const QImage img = textureImage->getImage();
421 object->setTextureImage( img );
425void Qgs3DSceneExporter::parseDemTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
430 Qt3DCore::QGeometry *geometry = mesh->geometry();
431 DemTerrainTileGeometry *tileGeometry = qobject_cast<DemTerrainTileGeometry *>( geometry );
434 QgsDebugError(
"DemTerrainTileGeometry* is expected but something else was given" );
438 Qt3DCore::QAttribute *positionAttribute = tileGeometry->positionAttribute();
439 const QByteArray positionBytes = positionAttribute->buffer()->data();
442 Qt3DCore::QAttribute *indexAttribute = tileGeometry->indexAttribute();
443 const QByteArray indexBytes = indexAttribute->buffer()->data();
444 const QVector<unsigned int> indexBuffer =
getIndexData( indexAttribute, indexBytes );
446 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( layerName + u
"DEM_tile"_s ) );
447 mObjects.push_back(
object );
449 object->setSmoothEdges( mSmoothEdges );
450 object->setupTriangle( positionBuffer, indexBuffer, transform->matrix() );
452 Qt3DCore::QAttribute *normalsAttributes = tileGeometry->normalAttribute();
453 if ( mExportNormals && normalsAttributes )
455 const QByteArray normalsBytes = normalsAttributes->buffer()->data();
457 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
460 Qt3DCore::QAttribute *texCoordsAttribute = tileGeometry->texCoordsAttribute();
461 if ( mExportTextures && texCoordsAttribute )
463 const QByteArray texCoordsBytes = texCoordsAttribute->buffer()->data();
465 object->setupTextureCoordinates( texCoordsBuffer );
467 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
468 const QImage img = textureImage->getImage();
469 object->setTextureImage( img );
473void Qgs3DSceneExporter::parseMeshTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
475 QString objectNamePrefix = layerName;
476 if ( objectNamePrefix != QString() )
477 objectNamePrefix +=
'_'_L1;
479 const QList<Qt3DRender::QGeometryRenderer *> renderers = tileEntity->findChildren<Qt3DRender::QGeometryRenderer *>();
480 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
482 Qgs3DExportObject *obj = processGeometryRenderer( renderer, objectNamePrefix );
489QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processInstancedPointGeometry( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
491 QVector<Qgs3DExportObject *> objects;
492 const QList<Qt3DCore::QGeometry *> geometriesList = entity->findChildren<Qt3DCore::QGeometry *>();
493 for ( Qt3DCore::QGeometry *geometry : geometriesList )
495 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
496 Qt3DCore::QAttribute *indexAttribute =
findAttribute( geometry, QString(), Qt3DCore::QAttribute::IndexAttribute );
497 if ( !positionAttribute || !indexAttribute )
499 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position or index attribute!" ).arg( objectNamePrefix ) );
503 const QByteArray vertexBytes = positionAttribute->buffer()->data();
504 const QByteArray indexBytes = indexAttribute->buffer()->data();
505 if ( vertexBytes.isNull() || indexBytes.isNull() )
507 QgsDebugError( QString(
"Geometry for '%1' has position or index attribute with empty data!" ).arg( objectNamePrefix ) );
512 const QVector<uint> indexData =
getIndexData( indexAttribute, indexBytes );
514 Qt3DCore::QAttribute *instanceDataAttribute =
findAttribute( geometry, u
"pos"_s, Qt3DCore::QAttribute::VertexAttribute );
515 if ( !instanceDataAttribute )
517 QgsDebugError( QString(
"Cannot export '%1' - geometry has no instanceData attribute!" ).arg( objectNamePrefix ) );
520 const QByteArray instancePositionBytes =
getData( instanceDataAttribute->buffer() );
521 if ( instancePositionBytes.isNull() )
523 QgsDebugError( QString(
"Geometry for '%1' has instanceData attribute with empty data!" ).arg( objectNamePrefix ) );
528 for (
int i = 0; i < instancePosition.size(); i += 3 )
530 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"instance_point"_s ) );
531 objects.push_back(
object );
532 QMatrix4x4 instanceTransform;
533 instanceTransform.translate( instancePosition[i], instancePosition[i + 1], instancePosition[i + 2] );
534 object->setupTriangle( positionData, indexData, instanceTransform );
536 object->setSmoothEdges( mSmoothEdges );
538 Qt3DCore::QAttribute *normalsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
539 if ( mExportNormals && normalsAttribute )
543 object->setupNormalCoordinates( normalsData, instanceTransform );
551QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processSceneLoaderGeometries( Qt3DRender::QSceneLoader *sceneLoader,
const QString &objectNamePrefix )
553 QVector<Qgs3DExportObject *> objects;
554 Qt3DCore::QEntity *sceneLoaderParent = qobject_cast<Qt3DCore::QEntity *>( sceneLoader->parent() );
556 QMatrix4x4 sceneTransform;
557 if ( entityTransform )
559 sceneTransform = entityTransform->matrix();
561 QStringList entityNames = sceneLoader->entityNames();
562 for (
const QString &entityName : entityNames )
564 Qt3DRender::QGeometryRenderer *mesh = qobject_cast<Qt3DRender::QGeometryRenderer *>( sceneLoader->component( entityName, Qt3DRender::QSceneLoader::GeometryRendererComponent ) );
565 Qgs3DExportObject *
object = processGeometryRenderer( mesh, objectNamePrefix, sceneTransform );
568 objects.push_back(
object );
573Qgs3DExportObject *Qgs3DSceneExporter::processGeometryRenderer( Qt3DRender::QGeometryRenderer *geomRenderer,
const QString &objectNamePrefix,
const QMatrix4x4 &sceneTransform )
576 if ( geomRenderer->primitiveType() != Qt3DRender::QGeometryRenderer::Triangles )
579 Qt3DCore::QGeometry *geometry = geomRenderer->geometry();
591 QVector<std::pair<uint, uint>> triangleIndexStartingIndiceToKeep;
592 QgsTessellatedPolygonGeometry *tessGeom =
dynamic_cast<QgsTessellatedPolygonGeometry *
>( geometry );
595 QVector<QgsFeatureId> featureIds = tessGeom->
featureIds();
597 QSet<QgsFeatureId> tempFeatToAdd;
600 for (
int idx = 0; idx < featureIds.size(); idx++ )
603 if ( !mExportedFeatureIds.contains( feat ) )
606 tempFeatToAdd += feat;
609 const uint startIdx = triangleIndex[idx];
610 const uint endIdx = idx < triangleIndex.size() - 1 ? triangleIndex[idx + 1] : std::numeric_limits<uint>::max();
612 if ( startIdx < endIdx )
613 triangleIndexStartingIndiceToKeep.append( std::pair<uint, uint>( startIdx, endIdx ) );
616 mExportedFeatureIds += tempFeatToAdd;
618 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
625 QMatrix4x4 transformMatrix = sceneTransform;
626 QObject *parent = geomRenderer->parent();
629 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( parent );
633 transformMatrix = transform->matrix() * transformMatrix;
635 parent = parent->parent();
638 Qt3DCore::QAttribute *positionAttribute =
nullptr;
639 Qt3DCore::QAttribute *indexAttribute =
nullptr;
640 QByteArray indexBytes, vertexBytes;
641 QVector<uint> indexDataTmp;
642 QVector<uint> indexData;
643 QVector<float> positionData;
646 positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
647 if ( !positionAttribute )
649 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
653 vertexBytes =
getData( positionAttribute->buffer() );
654 if ( vertexBytes.isNull() )
656 QgsDebugError( QString(
"Will not export '%1' as geometry has empty position data!" ).arg( objectNamePrefix ) );
663 QVector<Qt3DCore::QAttribute *> attributes = geometry->attributes();
664 for ( Qt3DCore::QAttribute *attribute : attributes )
666 if ( attribute->attributeType() == Qt3DCore::QAttribute::IndexAttribute )
668 indexAttribute = attribute;
669 indexBytes =
getData( indexAttribute->buffer() );
670 if ( indexBytes.isNull() )
672 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( objectNamePrefix ) );
676 indexDataTmp =
getIndexData( indexAttribute, indexBytes );
683 if ( !indexAttribute )
685 for ( uint i = 0; i < static_cast<uint>( positionData.size() / 3 ); ++i )
687 indexDataTmp.push_back( i );
692 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
695 indexData.append( indexDataTmp );
701 const int triangleIndexStartingIndiceToKeepSize = triangleIndexStartingIndiceToKeep.size();
702 const uint indexDataTmpSize =
static_cast<uint
>( indexDataTmp.size() );
703 for ( uint i = 0; i + 2 < indexDataTmpSize; i += 3 )
705 const uint triangleIdx = i / 3;
708 while ( intervalIdx < triangleIndexStartingIndiceToKeepSize && triangleIdx >= triangleIndexStartingIndiceToKeep[intervalIdx].second )
714 if ( intervalIdx < triangleIndexStartingIndiceToKeepSize && triangleIdx >= triangleIndexStartingIndiceToKeep[intervalIdx].first && triangleIdx < triangleIndexStartingIndiceToKeep[intervalIdx].second )
716 indexData.push_back( indexDataTmp[
static_cast<int>( i )] );
717 indexData.push_back( indexDataTmp[
static_cast<int>( i + 1 )] );
718 indexData.push_back( indexDataTmp[
static_cast<int>( i + 2 )] );
724 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"mesh_geometry"_s ) );
725 object->setupTriangle( positionData, indexData, transformMatrix );
727 Qt3DCore::QAttribute *normalsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
728 if ( mExportNormals && normalsAttribute )
732 object->setupNormalCoordinates( normalsData, transformMatrix );
735 Qt3DCore::QAttribute *texCoordsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultTextureCoordinateAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
736 if ( mExportTextures && texCoordsAttribute )
740 object->setupTextureCoordinates( texCoordsData );
746QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processLines( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
748 QVector<Qgs3DExportObject *> objs;
749 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
750 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
752 if ( renderer->primitiveType() != Qt3DRender::QGeometryRenderer::LineStripAdjacency )
754 Qt3DCore::QGeometry *geom = renderer->geometry();
755 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geom, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
756 Qt3DCore::QAttribute *indexAttribute =
findAttribute( geom, QString(), Qt3DCore::QAttribute::IndexAttribute );
757 if ( !positionAttribute || !indexAttribute )
759 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position or index attribute!" ).arg( objectNamePrefix ) );
763 const QByteArray vertexBytes =
getData( positionAttribute->buffer() );
764 const QByteArray indexBytes =
getData( indexAttribute->buffer() );
765 if ( vertexBytes.isNull() || indexBytes.isNull() )
767 QgsDebugError( QString(
"Geometry for '%1' has position or index attribute with empty data!" ).arg( objectNamePrefix ) );
772 Qgs3DExportObject *exportObject =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"line"_s ) );
775 objs.push_back( exportObject );
780Qgs3DExportObject *Qgs3DSceneExporter::processPoints( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
782 QVector<float> points;
783 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
784 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
786 Qt3DCore::QGeometry *geometry = qobject_cast<QgsBillboardGeometry *>( renderer->geometry() );
789 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
790 if ( !positionAttribute )
792 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
795 const QByteArray positionBytes =
getData( positionAttribute->buffer() );
796 if ( positionBytes.isNull() )
798 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( objectNamePrefix ) );
804 Qgs3DExportObject *obj =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"points"_s ) );
811 if ( mObjects.isEmpty() )
816 const QString objFilePath = QDir( sceneFolderPath ).filePath( sceneName + u
".obj"_s );
817 const QString mtlFilePath = QDir( sceneFolderPath ).filePath( sceneName + u
".mtl"_s );
819 QFile file( objFilePath );
820 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
822 QgsDebugError( u
"Scene can not be exported to '%1'. File access error."_s.arg( objFilePath ) );
825 QFile mtlFile( mtlFilePath );
826 if ( !mtlFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
828 QgsDebugError( u
"Scene can not be exported to '%1'. File access error."_s.arg( mtlFilePath ) );
832 float maxfloat = std::numeric_limits<float>::max(), minFloat = std::numeric_limits<float>::lowest();
833 float minX = maxfloat, minY = maxfloat, minZ = maxfloat, maxX = minFloat, maxY = minFloat, maxZ = minFloat;
836 obj->
objectBounds( minX, minY, minZ, maxX, maxY, maxZ );
839 float diffX = 1.0f, diffY = 1.0f, diffZ = 1.0f;
844 const float centerX = ( minX + maxX ) / 2.0f;
845 const float centerY = ( minY + maxY ) / 2.0f;
846 const float centerZ = ( minZ + maxZ ) / 2.0f;
848 const float scale = std::max( diffX, std::max( diffY, diffZ ) );
850 QTextStream out( &file );
852 const QString mtlLibName = sceneName +
".mtl";
853 out <<
"mtllib " << mtlLibName <<
"\n";
855 QTextStream mtlOut( &mtlFile );
861 const QString material = obj->
saveMaterial( mtlOut, sceneFolderPath );
862 out <<
"o " << obj->
name() <<
"\n";
863 if ( material != QString() )
864 out <<
"usemtl " << material <<
"\n";
865 obj->
saveTo( out,
scale / mScale, QVector3D( centerX, centerY, centerZ ), precision );
872QString Qgs3DSceneExporter::getObjectName(
const QString &name )
875 if ( mUsedObjectNamesCounter.contains( name ) )
877 ret = u
"%1%2"_s.arg( name ).arg( mUsedObjectNamesCounter[name] );
878 mUsedObjectNamesCounter[name]++;
881 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)