31#include "qgsmeshterraingenerator.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 );
199 mObjects << processLines( entity, layer->
name() + u
"_"_s );
201 return mObjects.size() > prevSize;
204 else if ( rendererType ==
"vector" )
207 if ( vectorLayerRenderer )
219 QgsDebugMsgLevel( u
"Type '%1' of layer '%2' is not exportable."_s.arg( layer->
name(), rendererType ), 2 );
226void Qgs3DSceneExporter::processEntityMaterial( Qt3DCore::QEntity *entity,
Qgs3DExportObject *
object )
const
232 object->setupMaterial( &material );
236 if ( diffuseMapMaterial )
238 const Qt3DRender::QTexture2D *diffuseTexture = diffuseMapMaterial->diffuse().value<Qt3DRender::QTexture2D *>();
239 if ( diffuseTexture )
241 const QVector<Qt3DRender::QAbstractTextureImage *> textureImages = diffuseTexture->textureImages();
242 for (
const Qt3DRender::QAbstractTextureImage *tex : textureImages )
244 const QgsImageTexture *imageTexture =
dynamic_cast<const QgsImageTexture *
>( tex );
247 const QImage image = imageTexture->
getImage();
248 object->setTextureImage( image );
262 QgsChunkNode *node = terrain->rootNode();
267 QgsTerrainTileEntity *terrainTile =
nullptr;
268 QgsTerrainTextureGenerator *textureGenerator = terrain->textureGenerator();
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 QFuture<QgsChunkLoaderResult> resultFuture = generator->
loadChunk( node );
299 Qt3DCore::QEntity *entity = resultFuture.takeResult().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 QFuture<QgsChunkLoaderResult> resultFuture = generator->
loadChunk( node );
319 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( resultFuture.takeResult().createEntity(
this ) );
321 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
322 for ( QgsGeoTransform *transform : transforms )
324 transform->setOrigin( mapOrigin );
331QgsTerrainTileEntity *Qgs3DSceneExporter::getMeshTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
333 QgsMeshTerrainGenerator *generator = qgis::down_cast<QgsMeshTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
334 QFuture<QgsChunkLoaderResult> resultFuture = generator->loadChunk( node );
336 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( resultFuture.takeResult().createEntity(
this ) );
338 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
339 for ( QgsGeoTransform *transform : transforms )
341 transform->setOrigin( mapOrigin );
347void Qgs3DSceneExporter::parseFlatTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
352 Qt3DCore::QGeometry *geometry = mesh->geometry();
353 Qt3DExtras::QPlaneGeometry *tileGeometry = qobject_cast<Qt3DExtras::QPlaneGeometry *>( geometry );
356 QgsDebugError(
"Qt3DExtras::QPlaneGeometry* is expected but something else was given" );
361 Qt3DCore::QAttribute *positionAttribute = tileGeometry->positionAttribute();
362 if ( !positionAttribute )
364 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( layerName ) );
367 const QByteArray verticesBytes =
getData( positionAttribute->buffer() );
368 if ( verticesBytes.isNull() )
370 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( layerName ) );
376 Qt3DCore::QAttribute *indexAttribute = tileGeometry->indexAttribute();
377 if ( !indexAttribute )
379 QgsDebugError( QString(
"Cannot export '%1' - geometry has no index attribute!" ).arg( layerName ) );
382 const QByteArray indexBytes =
getData( indexAttribute->buffer() );
383 if ( indexBytes.isNull() )
385 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( layerName ) );
388 const QVector<uint> indexesBuffer =
getIndexData( indexAttribute, indexBytes );
390 QString objectNamePrefix = layerName;
391 if ( objectNamePrefix != QString() )
392 objectNamePrefix += QString();
394 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"Flat_tile"_s ) );
395 mObjects.push_back(
object );
397 object->setSmoothEdges( mSmoothEdges );
398 object->setupTriangle( positionBuffer, indexesBuffer, transform->matrix() );
400 if ( mExportNormals )
403 QVector<float> normalsBuffer;
404 for (
int i = 0; i < positionBuffer.size(); i += 3 )
405 normalsBuffer << 0.0f << 1.0f << 0.0f;
406 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
409 Qt3DCore::QAttribute *texCoordsAttribute = tileGeometry->texCoordAttribute();
410 if ( mExportTextures && texCoordsAttribute )
414 object->setupTextureCoordinates( texCoords );
416 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
417 const QImage img = textureImage->getImage();
418 object->setTextureImage( img );
422void Qgs3DSceneExporter::parseDemTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
427 Qt3DCore::QGeometry *geometry = mesh->geometry();
428 DemTerrainTileGeometry *tileGeometry = qobject_cast<DemTerrainTileGeometry *>( geometry );
431 QgsDebugError(
"DemTerrainTileGeometry* is expected but something else was given" );
435 Qt3DCore::QAttribute *positionAttribute = tileGeometry->positionAttribute();
436 const QByteArray positionBytes = positionAttribute->buffer()->data();
439 Qt3DCore::QAttribute *indexAttribute = tileGeometry->indexAttribute();
440 const QByteArray indexBytes = indexAttribute->buffer()->data();
441 const QVector<unsigned int> indexBuffer =
getIndexData( indexAttribute, indexBytes );
443 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( layerName + u
"DEM_tile"_s ) );
444 mObjects.push_back(
object );
446 object->setSmoothEdges( mSmoothEdges );
447 object->setupTriangle( positionBuffer, indexBuffer, transform->matrix() );
449 Qt3DCore::QAttribute *normalsAttributes = tileGeometry->normalAttribute();
450 if ( mExportNormals && normalsAttributes )
452 const QByteArray normalsBytes = normalsAttributes->buffer()->data();
454 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
457 Qt3DCore::QAttribute *texCoordsAttribute = tileGeometry->texCoordsAttribute();
458 if ( mExportTextures && texCoordsAttribute )
460 const QByteArray texCoordsBytes = texCoordsAttribute->buffer()->data();
462 object->setupTextureCoordinates( texCoordsBuffer );
464 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
465 const QImage img = textureImage->getImage();
466 object->setTextureImage( img );
470void Qgs3DSceneExporter::parseMeshTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
472 QString objectNamePrefix = layerName;
473 if ( objectNamePrefix != QString() )
474 objectNamePrefix +=
'_'_L1;
476 const QList<Qt3DRender::QGeometryRenderer *> renderers = tileEntity->findChildren<Qt3DRender::QGeometryRenderer *>();
477 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
479 Qgs3DExportObject *obj = processGeometryRenderer( renderer, objectNamePrefix );
486QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processInstancedPointGeometry( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
491 const QMatrix4x4 instanceMaterialTransform( 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 );
493 QVector3D symbolScale;
494 QVector4D symbolRotation;
495 const QList<Qt3DRender::QMaterial *> materials = entity->findChildren<Qt3DRender::QMaterial *>();
496 for ( Qt3DRender::QMaterial *material : materials )
498 for (
const Qt3DRender::QParameter *parameter : material->parameters() )
500 if ( parameter->name() ==
"symbolScale"_L1 )
502 symbolScale = parameter->value().value<QVector3D>();
504 else if ( parameter->name() ==
"symbolRotation"_L1 )
506 symbolRotation = parameter->value().value<QVector4D>();
511 QQuaternion rotationQuat( symbolRotation.w(), symbolRotation.x(), symbolRotation.y(), symbolRotation.z() );
513 QVector<Qgs3DExportObject *> objects;
514 const QList<Qt3DCore::QGeometry *> geometriesList = entity->findChildren<Qt3DCore::QGeometry *>();
515 for ( Qt3DCore::QGeometry *geometry : geometriesList )
517 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
518 Qt3DCore::QAttribute *indexAttribute =
findAttribute( geometry, QString(), Qt3DCore::QAttribute::IndexAttribute );
519 if ( !positionAttribute || !indexAttribute )
521 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position or index attribute!" ).arg( objectNamePrefix ) );
525 Qt3DCore::QAttribute *instanceScaleAttribute =
findAttribute( geometry, u
"instanceScale"_s, Qt3DCore::QAttribute::VertexAttribute );
526 Qt3DCore::QAttribute *instanceRotationAttribute =
findAttribute( geometry, u
"instanceRotation"_s, Qt3DCore::QAttribute::VertexAttribute );
528 const QByteArray vertexBytes = positionAttribute->buffer()->data();
529 const QByteArray indexBytes = indexAttribute->buffer()->data();
530 if ( vertexBytes.isNull() || indexBytes.isNull() )
532 QgsDebugError( QString(
"Geometry for '%1' has position or index attribute with empty data!" ).arg( objectNamePrefix ) );
537 const QVector<uint> indexData =
getIndexData( indexAttribute, indexBytes );
539 const QVector<QVector3D> instanceScaleData = instanceScaleAttribute ?
getAttributeData<QVector3D>( instanceScaleAttribute, instanceScaleAttribute->buffer()->data() ) : QVector<QVector3D>();
540 const QVector<QVector4D> instanceRotationData = instanceRotationAttribute ?
getAttributeData<QVector4D>( instanceRotationAttribute, instanceRotationAttribute->buffer()->data() )
541 : QVector<QVector4D>();
543 Qt3DCore::QAttribute *instanceDataAttribute =
findAttribute( geometry, u
"instanceTranslation"_s, Qt3DCore::QAttribute::VertexAttribute );
544 if ( !instanceDataAttribute )
546 QgsDebugError( QString(
"Cannot export '%1' - geometry has no instanceTranslation attribute!" ).arg( objectNamePrefix ) );
549 const QByteArray instancePositionBytes =
getData( instanceDataAttribute->buffer() );
550 if ( instancePositionBytes.isNull() )
552 QgsDebugError( QString(
"Geometry for '%1' has instanceTranslation attribute with empty data!" ).arg( objectNamePrefix ) );
557 int instanceIndex = 0;
558 for (
int i = 0; i < instancePosition.size(); i += 3, instanceIndex++ )
560 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"instance_point"_s ) );
561 objects.push_back(
object );
562 QMatrix4x4 instanceTransform;
563 instanceTransform.translate( instancePosition[i], instancePosition[i + 1], instancePosition[i + 2] );
565 QQuaternion instanceRotation = rotationQuat;
566 if ( instanceRotationAttribute )
568 const QVector4D instanceRotationVec = instanceRotationData[instanceIndex];
569 instanceRotation = QQuaternion( instanceRotationVec.w(), instanceRotationVec.x(), instanceRotationVec.y(), instanceRotationVec.z() );
571 instanceTransform.rotate( instanceRotation );
572 instanceTransform.scale( instanceScaleAttribute ? instanceScaleData[instanceIndex] : symbolScale );
573 instanceTransform *= instanceMaterialTransform;
574 object->setupTriangle( positionData, indexData, instanceTransform );
576 object->setSmoothEdges( mSmoothEdges );
578 Qt3DCore::QAttribute *normalsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
579 if ( mExportNormals && normalsAttribute )
583 object->setupNormalCoordinates( normalsData, instanceTransform );
591QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processSceneLoaderGeometries( Qt3DRender::QSceneLoader *sceneLoader,
const QString &objectNamePrefix )
593 QVector<Qgs3DExportObject *> objects;
594 Qt3DCore::QEntity *sceneLoaderParent = qobject_cast<Qt3DCore::QEntity *>( sceneLoader->parent() );
596 QMatrix4x4 sceneTransform;
597 if ( entityTransform )
599 sceneTransform = entityTransform->matrix();
601 QStringList entityNames = sceneLoader->entityNames();
602 for (
const QString &entityName : entityNames )
604 Qt3DRender::QGeometryRenderer *mesh = qobject_cast<Qt3DRender::QGeometryRenderer *>( sceneLoader->component( entityName, Qt3DRender::QSceneLoader::GeometryRendererComponent ) );
605 Qgs3DExportObject *
object = processGeometryRenderer( mesh, objectNamePrefix, sceneTransform );
608 objects.push_back(
object );
613Qgs3DExportObject *Qgs3DSceneExporter::processGeometryRenderer( Qt3DRender::QGeometryRenderer *geomRenderer,
const QString &objectNamePrefix,
const QMatrix4x4 &sceneTransform )
616 if ( geomRenderer->primitiveType() != Qt3DRender::QGeometryRenderer::Triangles )
619 Qt3DCore::QGeometry *geometry = geomRenderer->geometry();
623 if (
findAttribute( geometry, u
"pointA"_s, Qt3DCore::QAttribute::VertexAttribute ) )
634 QVector<std::pair<uint, uint>> triangleIndexStartingIndiceToKeep;
635 QgsTessellatedPolygonGeometry *tessGeom =
dynamic_cast<QgsTessellatedPolygonGeometry *
>( geometry );
638 QVector<QgsFeatureId> featureIds = tessGeom->
featureIds();
640 QSet<QgsFeatureId> tempFeatToAdd;
643 for (
int idx = 0; idx < featureIds.size(); idx++ )
646 if ( !mExportedFeatureIds.contains( feat ) )
649 tempFeatToAdd += feat;
652 const uint startIdx = triangleIndex[idx];
653 const uint endIdx = idx < triangleIndex.size() - 1 ? triangleIndex[idx + 1] : std::numeric_limits<uint>::max();
655 if ( startIdx < endIdx )
656 triangleIndexStartingIndiceToKeep.append( std::pair<uint, uint>( startIdx, endIdx ) );
659 mExportedFeatureIds += tempFeatToAdd;
661 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
668 QMatrix4x4 transformMatrix = sceneTransform;
669 QObject *parent = geomRenderer->parent();
672 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( parent );
676 transformMatrix = transform->matrix() * transformMatrix;
678 parent = parent->parent();
681 Qt3DCore::QAttribute *positionAttribute =
nullptr;
682 Qt3DCore::QAttribute *indexAttribute =
nullptr;
683 QByteArray indexBytes, vertexBytes;
684 QVector<uint> indexDataTmp;
685 QVector<uint> indexData;
686 QVector<float> positionData;
689 positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
690 if ( !positionAttribute )
692 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
696 vertexBytes =
getData( positionAttribute->buffer() );
697 if ( vertexBytes.isNull() )
699 QgsDebugError( QString(
"Will not export '%1' as geometry has empty position data!" ).arg( objectNamePrefix ) );
706 QVector<Qt3DCore::QAttribute *> attributes = geometry->attributes();
707 for ( Qt3DCore::QAttribute *attribute : attributes )
709 if ( attribute->attributeType() == Qt3DCore::QAttribute::IndexAttribute )
711 indexAttribute = attribute;
712 indexBytes =
getData( indexAttribute->buffer() );
713 if ( indexBytes.isNull() )
715 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( objectNamePrefix ) );
719 indexDataTmp =
getIndexData( indexAttribute, indexBytes );
726 if ( !indexAttribute )
728 for ( uint i = 0; i < static_cast<uint>( positionData.size() / 3 ); ++i )
730 indexDataTmp.push_back( i );
735 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
738 indexData.append( indexDataTmp );
744 const int triangleIndexStartingIndiceToKeepSize = triangleIndexStartingIndiceToKeep.size();
745 const uint indexDataTmpSize =
static_cast<uint
>( indexDataTmp.size() );
746 for ( uint i = 0; i + 2 < indexDataTmpSize; i += 3 )
748 const uint triangleIdx = i / 3;
751 while ( intervalIdx < triangleIndexStartingIndiceToKeepSize && triangleIdx >= triangleIndexStartingIndiceToKeep[intervalIdx].second )
757 if ( intervalIdx < triangleIndexStartingIndiceToKeepSize && triangleIdx >= triangleIndexStartingIndiceToKeep[intervalIdx].first && triangleIdx < triangleIndexStartingIndiceToKeep[intervalIdx].second )
759 indexData.push_back( indexDataTmp[
static_cast<int>( i )] );
760 indexData.push_back( indexDataTmp[
static_cast<int>( i + 1 )] );
761 indexData.push_back( indexDataTmp[
static_cast<int>( i + 2 )] );
767 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"mesh_geometry"_s ) );
768 object->setupTriangle( positionData, indexData, transformMatrix );
770 Qt3DCore::QAttribute *normalsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
771 if ( mExportNormals && normalsAttribute )
775 object->setupNormalCoordinates( normalsData, transformMatrix );
778 Qt3DCore::QAttribute *texCoordsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultTextureCoordinateAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
779 if ( mExportTextures && texCoordsAttribute )
783 object->setupTextureCoordinates( texCoordsData );
789QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processLines( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
791 QVector<Qgs3DExportObject *> objs;
792 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
793 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
795 Qt3DCore::QGeometry *geom = renderer->geometry();
796 Qt3DCore::QAttribute *pointAAttribute =
findAttribute( geom, u
"pointA"_s, Qt3DCore::QAttribute::VertexAttribute );
797 Qt3DCore::QAttribute *pointBAttribute =
findAttribute( geom, u
"pointB"_s, Qt3DCore::QAttribute::VertexAttribute );
798 Qt3DCore::QAttribute *pointCAttribute =
findAttribute( geom, u
"pointC"_s, Qt3DCore::QAttribute::VertexAttribute );
799 if ( !pointAAttribute || !pointBAttribute || pointCAttribute )
802 const QByteArray pointABytes =
getData( pointAAttribute->buffer() );
803 const QByteArray pointBBytes =
getData( pointBAttribute->buffer() );
804 if ( pointABytes.isNull() || pointBBytes.isNull() )
806 QgsDebugError( QString(
"Geometry for '%1' has pointA or pointB attribute with empty data!" ).arg( objectNamePrefix ) );
812 QVector<float> positionData;
813 positionData.reserve( pointAData.size() + pointBData.size() );
814 for (
int i = 0; i + 2 < pointAData.size(); i += 3 )
816 positionData << pointAData[i] << pointAData[i + 1] << pointAData[i + 2];
817 positionData << pointBData[i] << pointBData[i + 1] << pointBData[i + 2];
820 Qgs3DExportObject *exportObject =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"line"_s ) );
823 objs.push_back( exportObject );
828Qgs3DExportObject *Qgs3DSceneExporter::processPoints( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
830 QVector<float> points;
831 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
832 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
834 Qt3DCore::QGeometry *geometry = qobject_cast<QgsBillboardGeometry *>( renderer->geometry() );
837 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geometry,
"instancePosition", Qt3DCore::QAttribute::VertexAttribute );
838 if ( !positionAttribute )
840 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
843 const QByteArray positionBytes =
getData( positionAttribute->buffer() );
844 if ( positionBytes.isNull() )
846 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( objectNamePrefix ) );
852 Qgs3DExportObject *obj =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"points"_s ) );
859 if ( mObjects.isEmpty() )
864 switch ( exportFormat )
867 return saveObj( sceneName, sceneFolderPath, precision );
869 return saveStl( sceneName, sceneFolderPath, precision );
875bool Qgs3DSceneExporter::saveObj( QString sceneName, QString sceneFolderPath,
int precision )
const
877 const QString objFilePath = QDir( sceneFolderPath ).filePath( sceneName + u
".obj"_s );
878 const QString mtlFilePath = QDir( sceneFolderPath ).filePath( sceneName + u
".mtl"_s );
880 QFile file( objFilePath );
881 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
883 QgsDebugError( u
"Scene can not be exported to '%1'. File access error."_s.arg( objFilePath ) );
886 QFile mtlFile( mtlFilePath );
887 if ( !mtlFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
889 QgsDebugError( u
"Scene can not be exported to '%1'. File access error."_s.arg( mtlFilePath ) );
895 getSceneCenterAndScale( center,
scale );
897 QTextStream out( &file );
899 const QString mtlLibName = sceneName +
".mtl";
900 out <<
"mtllib " << mtlLibName <<
"\n";
902 QTextStream mtlOut( &mtlFile );
903 for ( Qgs3DExportObject *obj : std::as_const( mObjects ) )
908 const QString materialName = obj->
saveMaterial( mtlOut, sceneFolderPath );
916bool Qgs3DSceneExporter::saveStl( QString sceneName, QString sceneFolderPath,
int precision )
const
918 const QString stlFilePath = QDir( sceneFolderPath ).filePath( sceneName + u
".stl"_s );
920 QFile file( stlFilePath );
921 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
923 QgsDebugError( u
"Scene can not be exported to '%1'. File access error."_s.arg( stlFilePath ) );
929 getSceneCenterAndScale( center,
scale );
931 QTextStream out( &file );
933 for ( Qgs3DExportObject *
object : std::as_const( mObjects ) )
945void Qgs3DSceneExporter::getSceneCenterAndScale( QVector3D ¢er,
float &scale )
const
947 const float minFloat = std::numeric_limits<float>::lowest();
948 const float maxfloat = std::numeric_limits<float>::max();
950 float minX = maxfloat;
951 float minY = maxfloat;
952 float minZ = maxfloat;
953 float maxX = minFloat;
954 float maxY = minFloat;
955 float maxZ = minFloat;
956 for ( Qgs3DExportObject *obj : std::as_const( mObjects ) )
958 obj->
objectBounds( minX, minY, minZ, maxX, maxY, maxZ );
961 const float diffX = maxX - minX;
962 const float diffY = maxY - minY;
963 const float diffZ = maxZ - minZ;
965 center.setX( ( minX + maxX ) / 2.0f );
966 center.setY( ( minY + maxY ) / 2.0f );
967 center.setZ( ( minZ + maxZ ) / 2.0f );
969 scale = std::max( diffX, std::max( diffY, diffZ ) ) / mScale;
972QString Qgs3DSceneExporter::getObjectName(
const QString &name )
975 if ( mUsedObjectNamesCounter.contains( name ) )
977 ret = u
"%1%2"_s.arg( name ).arg( mUsedObjectNamesCounter[name] );
978 mUsedObjectNamesCounter[name]++;
981 mUsedObjectNamesCounter[name] = 2;
Export3DSceneFormat
The file format used when exporting a 3D scene.
@ StlAscii
STL ascii format.
@ Obj
Wavefront OBJ format.
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
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
void saveTo(QTextStream &out, float scale, const QVector3D ¢er, const Qgis::Export3DSceneFormat &exportFormat=Qgis::Export3DSceneFormat::Obj, int precision=6, QString materialName=QString()) const
Exports the 3D object to an OBJ or STL compatible output stream.
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).
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...
bool save(QString sceneName, QString sceneFolderPath, const Qgis::Export3DSceneFormat &exportFormat=Qgis::Export3DSceneFormat::Obj, int precision=6) const
Saves the scene to a file Returns false if the operation failed.
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.
QFuture< QgsChunkLoaderResult > loadChunk(QgsChunkNode *node) 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.
QFuture< QgsChunkLoaderResult > loadChunk(QgsChunkNode *node) override
static void waitForFinished(QFuture< void > future)
Waits for the future to become finished without blocking the main thread's event loop.
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.
#define BUILTIN_UNREACHABLE
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)