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 );
198 return mObjects.size() > prevSize;
201 else if ( rendererType ==
"vector" )
204 if ( vectorLayerRenderer )
216 QgsDebugMsgLevel( u
"Type '%1' of layer '%2' is not exportable."_s.arg( layer->
name(), rendererType ), 2 );
223void Qgs3DSceneExporter::processEntityMaterial( Qt3DCore::QEntity *entity,
Qgs3DExportObject *
object )
const
229 object->setupMaterial( &material );
233 if ( diffuseMapMaterial )
235 const Qt3DRender::QTexture2D *diffuseTexture = diffuseMapMaterial->diffuse().value<Qt3DRender::QTexture2D *>();
236 if ( diffuseTexture )
238 const QVector<Qt3DRender::QAbstractTextureImage *> textureImages = diffuseTexture->textureImages();
239 for (
const Qt3DRender::QAbstractTextureImage *tex : textureImages )
241 const QgsImageTexture *imageTexture =
dynamic_cast<const QgsImageTexture *
>( tex );
244 const QImage image = imageTexture->
getImage();
245 object->setTextureImage( image );
259 QgsChunkNode *node = terrain->rootNode();
264 QgsTerrainTileEntity *terrainTile =
nullptr;
265 QgsTerrainTextureGenerator *textureGenerator = terrain->textureGenerator();
266 textureGenerator->waitForFinished();
267 const QSize oldResolution = textureGenerator->textureSize();
268 textureGenerator->setTextureSize( QSize( mTerrainTextureResolution, mTerrainTextureResolution ) );
269 switch ( generator->
type() )
272 terrainTile = getDemTerrainEntity( terrain, node, settings->
origin() );
273 parseDemTile( terrainTile, layerName + u
"_"_s );
276 terrainTile = getFlatTerrainEntity( terrain, node, settings->
origin() );
277 parseFlatTile( terrainTile, layerName + u
"_"_s );
280 terrainTile = getMeshTerrainEntity( terrain, node, settings->
origin() );
281 parseMeshTile( terrainTile, layerName + u
"_"_s );
288 textureGenerator->setTextureSize( oldResolution );
291QgsTerrainTileEntity *Qgs3DSceneExporter::getFlatTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
293 QgsFlatTerrainGenerator *generator = qgis::down_cast<QgsFlatTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
294 FlatTerrainChunkLoader *flatTerrainLoader = qobject_cast<FlatTerrainChunkLoader *>( generator->
createChunkLoader( node ) );
295 flatTerrainLoader->start();
296 if ( mExportTextures )
297 terrain->textureGenerator()->waitForFinished();
299 Qt3DCore::QEntity *entity = flatTerrainLoader->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 QgsDemTerrainTileLoader *loader = qobject_cast<QgsDemTerrainTileLoader *>( generator->
createChunkLoader( node ) );
320 if ( mExportTextures )
321 terrain->textureGenerator()->waitForFinished();
322 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity(
this ) );
324 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
325 for ( QgsGeoTransform *transform : transforms )
327 transform->setOrigin( mapOrigin );
334QgsTerrainTileEntity *Qgs3DSceneExporter::getMeshTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
336 QgsMeshTerrainGenerator *generator = qgis::down_cast<QgsMeshTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
337 QgsMeshTerrainTileLoader *loader = qobject_cast<QgsMeshTerrainTileLoader *>( generator->createChunkLoader( node ) );
340 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity(
this ) );
342 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
343 for ( QgsGeoTransform *transform : transforms )
345 transform->setOrigin( mapOrigin );
351void Qgs3DSceneExporter::parseFlatTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
356 Qt3DCore::QGeometry *geometry = mesh->geometry();
357 Qt3DExtras::QPlaneGeometry *tileGeometry = qobject_cast<Qt3DExtras::QPlaneGeometry *>( geometry );
360 QgsDebugError(
"Qt3DExtras::QPlaneGeometry* is expected but something else was given" );
365 Qt3DCore::QAttribute *positionAttribute = tileGeometry->positionAttribute();
366 if ( !positionAttribute )
368 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( layerName ) );
371 const QByteArray verticesBytes =
getData( positionAttribute->buffer() );
372 if ( verticesBytes.isNull() )
374 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( layerName ) );
380 Qt3DCore::QAttribute *indexAttribute = tileGeometry->indexAttribute();
381 if ( !indexAttribute )
383 QgsDebugError( QString(
"Cannot export '%1' - geometry has no index attribute!" ).arg( layerName ) );
386 const QByteArray indexBytes =
getData( indexAttribute->buffer() );
387 if ( indexBytes.isNull() )
389 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( layerName ) );
392 const QVector<uint> indexesBuffer =
getIndexData( indexAttribute, indexBytes );
394 QString objectNamePrefix = layerName;
395 if ( objectNamePrefix != QString() )
396 objectNamePrefix += QString();
398 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"Flat_tile"_s ) );
399 mObjects.push_back(
object );
401 object->setSmoothEdges( mSmoothEdges );
402 object->setupTriangle( positionBuffer, indexesBuffer, transform->matrix() );
404 if ( mExportNormals )
407 QVector<float> normalsBuffer;
408 for (
int i = 0; i < positionBuffer.size(); i += 3 )
409 normalsBuffer << 0.0f << 1.0f << 0.0f;
410 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
413 Qt3DCore::QAttribute *texCoordsAttribute = tileGeometry->texCoordAttribute();
414 if ( mExportTextures && texCoordsAttribute )
418 object->setupTextureCoordinates( texCoords );
420 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
421 const QImage img = textureImage->getImage();
422 object->setTextureImage( img );
426void Qgs3DSceneExporter::parseDemTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
431 Qt3DCore::QGeometry *geometry = mesh->geometry();
432 DemTerrainTileGeometry *tileGeometry = qobject_cast<DemTerrainTileGeometry *>( geometry );
435 QgsDebugError(
"DemTerrainTileGeometry* is expected but something else was given" );
439 Qt3DCore::QAttribute *positionAttribute = tileGeometry->positionAttribute();
440 const QByteArray positionBytes = positionAttribute->buffer()->data();
443 Qt3DCore::QAttribute *indexAttribute = tileGeometry->indexAttribute();
444 const QByteArray indexBytes = indexAttribute->buffer()->data();
445 const QVector<unsigned int> indexBuffer =
getIndexData( indexAttribute, indexBytes );
447 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( layerName + u
"DEM_tile"_s ) );
448 mObjects.push_back(
object );
450 object->setSmoothEdges( mSmoothEdges );
451 object->setupTriangle( positionBuffer, indexBuffer, transform->matrix() );
453 Qt3DCore::QAttribute *normalsAttributes = tileGeometry->normalAttribute();
454 if ( mExportNormals && normalsAttributes )
456 const QByteArray normalsBytes = normalsAttributes->buffer()->data();
458 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
461 Qt3DCore::QAttribute *texCoordsAttribute = tileGeometry->texCoordsAttribute();
462 if ( mExportTextures && texCoordsAttribute )
464 const QByteArray texCoordsBytes = texCoordsAttribute->buffer()->data();
466 object->setupTextureCoordinates( texCoordsBuffer );
468 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
469 const QImage img = textureImage->getImage();
470 object->setTextureImage( img );
474void Qgs3DSceneExporter::parseMeshTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
476 QString objectNamePrefix = layerName;
477 if ( objectNamePrefix != QString() )
478 objectNamePrefix +=
'_'_L1;
480 const QList<Qt3DRender::QGeometryRenderer *> renderers = tileEntity->findChildren<Qt3DRender::QGeometryRenderer *>();
481 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
483 Qgs3DExportObject *obj = processGeometryRenderer( renderer, objectNamePrefix );
490QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processInstancedPointGeometry( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
495 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 );
497 QVector3D symbolScale;
498 QVector4D symbolRotation;
499 const QList<Qt3DRender::QMaterial *> materials = entity->findChildren<Qt3DRender::QMaterial *>();
500 for ( Qt3DRender::QMaterial *material : materials )
502 for (
const Qt3DRender::QParameter *parameter : material->parameters() )
504 if ( parameter->name() ==
"symbolScale"_L1 )
506 symbolScale = parameter->value().value<QVector3D>();
508 else if ( parameter->name() ==
"symbolRotation"_L1 )
510 symbolRotation = parameter->value().value<QVector4D>();
515 QQuaternion rotationQuat( symbolRotation.w(), symbolRotation.x(), symbolRotation.y(), symbolRotation.z() );
517 QVector<Qgs3DExportObject *> objects;
518 const QList<Qt3DCore::QGeometry *> geometriesList = entity->findChildren<Qt3DCore::QGeometry *>();
519 for ( Qt3DCore::QGeometry *geometry : geometriesList )
521 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
522 Qt3DCore::QAttribute *indexAttribute =
findAttribute( geometry, QString(), Qt3DCore::QAttribute::IndexAttribute );
523 if ( !positionAttribute || !indexAttribute )
525 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position or index attribute!" ).arg( objectNamePrefix ) );
529 Qt3DCore::QAttribute *instanceScaleAttribute =
findAttribute( geometry, u
"instanceScale"_s, Qt3DCore::QAttribute::VertexAttribute );
530 Qt3DCore::QAttribute *instanceRotationAttribute =
findAttribute( geometry, u
"instanceRotation"_s, Qt3DCore::QAttribute::VertexAttribute );
532 const QByteArray vertexBytes = positionAttribute->buffer()->data();
533 const QByteArray indexBytes = indexAttribute->buffer()->data();
534 if ( vertexBytes.isNull() || indexBytes.isNull() )
536 QgsDebugError( QString(
"Geometry for '%1' has position or index attribute with empty data!" ).arg( objectNamePrefix ) );
541 const QVector<uint> indexData =
getIndexData( indexAttribute, indexBytes );
543 const QVector<QVector3D> instanceScaleData = instanceScaleAttribute ?
getAttributeData<QVector3D>( instanceScaleAttribute, instanceScaleAttribute->buffer()->data() ) : QVector<QVector3D>();
544 const QVector<QVector4D> instanceRotationData = instanceRotationAttribute ?
getAttributeData<QVector4D>( instanceRotationAttribute, instanceRotationAttribute->buffer()->data() )
545 : QVector<QVector4D>();
547 Qt3DCore::QAttribute *instanceDataAttribute =
findAttribute( geometry, u
"instanceTranslation"_s, Qt3DCore::QAttribute::VertexAttribute );
548 if ( !instanceDataAttribute )
550 QgsDebugError( QString(
"Cannot export '%1' - geometry has no instanceTranslation attribute!" ).arg( objectNamePrefix ) );
553 const QByteArray instancePositionBytes =
getData( instanceDataAttribute->buffer() );
554 if ( instancePositionBytes.isNull() )
556 QgsDebugError( QString(
"Geometry for '%1' has instanceTranslation attribute with empty data!" ).arg( objectNamePrefix ) );
561 int instanceIndex = 0;
562 for (
int i = 0; i < instancePosition.size(); i += 3, instanceIndex++ )
564 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"instance_point"_s ) );
565 objects.push_back(
object );
566 QMatrix4x4 instanceTransform;
567 instanceTransform.translate( instancePosition[i], instancePosition[i + 1], instancePosition[i + 2] );
569 QQuaternion instanceRotation = rotationQuat;
570 if ( instanceRotationAttribute )
572 const QVector4D instanceRotationVec = instanceRotationData[instanceIndex];
573 instanceRotation = QQuaternion( instanceRotationVec.w(), instanceRotationVec.x(), instanceRotationVec.y(), instanceRotationVec.z() );
575 instanceTransform.rotate( instanceRotation );
576 instanceTransform.scale( instanceScaleAttribute ? instanceScaleData[instanceIndex] : symbolScale );
577 instanceTransform *= instanceMaterialTransform;
578 object->setupTriangle( positionData, indexData, instanceTransform );
580 object->setSmoothEdges( mSmoothEdges );
582 Qt3DCore::QAttribute *normalsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
583 if ( mExportNormals && normalsAttribute )
587 object->setupNormalCoordinates( normalsData, instanceTransform );
595QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processSceneLoaderGeometries( Qt3DRender::QSceneLoader *sceneLoader,
const QString &objectNamePrefix )
597 QVector<Qgs3DExportObject *> objects;
598 Qt3DCore::QEntity *sceneLoaderParent = qobject_cast<Qt3DCore::QEntity *>( sceneLoader->parent() );
600 QMatrix4x4 sceneTransform;
601 if ( entityTransform )
603 sceneTransform = entityTransform->matrix();
605 QStringList entityNames = sceneLoader->entityNames();
606 for (
const QString &entityName : entityNames )
608 Qt3DRender::QGeometryRenderer *mesh = qobject_cast<Qt3DRender::QGeometryRenderer *>( sceneLoader->component( entityName, Qt3DRender::QSceneLoader::GeometryRendererComponent ) );
609 Qgs3DExportObject *
object = processGeometryRenderer( mesh, objectNamePrefix, sceneTransform );
612 objects.push_back(
object );
617Qgs3DExportObject *Qgs3DSceneExporter::processGeometryRenderer( Qt3DRender::QGeometryRenderer *geomRenderer,
const QString &objectNamePrefix,
const QMatrix4x4 &sceneTransform )
620 if ( geomRenderer->primitiveType() != Qt3DRender::QGeometryRenderer::Triangles )
623 Qt3DCore::QGeometry *geometry = geomRenderer->geometry();
635 QVector<std::pair<uint, uint>> triangleIndexStartingIndiceToKeep;
636 QgsTessellatedPolygonGeometry *tessGeom =
dynamic_cast<QgsTessellatedPolygonGeometry *
>( geometry );
639 QVector<QgsFeatureId> featureIds = tessGeom->
featureIds();
641 QSet<QgsFeatureId> tempFeatToAdd;
644 for (
int idx = 0; idx < featureIds.size(); idx++ )
647 if ( !mExportedFeatureIds.contains( feat ) )
650 tempFeatToAdd += feat;
653 const uint startIdx = triangleIndex[idx];
654 const uint endIdx = idx < triangleIndex.size() - 1 ? triangleIndex[idx + 1] : std::numeric_limits<uint>::max();
656 if ( startIdx < endIdx )
657 triangleIndexStartingIndiceToKeep.append( std::pair<uint, uint>( startIdx, endIdx ) );
660 mExportedFeatureIds += tempFeatToAdd;
662 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
669 QMatrix4x4 transformMatrix = sceneTransform;
670 QObject *parent = geomRenderer->parent();
673 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( parent );
677 transformMatrix = transform->matrix() * transformMatrix;
679 parent = parent->parent();
682 Qt3DCore::QAttribute *positionAttribute =
nullptr;
683 Qt3DCore::QAttribute *indexAttribute =
nullptr;
684 QByteArray indexBytes, vertexBytes;
685 QVector<uint> indexDataTmp;
686 QVector<uint> indexData;
687 QVector<float> positionData;
690 positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
691 if ( !positionAttribute )
693 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
697 vertexBytes =
getData( positionAttribute->buffer() );
698 if ( vertexBytes.isNull() )
700 QgsDebugError( QString(
"Will not export '%1' as geometry has empty position data!" ).arg( objectNamePrefix ) );
707 QVector<Qt3DCore::QAttribute *> attributes = geometry->attributes();
708 for ( Qt3DCore::QAttribute *attribute : attributes )
710 if ( attribute->attributeType() == Qt3DCore::QAttribute::IndexAttribute )
712 indexAttribute = attribute;
713 indexBytes =
getData( indexAttribute->buffer() );
714 if ( indexBytes.isNull() )
716 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( objectNamePrefix ) );
720 indexDataTmp =
getIndexData( indexAttribute, indexBytes );
727 if ( !indexAttribute )
729 for ( uint i = 0; i < static_cast<uint>( positionData.size() / 3 ); ++i )
731 indexDataTmp.push_back( i );
736 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
739 indexData.append( indexDataTmp );
745 const int triangleIndexStartingIndiceToKeepSize = triangleIndexStartingIndiceToKeep.size();
746 const uint indexDataTmpSize =
static_cast<uint
>( indexDataTmp.size() );
747 for ( uint i = 0; i + 2 < indexDataTmpSize; i += 3 )
749 const uint triangleIdx = i / 3;
752 while ( intervalIdx < triangleIndexStartingIndiceToKeepSize && triangleIdx >= triangleIndexStartingIndiceToKeep[intervalIdx].second )
758 if ( intervalIdx < triangleIndexStartingIndiceToKeepSize && triangleIdx >= triangleIndexStartingIndiceToKeep[intervalIdx].first && triangleIdx < triangleIndexStartingIndiceToKeep[intervalIdx].second )
760 indexData.push_back( indexDataTmp[
static_cast<int>( i )] );
761 indexData.push_back( indexDataTmp[
static_cast<int>( i + 1 )] );
762 indexData.push_back( indexDataTmp[
static_cast<int>( i + 2 )] );
768 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"mesh_geometry"_s ) );
769 object->setupTriangle( positionData, indexData, transformMatrix );
771 Qt3DCore::QAttribute *normalsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
772 if ( mExportNormals && normalsAttribute )
776 object->setupNormalCoordinates( normalsData, transformMatrix );
779 Qt3DCore::QAttribute *texCoordsAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultTextureCoordinateAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
780 if ( mExportTextures && texCoordsAttribute )
784 object->setupTextureCoordinates( texCoordsData );
790QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processLines( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
792 QVector<Qgs3DExportObject *> objs;
793 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
794 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
796 if ( renderer->primitiveType() != Qt3DRender::QGeometryRenderer::LineStripAdjacency )
798 Qt3DCore::QGeometry *geom = renderer->geometry();
799 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geom, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
800 Qt3DCore::QAttribute *indexAttribute =
findAttribute( geom, QString(), Qt3DCore::QAttribute::IndexAttribute );
801 if ( !positionAttribute || !indexAttribute )
803 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position or index attribute!" ).arg( objectNamePrefix ) );
807 const QByteArray vertexBytes =
getData( positionAttribute->buffer() );
808 const QByteArray indexBytes =
getData( indexAttribute->buffer() );
809 if ( vertexBytes.isNull() || indexBytes.isNull() )
811 QgsDebugError( QString(
"Geometry for '%1' has position or index attribute with empty data!" ).arg( objectNamePrefix ) );
816 Qgs3DExportObject *exportObject =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"line"_s ) );
819 objs.push_back( exportObject );
824Qgs3DExportObject *Qgs3DSceneExporter::processPoints( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
826 QVector<float> points;
827 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
828 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
830 Qt3DCore::QGeometry *geometry = qobject_cast<QgsBillboardGeometry *>( renderer->geometry() );
833 Qt3DCore::QAttribute *positionAttribute =
findAttribute( geometry, Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute );
834 if ( !positionAttribute )
836 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
839 const QByteArray positionBytes =
getData( positionAttribute->buffer() );
840 if ( positionBytes.isNull() )
842 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( objectNamePrefix ) );
848 Qgs3DExportObject *obj =
new Qgs3DExportObject( getObjectName( objectNamePrefix + u
"points"_s ) );
855 if ( mObjects.isEmpty() )
860 const QString objFilePath = QDir( sceneFolderPath ).filePath( sceneName + u
".obj"_s );
861 const QString mtlFilePath = QDir( sceneFolderPath ).filePath( sceneName + u
".mtl"_s );
863 QFile file( objFilePath );
864 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
866 QgsDebugError( u
"Scene can not be exported to '%1'. File access error."_s.arg( objFilePath ) );
869 QFile mtlFile( mtlFilePath );
870 if ( !mtlFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
872 QgsDebugError( u
"Scene can not be exported to '%1'. File access error."_s.arg( mtlFilePath ) );
876 float maxfloat = std::numeric_limits<float>::max(), minFloat = std::numeric_limits<float>::lowest();
877 float minX = maxfloat, minY = maxfloat, minZ = maxfloat, maxX = minFloat, maxY = minFloat, maxZ = minFloat;
880 obj->
objectBounds( minX, minY, minZ, maxX, maxY, maxZ );
883 float diffX = 1.0f, diffY = 1.0f, diffZ = 1.0f;
888 const float centerX = ( minX + maxX ) / 2.0f;
889 const float centerY = ( minY + maxY ) / 2.0f;
890 const float centerZ = ( minZ + maxZ ) / 2.0f;
892 const float scale = std::max( diffX, std::max( diffY, diffZ ) );
894 QTextStream out( &file );
896 const QString mtlLibName = sceneName +
".mtl";
897 out <<
"mtllib " << mtlLibName <<
"\n";
899 QTextStream mtlOut( &mtlFile );
905 const QString material = obj->
saveMaterial( mtlOut, sceneFolderPath );
906 out <<
"o " << obj->
name() <<
"\n";
907 if ( material != QString() )
908 out <<
"usemtl " << material <<
"\n";
909 obj->
saveTo( out,
scale / mScale, QVector3D( centerX, centerY, centerZ ), precision );
916QString Qgs3DSceneExporter::getObjectName(
const QString &name )
919 if ( mUsedObjectNamesCounter.contains( name ) )
921 ret = u
"%1%2"_s.arg( name ).arg( mUsedObjectNamesCounter[name] );
922 mUsedObjectNamesCounter[name]++;
925 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)