29#define TINYGLTF_NO_STB_IMAGE
30#define TINYGLTF_NO_STB_IMAGE_WRITE
36QString QgsGltfToVectorFeaturesAlgorithm::name()
const
38 return QStringLiteral(
"gltftovector" );
41QString QgsGltfToVectorFeaturesAlgorithm::displayName()
const
43 return QObject::tr(
"Convert GLTF to vector features" );
46QStringList QgsGltfToVectorFeaturesAlgorithm::tags()
const
48 return QObject::tr(
"3d,tiles,cesium" ).split(
',' );
51QString QgsGltfToVectorFeaturesAlgorithm::group()
const
53 return QObject::tr(
"3D Tiles" );
56QString QgsGltfToVectorFeaturesAlgorithm::groupId()
const
58 return QStringLiteral(
"3dtiles" );
61QString QgsGltfToVectorFeaturesAlgorithm::shortHelpString()
const
63 return QObject::tr(
"Converts GLTF content to standard vector layer formats." );
66QgsGltfToVectorFeaturesAlgorithm *QgsGltfToVectorFeaturesAlgorithm::createInstance()
const
68 return new QgsGltfToVectorFeaturesAlgorithm();
71void QgsGltfToVectorFeaturesAlgorithm::initAlgorithm(
const QVariantMap & )
74 QStringLiteral(
"gltf" ), QVariant(),
false, QStringLiteral(
"GLTF (*.gltf *.GLTF);;GLB (*.glb *.GLB)" ) ) );
80std::unique_ptr< QgsAbstractGeometry > extractTriangles(
81 const tinygltf::Model &model,
82 const tinygltf::Primitive &primitive,
85 const QMatrix4x4 *gltfLocalTransform,
88 auto posIt = primitive.attributes.find(
"POSITION" );
89 if ( posIt == primitive.attributes.end() )
91 feedback->
reportError( QObject::tr(
"Could not find POSITION attribute for primitive" ) );
94 int positionAccessorIndex = posIt->second;
99 QgsGltfUtils::accessorToMapCoordinates(
108 std::unique_ptr< QgsMultiPolygon > mp = std::make_unique< QgsMultiPolygon >();
110 if ( primitive.indices == -1 )
112 Q_ASSERT( x.size() % 3 == 0 );
114 mp->reserve( x.size() );
115 for (
int i = 0; i < x.size(); i += 3 )
117 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] ) } ) ) );
122 const tinygltf::Accessor &primitiveAccessor = model.accessors[primitive.indices];
123 const tinygltf::BufferView &bvPrimitive = model.bufferViews[primitiveAccessor.bufferView];
124 const tinygltf::Buffer &bPrimitive = model.buffers[bvPrimitive.buffer];
126 Q_ASSERT( ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT
127 || primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT
128 || primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE )
129 && primitiveAccessor.type == TINYGLTF_TYPE_SCALAR );
131 const char *primitivePtr =
reinterpret_cast< const char *
>( bPrimitive.data.data() ) + bvPrimitive.byteOffset + primitiveAccessor.byteOffset;
133 mp->reserve( primitiveAccessor.count / 3 );
134 for ( std::size_t i = 0; i < primitiveAccessor.count / 3; i++ )
136 unsigned int index1 = 0;
137 unsigned int index2 = 0;
138 unsigned int index3 = 0;
140 if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT )
142 const unsigned short *usPtrPrimitive =
reinterpret_cast< const unsigned short *
>( primitivePtr );
143 if ( bvPrimitive.byteStride )
144 primitivePtr += bvPrimitive.byteStride;
146 primitivePtr += 3 *
sizeof(
unsigned short );
148 index1 = usPtrPrimitive[0];
149 index2 = usPtrPrimitive[1];
150 index3 = usPtrPrimitive[2];
152 else if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE )
154 const unsigned char *usPtrPrimitive =
reinterpret_cast< const unsigned char *
>( primitivePtr );
155 if ( bvPrimitive.byteStride )
156 primitivePtr += bvPrimitive.byteStride;
158 primitivePtr += 3 *
sizeof(
unsigned char );
160 index1 = usPtrPrimitive[0];
161 index2 = usPtrPrimitive[1];
162 index3 = usPtrPrimitive[2];
166 const unsigned int *uintPtrPrimitive =
reinterpret_cast< const unsigned int *
>( primitivePtr );
167 if ( bvPrimitive.byteStride )
168 primitivePtr += bvPrimitive.byteStride;
170 primitivePtr += 3 *
sizeof(
unsigned int );
172 index1 = uintPtrPrimitive[0];
173 index2 = uintPtrPrimitive[1];
174 index3 = uintPtrPrimitive[2];
177 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] ) } ) ) );
183std::unique_ptr< QgsAbstractGeometry > extractLines(
184 const tinygltf::Model &model,
185 const tinygltf::Primitive &primitive,
188 const QMatrix4x4 *gltfLocalTransform,
191 auto posIt = primitive.attributes.find(
"POSITION" );
192 if ( posIt == primitive.attributes.end() )
194 feedback->
reportError( QObject::tr(
"Could not find POSITION attribute for primitive" ) );
197 int positionAccessorIndex = posIt->second;
202 QgsGltfUtils::accessorToMapCoordinates(
211 std::unique_ptr< QgsMultiLineString > ml = std::make_unique< QgsMultiLineString >();
213 if ( primitive.indices == -1 )
215 Q_ASSERT( x.size() % 2 == 0 );
217 ml->reserve( x.size() );
218 for (
int i = 0; i < x.size(); i += 2 )
225 const tinygltf::Accessor &primitiveAccessor = model.accessors[primitive.indices];
226 const tinygltf::BufferView &bvPrimitive = model.bufferViews[primitiveAccessor.bufferView];
227 const tinygltf::Buffer &bPrimitive = model.buffers[bvPrimitive.buffer];
229 Q_ASSERT( ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT
230 || primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT
231 || primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE )
232 && primitiveAccessor.type == TINYGLTF_TYPE_SCALAR );
234 const char *primitivePtr =
reinterpret_cast< const char *
>( bPrimitive.data.data() ) + bvPrimitive.byteOffset + primitiveAccessor.byteOffset;
236 ml->reserve( primitiveAccessor.count / 2 );
237 for ( std::size_t i = 0; i < primitiveAccessor.count / 2; i++ )
239 unsigned int index1 = 0;
240 unsigned int index2 = 0;
242 if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT )
244 const unsigned short *usPtrPrimitive =
reinterpret_cast< const unsigned short *
>( primitivePtr );
245 if ( bvPrimitive.byteStride )
246 primitivePtr += bvPrimitive.byteStride;
248 primitivePtr += 2 *
sizeof(
unsigned short );
250 index1 = usPtrPrimitive[0];
251 index2 = usPtrPrimitive[1];
253 else if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE )
255 const unsigned char *usPtrPrimitive =
reinterpret_cast< const unsigned char *
>( primitivePtr );
256 if ( bvPrimitive.byteStride )
257 primitivePtr += bvPrimitive.byteStride;
259 primitivePtr += 2 *
sizeof(
unsigned char );
261 index1 = usPtrPrimitive[0];
262 index2 = usPtrPrimitive[1];
266 const unsigned int *uintPtrPrimitive =
reinterpret_cast< const unsigned int *
>( primitivePtr );
267 if ( bvPrimitive.byteStride )
268 primitivePtr += bvPrimitive.byteStride;
270 primitivePtr += 2 *
sizeof(
unsigned int );
272 index1 = uintPtrPrimitive[0];
273 index2 = uintPtrPrimitive[1];
276 ml->addGeometry(
new QgsLineString( QVector<QgsPoint> {
QgsPoint( x[index1], y[index1], z[index1] ),
QgsPoint( x[index2], y[index2], z[index2] ) } ) );
284 const QString path = parameterAsFile( parameters, QStringLiteral(
"INPUT" ), context );
290 std::unique_ptr< QgsFeatureSink > polygonSink( parameterAsSink( parameters, QStringLiteral(
"OUTPUT_POLYGONS" ), context, polygonDest, fields,
292 if ( !polygonSink && parameters.value( QStringLiteral(
"OUTPUT_POLYGONS" ) ).isValid() )
295 std::unique_ptr< QgsFeatureSink > lineSink( parameterAsSink( parameters, QStringLiteral(
"OUTPUT_LINES" ), context, lineDest, fields,
297 if ( !lineSink && parameters.value( QStringLiteral(
"OUTPUT_LINES" ) ).isValid() )
301 QByteArray fileContent;
302 if ( f.open( QIODevice::ReadOnly ) )
304 fileContent = f.readAll();
313 tinygltf::Model model;
316 if ( !QgsGltfUtils::loadGltfModel( fileContent, model, &errors, &warnings ) )
320 if ( !warnings.isEmpty() )
324 feedback->
pushDebugInfo( QObject::tr(
"Found %1 scenes" ).arg( model.scenes.size() ) );
326 bool sceneOk =
false;
327 const std::size_t sceneIndex = QgsGltfUtils::sourceSceneForModel( model, sceneOk );
333 const tinygltf::Scene &scene = model.scenes[sceneIndex];
334 feedback->
pushDebugInfo( QObject::tr(
"Found %1 nodes in default scene [%2]" ).arg( scene.nodes.size() ).arg( sceneIndex ) );
336 QSet< int > warnedPrimitiveTypes;
338 const QgsVector3D tileTranslationEcef = QgsGltfUtils::extractTileTranslation( model );
339 std::function< void(
int nodeIndex,
const QMatrix4x4 &transform ) > traverseNode;
340 traverseNode = [&model, feedback, &polygonSink, &lineSink, &warnedPrimitiveTypes, &ecefTransform, &tileTranslationEcef, &traverseNode](
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.reset(
new 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 );
378 case TINYGLTF_MODE_LINE:
382 std::unique_ptr< QgsAbstractGeometry > geometry = extractLines( model, primitive, ecefTransform, tileTranslationEcef, gltfLocalTransform.get(), feedback );
393 case TINYGLTF_MODE_POINTS:
394 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_POINTS ) )
396 feedback->reportError( QObject::tr(
"Point objects are not supported" ) );
397 warnedPrimitiveTypes.insert( TINYGLTF_MODE_POINTS );
401 case TINYGLTF_MODE_LINE_LOOP:
402 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_LINE_LOOP ) )
404 feedback->reportError( QObject::tr(
"Line loops in are not supported" ) );
405 warnedPrimitiveTypes.insert( TINYGLTF_MODE_LINE_LOOP );
409 case TINYGLTF_MODE_LINE_STRIP:
410 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_LINE_STRIP ) )
412 feedback->reportError( QObject::tr(
"Line strips in are not supported" ) );
413 warnedPrimitiveTypes.insert( TINYGLTF_MODE_LINE_STRIP );
417 case TINYGLTF_MODE_TRIANGLE_STRIP:
418 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_TRIANGLE_STRIP ) )
420 feedback->reportError( QObject::tr(
"Triangular strips are not supported" ) );
421 warnedPrimitiveTypes.insert( TINYGLTF_MODE_TRIANGLE_STRIP );
425 case TINYGLTF_MODE_TRIANGLE_FAN:
426 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_TRIANGLE_FAN ) )
428 feedback->reportError( QObject::tr(
"Triangular fans are not supported" ) );
429 warnedPrimitiveTypes.insert( TINYGLTF_MODE_TRIANGLE_FAN );
434 if ( !warnedPrimitiveTypes.contains( primitive.mode ) )
436 feedback->reportError( QObject::tr(
"Primitive type %1 are not supported" ).arg( primitive.mode ) );
437 warnedPrimitiveTypes.insert( primitive.mode );
444 for (
int childNode : gltfNode.children )
446 traverseNode( childNode, gltfLocalTransform ? *gltfLocalTransform : QMatrix4x4() );
451 if ( !scene.nodes.empty() )
453 for (
const int nodeIndex : scene.nodes )
455 traverseNode( nodeIndex, QMatrix4x4() );
462 polygonSink->finalize();
463 outputs.insert( QStringLiteral(
"OUTPUT_POLYGONS" ), polygonDest );
467 lineSink->finalize();
468 outputs.insert( QStringLiteral(
"OUTPUT_LINES" ), lineDest );
@ VectorPolygon
Vector polygon layers.
@ VectorLine
Vector line layers.
@ File
Parameter is a single file.
@ MultiLineStringZ
MultiLineStringZ.
@ MultiPolygonZ
MultiPolygonZ.
This class 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.
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...