32#include "qgsmeshterraingenerator.h"
33#include "qgsmeshterraintileloader_p.h"
48#include <Qt3DCore/QAttribute>
49#include <Qt3DCore/QBuffer>
50#include <Qt3DCore/QComponent>
51#include <Qt3DCore/QEntity>
52#include <Qt3DCore/QGeometry>
53#include <Qt3DCore/QNode>
54#include <Qt3DCore/QTransform>
55#include <Qt3DExtras/QConeGeometry>
56#include <Qt3DExtras/QCuboidGeometry>
57#include <Qt3DExtras/QCylinderGeometry>
58#include <Qt3DExtras/QDiffuseSpecularMaterial>
59#include <Qt3DExtras/QExtrudedTextMesh>
60#include <Qt3DExtras/QPhongMaterial>
61#include <Qt3DExtras/QPlaneGeometry>
62#include <Qt3DExtras/QSphereGeometry>
63#include <Qt3DExtras/QTextureMaterial>
64#include <Qt3DExtras/QTorusGeometry>
65#include <Qt3DRender/QAbstractTexture>
66#include <Qt3DRender/QAbstractTextureImage>
67#include <Qt3DRender/QGeometryRenderer>
68#include <Qt3DRender/QMesh>
69#include <Qt3DRender/QSceneLoader>
70#include <Qt3DRender/QTexture>
71#include <Qt3DRender/QTextureImage>
73#include "moc_qgs3dsceneexporter.cpp"
75using namespace Qt::StringLiterals;
80 const uint bytesOffset = attribute->byteOffset();
81 const uint bytesStride = attribute->byteStride();
82 const uint vertexSize = attribute->vertexSize();
83 const uint dataSize =
static_cast<uint
>( data.size() );
86 if ( bytesStride == 0 )
88 QgsDebugError(
"bytesStride==0, the attribute probably was not set properly" );
92 const char *pData = data.constData();
93 for (
unsigned int i = bytesOffset; i < dataSize; i += bytesStride )
95 for (
unsigned int j = 0; j < vertexSize *
sizeof( T ); j +=
sizeof( T ) )
98 memcpy( &v, pData + i + j,
sizeof( T ) );
99 result.push_back( v );
108 QVector<uint> result;
109 const char *pData = data.constData();
110 for (
int i = 0; i < data.size(); i +=
sizeof( T ) )
113 memcpy( &v, pData + i,
sizeof( T ) );
114 result.push_back( ( uint ) v );
119QVector<uint>
getIndexData( Qt3DCore::QAttribute *indexAttribute,
const QByteArray &data )
121 switch ( indexAttribute->vertexBaseType() )
123 case Qt3DCore::QAttribute::VertexBaseType::Int:
125 case Qt3DCore::QAttribute::VertexBaseType::UnsignedInt:
127 case Qt3DCore::QAttribute::VertexBaseType::Short:
129 case Qt3DCore::QAttribute::VertexBaseType::UnsignedShort:
131 case Qt3DCore::QAttribute::VertexBaseType::Byte:
133 case Qt3DCore::QAttribute::VertexBaseType::UnsignedByte:
136 QgsDebugError(
"Probably trying to get index data using an attribute that has vertex data" );
139 return QVector<uint>();
142QByteArray
getData( Qt3DCore::QBuffer *buffer )
144 QByteArray bytes = buffer->data();
145 if ( bytes.isNull() )
152Qt3DCore::QAttribute *
findAttribute( Qt3DCore::QGeometry *geometry,
const QString &name, Qt3DCore::QAttribute::AttributeType type )
154 QVector<Qt3DCore::QAttribute *> attributes = geometry->attributes();
155 for ( Qt3DCore::QAttribute *attribute : attributes )
157 if ( attribute->attributeType() != type )
159 if ( name.isEmpty() || attribute->name() == name )
165template<
typename Component>
170 QVector<Qt3DCore::QComponent *> components = entity->components();
171 for ( Qt3DCore::QComponent *component : components )
173 Component *typedComponent = qobject_cast<Component *>( component );
174 if ( typedComponent )
175 return typedComponent;
183 const QString rendererType = abstractRenderer->
type();
185 if ( rendererType ==
"rulebased" )
187 int prevSize = mObjects.size();
189 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
190 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
192 Qt3DCore::QEntity *parentEntity = qobject_cast<Qt3DCore::QEntity *>( renderer->parent() );
198 if ( mExportTextures )
199 processEntityMaterial( parentEntity,
object );
200 mObjects.push_back(
object );
202 return mObjects.size() > prevSize;
205 else if ( rendererType ==
"vector" )
208 if ( vectorLayerRenderer )
220 QgsDebugMsgLevel( u
"Type '%1' of layer '%2' is not exportable."_s.arg( layer->
name(), rendererType ), 2 );
227void Qgs3DSceneExporter::processEntityMaterial( Qt3DCore::QEntity *entity,
Qgs3DExportObject *
object )
const
233 object->setupMaterial( &material );
237 if ( diffuseMapMaterial )
239 const Qt3DRender::QTexture2D *diffuseTexture = diffuseMapMaterial->diffuse().value<Qt3DRender::QTexture2D *>();
240 if ( diffuseTexture )
242 const QVector<Qt3DRender::QAbstractTextureImage *> textureImages = diffuseTexture->textureImages();
243 for (
const Qt3DRender::QAbstractTextureImage *tex : textureImages )
245 const QgsImageTexture *imageTexture =
dynamic_cast<const QgsImageTexture *
>( tex );
248 const QImage image = imageTexture->
getImage();
249 object->setTextureImage( image );
263 QgsChunkNode *node = terrain->rootNode();
268 QgsTerrainTileEntity *terrainTile =
nullptr;
269 QgsTerrainTextureGenerator *textureGenerator = terrain->textureGenerator();
270 textureGenerator->waitForFinished();
271 const QSize oldResolution = textureGenerator->textureSize();
272 textureGenerator->setTextureSize( QSize( mTerrainTextureResolution, mTerrainTextureResolution ) );
273 switch ( generator->
type() )
276 terrainTile = getDemTerrainEntity( terrain, node, settings->
origin() );
277 parseDemTile( terrainTile, layerName + u
"_"_s );
280 terrainTile = getFlatTerrainEntity( terrain, node, settings->
origin() );
281 parseFlatTile( terrainTile, layerName + u
"_"_s );
284 terrainTile = getMeshTerrainEntity( terrain, node, settings->
origin() );
285 parseMeshTile( terrainTile, layerName + u
"_"_s );
292 textureGenerator->setTextureSize( oldResolution );
295QgsTerrainTileEntity *Qgs3DSceneExporter::getFlatTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
297 QgsFlatTerrainGenerator *generator = qgis::down_cast<QgsFlatTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
298 FlatTerrainChunkLoader *flatTerrainLoader = qobject_cast<FlatTerrainChunkLoader *>( generator->
createChunkLoader( node ) );
299 flatTerrainLoader->start();
300 if ( mExportTextures )
301 terrain->textureGenerator()->waitForFinished();
303 Qt3DCore::QEntity *entity = flatTerrainLoader->createEntity(
this );
304 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( entity );
306 const QList<QgsGeoTransform *> transforms = entity->findChildren<QgsGeoTransform *>();
307 for ( QgsGeoTransform *transform : transforms )
309 transform->setOrigin( mapOrigin );
315QgsTerrainTileEntity *Qgs3DSceneExporter::getDemTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
319 QgsDemTerrainGenerator *generator = qgis::down_cast<QgsDemTerrainGenerator *>( terrain->mapSettings()->terrainGenerator()->clone() );
321 QgsDemTerrainTileLoader *loader = qobject_cast<QgsDemTerrainTileLoader *>( generator->
createChunkLoader( node ) );
324 if ( mExportTextures )
325 terrain->textureGenerator()->waitForFinished();
326 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity(
this ) );
328 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
329 for ( QgsGeoTransform *transform : transforms )
331 transform->setOrigin( mapOrigin );
338QgsTerrainTileEntity *Qgs3DSceneExporter::getMeshTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
340 QgsMeshTerrainGenerator *generator = qgis::down_cast<QgsMeshTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
341 QgsMeshTerrainTileLoader *loader = qobject_cast<QgsMeshTerrainTileLoader *>( generator->createChunkLoader( node ) );
344 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity(
this ) );
346 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
347 for ( QgsGeoTransform *transform : transforms )
349 transform->setOrigin( mapOrigin );
355void Qgs3DSceneExporter::parseFlatTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
360 Qt3DCore::QGeometry *geometry = mesh->geometry();
361 Qt3DExtras::QPlaneGeometry *tileGeometry = qobject_cast<Qt3DExtras::QPlaneGeometry *>( geometry );
364 QgsDebugError(
"Qt3DExtras::QPlaneGeometry* is expected but something else was given" );
369 Qt3DCore::QAttribute *positionAttribute = tileGeometry->positionAttribute();
370 if ( !positionAttribute )
372 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( layerName ) );
375 const QByteArray verticesBytes =
getData( positionAttribute->buffer() );
376 if ( verticesBytes.isNull() )
378 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( layerName ) );
384 Qt3DCore::QAttribute *indexAttribute = tileGeometry->indexAttribute();
385 if ( !indexAttribute )
387 QgsDebugError( QString(
"Cannot export '%1' - geometry has no index attribute!" ).arg( layerName ) );
390 const QByteArray indexBytes =
getData( indexAttribute->buffer() );
391 if ( indexBytes.isNull() )
393 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( layerName ) );
396 const QVector<uint> indexesBuffer =
getIndexData( indexAttribute, indexBytes );
398 QString objectNamePrefix = layerName;
399 if ( objectNamePrefix != QString() )
400 objectNamePrefix += QString();
402 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"Flat_tile"_s ) );
403 mObjects.push_back(
object );
405 object->setSmoothEdges( mSmoothEdges );
406 object->setupTriangle( positionBuffer, indexesBuffer, transform->matrix() );
408 if ( mExportNormals )
411 QVector<float> normalsBuffer;
412 for (
int i = 0; i < positionBuffer.size(); i += 3 )
413 normalsBuffer << 0.0f << 1.0f << 0.0f;
414 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
417 Qt3DCore::QAttribute *texCoordsAttribute = tileGeometry->texCoordAttribute();
418 if ( mExportTextures && texCoordsAttribute )
422 object->setupTextureCoordinates( texCoords );
424 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
425 const QImage img = textureImage->getImage();
426 object->setTextureImage( img );
430void Qgs3DSceneExporter::parseDemTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
435 Qt3DCore::QGeometry *geometry = mesh->geometry();
436 DemTerrainTileGeometry *tileGeometry = qobject_cast<DemTerrainTileGeometry *>( geometry );
439 QgsDebugError(
"DemTerrainTileGeometry* is expected but something else was given" );
443 Qt3DCore::QAttribute *positionAttribute = tileGeometry->positionAttribute();
444 const QByteArray positionBytes = positionAttribute->buffer()->data();
447 Qt3DCore::QAttribute *indexAttribute = tileGeometry->indexAttribute();
448 const QByteArray indexBytes = indexAttribute->buffer()->data();
449 const QVector<unsigned int> indexBuffer =
getIndexData( indexAttribute, indexBytes );
451 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( layerName + u
"DEM_tile"_s ) );
452 mObjects.push_back(
object );
454 object->setSmoothEdges( mSmoothEdges );
455 object->setupTriangle( positionBuffer, indexBuffer, transform->matrix() );
457 Qt3DCore::QAttribute *normalsAttributes = tileGeometry->normalAttribute();
458 if ( mExportNormals && normalsAttributes )
460 const QByteArray normalsBytes = normalsAttributes->buffer()->data();
462 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
465 Qt3DCore::QAttribute *texCoordsAttribute = tileGeometry->texCoordsAttribute();
466 if ( mExportTextures && texCoordsAttribute )
468 const QByteArray texCoordsBytes = texCoordsAttribute->buffer()->data();
470 object->setupTextureCoordinates( texCoordsBuffer );
472 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
473 const QImage img = textureImage->getImage();
474 object->setTextureImage( img );
478void Qgs3DSceneExporter::parseMeshTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
480 QString objectNamePrefix = layerName;
481 if ( objectNamePrefix != QString() )
482 objectNamePrefix +=
'_'_L1;
484 const QList<Qt3DRender::QGeometryRenderer *> renderers = tileEntity->findChildren<Qt3DRender::QGeometryRenderer *>();
485 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
487 Qgs3DExportObject *obj = processGeometryRenderer( renderer, objectNamePrefix );
494QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processInstancedPointGeometry( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
496 QVector<Qgs3DExportObject *> objects;
497 const QList<Qt3DCore::QGeometry *> geometriesList = entity->findChildren<Qt3DCore::QGeometry *>();
498 for ( Qt3DCore::QGeometry *geometry : geometriesList )
500 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
501 Qt3DCore::QAttribute *indexAttribute =
findAttribute( geometry, QString(), Qt3DCore::QAttribute::IndexAttribute );
502 if ( !positionAttribute || !indexAttribute )
504 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position or index attribute!" ).arg( objectNamePrefix ) );
508 const QByteArray vertexBytes = positionAttribute->buffer()->data();
509 const QByteArray indexBytes = indexAttribute->buffer()->data();
510 if ( vertexBytes.isNull() || indexBytes.isNull() )
512 QgsDebugError( QString(
"Geometry for '%1' has position or index attribute with empty data!" ).arg( objectNamePrefix ) );
517 const QVector<uint> indexData =
getIndexData( indexAttribute, indexBytes );
519 Qt3DCore::QAttribute *instanceDataAttribute =
findAttribute( geometry, u
"pos"_s, Qt3DCore::QAttribute::VertexAttribute );
520 if ( !instanceDataAttribute )
522 QgsDebugError( QString(
"Cannot export '%1' - geometry has no instanceData attribute!" ).arg( objectNamePrefix ) );
525 const QByteArray instancePositionBytes =
getData( instanceDataAttribute->buffer() );
526 if ( instancePositionBytes.isNull() )
528 QgsDebugError( QString(
"Geometry for '%1' has instanceData attribute with empty data!" ).arg( objectNamePrefix ) );
533 for (
int i = 0; i < instancePosition.size(); i += 3 )
535 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"instance_point"_s ) );
536 objects.push_back(
object );
537 QMatrix4x4 instanceTransform;
538 instanceTransform.translate( instancePosition[i], instancePosition[i + 1], instancePosition[i + 2] );
539 object->setupTriangle( positionData, indexData, instanceTransform );
541 object->setSmoothEdges( mSmoothEdges );
543 Qt3DCore::QAttribute *normalsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
544 if ( mExportNormals && normalsAttribute )
548 object->setupNormalCoordinates( normalsData, instanceTransform );
556QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processSceneLoaderGeometries( Qt3DRender::QSceneLoader *sceneLoader,
const QString &objectNamePrefix )
558 QVector<Qgs3DExportObject *> objects;
559 Qt3DCore::QEntity *sceneLoaderParent = qobject_cast<Qt3DCore::QEntity *>( sceneLoader->parent() );
561 QMatrix4x4 sceneTransform;
562 if ( entityTransform )
564 sceneTransform = entityTransform->matrix();
566 QStringList entityNames = sceneLoader->entityNames();
567 for (
const QString &entityName : entityNames )
569 Qt3DRender::QGeometryRenderer *mesh = qobject_cast<Qt3DRender::QGeometryRenderer *>( sceneLoader->component( entityName, Qt3DRender::QSceneLoader::GeometryRendererComponent ) );
570 Qgs3DExportObject *
object = processGeometryRenderer( mesh, objectNamePrefix, sceneTransform );
573 objects.push_back(
object );
578Qgs3DExportObject *Qgs3DSceneExporter::processGeometryRenderer( Qt3DRender::QGeometryRenderer *geomRenderer,
const QString &objectNamePrefix,
const QMatrix4x4 &sceneTransform )
581 if ( geomRenderer->primitiveType() != Qt3DRender::QGeometryRenderer::Triangles )
584 Qt3DCore::QGeometry *geometry = geomRenderer->geometry();
596 QVector<std::pair<uint, uint>> triangleIndexStartingIndiceToKeep;
597 QgsTessellatedPolygonGeometry *tessGeom =
dynamic_cast<QgsTessellatedPolygonGeometry *
>( geometry );
600 QVector<QgsFeatureId> featureIds = tessGeom->
featureIds();
602 QSet<QgsFeatureId> tempFeatToAdd;
605 for (
int idx = 0; idx < featureIds.size(); idx++ )
608 if ( !mExportedFeatureIds.contains( feat ) )
611 tempFeatToAdd += feat;
614 const uint startIdx = triangleIndex[idx] * 3;
615 const uint endIdx = idx < triangleIndex.size() - 1 ? triangleIndex[idx + 1] * 3 : std::numeric_limits<uint>::max();
617 if ( startIdx < endIdx )
618 triangleIndexStartingIndiceToKeep.append( std::pair<uint, uint>( startIdx, endIdx ) );
621 mExportedFeatureIds += tempFeatToAdd;
623 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
630 QMatrix4x4 transformMatrix = sceneTransform;
631 QObject *parent = geomRenderer->parent();
634 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( parent );
638 transformMatrix = transform->matrix() * transformMatrix;
640 parent = parent->parent();
643 Qt3DCore::QAttribute *positionAttribute =
nullptr;
644 Qt3DCore::QAttribute *indexAttribute =
nullptr;
645 QByteArray indexBytes, vertexBytes;
646 QVector<uint> indexDataTmp;
647 QVector<uint> indexData;
648 QVector<float> positionData;
651 positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
652 if ( !positionAttribute )
654 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
658 vertexBytes =
getData( positionAttribute->buffer() );
659 if ( vertexBytes.isNull() )
661 QgsDebugError( QString(
"Will not export '%1' as geometry has empty position data!" ).arg( objectNamePrefix ) );
668 QVector<Qt3DCore::QAttribute *> attributes = geometry->attributes();
669 for ( Qt3DCore::QAttribute *attribute : attributes )
671 if ( attribute->attributeType() == Qt3DCore::QAttribute::IndexAttribute )
673 indexAttribute = attribute;
674 indexBytes =
getData( indexAttribute->buffer() );
675 if ( indexBytes.isNull() )
677 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( objectNamePrefix ) );
681 indexDataTmp =
getIndexData( indexAttribute, indexBytes );
688 if ( !indexAttribute )
690 for ( uint i = 0; i < static_cast<uint>( positionData.size() / 3 ); ++i )
692 indexDataTmp.push_back( i );
697 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
700 indexData.append( indexDataTmp );
706 const int triangleIndexStartingIndiceToKeepSize = triangleIndexStartingIndiceToKeep.size();
707 const uint indexDataTmpSize =
static_cast<uint
>( indexDataTmp.size() );
708 for ( uint i = 0; i < indexDataTmpSize; ++i )
710 uint idx = indexDataTmp[
static_cast<int>( i )];
712 while ( intervalIdx < triangleIndexStartingIndiceToKeepSize
713 && idx > triangleIndexStartingIndiceToKeep[intervalIdx].first
714 && idx >= triangleIndexStartingIndiceToKeep[intervalIdx].second )
720 if ( intervalIdx < triangleIndexStartingIndiceToKeepSize
721 && idx >= triangleIndexStartingIndiceToKeep[intervalIdx].first
722 && idx < triangleIndexStartingIndiceToKeep[intervalIdx].second )
724 indexData.push_back( idx );
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)