35#include <Qt3DCore/QAttribute>
36#include <Qt3DCore/QBuffer>
37#include <Qt3DCore/QEntity>
38#include <Qt3DCore/QGeometry>
39#include <Qt3DRender/QGeometryRenderer>
40#include <Qt3DRender/QTexture>
42using namespace Qt::StringLiterals;
46static Qt3DCore::QAttribute::VertexBaseType parseVertexBaseType(
int componentType )
48 switch ( componentType )
50 case TINYGLTF_COMPONENT_TYPE_BYTE:
51 return Qt3DCore::QAttribute::Byte;
52 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
53 return Qt3DCore::QAttribute::UnsignedByte;
54 case TINYGLTF_COMPONENT_TYPE_SHORT:
55 return Qt3DCore::QAttribute::Short;
56 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
57 return Qt3DCore::QAttribute::UnsignedShort;
58 case TINYGLTF_COMPONENT_TYPE_INT:
59 return Qt3DCore::QAttribute::Int;
60 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
61 return Qt3DCore::QAttribute::UnsignedInt;
62 case TINYGLTF_COMPONENT_TYPE_FLOAT:
63 return Qt3DCore::QAttribute::Float;
64 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
65 return Qt3DCore::QAttribute::Double;
68 return Qt3DCore::QAttribute::UnsignedInt;
72static Qt3DRender::QAbstractTexture::Filter parseTextureFilter(
int filter )
76 case TINYGLTF_TEXTURE_FILTER_NEAREST:
77 return Qt3DRender::QTexture2D::Nearest;
78 case TINYGLTF_TEXTURE_FILTER_LINEAR:
79 return Qt3DRender::QTexture2D::Linear;
80 case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST:
81 return Qt3DRender::QTexture2D::NearestMipMapNearest;
82 case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST:
83 return Qt3DRender::QTexture2D::LinearMipMapNearest;
84 case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR:
85 return Qt3DRender::QTexture2D::NearestMipMapLinear;
86 case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR:
87 return Qt3DRender::QTexture2D::LinearMipMapLinear;
91 return Qt3DRender::QTexture2D::Nearest;
94static Qt3DRender::QTextureWrapMode::WrapMode parseTextureWrapMode(
int wrapMode )
98 case TINYGLTF_TEXTURE_WRAP_REPEAT:
99 return Qt3DRender::QTextureWrapMode::Repeat;
100 case TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE:
101 return Qt3DRender::QTextureWrapMode::ClampToEdge;
102 case TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT:
103 return Qt3DRender::QTextureWrapMode::MirroredRepeat;
107 return Qt3DRender::QTextureWrapMode::Repeat;
111static Qt3DCore::QAttribute *parseAttribute( tinygltf::Model &model,
int accessorIndex )
113 tinygltf::Accessor &accessor = model.accessors[accessorIndex];
114 tinygltf::BufferView &bv = model.bufferViews[accessor.bufferView];
115 tinygltf::Buffer &b = model.buffers[bv.buffer];
118 QByteArray byteArray(
reinterpret_cast<const char *
>( b.data.data() ),
119 static_cast<int>( b.data.size() ) );
120 Qt3DCore::QBuffer *buffer =
new Qt3DCore::QBuffer();
121 buffer->setData( byteArray );
123 Qt3DCore::QAttribute *attribute =
new Qt3DCore::QAttribute();
126 if ( bv.target == TINYGLTF_TARGET_ARRAY_BUFFER )
127 attribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
128 else if ( bv.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER )
129 attribute->setAttributeType( Qt3DCore::QAttribute::IndexAttribute );
131 attribute->setBuffer( buffer );
132 attribute->setByteOffset( bv.byteOffset + accessor.byteOffset );
133 attribute->setByteStride( bv.byteStride );
134 attribute->setCount( accessor.count );
135 attribute->setVertexBaseType( parseVertexBaseType( accessor.componentType ) );
136 attribute->setVertexSize( tinygltf::GetNumComponentsInType( accessor.type ) );
142static Qt3DCore::QAttribute *reprojectPositions( tinygltf::Model &model,
int accessorIndex,
const QgsGltf3DUtils::EntityTransform &transform,
const QgsVector3D &tileTranslationEcef, QMatrix4x4 *matrix )
144 tinygltf::Accessor &accessor = model.accessors[accessorIndex];
146 QVector<double> vx, vy, vz;
147 bool res = QgsGltfUtils::accessorToMapCoordinates( model, accessorIndex, transform.tileTransform, transform.ecefToTargetCrs, tileTranslationEcef, matrix, transform.gltfUpAxis, vx, vy, vz );
151 QByteArray byteArray;
152 byteArray.resize( accessor.count * 4 * 3 );
153 float *out =
reinterpret_cast<float *
>( byteArray.data() );
155 QgsVector3D sceneOrigin = transform.chunkOriginTargetCrs;
156 for (
int i = 0; i < static_cast<int>( accessor.count ); ++i )
158 double x = vx[i] - sceneOrigin.
x();
159 double y = vy[i] - sceneOrigin.
y();
160 double z = ( vz[i] * transform.zValueScale ) + transform.zValueOffset - sceneOrigin.
z();
162 out[i * 3 + 0] =
static_cast<float>( x );
163 out[i * 3 + 1] =
static_cast<float>( y );
164 out[i * 3 + 2] =
static_cast<float>( z );
167 Qt3DCore::QBuffer *buffer =
new Qt3DCore::QBuffer();
168 buffer->setData( byteArray );
170 Qt3DCore::QAttribute *attribute =
new Qt3DCore::QAttribute();
171 attribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
172 attribute->setBuffer( buffer );
173 attribute->setByteOffset( 0 );
174 attribute->setByteStride( 12 );
175 attribute->setCount( accessor.count );
176 attribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
177 attribute->setVertexSize( 3 );
182class TinyGltfTextureImageDataGenerator :
public Qt3DRender::QTextureImageDataGenerator
185 TinyGltfTextureImageDataGenerator( Qt3DRender::QTextureImageDataPtr imagePtr )
186 : mImagePtr( imagePtr )
189 Qt3DRender::QTextureImageDataPtr operator()()
override {
return mImagePtr; }
191 qintptr id()
const override {
return reinterpret_cast<qintptr
>( &Qt3DCore::FunctorType<TinyGltfTextureImageDataGenerator>::id ); }
193 bool operator==(
const QTextureImageDataGenerator &other )
const override
195 const TinyGltfTextureImageDataGenerator *otherFunctor =
dynamic_cast<const TinyGltfTextureImageDataGenerator *
>( &other );
196 return otherFunctor && mImagePtr.get() == otherFunctor->mImagePtr.get();
199 Qt3DRender::QTextureImageDataPtr mImagePtr;
202class TinyGltfTextureImage :
public Qt3DRender::QAbstractTextureImage
206 TinyGltfTextureImage( tinygltf::Image &image )
208 Q_ASSERT( image.bits == 8 );
209 Q_ASSERT( image.component == 4 );
210 Q_ASSERT( image.pixel_type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE );
212 imgDataPtr.reset(
new Qt3DRender::QTextureImageData );
213 imgDataPtr->setWidth( image.width );
214 imgDataPtr->setHeight( image.height );
215 imgDataPtr->setDepth( 1 );
216 imgDataPtr->setFaces( 1 );
217 imgDataPtr->setLayers( 1 );
218 imgDataPtr->setMipLevels( 1 );
219 QByteArray imageBytes(
reinterpret_cast<const char *
>( image.image.data() ), image.image.size() );
220 imgDataPtr->setData( imageBytes, 4 );
221 imgDataPtr->setFormat( QOpenGLTexture::RGBA8_UNorm );
222 imgDataPtr->setPixelFormat( QOpenGLTexture::BGRA );
223 imgDataPtr->setPixelType( QOpenGLTexture::UInt8 );
224 imgDataPtr->setTarget( QOpenGLTexture::Target2D );
227 Qt3DRender::QTextureImageDataGeneratorPtr dataGenerator()
const override {
return Qt3DRender::QTextureImageDataGeneratorPtr(
new TinyGltfTextureImageDataGenerator( imgDataPtr ) ); }
229 Qt3DRender::QTextureImageDataPtr imgDataPtr;
234static QByteArray fetchUri(
const QUrl &url, QStringList *errors )
236 if ( url.scheme().startsWith(
"http" ) )
238 QNetworkRequest request = QNetworkRequest( url );
239 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
240 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
246 *errors << u
"Failed to download image: %1"_s.arg( url.toString() );
254 else if ( url.isLocalFile() )
256 QString localFilePath = url.toLocalFile();
257 if ( localFilePath.contains(
".slpk/" ) )
259 const QStringList parts = localFilePath.split( u
".slpk/"_s );
260 if ( parts.size() == 2 )
262 QString slpkPath = parts[0] +
".slpk";
263 QString imagePath = parts[1];
265 QByteArray imageData;
273 *errors << u
"Unable to extract image '%1' from SLPK archive: %2"_s.arg( imagePath ).arg( slpkPath );
279 *errors << u
"Missing image path in SLPK archive: %1"_s.arg( localFilePath );
282 else if ( QFile::exists( localFilePath ) )
284 QFile f( localFilePath );
285 if ( f.open( QIODevice::ReadOnly ) )
293 *errors << u
"Unable to open image: %1"_s.arg( url.toString() );
300static std::unique_ptr<QgsMaterial> parseMaterial( tinygltf::Model &model,
int materialIndex, QString baseUri, QStringList *errors,
const QgsMaterialContext &context )
302 if ( materialIndex < 0 )
305 auto defaultMaterial = std::make_unique<QgsMetalRoughMaterial>();
306 defaultMaterial->setMetalness( 1 );
307 defaultMaterial->setRoughness( 1 );
308 defaultMaterial->setBaseColor( QColor::fromRgbF( 1, 1, 1 ) );
309 return defaultMaterial;
312 tinygltf::Material &material = model.materials[materialIndex];
313 tinygltf::PbrMetallicRoughness &pbr = material.pbrMetallicRoughness;
315 if ( pbr.baseColorTexture.index >= 0 )
317 tinygltf::Texture &tex = model.textures[pbr.baseColorTexture.index];
320 if ( tex.source < 0 )
322 auto pbrMaterial = std::make_unique<QgsMetalRoughMaterial>();
323 pbrMaterial->setMetalness( pbr.metallicFactor );
324 pbrMaterial->setRoughness( pbr.roughnessFactor );
325 pbrMaterial->setBaseColor( QColor::fromRgbF( pbr.baseColorFactor[0], pbr.baseColorFactor[1], pbr.baseColorFactor[2], pbr.baseColorFactor[3] ) );
329 tinygltf::Image &img = model.images[tex.source];
331 if ( !img.uri.empty() )
333 QString imgUri = QString::fromStdString( img.uri );
334 QUrl url = QUrl( baseUri ).resolved( imgUri );
335 QByteArray ba = fetchUri( url, errors );
338 if ( !QgsGltfUtils::loadImageDataWithQImage( &img, -1,
nullptr,
nullptr, 0, 0, (
const unsigned char * ) ba.constData(), ba.size(),
nullptr ) )
341 *errors << u
"Failed to load image: %1"_s.arg( imgUri );
346 if ( img.image.empty() )
348 auto pbrMaterial = std::make_unique<QgsMetalRoughMaterial>();
349 pbrMaterial->setMetalness( pbr.metallicFactor );
350 pbrMaterial->setRoughness( pbr.roughnessFactor );
351 pbrMaterial->setBaseColor( QColor::fromRgbF( pbr.baseColorFactor[0], pbr.baseColorFactor[1], pbr.baseColorFactor[2], pbr.baseColorFactor[3] ) );
355 TinyGltfTextureImage *textureImage =
new TinyGltfTextureImage( img );
357 Qt3DRender::QTexture2D *texture =
new Qt3DRender::QTexture2D;
358 texture->addTextureImage( textureImage );
362 texture->setFormat( Qt3DRender::QAbstractTexture::SRGB8_Alpha8 );
364 if ( tex.sampler >= 0 )
366 tinygltf::Sampler &sampler = model.samplers[tex.sampler];
367 if ( sampler.minFilter >= 0 )
368 texture->setMinificationFilter( parseTextureFilter( sampler.minFilter ) );
369 if ( sampler.magFilter >= 0 )
370 texture->setMagnificationFilter( parseTextureFilter( sampler.magFilter ) );
371 Qt3DRender::QTextureWrapMode wrapMode;
372 wrapMode.setX( parseTextureWrapMode( sampler.wrapS ) );
373 wrapMode.setY( parseTextureWrapMode( sampler.wrapT ) );
374 texture->setWrapMode( wrapMode );
380 auto mat = std::make_unique<QgsTextureMaterial>();
381 mat->setTexture( texture );
388 auto pbrMaterial = std::make_unique<QgsMetalRoughMaterial>();
389 pbrMaterial->setMetalness( pbr.metallicFactor );
390 pbrMaterial->setRoughness( pbr.roughnessFactor );
391 pbrMaterial->setBaseColor( QColor::fromRgbF( pbr.baseColorFactor[0], pbr.baseColorFactor[1], pbr.baseColorFactor[2], pbr.baseColorFactor[3] ) );
396static QVector<Qt3DCore::QEntity *> parseNode(
397 tinygltf::Model &model,
399 const QgsGltf3DUtils::EntityTransform &transform,
402 QMatrix4x4 parentTransform,
408 tinygltf::Node &node = model.nodes[nodeIndex];
410 QVector<Qt3DCore::QEntity *> entities;
413 std::unique_ptr<QMatrix4x4> matrix = QgsGltfUtils::parseNodeTransform( node );
414 if ( !parentTransform.isIdentity() )
417 *matrix = parentTransform * *matrix;
420 matrix = std::make_unique<QMatrix4x4>( parentTransform );
425 if ( node.mesh >= 0 && node.extensions.find(
"EXT_mesh_gpu_instancing" ) == node.extensions.end() )
427 tinygltf::Mesh &mesh = model.meshes[node.mesh];
429 for (
const tinygltf::Primitive &primitive : mesh.primitives )
431 if ( primitive.mode != TINYGLTF_MODE_TRIANGLES )
434 *errors << u
"Unsupported mesh primitive: %1"_s.arg( primitive.mode );
438 auto posIt = primitive.attributes.find(
"POSITION" );
439 Q_ASSERT( posIt != primitive.attributes.end() );
440 int positionAccessorIndex = posIt->second;
442 tinygltf::Accessor &posAccessor = model.accessors[positionAccessorIndex];
443 if ( posAccessor.componentType != TINYGLTF_PARAMETER_TYPE_FLOAT || posAccessor.type != TINYGLTF_TYPE_VEC3 )
446 *errors << u
"Unsupported position accessor type: %1 / %2"_s.arg( posAccessor.componentType ).arg( posAccessor.type );
450 std::unique_ptr<QgsMaterial> material = parseMaterial( model, primitive.material, baseUri, errors, materialContext );
457 Qt3DCore::QGeometry *geom =
new Qt3DCore::QGeometry;
459 Qt3DCore::QAttribute *positionAttribute = reprojectPositions( model, positionAccessorIndex, transform, tileTranslationEcef, matrix.get() );
460 positionAttribute->setName( Qt3DCore::QAttribute::defaultPositionAttributeName() );
461 geom->addAttribute( positionAttribute );
463 auto normalIt = primitive.attributes.find(
"NORMAL" );
464 if ( normalIt != primitive.attributes.end() )
466 int normalAccessorIndex = normalIt->second;
467 Qt3DCore::QAttribute *normalAttribute = parseAttribute( model, normalAccessorIndex );
468 normalAttribute->setName( Qt3DCore::QAttribute::defaultNormalAttributeName() );
469 geom->addAttribute( normalAttribute );
475 auto texIt = primitive.attributes.find(
"TEXCOORD_0" );
476 if ( texIt != primitive.attributes.end() )
478 int texAccessorIndex = texIt->second;
479 Qt3DCore::QAttribute *texAttribute = parseAttribute( model, texAccessorIndex );
480 texAttribute->setName( Qt3DCore::QAttribute::defaultTextureCoordinateAttributeName() );
481 geom->addAttribute( texAttribute );
484 Qt3DCore::QAttribute *indexAttribute =
nullptr;
485 if ( primitive.indices != -1 )
487 indexAttribute = parseAttribute( model, primitive.indices );
488 geom->addAttribute( indexAttribute );
491 Qt3DRender::QGeometryRenderer *geomRenderer =
new Qt3DRender::QGeometryRenderer;
492 geomRenderer->setGeometry( geom );
493 geomRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
494 geomRenderer->setVertexCount( indexAttribute ? indexAttribute->count() : model.accessors[positionAccessorIndex].count );
498 if ( normalIt == primitive.attributes.end() )
500 if ( QgsMetalRoughMaterial *pbrMat = qobject_cast<QgsMetalRoughMaterial *>( material.get() ) )
502 pbrMat->setFlatShadingEnabled(
true );
506 Qt3DCore::QEntity *primitiveEntity =
new Qt3DCore::QEntity;
507 primitiveEntity->addComponent( geomRenderer );
508 primitiveEntity->addComponent( material.release() );
509 entities << primitiveEntity;
514 for (
int childNodeIndex : node.children )
516 entities << parseNode( model, childNodeIndex, transform, tileTranslationEcef, baseUri, matrix ? *matrix : QMatrix4x4(), context, errors );
526static QgsMatrix4x4 floatToDoubleMatrix(
const QMatrix4x4 &fm )
529 return QgsMatrix4x4( f[0], f[4], f[8], f[12], f[1], f[5], f[9], f[13], f[2], f[6], f[10], f[14], f[3], f[7], f[11], f[15] );
536static QMatrix3x3 matrixFromColumns(
const QVector3D &col0,
const QVector3D &col1,
const QVector3D &col2 )
550 return QMatrix3x3( d );
559static QMatrix3x3 extractRotation3x3(
const QgsMatrix4x4 &matrix, QVector3D &scale )
563 const QVector3D col0(
static_cast<float>( md[0] ),
static_cast<float>( md[1] ),
static_cast<float>( md[2] ) );
564 const QVector3D col1(
static_cast<float>( md[4] ),
static_cast<float>( md[5] ),
static_cast<float>( md[6] ) );
565 const QVector3D col2(
static_cast<float>( md[8] ),
static_cast<float>( md[9] ),
static_cast<float>( md[10] ) );
567 const float sx = col0.length();
568 const float sy = col1.length();
569 const float sz = col2.length();
570 scale = QVector3D( sx, sy, sz );
572 if ( sx == 0 || sy == 0 || sz == 0 )
575 return matrixFromColumns( col0 / sx, col1 / sy, col2 / sz );
585 const double x = ecefPos.
x(), y = ecefPos.
y(), z = ecefPos.
z();
586 const double len = std::sqrt( x * x + y * y + z * z );
587 const QgsVector3D upEcef( x / len, y / len, z / len );
589 const double eastLenXY = std::sqrt( y * y + x * x );
590 const QgsVector3D eastEcef( -y / eastLenXY, x / eastLenXY, 0 );
596 constexpr double delta = 1.0;
597 double eX = x + delta * eastEcef.x(), eY = y + delta * eastEcef.y(), eZ = z + delta * eastEcef.z();
598 double nX = x + delta * northEcef.
x(), nY = y + delta * northEcef.
y(), nZ = z + delta * northEcef.
z();
599 double uX = x + delta * upEcef.x(), uY = y + delta * upEcef.y(), uZ = z + delta * upEcef.z();
613 QgsVector3D eastCrs( eX - mapPos.
x(), eY - mapPos.
y(), eZ - mapPos.
z() );
614 QgsVector3D northCrs( nX - mapPos.
x(), nY - mapPos.
y(), nZ - mapPos.
z() );
615 QgsVector3D upCrs( uX - mapPos.
x(), uY - mapPos.
y(), uZ - mapPos.
z() );
617 northCrs.normalize();
624 const QMatrix3x3 ecefBasis = matrixFromColumns( eastEcef.toVector3D(), northEcef.
toVector3D(), upEcef.toVector3D() );
625 const QMatrix3x3 crsBasis = matrixFromColumns( eastCrs.toVector3D(), northCrs.toVector3D(), upCrs.toVector3D() );
626 return crsBasis * ecefBasis.transposed();
630QVector<QgsGltf3DUtils::InstanceChunkTransform> QgsGltf3DUtils::tileSpaceToChunkLocal(
const QgsGltfUtils::InstancedPrimitive &primitive,
const QgsGltf3DUtils::EntityTransform &transform )
632 QVector<InstanceChunkTransform> result;
633 result.resize( primitive.instanceTransforms.size() );
635 if ( primitive.instanceTransforms.isEmpty() )
638 const QgsVector3D sceneOrigin = transform.chunkOriginTargetCrs;
642 QMatrix3x3 correctionMatrix;
643 bool hasCorrectionMatrix =
false;
645 for (
int i = 0; i < primitive.instanceTransforms.size(); ++i )
649 const QgsMatrix4x4 instanceDouble = floatToDoubleMatrix( primitive.instanceTransforms[i] );
650 const QgsMatrix4x4 ecefMatrix = transform.tileTransform * instanceDouble;
653 const double *md = ecefMatrix.
constData();
654 const QgsVector3D ecefPos( md[12], md[13], md[14] );
657 double mapX = ecefPos.
x();
658 double mapY = ecefPos.
y();
659 double mapZ = ecefPos.
z();
660 if ( transform.ecefToTargetCrs )
664 transform.ecefToTargetCrs->transformInPlace( mapX, mapY, mapZ );
673 if ( !hasCorrectionMatrix && transform.ecefToTargetCrs )
675 hasCorrectionMatrix =
true;
676 correctionMatrix = ecefToTargetCrsRotationCorrection( ecefPos,
QgsVector3D( mapX, mapY, mapZ ), *transform.ecefToTargetCrs );
680 mapZ = mapZ * transform.zValueScale + transform.zValueOffset;
683 result[i].translation = QVector3D(
static_cast<float>( mapX - sceneOrigin.
x() ),
static_cast<float>( mapY - sceneOrigin.
y() ),
static_cast<float>( mapZ - sceneOrigin.
z() ) );
687 QMatrix3x3 rotEcef = extractRotation3x3( ecefMatrix, scale );
689 result[i].scale = scale;
691 if ( scale.x() == 0 || scale.y() == 0 || scale.z() == 0 )
693 result[i].rotation = QQuaternion();
698 const QMatrix3x3 rotCrs = hasCorrectionMatrix ? correctionMatrix * rotEcef : rotEcef;
699 result[i].rotation = QQuaternion::fromRotationMatrix( rotCrs );
706void QgsGltf3DUtils::createInstanceBuffer( Qt3DCore::QGeometry *geometry,
const QVector<InstanceChunkTransform> &instances )
708 const int stride = 10 *
sizeof( float );
709 QByteArray bufferData;
710 bufferData.resize( instances.size() * stride );
711 float *dst =
reinterpret_cast<float *
>( bufferData.data() );
713 for (
const auto &inst : instances )
716 *dst++ = inst.translation.x();
717 *dst++ = inst.translation.y();
718 *dst++ = inst.translation.z();
720 *dst++ = inst.rotation.x();
721 *dst++ = inst.rotation.y();
722 *dst++ = inst.rotation.z();
723 *dst++ = inst.rotation.scalar();
725 *dst++ = inst.scale.x();
726 *dst++ = inst.scale.y();
727 *dst++ = inst.scale.z();
730 Qt3DCore::QBuffer *buffer =
new Qt3DCore::QBuffer;
731 buffer->setData( bufferData );
734 Qt3DCore::QAttribute *transAttr =
new Qt3DCore::QAttribute;
735 transAttr->setName( u
"instanceTranslation"_s );
736 transAttr->setVertexBaseType( Qt3DCore::QAttribute::Float );
737 transAttr->setVertexSize( 3 );
738 transAttr->setByteStride( stride );
739 transAttr->setByteOffset( 0 );
740 transAttr->setDivisor( 1 );
741 transAttr->setCount( instances.size() );
742 transAttr->setBuffer( buffer );
743 geometry->addAttribute( transAttr );
746 Qt3DCore::QAttribute *rotAttr =
new Qt3DCore::QAttribute;
747 rotAttr->setName( u
"instanceRotation"_s );
748 rotAttr->setVertexBaseType( Qt3DCore::QAttribute::Float );
749 rotAttr->setVertexSize( 4 );
750 rotAttr->setByteStride( stride );
751 rotAttr->setByteOffset( 3 *
sizeof(
float ) );
752 rotAttr->setDivisor( 1 );
753 rotAttr->setCount( instances.size() );
754 rotAttr->setBuffer( buffer );
755 geometry->addAttribute( rotAttr );
758 Qt3DCore::QAttribute *scaleAttr =
new Qt3DCore::QAttribute;
759 scaleAttr->setName( u
"instanceScale"_s );
760 scaleAttr->setVertexBaseType( Qt3DCore::QAttribute::Float );
761 scaleAttr->setVertexSize( 3 );
762 scaleAttr->setByteStride( stride );
763 scaleAttr->setByteOffset( 7 *
sizeof(
float ) );
764 scaleAttr->setDivisor( 1 );
765 scaleAttr->setCount( instances.size() );
766 scaleAttr->setBuffer( buffer );
767 geometry->addAttribute( scaleAttr );
771static Qt3DCore::QAttribute *rawPositions( tinygltf::Model &model,
int accessorIndex )
773 tinygltf::Accessor &accessor = model.accessors[accessorIndex];
774 tinygltf::BufferView &bv = model.bufferViews[accessor.bufferView];
775 tinygltf::Buffer &b = model.buffers[bv.buffer];
777 if ( accessor.componentType != TINYGLTF_PARAMETER_TYPE_FLOAT || accessor.type != TINYGLTF_TYPE_VEC3 )
780 const unsigned char *ptr = b.data.data() + bv.byteOffset + accessor.byteOffset;
781 const int byteStride = bv.byteStride ? bv.byteStride : 3 *
sizeof( float );
783 QByteArray byteArray;
784 byteArray.resize( accessor.count * 3 *
sizeof(
float ) );
785 float *out =
reinterpret_cast<float *
>( byteArray.data() );
787 for ( std::size_t i = 0; i < accessor.count; ++i )
789 const float *fptr =
reinterpret_cast<const float *
>( ptr + i * byteStride );
790 out[i * 3 + 0] = fptr[0];
791 out[i * 3 + 1] = fptr[1];
792 out[i * 3 + 2] = fptr[2];
795 Qt3DCore::QBuffer *buffer =
new Qt3DCore::QBuffer;
796 buffer->setData( byteArray );
798 Qt3DCore::QAttribute *attribute =
new Qt3DCore::QAttribute;
799 attribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
800 attribute->setBuffer( buffer );
801 attribute->setByteOffset( 0 );
802 attribute->setByteStride( 12 );
803 attribute->setCount( accessor.count );
804 attribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
805 attribute->setVertexSize( 3 );
811QVector<Qt3DCore::QEntity *> QgsGltf3DUtils::createInstancedEntities(
812 tinygltf::Model &model,
813 const QVector<QgsGltfUtils::InstancedPrimitive> &primitives,
814 const QgsGltf3DUtils::EntityTransform &transform,
815 const QString &baseUri,
820 QVector<Qt3DCore::QEntity *> entities;
822 for (
const QgsGltfUtils::InstancedPrimitive &entry : primitives )
824 if ( entry.meshIndex < 0 || entry.meshIndex >=
static_cast<int>( model.meshes.size() ) )
827 const tinygltf::Mesh &mesh = model.meshes[entry.meshIndex];
828 if ( entry.primitiveIndex < 0 || entry.primitiveIndex >=
static_cast<int>( mesh.primitives.size() ) )
831 const tinygltf::Primitive &primitive = mesh.primitives[entry.primitiveIndex];
832 if ( primitive.mode != TINYGLTF_MODE_TRIANGLES )
835 *errors << u
"Unsupported mesh primitive mode for instancing: %1"_s.arg( primitive.mode );
839 auto posIt = primitive.attributes.find(
"POSITION" );
840 if ( posIt == primitive.attributes.end() )
843 int positionAccessorIndex = posIt->second;
846 std::unique_ptr<QgsMaterial> material = parseMaterial( model, entry.materialIndex, baseUri, errors, context );
851 if ( QgsMetalRoughMaterial *pbrMat = qobject_cast<QgsMetalRoughMaterial *>( material.get() ) )
852 pbrMat->setInstancingEnabled(
true );
853 else if ( QgsTextureMaterial *texMat = qobject_cast<QgsTextureMaterial *>( material.get() ) )
854 texMat->setInstancingEnabled(
true );
857 auto geom = std::make_unique<Qt3DCore::QGeometry>();
859 Qt3DCore::QAttribute *positionAttribute = rawPositions( model, positionAccessorIndex );
860 if ( !positionAttribute )
864 positionAttribute->setName( Qt3DCore::QAttribute::defaultPositionAttributeName() );
865 geom->addAttribute( positionAttribute );
867 auto normalIt = primitive.attributes.find(
"NORMAL" );
868 if ( normalIt != primitive.attributes.end() )
870 Qt3DCore::QAttribute *normalAttribute = parseAttribute( model, normalIt->second );
871 normalAttribute->setName( Qt3DCore::QAttribute::defaultNormalAttributeName() );
872 geom->addAttribute( normalAttribute );
877 if ( QgsMetalRoughMaterial *pbrMat = qobject_cast<QgsMetalRoughMaterial *>( material.get() ) )
878 pbrMat->setFlatShadingEnabled(
true );
881 auto texIt = primitive.attributes.find(
"TEXCOORD_0" );
882 if ( texIt != primitive.attributes.end() )
884 Qt3DCore::QAttribute *texAttribute = parseAttribute( model, texIt->second );
885 texAttribute->setName( Qt3DCore::QAttribute::defaultTextureCoordinateAttributeName() );
886 geom->addAttribute( texAttribute );
889 Qt3DCore::QAttribute *indexAttribute =
nullptr;
890 if ( primitive.indices != -1 )
892 indexAttribute = parseAttribute( model, primitive.indices );
893 geom->addAttribute( indexAttribute );
897 const QVector<InstanceChunkTransform> chunkTransforms = tileSpaceToChunkLocal( entry, transform );
898 if ( chunkTransforms.isEmpty() )
904 createInstanceBuffer( geom.get(), chunkTransforms );
907 Qt3DRender::QGeometryRenderer *geomRenderer =
new Qt3DRender::QGeometryRenderer;
908 geomRenderer->setGeometry( geom.release() );
909 geomRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
910 geomRenderer->setVertexCount( indexAttribute ? indexAttribute->count() : model.accessors[positionAccessorIndex].count );
911 geomRenderer->setInstanceCount( chunkTransforms.size() );
913 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
914 entity->addComponent( geomRenderer );
915 entity->addComponent( material.release() );
922Qt3DCore::QEntity *QgsGltf3DUtils::parsedGltfToEntity( tinygltf::Model &model,
const QgsGltf3DUtils::EntityTransform &transform, QString baseUri,
const Qgs3DRenderContext &context, QStringList *errors )
924 bool sceneOk =
false;
925 const std::size_t sceneIndex = QgsGltfUtils::sourceSceneForModel( model, sceneOk );
929 *errors <<
"No scenes present in the gltf data!";
933 tinygltf::Scene &scene = model.scenes[sceneIndex];
935 if ( scene.nodes.size() == 0 )
938 *errors <<
"No nodes present in the gltf data!";
942 const QgsVector3D tileTranslationEcef = QgsGltfUtils::extractTileTranslation( model );
944 Qt3DCore::QEntity *rootEntity =
new Qt3DCore::QEntity;
945 for (
const int nodeIndex : scene.nodes )
947 const QVector<Qt3DCore::QEntity *> entities = parseNode( model, nodeIndex, transform, tileTranslationEcef, baseUri, QMatrix4x4(), context, errors );
948 for ( Qt3DCore::QEntity *e : entities )
949 e->setParent( rootEntity );
955Qt3DCore::QEntity *QgsGltf3DUtils::gltfToEntity(
const QByteArray &data,
const QgsGltf3DUtils::EntityTransform &transform,
const QString &baseUri,
const Qgs3DRenderContext &context, QStringList *errors )
957 tinygltf::Model model;
958 QString gltfErrors, gltfWarnings;
960 bool res = QgsGltfUtils::loadGltfModel( data, model, &gltfErrors, &gltfWarnings );
961 if ( !gltfErrors.isEmpty() )
963 QgsDebugError( u
"Error raised reading %1: %2"_s.arg( baseUri, gltfErrors ) );
965 if ( !gltfWarnings.isEmpty() )
967 QgsDebugError( u
"Warnings raised reading %1: %2"_s.arg( baseUri, gltfWarnings ) );
973 errors->append( u
"GLTF load error: "_s + gltfErrors );
978 return parsedGltfToEntity( model, transform, baseUri, context, errors );
982#include "qgsgltf3dutils.moc"
Rendering context for preparation of 3D entities.
static void setTextureFiltering(Qt3DRender::QAbstractTexture *texture, const QgsMaterialContext &context)
Sets the default filtering options for a texture.
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...
Custom exception class for Coordinate Reference System related exceptions.
Context settings for a material.
static QgsMaterialContext fromRenderContext(const Qgs3DRenderContext &context)
Constructs a material context from the settings in a 3D render context.
A simple 4x4 matrix implementation useful for transformation in 3D space.
const double * constData() const
Returns pointer to the matrix data (stored in column-major order).
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
QByteArray content() const
Returns the reply content.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
QVector3D toVector3D() const
Converts the current object to QVector3D.
double x() const
Returns X coordinate.
static QgsVector3D crossProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the cross product of two vectors.
static bool extractFileFromZip(const QString &zipFilename, const QString &filenameInZip, QByteArray &bytesOut)
Extracts a file from a zip archive, returns true on success.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
bool operator==(const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2)
#define QgsDebugError(str)