26#include <Qt3DCore/QEntity>
28#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
29#include <Qt3DRender/QAttribute>
30#include <Qt3DRender/QBuffer>
31#include <Qt3DRender/QGeometry>
36#include <Qt3DCore/QAttribute>
37#include <Qt3DCore/QBuffer>
38#include <Qt3DCore/QGeometry>
44#include <Qt3DRender/QGeometryRenderer>
45#include <Qt3DRender/QTexture>
53static Qt3DQAttribute::VertexBaseType parseVertexBaseType(
int componentType )
55 switch ( componentType )
57 case TINYGLTF_COMPONENT_TYPE_BYTE:
58 return Qt3DQAttribute::Byte;
59 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
60 return Qt3DQAttribute::UnsignedByte;
61 case TINYGLTF_COMPONENT_TYPE_SHORT:
62 return Qt3DQAttribute::Short;
63 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
64 return Qt3DQAttribute::UnsignedShort;
65 case TINYGLTF_COMPONENT_TYPE_INT:
66 return Qt3DQAttribute::Int;
67 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
68 return Qt3DQAttribute::UnsignedInt;
69 case TINYGLTF_COMPONENT_TYPE_FLOAT:
70 return Qt3DQAttribute::Float;
71 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
72 return Qt3DQAttribute::Double;
75 return Qt3DQAttribute::UnsignedInt;
79static Qt3DRender::QAbstractTexture::Filter parseTextureFilter(
int filter )
83 case TINYGLTF_TEXTURE_FILTER_NEAREST:
84 return Qt3DRender::QTexture2D::Nearest;
85 case TINYGLTF_TEXTURE_FILTER_LINEAR:
86 return Qt3DRender::QTexture2D::Linear;
87 case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST:
88 return Qt3DRender::QTexture2D::NearestMipMapNearest;
89 case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST:
90 return Qt3DRender::QTexture2D::LinearMipMapNearest;
91 case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR:
92 return Qt3DRender::QTexture2D::NearestMipMapLinear;
93 case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR:
94 return Qt3DRender::QTexture2D::LinearMipMapLinear;
98 return Qt3DRender::QTexture2D::Nearest;
101static Qt3DRender::QTextureWrapMode::WrapMode parseTextureWrapMode(
int wrapMode )
105 case TINYGLTF_TEXTURE_WRAP_REPEAT:
106 return Qt3DRender::QTextureWrapMode::Repeat;
107 case TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE:
108 return Qt3DRender::QTextureWrapMode::ClampToEdge;
109 case TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT:
110 return Qt3DRender::QTextureWrapMode::MirroredRepeat;
114 return Qt3DRender::QTextureWrapMode::Repeat;
118static Qt3DQAttribute *parseAttribute( tinygltf::Model &model,
int accessorIndex )
120 tinygltf::Accessor &accessor = model.accessors[accessorIndex];
121 tinygltf::BufferView &bv = model.bufferViews[accessor.bufferView];
122 tinygltf::Buffer &b = model.buffers[bv.buffer];
125 QByteArray byteArray(
reinterpret_cast<const char *
>( b.data.data() ),
126 static_cast<int>( b.data.size() ) );
128 buffer->setData( byteArray );
133 if ( bv.target == TINYGLTF_TARGET_ARRAY_BUFFER )
134 attribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
135 else if ( bv.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER )
136 attribute->setAttributeType( Qt3DQAttribute::IndexAttribute );
138 attribute->setBuffer( buffer );
139 attribute->setByteOffset( bv.byteOffset + accessor.byteOffset );
140 attribute->setByteStride( bv.byteStride );
141 attribute->setCount( accessor.count );
142 attribute->setVertexBaseType( parseVertexBaseType( accessor.componentType ) );
143 attribute->setVertexSize( tinygltf::GetNumComponentsInType( accessor.type ) );
149static Qt3DQAttribute *reprojectPositions( tinygltf::Model &model,
int accessorIndex,
const QgsGltf3DUtils::EntityTransform &transform,
const QgsVector3D &tileTranslationEcef, QMatrix4x4 *matrix )
151 tinygltf::Accessor &accessor = model.accessors[accessorIndex];
153 QVector<double> vx, vy, vz;
154 bool res = QgsGltfUtils::accessorToMapCoordinates( model, accessorIndex, transform.tileTransform, transform.ecefToTargetCrs, tileTranslationEcef, matrix, transform.gltfUpAxis, vx, vy, vz );
158 QByteArray byteArray;
159 byteArray.resize( accessor.count * 4 * 3 );
160 float *out =
reinterpret_cast<float *
>( byteArray.data() );
162 QgsVector3D sceneOrigin = transform.sceneOriginTargetCrs;
163 for (
int i = 0; i < static_cast<int>( accessor.count ); ++i )
165 double x = vx[i] - sceneOrigin.
x();
166 double y = vy[i] - sceneOrigin.
y();
167 double z = ( vz[i] * transform.zValueScale ) + transform.zValueOffset - sceneOrigin.
z();
170 out[i * 3 + 0] =
static_cast< float >( x );
171 out[i * 3 + 1] =
static_cast< float >( z );
172 out[i * 3 + 2] =
static_cast< float >( -y );
176 buffer->setData( byteArray );
179 attribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
180 attribute->setBuffer( buffer );
181 attribute->setByteOffset( 0 );
182 attribute->setByteStride( 12 );
183 attribute->setCount( accessor.count );
184 attribute->setVertexBaseType( Qt3DQAttribute::Float );
185 attribute->setVertexSize( 3 );
194class TinyGltfTextureImageDataGenerator :
public Qt3DRender::QTextureImageDataGenerator
197 TinyGltfTextureImageDataGenerator( Qt3DRender::QTextureImageDataPtr imagePtr )
198 : mImagePtr( imagePtr ) {}
200 QT3D_FUNCTOR( TinyGltfTextureImageDataGenerator )
202 Qt3DRender::QTextureImageDataPtr operator()()
override
207 bool operator ==(
const QTextureImageDataGenerator &other )
const override
209 const TinyGltfTextureImageDataGenerator *otherFunctor = functor_cast<TinyGltfTextureImageDataGenerator>( &other );
210 return mImagePtr.get() == otherFunctor->mImagePtr.get();
213 Qt3DRender::QTextureImageDataPtr mImagePtr;
218class TinyGltfTextureImage :
public Qt3DRender::QAbstractTextureImage
222 TinyGltfTextureImage( tinygltf::Image &image )
224 Q_ASSERT( image.bits == 8 );
225 Q_ASSERT( image.component == 4 );
226 Q_ASSERT( image.pixel_type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE );
228 imgDataPtr.reset(
new Qt3DRender::QTextureImageData );
229 imgDataPtr->setWidth( image.width );
230 imgDataPtr->setHeight( image.height );
231 imgDataPtr->setDepth( 1 );
232 imgDataPtr->setFaces( 1 );
233 imgDataPtr->setLayers( 1 );
234 imgDataPtr->setMipLevels( 1 );
235 QByteArray imageBytes(
reinterpret_cast<const char *
>( image.image.data() ), image.image.size() );
236 imgDataPtr->setData( imageBytes, 4 );
237 imgDataPtr->setFormat( QOpenGLTexture::RGBA8_UNorm );
238 imgDataPtr->setPixelFormat( QOpenGLTexture::BGRA );
239 imgDataPtr->setPixelType( QOpenGLTexture::UInt8 );
240 imgDataPtr->setTarget( QOpenGLTexture::Target2D );
243 Qt3DRender::QTextureImageDataGeneratorPtr dataGenerator()
const override
245 return Qt3DRender::QTextureImageDataGeneratorPtr(
new TinyGltfTextureImageDataGenerator( imgDataPtr ) );
248 Qt3DRender::QTextureImageDataPtr imgDataPtr;
253static QByteArray fetchUri(
const QUrl &url, QStringList *errors )
255 if ( url.scheme().startsWith(
"http" ) )
257 QNetworkRequest request = QNetworkRequest( url );
258 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
259 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
265 *errors << QStringLiteral(
"Failed to download image: %1" ).arg( url.toString() );
273 else if ( url.isLocalFile() && QFile::exists( url.toLocalFile() ) )
275 QFile f( url.toLocalFile() );
276 if ( f.open( QIODevice::ReadOnly ) )
283 *errors << QStringLiteral(
"Unable to open image: %1" ).arg( url.toString() );
290static QgsMaterial *parseMaterial( tinygltf::Model &model,
int materialIndex, QString baseUri, QStringList *errors )
292 if ( materialIndex < 0 )
295 QgsMetalRoughMaterial *defaultMaterial =
new QgsMetalRoughMaterial;
296 defaultMaterial->setMetalness( 1 );
297 defaultMaterial->setRoughness( 1 );
298 defaultMaterial->setBaseColor( QColor::fromRgbF( 1, 1, 1 ) );
299 return defaultMaterial;
302 tinygltf::Material &material = model.materials[materialIndex];
303 tinygltf::PbrMetallicRoughness &pbr = material.pbrMetallicRoughness;
305 if ( pbr.baseColorTexture.index >= 0 )
307 tinygltf::Texture &tex = model.textures[pbr.baseColorTexture.index];
309 tinygltf::Image &img = model.images[tex.source];
311 if ( !img.uri.empty() )
313 QString imgUri = QString::fromStdString( img.uri );
314 QUrl url = QUrl( baseUri ).resolved( imgUri );
315 QByteArray ba = fetchUri( url, errors );
318 if ( !QgsGltfUtils::loadImageDataWithQImage( &img, -1,
nullptr,
nullptr, 0, 0, (
const unsigned char * ) ba.constData(), ba.size(),
nullptr ) )
321 *errors << QStringLiteral(
"Failed to load image: %1" ).arg( imgUri );
326 if ( img.image.empty() )
328 QgsMetalRoughMaterial *pbrMaterial =
new QgsMetalRoughMaterial;
329 pbrMaterial->setMetalness( pbr.metallicFactor );
330 pbrMaterial->setRoughness( pbr.roughnessFactor );
331 pbrMaterial->setBaseColor( QColor::fromRgbF( pbr.baseColorFactor[0], pbr.baseColorFactor[1], pbr.baseColorFactor[2], pbr.baseColorFactor[3] ) );
335 TinyGltfTextureImage *textureImage =
new TinyGltfTextureImage( img );
337 Qt3DRender::QTexture2D *texture =
new Qt3DRender::QTexture2D;
338 texture->addTextureImage( textureImage );
341 texture->setMinificationFilter( Qt3DRender::QTexture2D::Linear );
342 texture->setMagnificationFilter( Qt3DRender::QTexture2D::Linear );
344 if ( tex.sampler >= 0 )
346 tinygltf::Sampler &sampler = model.samplers[tex.sampler];
347 if ( sampler.minFilter >= 0 )
348 texture->setMinificationFilter( parseTextureFilter( sampler.minFilter ) );
349 if ( sampler.magFilter >= 0 )
350 texture->setMagnificationFilter( parseTextureFilter( sampler.magFilter ) );
351 Qt3DRender::QTextureWrapMode wrapMode;
352 wrapMode.setX( parseTextureWrapMode( sampler.wrapS ) );
353 wrapMode.setY( parseTextureWrapMode( sampler.wrapT ) );
354 texture->setWrapMode( wrapMode );
360 QgsTextureMaterial *mat =
new QgsTextureMaterial;
361 mat->setTexture( texture );
368 QgsMetalRoughMaterial *pbrMaterial =
new QgsMetalRoughMaterial;
369 pbrMaterial->setMetalness( pbr.metallicFactor );
370 pbrMaterial->setRoughness( pbr.roughnessFactor );
371 pbrMaterial->setBaseColor( QColor::fromRgbF( pbr.baseColorFactor[0], pbr.baseColorFactor[1], pbr.baseColorFactor[2], pbr.baseColorFactor[3] ) );
376static QVector<Qt3DCore::QEntity *> parseNode( tinygltf::Model &model,
int nodeIndex,
const QgsGltf3DUtils::EntityTransform &transform,
const QgsVector3D &tileTranslationEcef, QString baseUri, QMatrix4x4 parentTransform, QStringList *errors )
378 tinygltf::Node &node = model.nodes[nodeIndex];
380 QVector<Qt3DCore::QEntity *> entities;
383 std::unique_ptr<QMatrix4x4> matrix = QgsGltfUtils::parseNodeTransform( node );
384 if ( !parentTransform.isIdentity() )
387 *matrix = parentTransform * *matrix;
390 matrix.reset(
new QMatrix4x4( parentTransform ) );
395 if ( node.mesh >= 0 )
397 tinygltf::Mesh &mesh = model.meshes[node.mesh];
399 for (
const tinygltf::Primitive &primitive : mesh.primitives )
401 if ( primitive.mode != TINYGLTF_MODE_TRIANGLES )
404 *errors << QStringLiteral(
"Unsupported mesh primitive: %1" ).arg( primitive.mode );
408 auto posIt = primitive.attributes.find(
"POSITION" );
409 Q_ASSERT( posIt != primitive.attributes.end() );
410 int positionAccessorIndex = posIt->second;
412 tinygltf::Accessor &posAccessor = model.accessors[positionAccessorIndex];
413 if ( posAccessor.componentType != TINYGLTF_PARAMETER_TYPE_FLOAT || posAccessor.type != TINYGLTF_TYPE_VEC3 )
416 *errors << QStringLiteral(
"Unsupported position accessor type: %1 / %2" ).arg( posAccessor.componentType ).arg( posAccessor.type );
420 QgsMaterial *material = parseMaterial( model, primitive.material, baseUri, errors );
429 Qt3DQAttribute *positionAttribute = reprojectPositions( model, positionAccessorIndex, transform, tileTranslationEcef, matrix.get() );
430 positionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() );
431 geom->addAttribute( positionAttribute );
433 auto normalIt = primitive.attributes.find(
"NORMAL" );
434 if ( normalIt != primitive.attributes.end() )
436 int normalAccessorIndex = normalIt->second;
437 Qt3DQAttribute *normalAttribute = parseAttribute( model, normalAccessorIndex );
438 normalAttribute->setName( Qt3DQAttribute::defaultNormalAttributeName() );
439 geom->addAttribute( normalAttribute );
445 auto texIt = primitive.attributes.find(
"TEXCOORD_0" );
446 if ( texIt != primitive.attributes.end() )
448 int texAccessorIndex = texIt->second;
449 Qt3DQAttribute *texAttribute = parseAttribute( model, texAccessorIndex );
450 texAttribute->setName( Qt3DQAttribute::defaultTextureCoordinateAttributeName() );
451 geom->addAttribute( texAttribute );
455 if ( primitive.indices != -1 )
457 indexAttribute = parseAttribute( model, primitive.indices );
458 geom->addAttribute( indexAttribute );
461 Qt3DRender::QGeometryRenderer *geomRenderer =
new Qt3DRender::QGeometryRenderer;
462 geomRenderer->setGeometry( geom );
463 geomRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
464 geomRenderer->setVertexCount( indexAttribute ? indexAttribute->count() : model.accessors[positionAccessorIndex].count );
468 if ( normalIt == primitive.attributes.end() )
470 if ( QgsMetalRoughMaterial *pbrMat = qobject_cast<QgsMetalRoughMaterial *>( material ) )
472 pbrMat->setFlatShadingEnabled(
true );
476 Qt3DCore::QEntity *primitiveEntity =
new Qt3DCore::QEntity;
477 primitiveEntity->addComponent( geomRenderer );
478 primitiveEntity->addComponent( material );
479 entities << primitiveEntity;
484 for (
int childNodeIndex : node.children )
486 entities << parseNode( model, childNodeIndex, transform, tileTranslationEcef, baseUri, matrix ? *matrix : QMatrix4x4(), errors );
493Qt3DCore::QEntity *QgsGltf3DUtils::parsedGltfToEntity( tinygltf::Model &model,
const QgsGltf3DUtils::EntityTransform &transform, QString baseUri, QStringList *errors )
495 bool sceneOk =
false;
496 const std::size_t sceneIndex = QgsGltfUtils::sourceSceneForModel( model, sceneOk );
500 *errors <<
"No scenes present in the gltf data!";
504 tinygltf::Scene &scene = model.scenes[sceneIndex];
506 if ( scene.nodes.size() == 0 )
509 *errors <<
"No nodes present in the gltf data!";
513 const QgsVector3D tileTranslationEcef = QgsGltfUtils::extractTileTranslation( model );
515 Qt3DCore::QEntity *rootEntity =
new Qt3DCore::QEntity;
516 for (
const int nodeIndex : scene.nodes )
518 const QVector<Qt3DCore::QEntity *> entities = parseNode( model, nodeIndex, transform, tileTranslationEcef, baseUri, QMatrix4x4(), errors );
519 for ( Qt3DCore::QEntity *e : entities )
520 e->setParent( rootEntity );
526Qt3DCore::QEntity *QgsGltf3DUtils::gltfToEntity(
const QByteArray &data,
const QgsGltf3DUtils::EntityTransform &transform,
const QString &baseUri, QStringList *errors )
528 tinygltf::Model model;
529 QString gltfErrors, gltfWarnings;
531 bool res = QgsGltfUtils::loadGltfModel( data, model, &gltfErrors, &gltfWarnings );
532 if ( !gltfErrors.isEmpty() )
534 QgsDebugError( QStringLiteral(
"Error raised reading %1: %2" ).arg( baseUri, gltfErrors ) );
536 if ( !gltfWarnings.isEmpty() )
538 QgsDebugError( QStringLiteral(
"Warnings raised reading %1: %2" ).arg( baseUri, gltfWarnings ) );
544 errors->append( QStringLiteral(
"GLTF load error: " ) + gltfErrors );
549 return parsedGltfToEntity( model, transform, baseUri, errors );
553#include "qgsgltf3dutils.moc"
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr, RequestFlags requestFlags=QgsBlockingNetworkRequest::RequestFlags())
Performs a "get" operation on the specified request.
@ NoError
No error was encountered.
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get(), post(), head() or put() request has been mad...
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
QByteArray content() const
Returns the reply content.
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
double x() const
Returns X coordinate.
#define Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Qt3DCore::QAttribute Qt3DQAttribute
Qt3DCore::QBuffer Qt3DQBuffer
Qt3DCore::QGeometry Qt3DQGeometry
bool operator==(const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2)
Qt3DCore::QAttribute Qt3DQAttribute
Qt3DCore::QBuffer Qt3DQBuffer
Qt3DCore::QGeometry Qt3DQGeometry
#define QgsDebugError(str)