19#include <Qt3DCore/QComponent>
20#include <Qt3DCore/QEntity>
21#include <Qt3DCore/QNode>
23#include "moc_qgs3dsceneexporter.cpp"
25#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
26#include <Qt3DRender/QAttribute>
27#include <Qt3DRender/QBuffer>
28#include <Qt3DRender/QGeometry>
34#include <Qt3DCore/QAttribute>
35#include <Qt3DCore/QBuffer>
36#include <Qt3DCore/QGeometry>
43#include <Qt3DRender/QGeometryRenderer>
44#include <Qt3DExtras/QPlaneGeometry>
45#include <Qt3DCore/QTransform>
46#include <Qt3DExtras/QDiffuseSpecularMaterial>
47#include <Qt3DExtras/QTextureMaterial>
48#include <Qt3DRender/QTextureImage>
49#include <Qt3DRender/QTexture>
50#include <Qt3DRender/QMesh>
51#include <Qt3DRender/QSceneLoader>
52#include <Qt3DRender/QAbstractTexture>
53#include <Qt3DExtras/QCylinderGeometry>
54#include <Qt3DExtras/QConeGeometry>
55#include <Qt3DExtras/QSphereGeometry>
56#include <Qt3DExtras/QCuboidGeometry>
57#include <Qt3DExtras/QTorusGeometry>
58#include <Qt3DExtras/QExtrudedTextMesh>
59#include <Qt3DExtras/QPhongMaterial>
60#include <Qt3DRender/QAbstractTextureImage>
79#include "qgsmeshterraingenerator.h"
80#include "qgsmeshterraintileloader_p.h"
94 const uint bytesOffset = attribute->byteOffset();
95 const uint bytesStride = attribute->byteStride();
96 const uint vertexSize = attribute->vertexSize();
97 const uint dataSize =
static_cast<uint
>( data.size() );
100 if ( bytesStride == 0 )
102 QgsDebugError(
"bytesStride==0, the attribute probably was not set properly" );
106 const char *pData = data.constData();
107 for (
unsigned int i = bytesOffset; i < dataSize; i += bytesStride )
109 for (
unsigned int j = 0; j < vertexSize *
sizeof( T ); j +=
sizeof( T ) )
112 memcpy( &v, pData + i + j,
sizeof( T ) );
113 result.push_back( v );
122 QVector<uint> result;
123 const char *pData = data.constData();
124 for (
int i = 0; i < data.size(); i +=
sizeof( T ) )
127 memcpy( &v, pData + i,
sizeof( T ) );
128 result.push_back( ( uint ) v );
135 switch ( indexAttribute->vertexBaseType() )
137 case Qt3DQAttribute::VertexBaseType::Int:
139 case Qt3DQAttribute::VertexBaseType::UnsignedInt:
141 case Qt3DQAttribute::VertexBaseType::Short:
143 case Qt3DQAttribute::VertexBaseType::UnsignedShort:
145 case Qt3DQAttribute::VertexBaseType::Byte:
147 case Qt3DQAttribute::VertexBaseType::UnsignedByte:
150 QgsDebugError(
"Probably trying to get index data using an attribute that has vertex data" );
153 return QVector<uint>();
158 QByteArray bytes = buffer->data();
159 if ( bytes.isNull() )
168 QVector<Qt3DQAttribute *> attributes = geometry->attributes();
171 if ( attribute->attributeType() != type )
173 if ( name.isEmpty() || attribute->name() == name )
179template<
typename Component>
184 QVector<Qt3DCore::QComponent *> components = entity->components();
185 for ( Qt3DCore::QComponent *component : components )
187 Component *typedComponent = qobject_cast<Component *>( component );
188 if ( typedComponent )
189 return typedComponent;
197 const QString rendererType = abstractRenderer->
type();
199 if ( rendererType ==
"rulebased" )
201 int prevSize = mObjects.size();
203 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
204 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
206 Qt3DCore::QEntity *parentEntity = qobject_cast<Qt3DCore::QEntity *>( renderer->parent() );
209 Qgs3DExportObject *
object = processGeometryRenderer( renderer, layer->
name() + QStringLiteral(
"_" ) );
212 if ( mExportTextures )
213 processEntityMaterial( parentEntity,
object );
214 mObjects.push_back(
object );
216 return mObjects.size() > prevSize;
219 else if ( rendererType ==
"vector" )
222 if ( vectorLayerRenderer )
234 QgsDebugMsgLevel( QStringLiteral(
"Type '%1' of layer '%2' is not exportable." ).arg( layer->
name(), rendererType ), 2 );
241void Qgs3DSceneExporter::processEntityMaterial( Qt3DCore::QEntity *entity,
Qgs3DExportObject *
object )
const
247 object->setupMaterial( &material );
251 if ( diffuseMapMaterial )
253 const Qt3DRender::QTexture2D *diffuseTexture = diffuseMapMaterial->diffuse().value<Qt3DRender::QTexture2D *>();
254 if ( diffuseTexture )
256 const QVector<Qt3DRender::QAbstractTextureImage *> textureImages = diffuseTexture->textureImages();
257 for (
const Qt3DRender::QAbstractTextureImage *tex : textureImages )
259 const QgsImageTexture *imageTexture =
dynamic_cast<const QgsImageTexture *
>( tex );
262 const QImage image = imageTexture->
getImage();
263 object->setTextureImage( image );
277 QgsChunkNode *node = terrain->rootNode();
282 QgsTerrainTileEntity *terrainTile =
nullptr;
283 QgsTerrainTextureGenerator *textureGenerator = terrain->textureGenerator();
284 textureGenerator->waitForFinished();
285 const QSize oldResolution = textureGenerator->textureSize();
286 textureGenerator->setTextureSize( QSize( mTerrainTextureResolution, mTerrainTextureResolution ) );
287 switch ( generator->
type() )
290 terrainTile = getDemTerrainEntity( terrain, node, settings->
origin() );
291 parseDemTile( terrainTile, layerName + QStringLiteral(
"_" ) );
294 terrainTile = getFlatTerrainEntity( terrain, node, settings->
origin() );
295 parseFlatTile( terrainTile, layerName + QStringLiteral(
"_" ) );
298 terrainTile = getMeshTerrainEntity( terrain, node, settings->
origin() );
299 parseMeshTile( terrainTile, layerName + QStringLiteral(
"_" ) );
306 textureGenerator->setTextureSize( oldResolution );
309QgsTerrainTileEntity *Qgs3DSceneExporter::getFlatTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
311 QgsFlatTerrainGenerator *generator = qgis::down_cast<QgsFlatTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
312 FlatTerrainChunkLoader *flatTerrainLoader = qobject_cast<FlatTerrainChunkLoader *>( generator->
createChunkLoader( node ) );
313 flatTerrainLoader->start();
314 if ( mExportTextures )
315 terrain->textureGenerator()->waitForFinished();
317 Qt3DCore::QEntity *entity = flatTerrainLoader->createEntity(
this );
318 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( entity );
320 const QList<QgsGeoTransform *> transforms = entity->findChildren<QgsGeoTransform *>();
321 for ( QgsGeoTransform *transform : transforms )
323 transform->setOrigin( mapOrigin );
329QgsTerrainTileEntity *Qgs3DSceneExporter::getDemTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
333 QgsDemTerrainGenerator *generator = qgis::down_cast<QgsDemTerrainGenerator *>( terrain->mapSettings()->terrainGenerator()->clone() );
335 QgsDemTerrainTileLoader *loader = qobject_cast<QgsDemTerrainTileLoader *>( generator->
createChunkLoader( node ) );
338 if ( mExportTextures )
339 terrain->textureGenerator()->waitForFinished();
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 );
352QgsTerrainTileEntity *Qgs3DSceneExporter::getMeshTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node,
const QgsVector3D &mapOrigin )
354 QgsMeshTerrainGenerator *generator = qgis::down_cast<QgsMeshTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
355 QgsMeshTerrainTileLoader *loader = qobject_cast<QgsMeshTerrainTileLoader *>( generator->createChunkLoader( node ) );
358 QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity(
this ) );
360 const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
361 for ( QgsGeoTransform *transform : transforms )
363 transform->setOrigin( mapOrigin );
369void Qgs3DSceneExporter::parseFlatTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
375 Qt3DExtras::QPlaneGeometry *tileGeometry = qobject_cast<Qt3DExtras::QPlaneGeometry *>( geometry );
378 QgsDebugError(
"Qt3DExtras::QPlaneGeometry* is expected but something else was given" );
383 Qt3DQAttribute *positionAttribute = tileGeometry->positionAttribute();
384 if ( !positionAttribute )
386 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( layerName ) );
389 const QByteArray verticesBytes =
getData( positionAttribute->buffer() );
390 if ( verticesBytes.isNull() )
392 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( layerName ) );
399 if ( !indexAttribute )
401 QgsDebugError( QString(
"Cannot export '%1' - geometry has no index attribute!" ).arg( layerName ) );
404 const QByteArray indexBytes =
getData( indexAttribute->buffer() );
405 if ( indexBytes.isNull() )
407 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( layerName ) );
410 const QVector<uint> indexesBuffer =
getIndexData( indexAttribute, indexBytes );
412 QString objectNamePrefix = layerName;
413 if ( objectNamePrefix != QString() )
414 objectNamePrefix += QString();
416 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + QStringLiteral(
"Flat_tile" ) ) );
417 mObjects.push_back(
object );
419 object->setSmoothEdges( mSmoothEdges );
420 object->setupTriangle( positionBuffer, indexesBuffer, transform->matrix() );
422 if ( mExportNormals )
425 QVector<float> normalsBuffer;
426 for (
int i = 0; i < positionBuffer.size(); i += 3 )
427 normalsBuffer << 0.0f << 1.0f << 0.0f;
428 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
431 Qt3DQAttribute *texCoordsAttribute = tileGeometry->texCoordAttribute();
432 if ( mExportTextures && texCoordsAttribute )
436 object->setupTextureCoordinates( texCoords );
438 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
439 const QImage img = textureImage->getImage();
440 object->setTextureImage( img );
444void Qgs3DSceneExporter::parseDemTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
450 DemTerrainTileGeometry *tileGeometry = qobject_cast<DemTerrainTileGeometry *>( geometry );
453 QgsDebugError(
"DemTerrainTileGeometry* is expected but something else was given" );
457 Qt3DQAttribute *positionAttribute = tileGeometry->positionAttribute();
458 const QByteArray positionBytes = positionAttribute->buffer()->data();
462 const QByteArray indexBytes = indexAttribute->buffer()->data();
463 const QVector<unsigned int> indexBuffer =
getIndexData( indexAttribute, indexBytes );
465 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( layerName + QStringLiteral(
"DEM_tile" ) ) );
466 mObjects.push_back(
object );
468 object->setSmoothEdges( mSmoothEdges );
469 object->setupTriangle( positionBuffer, indexBuffer, transform->matrix() );
471 Qt3DQAttribute *normalsAttributes = tileGeometry->normalAttribute();
472 if ( mExportNormals && normalsAttributes )
474 const QByteArray normalsBytes = normalsAttributes->buffer()->data();
476 object->setupNormalCoordinates( normalsBuffer, transform->matrix() );
479 Qt3DQAttribute *texCoordsAttribute = tileGeometry->texCoordsAttribute();
480 if ( mExportTextures && texCoordsAttribute )
482 const QByteArray texCoordsBytes = texCoordsAttribute->buffer()->data();
484 object->setupTextureCoordinates( texCoordsBuffer );
486 QgsTerrainTextureImage *textureImage = tileEntity->textureImage();
487 const QImage img = textureImage->getImage();
488 object->setTextureImage( img );
492void Qgs3DSceneExporter::parseMeshTile( QgsTerrainTileEntity *tileEntity,
const QString &layerName )
494 QString objectNamePrefix = layerName;
495 if ( objectNamePrefix != QString() )
496 objectNamePrefix += QLatin1Char(
'_' );
498 const QList<Qt3DRender::QGeometryRenderer *> renderers = tileEntity->findChildren<Qt3DRender::QGeometryRenderer *>();
499 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
501 Qgs3DExportObject *obj = processGeometryRenderer( renderer, objectNamePrefix );
508QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processInstancedPointGeometry( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
510 QVector<Qgs3DExportObject *> objects;
511 const QList<Qt3DQGeometry *> geometriesList = entity->findChildren<
Qt3DQGeometry *>();
514 Qt3DQAttribute *positionAttribute =
findAttribute( geometry, Qt3DQAttribute::defaultPositionAttributeName(), Qt3DQAttribute::VertexAttribute );
516 if ( !positionAttribute || !indexAttribute )
518 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position or index attribute!" ).arg( objectNamePrefix ) );
522 const QByteArray vertexBytes = positionAttribute->buffer()->data();
523 const QByteArray indexBytes = indexAttribute->buffer()->data();
524 if ( vertexBytes.isNull() || indexBytes.isNull() )
526 QgsDebugError( QString(
"Geometry for '%1' has position or index attribute with empty data!" ).arg( objectNamePrefix ) );
531 const QVector<uint> indexData =
getIndexData( indexAttribute, indexBytes );
534 if ( !instanceDataAttribute )
536 QgsDebugError( QString(
"Cannot export '%1' - geometry has no instanceData attribute!" ).arg( objectNamePrefix ) );
539 const QByteArray instancePositionBytes =
getData( instanceDataAttribute->buffer() );
540 if ( instancePositionBytes.isNull() )
542 QgsDebugError( QString(
"Geometry for '%1' has instanceData attribute with empty data!" ).arg( objectNamePrefix ) );
547 for (
int i = 0; i < instancePosition.size(); i += 3 )
549 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + QStringLiteral(
"instance_point" ) ) );
550 objects.push_back(
object );
551 QMatrix4x4 instanceTransform;
552 instanceTransform.translate( instancePosition[i], instancePosition[i + 1], instancePosition[i + 2] );
553 object->setupTriangle( positionData, indexData, instanceTransform );
555 object->setSmoothEdges( mSmoothEdges );
557 Qt3DQAttribute *normalsAttribute =
findAttribute( geometry, Qt3DQAttribute::defaultNormalAttributeName(), Qt3DQAttribute::VertexAttribute );
558 if ( mExportNormals && normalsAttribute )
562 object->setupNormalCoordinates( normalsData, instanceTransform );
570QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processSceneLoaderGeometries( Qt3DRender::QSceneLoader *sceneLoader,
const QString &objectNamePrefix )
572 QVector<Qgs3DExportObject *> objects;
573 Qt3DCore::QEntity *sceneLoaderParent = qobject_cast<Qt3DCore::QEntity *>( sceneLoader->parent() );
575 QMatrix4x4 sceneTransform;
576 if ( entityTransform )
578 sceneTransform = entityTransform->matrix();
580 QStringList entityNames = sceneLoader->entityNames();
581 for (
const QString &entityName : entityNames )
583 Qt3DRender::QGeometryRenderer *mesh = qobject_cast<Qt3DRender::QGeometryRenderer *>( sceneLoader->component( entityName, Qt3DRender::QSceneLoader::GeometryRendererComponent ) );
584 Qgs3DExportObject *
object = processGeometryRenderer( mesh, objectNamePrefix, sceneTransform );
587 objects.push_back(
object );
592Qgs3DExportObject *Qgs3DSceneExporter::processGeometryRenderer( Qt3DRender::QGeometryRenderer *geomRenderer,
const QString &objectNamePrefix,
const QMatrix4x4 &sceneTransform )
595 if ( geomRenderer->primitiveType() != Qt3DRender::QGeometryRenderer::Triangles )
610 QVector<std::pair<uint, uint>> triangleIndexStartingIndiceToKeep;
611 QgsTessellatedPolygonGeometry *tessGeom =
dynamic_cast<QgsTessellatedPolygonGeometry *
>( geometry );
614 QVector<QgsFeatureId> featureIds = tessGeom->
featureIds();
616 QSet<QgsFeatureId> tempFeatToAdd;
619 for (
int idx = 0; idx < featureIds.size(); idx++ )
622 if ( !mExportedFeatureIds.contains( feat ) )
625 tempFeatToAdd += feat;
628 const uint startIdx = triangleIndex[idx] * 3;
629 const uint endIdx = idx < triangleIndex.size() - 1 ? triangleIndex[idx + 1] * 3 : std::numeric_limits<uint>::max();
631 if ( startIdx < endIdx )
632 triangleIndexStartingIndiceToKeep.append( std::pair<uint, uint>( startIdx, endIdx ) );
635 mExportedFeatureIds += tempFeatToAdd;
637 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
644 QMatrix4x4 transformMatrix = sceneTransform;
645 QObject *parent = geomRenderer->parent();
648 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( parent );
652 transformMatrix = transform->matrix() * transformMatrix;
654 parent = parent->parent();
659 QByteArray indexBytes, vertexBytes;
660 QVector<uint> indexDataTmp;
661 QVector<uint> indexData;
662 QVector<float> positionData;
665 positionAttribute =
findAttribute( geometry, Qt3DQAttribute::defaultPositionAttributeName(), Qt3DQAttribute::VertexAttribute );
666 if ( !positionAttribute )
668 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
672 vertexBytes =
getData( positionAttribute->buffer() );
673 if ( vertexBytes.isNull() )
675 QgsDebugError( QString(
"Will not export '%1' as geometry has empty position data!" ).arg( objectNamePrefix ) );
682 QVector<Qt3DQAttribute *> attributes = geometry->attributes();
685 if ( attribute->attributeType() == Qt3DQAttribute::IndexAttribute )
687 indexAttribute = attribute;
688 indexBytes =
getData( indexAttribute->buffer() );
689 if ( indexBytes.isNull() )
691 QgsDebugError( QString(
"Geometry for '%1' has index attribute with empty data!" ).arg( objectNamePrefix ) );
695 indexDataTmp =
getIndexData( indexAttribute, indexBytes );
702 if ( !indexAttribute )
704 for ( uint i = 0; i < static_cast<uint>( positionData.size() / 3 ); ++i )
706 indexDataTmp.push_back( i );
711 if ( triangleIndexStartingIndiceToKeep.isEmpty() )
714 indexData.append( indexDataTmp );
720 const int triangleIndexStartingIndiceToKeepSize = triangleIndexStartingIndiceToKeep.size();
721 const uint indexDataTmpSize =
static_cast<uint
>( indexDataTmp.size() );
722 for ( uint i = 0; i < indexDataTmpSize; ++i )
724 uint idx = indexDataTmp[
static_cast<int>( i )];
726 while ( intervalIdx < triangleIndexStartingIndiceToKeepSize
727 && idx > triangleIndexStartingIndiceToKeep[intervalIdx].first
728 && idx >= triangleIndexStartingIndiceToKeep[intervalIdx].second )
734 if ( intervalIdx < triangleIndexStartingIndiceToKeepSize
735 && idx >= triangleIndexStartingIndiceToKeep[intervalIdx].first
736 && idx < triangleIndexStartingIndiceToKeep[intervalIdx].second )
738 indexData.push_back( idx );
744 Qgs3DExportObject *
object =
new Qgs3DExportObject( getObjectName( objectNamePrefix + QStringLiteral(
"mesh_geometry" ) ) );
745 object->setupTriangle( positionData, indexData, transformMatrix );
747 Qt3DQAttribute *normalsAttribute =
findAttribute( geometry, Qt3DQAttribute::defaultNormalAttributeName(), Qt3DQAttribute::VertexAttribute );
748 if ( mExportNormals && normalsAttribute )
752 object->setupNormalCoordinates( normalsData, transformMatrix );
755 Qt3DQAttribute *texCoordsAttribute =
findAttribute( geometry, Qt3DQAttribute::defaultTextureCoordinateAttributeName(), Qt3DQAttribute::VertexAttribute );
756 if ( mExportTextures && texCoordsAttribute )
760 object->setupTextureCoordinates( texCoordsData );
766QVector<Qgs3DExportObject *> Qgs3DSceneExporter::processLines( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
768 QVector<Qgs3DExportObject *> objs;
769 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
770 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
772 if ( renderer->primitiveType() != Qt3DRender::QGeometryRenderer::LineStripAdjacency )
775 Qt3DQAttribute *positionAttribute =
findAttribute( geom, Qt3DQAttribute::defaultPositionAttributeName(), Qt3DQAttribute::VertexAttribute );
777 if ( !positionAttribute || !indexAttribute )
779 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position or index attribute!" ).arg( objectNamePrefix ) );
783 const QByteArray vertexBytes =
getData( positionAttribute->buffer() );
784 const QByteArray indexBytes =
getData( indexAttribute->buffer() );
785 if ( vertexBytes.isNull() || indexBytes.isNull() )
787 QgsDebugError( QString(
"Geometry for '%1' has position or index attribute with empty data!" ).arg( objectNamePrefix ) );
792 Qgs3DExportObject *exportObject =
new Qgs3DExportObject( getObjectName( objectNamePrefix + QStringLiteral(
"line" ) ) );
795 objs.push_back( exportObject );
800Qgs3DExportObject *Qgs3DSceneExporter::processPoints( Qt3DCore::QEntity *entity,
const QString &objectNamePrefix )
802 QVector<float> points;
803 const QList<Qt3DRender::QGeometryRenderer *> renderers = entity->findChildren<Qt3DRender::QGeometryRenderer *>();
804 for ( Qt3DRender::QGeometryRenderer *renderer : renderers )
806 Qt3DQGeometry *geometry = qobject_cast<QgsBillboardGeometry *>( renderer->geometry() );
809 Qt3DQAttribute *positionAttribute =
findAttribute( geometry, Qt3DQAttribute::defaultPositionAttributeName(), Qt3DQAttribute::VertexAttribute );
810 if ( !positionAttribute )
812 QgsDebugError( QString(
"Cannot export '%1' - geometry has no position attribute!" ).arg( objectNamePrefix ) );
815 const QByteArray positionBytes =
getData( positionAttribute->buffer() );
816 if ( positionBytes.isNull() )
818 QgsDebugError( QString(
"Geometry for '%1' has position attribute with empty data!" ).arg( objectNamePrefix ) );
824 Qgs3DExportObject *obj =
new Qgs3DExportObject( getObjectName( objectNamePrefix + QStringLiteral(
"points" ) ) );
831 if ( mObjects.isEmpty() )
836 const QString objFilePath = QDir( sceneFolderPath ).filePath( sceneName + QStringLiteral(
".obj" ) );
837 const QString mtlFilePath = QDir( sceneFolderPath ).filePath( sceneName + QStringLiteral(
".mtl" ) );
839 QFile file( objFilePath );
840 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
842 QgsDebugError( QStringLiteral(
"Scene can not be exported to '%1'. File access error." ).arg( objFilePath ) );
845 QFile mtlFile( mtlFilePath );
846 if ( !mtlFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
848 QgsDebugError( QStringLiteral(
"Scene can not be exported to '%1'. File access error." ).arg( mtlFilePath ) );
852 float maxfloat = std::numeric_limits<float>::max(), minFloat = std::numeric_limits<float>::lowest();
853 float minX = maxfloat, minY = maxfloat, minZ = maxfloat, maxX = minFloat, maxY = minFloat, maxZ = minFloat;
856 obj->
objectBounds( minX, minY, minZ, maxX, maxY, maxZ );
859 float diffX = 1.0f, diffY = 1.0f, diffZ = 1.0f;
864 const float centerX = ( minX + maxX ) / 2.0f;
865 const float centerY = ( minY + maxY ) / 2.0f;
866 const float centerZ = ( minZ + maxZ ) / 2.0f;
868 const float scale = std::max( diffX, std::max( diffY, diffZ ) );
870 QTextStream out( &file );
872 const QString mtlLibName = sceneName +
".mtl";
873 out <<
"mtllib " << mtlLibName <<
"\n";
875 QTextStream mtlOut( &mtlFile );
881 const QString material = obj->
saveMaterial( mtlOut, sceneFolderPath );
882 out <<
"o " << obj->
name() <<
"\n";
883 if ( material != QString() )
884 out <<
"usemtl " << material <<
"\n";
885 obj->
saveTo( out,
scale / mScale, QVector3D( centerX, centerY, centerZ ), precision );
888 QgsDebugMsgLevel( QStringLiteral(
"Scene exported to '%1'" ).arg( objFilePath ), 2 );
892QString Qgs3DSceneExporter::getObjectName(
const QString &name )
895 if ( mUsedObjectNamesCounter.contains( name ) )
897 ret = QStringLiteral(
"%1%2" ).arg( name ).arg( mUsedObjectNamesCounter[name] );
898 mUsedObjectNamesCounter[name]++;
901 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.
Qt3DCore::QAttribute Qt3DQAttribute
Qt3DCore::QBuffer Qt3DQBuffer
QVector< T > getAttributeData(Qt3DQAttribute *attribute, const QByteArray &data)
Qt3DQAttribute * findAttribute(Qt3DQGeometry *geometry, const QString &name, Qt3DQAttribute::AttributeType type)
QVector< uint > getIndexData(Qt3DQAttribute *indexAttribute, const QByteArray &data)
QByteArray getData(Qt3DQBuffer *buffer)
QVector< uint > _getIndexDataImplementation(const QByteArray &data)
Component * findTypedComponent(Qt3DCore::QEntity *entity)
Qt3DCore::QGeometry Qt3DQGeometry
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)