30#include "qgsmeshterraingenerator.h"
31#include "qgsmeshterraintileloader_p.h"
46#include <Qt3DCore/QAttribute>
47#include <Qt3DCore/QBuffer>
48#include <Qt3DCore/QComponent>
49#include <Qt3DCore/QEntity>
50#include <Qt3DCore/QGeometry>
51#include <Qt3DCore/QNode>
52#include <Qt3DCore/QTransform>
53#include <Qt3DExtras/QConeGeometry>
54#include <Qt3DExtras/QCuboidGeometry>
55#include <Qt3DExtras/QCylinderGeometry>
56#include <Qt3DExtras/QDiffuseSpecularMaterial>
57#include <Qt3DExtras/QExtrudedTextMesh>
58#include <Qt3DExtras/QPhongMaterial>
59#include <Qt3DExtras/QPlaneGeometry>
60#include <Qt3DExtras/QSphereGeometry>
61#include <Qt3DExtras/QTextureMaterial>
62#include <Qt3DExtras/QTorusGeometry>
63#include <Qt3DRender/QAbstractTexture>
64#include <Qt3DRender/QAbstractTextureImage>
65#include <Qt3DRender/QGeometryRenderer>
66#include <Qt3DRender/QMesh>
67#include <Qt3DRender/QParameter>
68#include <Qt3DRender/QSceneLoader>
69#include <Qt3DRender/QTexture>
70#include <Qt3DRender/QTextureImage>
72#include "moc_qgs3dsceneexporter.cpp"
74using namespace Qt::StringLiterals;
76template<
typename T> QVector<T>
getAttributeData( Qt3DCore::QAttribute *attribute,
const QByteArray &data )
78 const uint bytesOffset = attribute->byteOffset();
79 const uint bytesStride = attribute->byteStride();
80 const uint vertexSize = attribute->vertexSize();
81 const uint dataSize =
static_cast<uint
>( data.size() );
84 if ( bytesStride == 0 )
86 QgsDebugError(
"bytesStride==0, the attribute probably was not set properly" );
90 const char *pData = data.constData();
91 for (
unsigned int i = bytesOffset; i < dataSize; i += bytesStride )
93 for (
unsigned int j = 0; j < vertexSize *
sizeof( T ); j +=
sizeof( T ) )
96 memcpy( &v, pData + i + j,
sizeof( T ) );
97 result.push_back( v );
105 QVector<uint> result;
106 const char *pData = data.constData();
107 for (
int i = 0; i < data.size(); i +=
sizeof( T ) )
110 memcpy( &v, pData + i,
sizeof( T ) );
111 result.push_back( ( uint ) v );
116QVector<uint>
getIndexData( Qt3DCore::QAttribute *indexAttribute,
const QByteArray &data )
118 switch ( indexAttribute->vertexBaseType() )
120 case Qt3DCore::QAttribute::VertexBaseType::Int:
122 case Qt3DCore::QAttribute::VertexBaseType::UnsignedInt:
124 case Qt3DCore::QAttribute::VertexBaseType::Short:
126 case Qt3DCore::QAttribute::VertexBaseType::UnsignedShort:
128 case Qt3DCore::QAttribute::VertexBaseType::Byte:
130 case Qt3DCore::QAttribute::VertexBaseType::UnsignedByte:
133 QgsDebugError(
"Probably trying to get index data using an attribute that has vertex data" );
136 return QVector<uint>();
139QByteArray
getData( Qt3DCore::QBuffer *buffer )
141 QByteArray bytes = buffer->data();
142 if ( bytes.isNull() )
149Qt3DCore::QAttribute *
findAttribute( Qt3DCore::QGeometry *geometry,
const QString &name, Qt3DCore::QAttribute::AttributeType type )
151 QVector<Qt3DCore::QAttribute *> attributes = geometry->attributes();
152 for ( Qt3DCore::QAttribute *attribute : attributes )
154 if ( attribute->attributeType() != type )
156 if ( name.isEmpty() || attribute->name() == name )
166 QVector<Qt3DCore::QComponent *> components = entity->components();
167 for ( Qt3DCore::QComponent *component : components )
169 Component *typedComponent = qobject_cast<Component *>( component );
170 if ( typedComponent )
171 return typedComponent;
179 const QString rendererType = abstractRenderer->
type();
181 if ( rendererType ==
"rulebased" )
183 int prevSize = mObjects.size();
185 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
186 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
188 Qt3DCore::QEntity *parentEntity = qobject_cast<Qt3DCore::QEntity *>( renderer->parent() );
194 if ( mExportTextures )
195 processEntityMaterial( parentEntity,
object );
196 mObjects.push_back(
object );
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 textureGenerator->waitForFinished();
270 const QSize oldResolution = textureGenerator->textureSize();
271 textureGenerator->setTextureSize( QSize( mTerrainTextureResolution, mTerrainTextureResolution ) );
272 switch ( generator->
type() )
275 terrainTile = getDemTerrainEntity( terrain, node, settings->
origin() );
276 parseDemTile( terrainTile, layerName + u
"_"_s );
279 terrainTile = getFlatTerrainEntity( terrain, node, settings->
origin() );
280 parseFlatTile( terrainTile, layerName + u
"_"_s );
283 terrainTile = getMeshTerrainEntity( terrain, node, settings->
origin() );
284 parseMeshTile( terrainTile, layerName + u
"_"_s );
291 textureGenerator->setTextureSize( oldResolution );
294QgsTerrainTileEntity *Qgs3DSceneExporter::getFlatTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
296 QgsFlatTerrainGenerator *generator = qgis::down_cast<QgsFlatTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
297 FlatTerrainChunkLoader *flatTerrainLoader = qobject_cast<FlatTerrainChunkLoader *>( generator->
createChunkLoader( node ) );
298 flatTerrainLoader->start();
299 if ( mExportTextures )
300 terrain->textureGenerator()->waitForFinished();
302 Qt3DCore::QEntity *entity = flatTerrainLoader->createEntity(
this );
303 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( entity );
305 const QList<QgsGeoTransform *> transforms = entity->findChildren<QgsGeoTransform *>();
306 for ( QgsGeoTransform *transform : transforms )
308 transform->setOrigin( mapOrigin );
314QgsTerrainTileEntity *Qgs3DSceneExporter::getDemTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
318 QgsDemTerrainGenerator *generator = qgis::down_cast<QgsDemTerrainGenerator *>( terrain->mapSettings()->terrainGenerator()->clone() );
320 QgsDemTerrainTileLoader *loader = qobject_cast<QgsDemTerrainTileLoader *>( generator->
createChunkLoader( node ) );
323 if ( mExportTextures )
324 terrain->textureGenerator()->waitForFinished();
325 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity(
this ) );
327 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
328 for ( QgsGeoTransform *transform : transforms )
330 transform->setOrigin( mapOrigin );
337QgsTerrainTileEntity *Qgs3DSceneExporter::getMeshTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
339 QgsMeshTerrainGenerator *generator = qgis::down_cast<QgsMeshTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
340 QgsMeshTerrainTileLoader *loader = qobject_cast<QgsMeshTerrainTileLoader *>( generator->createChunkLoader( node ) );
343 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity(
this ) );
345 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
346 for ( QgsGeoTransform *transform : transforms )
348 transform->setOrigin( mapOrigin );
354void Qgs3DSceneExporter::parseFlatTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
359 Qt3DCore::QGeometry *geometry = mesh->geometry();
360 Qt3DExtras::QPlaneGeometry *tileGeometry = qobject_cast<Qt3DExtras::QPlaneGeometry *>( geometry );
363 QgsDebugError(
"Qt3DExtras::QPlaneGeometry* is expected but something else was given" );
368 Qt3DCore::QAttribute *positionAttribute = tileGeometry->positionAttribute();
369 if ( !positionAttribute )
371 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( layerName ) );
374 const QByteArray verticesBytes =
getData( positionAttribute->buffer() );
375 if ( verticesBytes.isNull() )
377 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( layerName ) );
383 Qt3DCore::QAttribute *indexAttribute = tileGeometry->indexAttribute();
384 if ( !indexAttribute )
386 QgsDebugError( QString(
"Cannot export '%1' - geometry has no index attribute!" ).arg( layerName ) );
389 const QByteArray indexBytes =
getData( indexAttribute->buffer() );
390 if ( indexBytes.isNull() )
392 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( layerName ) );
395 const QVector<uint> indexesBuffer =
getIndexData( indexAttribute, indexBytes );
397 QString objectNamePrefix = layerName;
398 if ( objectNamePrefix != QString() )
399 objectNamePrefix += QString();
401 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"Flat_tile"_s ) );
402 mObjects.push_back(
object );
404 object->setSmoothEdges( mSmoothEdges );
405 object->setupTriangle( positionBuffer, indexesBuffer, transform->matrix() );
407 if ( mExportNormals )
410 QVector<float> normalsBuffer;
411 for (
int i = 0; i < positionBuffer.size(); i += 3 )
412 normalsBuffer << 0.0f << 1.0f << 0.0f;
413 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
416 Qt3DCore::QAttribute *texCoordsAttribute = tileGeometry->texCoordAttribute();
417 if ( mExportTextures && texCoordsAttribute )
421 object->setupTextureCoordinates( texCoords );
423 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
424 const QImage img = textureImage->getImage();
425 object->setTextureImage( img );
429void Qgs3DSceneExporter::parseDemTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
434 Qt3DCore::QGeometry *geometry = mesh->geometry();
435 DemTerrainTileGeometry *tileGeometry = qobject_cast<DemTerrainTileGeometry *>( geometry );
438 QgsDebugError(
"DemTerrainTileGeometry* is expected but something else was given" );
442 Qt3DCore::QAttribute *positionAttribute = tileGeometry->positionAttribute();
443 const QByteArray positionBytes = positionAttribute->buffer()->data();
446 Qt3DCore::QAttribute *indexAttribute = tileGeometry->indexAttribute();
447 const QByteArray indexBytes = indexAttribute->buffer()->data();
448 const QVector<unsigned int> indexBuffer =
getIndexData( indexAttribute, indexBytes );
450 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( layerName + u
"DEM_tile"_s ) );
451 mObjects.push_back(
object );
453 object->setSmoothEdges( mSmoothEdges );
454 object->setupTriangle( positionBuffer, indexBuffer, transform->matrix() );
456 Qt3DCore::QAttribute *normalsAttributes = tileGeometry->normalAttribute();
457 if ( mExportNormals && normalsAttributes )
459 const QByteArray normalsBytes = normalsAttributes->buffer()->data();
461 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
464 Qt3DCore::QAttribute *texCoordsAttribute = tileGeometry->texCoordsAttribute();
465 if ( mExportTextures && texCoordsAttribute )
467 const QByteArray texCoordsBytes = texCoordsAttribute->buffer()->data();
469 object->setupTextureCoordinates( texCoordsBuffer );
471 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
472 const QImage img = textureImage->getImage();
473 object->setTextureImage( img );
477void Qgs3DSceneExporter::parseMeshTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
479 QString objectNamePrefix = layerName;
480 if ( objectNamePrefix != QString() )
481 objectNamePrefix +=
'_'_L1;
483 const QList<Qt3DRender::QGeometryRenderer *> renderers = tileEntity->findChildren<Qt3DRender::QGeometryRenderer *>();
484 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
486 Qgs3DExportObject *obj = processGeometryRenderer( renderer, objectNamePrefix );
493QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processInstancedPointGeometry( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
498 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 );
500 QVector3D symbolScale;
501 QVector4D symbolRotation;
502 const QList<Qt3DRender::QMaterial *> materials = entity->findChildren<Qt3DRender::QMaterial *>();
503 for ( Qt3DRender::QMaterial *material : materials )
505 for (
const Qt3DRender::QParameter *parameter : material->parameters() )
507 if ( parameter->name() ==
"symbolScale"_L1 )
509 symbolScale = parameter->value().value<QVector3D>();
511 else if ( parameter->name() ==
"symbolRotation"_L1 )
513 symbolRotation = parameter->value().value<QVector4D>();
518 QQuaternion rotationQuat( symbolRotation.w(), symbolRotation.x(), symbolRotation.y(), symbolRotation.z() );
520 QVector<Qgs3DExportObject *> objects;
521 const QList<Qt3DCore::QGeometry *> geometriesList = entity->findChildren<Qt3DCore::QGeometry *>();
522 for ( Qt3DCore::QGeometry *geometry : geometriesList )
524 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
525 Qt3DCore::QAttribute *indexAttribute =
findAttribute( geometry, QString(), Qt3DCore::QAttribute::IndexAttribute );
526 if ( !positionAttribute || !indexAttribute )
528 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position or index attribute!" ).arg( objectNamePrefix ) );
532 Qt3DCore::QAttribute *instanceScaleAttribute =
findAttribute( geometry, u
"instanceScale"_s, Qt3DCore::QAttribute::VertexAttribute );
533 Qt3DCore::QAttribute *instanceRotationAttribute =
findAttribute( geometry, u
"instanceRotation"_s, Qt3DCore::QAttribute::VertexAttribute );
535 const QByteArray vertexBytes = positionAttribute->buffer()->data();
536 const QByteArray indexBytes = indexAttribute->buffer()->data();
537 if ( vertexBytes.isNull() || indexBytes.isNull() )
539 QgsDebugError( QString(
"Geometry for '%1' has position or index attribute with empty data!" ).arg( objectNamePrefix ) );
544 const QVector<uint> indexData =
getIndexData( indexAttribute, indexBytes );
546 const QVector<QVector3D> instanceScaleData = instanceScaleAttribute ?
getAttributeData<QVector3D>( instanceScaleAttribute, instanceScaleAttribute->buffer()->data() ) : QVector<QVector3D>();
547 const QVector<QVector4D> instanceRotationData = instanceRotationAttribute ?
getAttributeData<QVector4D>( instanceRotationAttribute, instanceRotationAttribute->buffer()->data() )
548 : QVector<QVector4D>();
550 Qt3DCore::QAttribute *instanceDataAttribute =
findAttribute( geometry, u
"instanceTranslation"_s, Qt3DCore::QAttribute::VertexAttribute );
551 if ( !instanceDataAttribute )
553 QgsDebugError( QString(
"Cannot export '%1' - geometry has no instanceTranslation attribute!" ).arg( objectNamePrefix ) );
556 const QByteArray instancePositionBytes =
getData( instanceDataAttribute->buffer() );
557 if ( instancePositionBytes.isNull() )
559 QgsDebugError( QString(
"Geometry for '%1' has instanceTranslation attribute with empty data!" ).arg( objectNamePrefix ) );
564 int instanceIndex = 0;
565 for (
int i = 0; i < instancePosition.size(); i += 3, instanceIndex++ )
567 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"instance_point"_s ) );
568 objects.push_back(
object );
569 QMatrix4x4 instanceTransform;
570 instanceTransform.translate( instancePosition[i], instancePosition[i + 1], instancePosition[i + 2] );
572 QQuaternion instanceRotation = rotationQuat;
573 if ( instanceRotationAttribute )
575 const QVector4D instanceRotationVec = instanceRotationData[instanceIndex];
576 instanceRotation = QQuaternion( instanceRotationVec.w(), instanceRotationVec.x(), instanceRotationVec.y(), instanceRotationVec.z() );
578 instanceTransform.rotate( instanceRotation );
579 instanceTransform.scale( instanceScaleAttribute ? instanceScaleData[instanceIndex] : symbolScale );
580 instanceTransform *= instanceMaterialTransform;
581 object->setupTriangle( positionData, indexData, instanceTransform );
583 object->setSmoothEdges( mSmoothEdges );
585 Qt3DCore::QAttribute *normalsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
586 if ( mExportNormals && normalsAttribute )
590 object->setupNormalCoordinates( normalsData, instanceTransform );
598QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processSceneLoaderGeometries( Qt3DRender::QSceneLoader *sceneLoader,
const QString &objectNamePrefix )
600 QVector<Qgs3DExportObject *> objects;
601 Qt3DCore::QEntity *sceneLoaderParent = qobject_cast<Qt3DCore::QEntity *>( sceneLoader->parent() );
603 QMatrix4x4 sceneTransform;
604 if ( entityTransform )
606 sceneTransform = entityTransform->matrix();
608 QStringList entityNames = sceneLoader->entityNames();
609 for (
const QString &entityName : entityNames )
611 Qt3DRender::QGeometryRenderer *mesh = qobject_cast<Qt3DRender::QGeometryRenderer *>( sceneLoader->component( entityName, Qt3DRender::QSceneLoader::GeometryRendererComponent ) );
612 Qgs3DExportObject *
object = processGeometryRenderer( mesh, objectNamePrefix, sceneTransform );
615 objects.push_back(
object );
620Qgs3DExportObject *Qgs3DSceneExporter::processGeometryRenderer( Qt3DRender::QGeometryRenderer *geomRenderer,
const QString &objectNamePrefix,
const QMatrix4x4 &sceneTransform )
623 if ( geomRenderer->primitiveType() != Qt3DRender::QGeometryRenderer::Triangles )
626 Qt3DCore::QGeometry *geometry = geomRenderer->geometry();
630 if (
findAttribute( geometry, u
"pointA"_s, Qt3DCore::QAttribute::VertexAttribute ) )
641 QVector<std::pair<uint, uint>> triangleIndexStartingIndiceToKeep;
642 QgsTessellatedPolygonGeometry *tessGeom =
dynamic_cast<QgsTessellatedPolygonGeometry *
>( geometry );
645 QVector<QgsFeatureId> featureIds = tessGeom->
featureIds();
647 QSet<QgsFeatureId> tempFeatToAdd;
650 for (
int idx = 0; idx < featureIds.size(); idx++ )
653 if ( !mExportedFeatureIds.contains( feat ) )
656 tempFeatToAdd += feat;
659 const uint startIdx = triangleIndex[idx];
660 const uint endIdx = idx < triangleIndex.size() - 1 ? triangleIndex[idx + 1] : std::numeric_limits<uint>::max();
662 if ( startIdx < endIdx )
663 triangleIndexStartingIndiceToKeep.append( std::pair<uint, uint>( startIdx, endIdx ) );
666 mExportedFeatureIds += tempFeatToAdd;
668 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
675 QMatrix4x4 transformMatrix = sceneTransform;
676 QObject *parent = geomRenderer->parent();
679 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( parent );
683 transformMatrix = transform->matrix() * transformMatrix;
685 parent = parent->parent();
688 Qt3DCore::QAttribute *positionAttribute =
nullptr;
689 Qt3DCore::QAttribute *indexAttribute =
nullptr;
690 QByteArray indexBytes, vertexBytes;
691 QVector<uint> indexDataTmp;
692 QVector<uint> indexData;
693 QVector<float> positionData;
696 positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
697 if ( !positionAttribute )
699 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
703 vertexBytes =
getData( positionAttribute->buffer() );
704 if ( vertexBytes.isNull() )
706 QgsDebugError( QString(
"Will not export '%1' as geometry has empty position data!" ).arg( objectNamePrefix ) );
713 QVector<Qt3DCore::QAttribute *> attributes = geometry->attributes();
714 for ( Qt3DCore::QAttribute *attribute : attributes )
716 if ( attribute->attributeType() == Qt3DCore::QAttribute::IndexAttribute )
718 indexAttribute = attribute;
719 indexBytes =
getData( indexAttribute->buffer() );
720 if ( indexBytes.isNull() )
722 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( objectNamePrefix ) );
726 indexDataTmp =
getIndexData( indexAttribute, indexBytes );
733 if ( !indexAttribute )
735 for ( uint i = 0; i < static_cast<uint>( positionData.size() / 3 ); ++i )
737 indexDataTmp.push_back( i );
742 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
745 indexData.append( indexDataTmp );
751 const int triangleIndexStartingIndiceToKeepSize = triangleIndexStartingIndiceToKeep.size();
752 const uint indexDataTmpSize =
static_cast<uint
>( indexDataTmp.size() );
753 for ( uint i = 0; i + 2 < indexDataTmpSize; i += 3 )
755 const uint triangleIdx = i / 3;
758 while ( intervalIdx < triangleIndexStartingIndiceToKeepSize && triangleIdx >= triangleIndexStartingIndiceToKeep[intervalIdx].second )
764 if ( intervalIdx < triangleIndexStartingIndiceToKeepSize && triangleIdx >= triangleIndexStartingIndiceToKeep[intervalIdx].first && triangleIdx < triangleIndexStartingIndiceToKeep[intervalIdx].second )
766 indexData.push_back( indexDataTmp[
static_cast<int>( i )] );
767 indexData.push_back( indexDataTmp[
static_cast<int>( i + 1 )] );
768 indexData.push_back( indexDataTmp[
static_cast<int>( i + 2 )] );
774 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"mesh_geometry"_s ) );
775 object->setupTriangle( positionData, indexData, transformMatrix );
777 Qt3DCore::QAttribute *normalsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
778 if ( mExportNormals && normalsAttribute )
782 object->setupNormalCoordinates( normalsData, transformMatrix );
785 Qt3DCore::QAttribute *texCoordsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultTextureCoordinateAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
786 if ( mExportTextures && texCoordsAttribute )
790 object->setupTextureCoordinates( texCoordsData );
796QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processLines( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
798 QVector<Qgs3DExportObject *> objs;
799 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
800 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
802 Qt3DCore::QGeometry *geom = renderer->geometry();
803 Qt3DCore::QAttribute *pointAAttribute =
findAttribute( geom, u
"pointA"_s, Qt3DCore::QAttribute::VertexAttribute );
804 Qt3DCore::QAttribute *pointBAttribute =
findAttribute( geom, u
"pointB"_s, Qt3DCore::QAttribute::VertexAttribute );
805 Qt3DCore::QAttribute *pointCAttribute =
findAttribute( geom, u
"pointC"_s, Qt3DCore::QAttribute::VertexAttribute );
806 if ( !pointAAttribute || !pointBAttribute || pointCAttribute )
809 const QByteArray pointABytes =
getData( pointAAttribute->buffer() );
810 const QByteArray pointBBytes =
getData( pointBAttribute->buffer() );
811 if ( pointABytes.isNull() || pointBBytes.isNull() )
813 QgsDebugError( QString(
"Geometry for '%1' has pointA or pointB attribute with empty data!" ).arg( objectNamePrefix ) );
819 QVector<float> positionData;
820 positionData.reserve( pointAData.size() + pointBData.size() );
821 for (
int i = 0; i + 2 < pointAData.size(); i += 3 )
823 positionData << pointAData[i] << pointAData[i + 1] << pointAData[i + 2];
824 positionData << pointBData[i] << pointBData[i + 1] << pointBData[i + 2];
827 Qgs3DExportObject *exportObject =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"line"_s ) );
830 objs.push_back( exportObject );
835Qgs3DExportObject *Qgs3DSceneExporter::processPoints( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
837 QVector<float> points;
838 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
839 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
841 Qt3DCore::QGeometry *geometry = qobject_cast<QgsBillboardGeometry *>( renderer->geometry() );
844 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geometry,
"instancePosition", Qt3DCore::QAttribute::VertexAttribute );
845 if ( !positionAttribute )
847 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
850 const QByteArray positionBytes =
getData( positionAttribute->buffer() );
851 if ( positionBytes.isNull() )
853 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( objectNamePrefix ) );
859 Qgs3DExportObject *obj =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"points"_s ) );
866 if ( mObjects.isEmpty() )
871 switch ( exportFormat )
874 return saveObj( sceneName, sceneFolderPath, precision );
876 return saveStl( sceneName, sceneFolderPath, precision );
882bool Qgs3DSceneExporter::saveObj( QString sceneName, QString sceneFolderPath,
int precision )
const
884 const QString objFilePath = QDir( sceneFolderPath ).filePath( sceneName + u
".obj"_s );
885 const QString mtlFilePath = QDir( sceneFolderPath ).filePath( sceneName + u
".mtl"_s );
887 QFile file( objFilePath );
888 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
890 QgsDebugError( u
"Scene can not be exported to '%1'. File access error."_s.arg( objFilePath ) );
893 QFile mtlFile( mtlFilePath );
894 if ( !mtlFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
896 QgsDebugError( u
"Scene can not be exported to '%1'. File access error."_s.arg( mtlFilePath ) );
902 getSceneCenterAndScale( center,
scale );
904 QTextStream out( &file );
906 const QString mtlLibName = sceneName +
".mtl";
907 out <<
"mtllib " << mtlLibName <<
"\n";
909 QTextStream mtlOut( &mtlFile );
910 for ( Qgs3DExportObject *obj : std::as_const( mObjects ) )
915 const QString materialName = obj->
saveMaterial( mtlOut, sceneFolderPath );
923bool Qgs3DSceneExporter::saveStl( QString sceneName, QString sceneFolderPath,
int precision )
const
925 const QString stlFilePath = QDir( sceneFolderPath ).filePath( sceneName + u
".stl"_s );
927 QFile file( stlFilePath );
928 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
930 QgsDebugError( u
"Scene can not be exported to '%1'. File access error."_s.arg( stlFilePath ) );
936 getSceneCenterAndScale( center,
scale );
938 QTextStream out( &file );
940 for ( Qgs3DExportObject *
object : std::as_const( mObjects ) )
952void Qgs3DSceneExporter::getSceneCenterAndScale( QVector3D ¢er,
float &scale )
const
954 const float minFloat = std::numeric_limits<float>::lowest();
955 const float maxfloat = std::numeric_limits<float>::max();
957 float minX = maxfloat;
958 float minY = maxfloat;
959 float minZ = maxfloat;
960 float maxX = minFloat;
961 float maxY = minFloat;
962 float maxZ = minFloat;
963 for ( Qgs3DExportObject *obj : std::as_const( mObjects ) )
965 obj->
objectBounds( minX, minY, minZ, maxX, maxY, maxZ );
968 const float diffX = maxX - minX;
969 const float diffY = maxY - minY;
970 const float diffZ = maxZ - minZ;
972 center.setX( ( minX + maxX ) / 2.0f );
973 center.setY( ( minY + maxY ) / 2.0f );
974 center.setZ( ( minZ + maxZ ) / 2.0f );
976 scale = std::max( diffX, std::max( diffY, diffZ ) ) / mScale;
979QString Qgs3DSceneExporter::getObjectName(
const QString &name )
982 if ( mUsedObjectNamesCounter.contains( name ) )
984 ret = u
"%1%2"_s.arg( name ).arg( mUsedObjectNamesCounter[name] );
985 mUsedObjectNamesCounter[name]++;
988 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.
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.
#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)