32#define TINYGLTF_NO_STB_IMAGE
33#define TINYGLTF_NO_STB_IMAGE_WRITE
39QString QgsGltfToVectorFeaturesAlgorithm::name()
const
41 return QStringLiteral(
"gltftovector" );
44QString QgsGltfToVectorFeaturesAlgorithm::displayName()
const
46 return QObject::tr(
"Convert GLTF to vector features" );
49QStringList QgsGltfToVectorFeaturesAlgorithm::tags()
const
51 return QObject::tr(
"3d,tiles,cesium" ).split(
',' );
54QString QgsGltfToVectorFeaturesAlgorithm::group()
const
56 return QObject::tr(
"3D Tiles" );
59QString QgsGltfToVectorFeaturesAlgorithm::groupId()
const
61 return QStringLiteral(
"3dtiles" );
64QString QgsGltfToVectorFeaturesAlgorithm::shortHelpString()
const
66 return QObject::tr(
"This algorithm converts GLTF content to standard vector layer formats." );
69QString QgsGltfToVectorFeaturesAlgorithm::shortDescription()
const
71 return QObject::tr(
"Converts GLTF content to standard vector layer formats." );
74QgsGltfToVectorFeaturesAlgorithm *QgsGltfToVectorFeaturesAlgorithm::createInstance()
const
76 return new QgsGltfToVectorFeaturesAlgorithm();
79void QgsGltfToVectorFeaturesAlgorithm::initAlgorithm(
const QVariantMap & )
87std::unique_ptr<QgsAbstractGeometry> extractTriangles(
88 const tinygltf::Model &model,
89 const tinygltf::Primitive &primitive,
92 const QMatrix4x4 *gltfLocalTransform,
96 auto posIt = primitive.attributes.find(
"POSITION" );
97 if ( posIt == primitive.attributes.end() )
99 feedback->
reportError( QObject::tr(
"Could not find POSITION attribute for primitive" ) );
102 int positionAccessorIndex = posIt->second;
107 QgsGltfUtils::accessorToMapCoordinates(
116 auto mp = std::make_unique<QgsMultiPolygon>();
118 if ( primitive.indices == -1 )
120 Q_ASSERT( x.size() % 3 == 0 );
122 mp->reserve( x.size() );
123 for (
int i = 0; i < x.size(); i += 3 )
125 mp->addGeometry(
new QgsPolygon(
new QgsLineString( QVector<QgsPoint> {
QgsPoint( x[i], y[i], z[i] ),
QgsPoint( x[i + 1], y[i + 1], z[i + 1] ),
QgsPoint( x[i + 2], y[i + 2], z[i + 2] ),
QgsPoint( x[i], y[i], z[i] ) } ) ) );
130 const tinygltf::Accessor &primitiveAccessor = model.accessors[primitive.indices];
131 const tinygltf::BufferView &bvPrimitive = model.bufferViews[primitiveAccessor.bufferView];
132 const tinygltf::Buffer &bPrimitive = model.buffers[bvPrimitive.buffer];
134 Q_ASSERT( ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT || primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT || primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE ) && primitiveAccessor.type == TINYGLTF_TYPE_SCALAR );
136 const char *primitivePtr =
reinterpret_cast<const char *
>( bPrimitive.data.data() ) + bvPrimitive.byteOffset + primitiveAccessor.byteOffset;
138 mp->reserve( primitiveAccessor.count / 3 );
139 for ( std::size_t i = 0; i < primitiveAccessor.count / 3; i++ )
141 unsigned int index1 = 0;
142 unsigned int index2 = 0;
143 unsigned int index3 = 0;
145 if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT )
147 const unsigned short *usPtrPrimitive =
reinterpret_cast<const unsigned short *
>( primitivePtr );
148 if ( bvPrimitive.byteStride )
149 primitivePtr += bvPrimitive.byteStride;
151 primitivePtr += 3 *
sizeof(
unsigned short );
153 index1 = usPtrPrimitive[0];
154 index2 = usPtrPrimitive[1];
155 index3 = usPtrPrimitive[2];
157 else if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE )
159 const unsigned char *usPtrPrimitive =
reinterpret_cast<const unsigned char *
>( primitivePtr );
160 if ( bvPrimitive.byteStride )
161 primitivePtr += bvPrimitive.byteStride;
163 primitivePtr += 3 *
sizeof(
unsigned char );
165 index1 = usPtrPrimitive[0];
166 index2 = usPtrPrimitive[1];
167 index3 = usPtrPrimitive[2];
171 const unsigned int *uintPtrPrimitive =
reinterpret_cast<const unsigned int *
>( primitivePtr );
172 if ( bvPrimitive.byteStride )
173 primitivePtr += bvPrimitive.byteStride;
175 primitivePtr += 3 *
sizeof(
unsigned int );
177 index1 = uintPtrPrimitive[0];
178 index2 = uintPtrPrimitive[1];
179 index3 = uintPtrPrimitive[2];
182 mp->addGeometry(
new QgsPolygon(
new QgsLineString( QVector<QgsPoint> {
QgsPoint( x[index1], y[index1], z[index1] ),
QgsPoint( x[index2], y[index2], z[index2] ),
QgsPoint( x[index3], y[index3], z[index3] ),
QgsPoint( x[index1], y[index1], z[index1] ) } ) ) );
188std::unique_ptr<QgsAbstractGeometry> extractLines(
189 const tinygltf::Model &model,
190 const tinygltf::Primitive &primitive,
193 const QMatrix4x4 *gltfLocalTransform,
197 auto posIt = primitive.attributes.find(
"POSITION" );
198 if ( posIt == primitive.attributes.end() )
200 feedback->
reportError( QObject::tr(
"Could not find POSITION attribute for primitive" ) );
203 int positionAccessorIndex = posIt->second;
208 QgsGltfUtils::accessorToMapCoordinates(
217 auto ml = std::make_unique<QgsMultiLineString>();
219 if ( primitive.indices == -1 )
221 Q_ASSERT( x.size() % 2 == 0 );
223 ml->reserve( x.size() );
224 for (
int i = 0; i < x.size(); i += 2 )
231 const tinygltf::Accessor &primitiveAccessor = model.accessors[primitive.indices];
232 const tinygltf::BufferView &bvPrimitive = model.bufferViews[primitiveAccessor.bufferView];
233 const tinygltf::Buffer &bPrimitive = model.buffers[bvPrimitive.buffer];
235 Q_ASSERT( ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT || primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT || primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE ) && primitiveAccessor.type == TINYGLTF_TYPE_SCALAR );
237 const char *primitivePtr =
reinterpret_cast<const char *
>( bPrimitive.data.data() ) + bvPrimitive.byteOffset + primitiveAccessor.byteOffset;
239 ml->reserve( primitiveAccessor.count / 2 );
240 for ( std::size_t i = 0; i < primitiveAccessor.count / 2; i++ )
242 unsigned int index1 = 0;
243 unsigned int index2 = 0;
245 if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT )
247 const unsigned short *usPtrPrimitive =
reinterpret_cast<const unsigned short *
>( primitivePtr );
248 if ( bvPrimitive.byteStride )
249 primitivePtr += bvPrimitive.byteStride;
251 primitivePtr += 2 *
sizeof(
unsigned short );
253 index1 = usPtrPrimitive[0];
254 index2 = usPtrPrimitive[1];
256 else if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE )
258 const unsigned char *usPtrPrimitive =
reinterpret_cast<const unsigned char *
>( primitivePtr );
259 if ( bvPrimitive.byteStride )
260 primitivePtr += bvPrimitive.byteStride;
262 primitivePtr += 2 *
sizeof(
unsigned char );
264 index1 = usPtrPrimitive[0];
265 index2 = usPtrPrimitive[1];
269 const unsigned int *uintPtrPrimitive =
reinterpret_cast<const unsigned int *
>( primitivePtr );
270 if ( bvPrimitive.byteStride )
271 primitivePtr += bvPrimitive.byteStride;
273 primitivePtr += 2 *
sizeof(
unsigned int );
275 index1 = uintPtrPrimitive[0];
276 index2 = uintPtrPrimitive[1];
279 ml->addGeometry(
new QgsLineString( QVector<QgsPoint> {
QgsPoint( x[index1], y[index1], z[index1] ),
QgsPoint( x[index2], y[index2], z[index2] ) } ) );
287 const QString path = parameterAsFile( parameters, QStringLiteral(
"INPUT" ), context );
293 std::unique_ptr<QgsFeatureSink> polygonSink( parameterAsSink( parameters, QStringLiteral(
"OUTPUT_POLYGONS" ), context, polygonDest, fields,
Qgis::WkbType::MultiPolygonZ, destCrs ) );
294 if ( !polygonSink && parameters.value( QStringLiteral(
"OUTPUT_POLYGONS" ) ).isValid() )
297 std::unique_ptr<QgsFeatureSink> lineSink( parameterAsSink( parameters, QStringLiteral(
"OUTPUT_LINES" ), context, lineDest, fields,
Qgis::WkbType::MultiLineStringZ, destCrs ) );
298 if ( !lineSink && parameters.value( QStringLiteral(
"OUTPUT_LINES" ) ).isValid() )
302 QByteArray fileContent;
303 if ( f.open( QIODevice::ReadOnly ) )
305 fileContent = f.readAll();
314 tinygltf::Model model;
317 if ( !QgsGltfUtils::loadGltfModel( fileContent, model, &errors, &warnings ) )
321 if ( !warnings.isEmpty() )
325 feedback->
pushDebugInfo( QObject::tr(
"Found %1 scenes" ).arg( model.scenes.size() ) );
327 bool sceneOk =
false;
328 const std::size_t sceneIndex = QgsGltfUtils::sourceSceneForModel( model, sceneOk );
334 const tinygltf::Scene &scene = model.scenes[sceneIndex];
335 feedback->
pushDebugInfo( QObject::tr(
"Found %1 nodes in default scene [%2]" ).arg( scene.nodes.size() ).arg( sceneIndex ) );
337 QSet<int> warnedPrimitiveTypes;
339 const QgsVector3D tileTranslationEcef = QgsGltfUtils::extractTileTranslation( model );
340 std::function<void(
int nodeIndex,
const QMatrix4x4 &transform )> traverseNode;
341 traverseNode = [&model, feedback, &polygonSink, &lineSink, &warnedPrimitiveTypes, &ecefTransform, &tileTranslationEcef, &traverseNode, ¶meters](
int nodeIndex,
const QMatrix4x4 &parentTransform ) {
342 const tinygltf::Node &gltfNode = model.nodes[nodeIndex];
343 std::unique_ptr<QMatrix4x4> gltfLocalTransform = QgsGltfUtils::parseNodeTransform( gltfNode );
344 if ( !parentTransform.isIdentity() )
346 if ( gltfLocalTransform )
347 *gltfLocalTransform = parentTransform * *gltfLocalTransform;
350 gltfLocalTransform = std::make_unique<QMatrix4x4>( parentTransform );
354 if ( gltfNode.mesh >= 0 )
356 const tinygltf::Mesh &mesh = model.meshes[gltfNode.mesh];
357 feedback->pushDebugInfo( QObject::tr(
"Found %1 primitives in node [%2]" ).arg( mesh.primitives.size() ).arg( nodeIndex ) );
359 for (
const tinygltf::Primitive &primitive : mesh.primitives )
361 switch ( primitive.mode )
363 case TINYGLTF_MODE_TRIANGLES:
367 std::unique_ptr<QgsAbstractGeometry> geometry = extractTriangles( model, primitive, ecefTransform, tileTranslationEcef, gltfLocalTransform.get(), feedback );
373 throw QgsProcessingException( writeFeatureError( polygonSink.get(), parameters, QStringLiteral(
"OUTPUT_POLYGONS" ) ) );
379 case TINYGLTF_MODE_LINE:
383 std::unique_ptr<QgsAbstractGeometry> geometry = extractLines( model, primitive, ecefTransform, tileTranslationEcef, gltfLocalTransform.get(), feedback );
389 throw QgsProcessingException( writeFeatureError( lineSink.get(), parameters, QStringLiteral(
"OUTPUT_LINES" ) ) );
395 case TINYGLTF_MODE_POINTS:
396 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_POINTS ) )
398 feedback->reportError( QObject::tr(
"Point objects are not supported" ) );
399 warnedPrimitiveTypes.insert( TINYGLTF_MODE_POINTS );
403 case TINYGLTF_MODE_LINE_LOOP:
404 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_LINE_LOOP ) )
406 feedback->reportError( QObject::tr(
"Line loops in are not supported" ) );
407 warnedPrimitiveTypes.insert( TINYGLTF_MODE_LINE_LOOP );
411 case TINYGLTF_MODE_LINE_STRIP:
412 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_LINE_STRIP ) )
414 feedback->reportError( QObject::tr(
"Line strips in are not supported" ) );
415 warnedPrimitiveTypes.insert( TINYGLTF_MODE_LINE_STRIP );
419 case TINYGLTF_MODE_TRIANGLE_STRIP:
420 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_TRIANGLE_STRIP ) )
422 feedback->reportError( QObject::tr(
"Triangular strips are not supported" ) );
423 warnedPrimitiveTypes.insert( TINYGLTF_MODE_TRIANGLE_STRIP );
427 case TINYGLTF_MODE_TRIANGLE_FAN:
428 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_TRIANGLE_FAN ) )
430 feedback->reportError( QObject::tr(
"Triangular fans are not supported" ) );
431 warnedPrimitiveTypes.insert( TINYGLTF_MODE_TRIANGLE_FAN );
436 if ( !warnedPrimitiveTypes.contains( primitive.mode ) )
438 feedback->reportError( QObject::tr(
"Primitive type %1 are not supported" ).arg( primitive.mode ) );
439 warnedPrimitiveTypes.insert( primitive.mode );
446 for (
int childNode : gltfNode.children )
448 traverseNode( childNode, gltfLocalTransform ? *gltfLocalTransform : QMatrix4x4() );
452 if ( !scene.nodes.empty() )
454 for (
const int nodeIndex : scene.nodes )
456 traverseNode( nodeIndex, QMatrix4x4() );
463 polygonSink->finalize();
464 outputs.insert( QStringLiteral(
"OUTPUT_POLYGONS" ), polygonDest );
468 lineSink->finalize();
469 outputs.insert( QStringLiteral(
"OUTPUT_LINES" ), lineDest );
@ VectorPolygon
Vector polygon layers.
@ VectorLine
Vector line layers.
@ File
Parameter is a single file.
@ MultiLineStringZ
MultiLineStringZ.
@ MultiPolygonZ
MultiPolygonZ.
Represents a coordinate reference system (CRS).
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Container of fields for a vector layer.
Line string geometry type, with support for z-dimension and m-values.
A simple 4x4 matrix implementation useful for transformation in 3D space.
Point geometry type, with support for z-dimension and m-values.
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
virtual void pushDebugInfo(const QString &info)
Pushes an informational message containing debugging helpers from the algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
A feature sink output for processing algorithms.
An input file or folder parameter for processing algorithms.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...