34using namespace Qt::StringLiterals;
36#define TINYGLTF_NO_STB_IMAGE
37#define TINYGLTF_NO_STB_IMAGE_WRITE
43QString QgsGltfToVectorFeaturesAlgorithm::name()
const
45 return u
"gltftovector"_s;
48QString QgsGltfToVectorFeaturesAlgorithm::displayName()
const
50 return QObject::tr(
"Convert GLTF to vector features" );
53QStringList QgsGltfToVectorFeaturesAlgorithm::tags()
const
55 return QObject::tr(
"3d,tiles,cesium" ).split(
',' );
58QString QgsGltfToVectorFeaturesAlgorithm::group()
const
60 return QObject::tr(
"3D Tiles" );
63QString QgsGltfToVectorFeaturesAlgorithm::groupId()
const
68QString QgsGltfToVectorFeaturesAlgorithm::shortHelpString()
const
70 return QObject::tr(
"This algorithm converts GLTF content to standard vector layer formats." );
73QString QgsGltfToVectorFeaturesAlgorithm::shortDescription()
const
75 return QObject::tr(
"Converts GLTF content to standard vector layer formats." );
78QgsGltfToVectorFeaturesAlgorithm *QgsGltfToVectorFeaturesAlgorithm::createInstance()
const
80 return new QgsGltfToVectorFeaturesAlgorithm();
83void QgsGltfToVectorFeaturesAlgorithm::initAlgorithm(
const QVariantMap & )
93std::unique_ptr<QgsAbstractGeometry> extractTriangles(
94 const tinygltf::Model &model,
95 const tinygltf::Primitive &primitive,
98 const QMatrix4x4 *gltfLocalTransform,
102 auto posIt = primitive.attributes.find(
"POSITION" );
103 if ( posIt == primitive.attributes.end() )
105 feedback->
reportError( QObject::tr(
"Could not find POSITION attribute for primitive" ) );
108 int positionAccessorIndex = posIt->second;
113 QgsGltfUtils::accessorToMapCoordinates( model, positionAccessorIndex,
QgsMatrix4x4(), &ecefTransform, tileTranslationEcef, gltfLocalTransform,
Qgis::Axis::Y, x, y, z );
115 auto mp = std::make_unique<QgsMultiPolygon>();
117 if ( primitive.indices == -1 )
119 Q_ASSERT( x.size() % 3 == 0 );
121 mp->reserve( x.size() );
122 for (
int i = 0; i < x.size(); i += 3 )
125 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] ) } )
131 const tinygltf::Accessor &primitiveAccessor = model.accessors[primitive.indices];
132 const tinygltf::BufferView &bvPrimitive = model.bufferViews[primitiveAccessor.bufferView];
133 const tinygltf::Buffer &bPrimitive = model.buffers[bvPrimitive.buffer];
136 ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT
137 || primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT
138 || primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE )
139 && primitiveAccessor.type == TINYGLTF_TYPE_SCALAR
142 const char *primitivePtr =
reinterpret_cast<const char *
>( bPrimitive.data.data() ) + bvPrimitive.byteOffset + primitiveAccessor.byteOffset;
144 mp->reserve( primitiveAccessor.count / 3 );
145 for ( std::size_t i = 0; i < primitiveAccessor.count / 3; i++ )
147 unsigned int index1 = 0;
148 unsigned int index2 = 0;
149 unsigned int index3 = 0;
151 if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT )
153 const unsigned short *usPtrPrimitive =
reinterpret_cast<const unsigned short *
>( primitivePtr );
154 if ( bvPrimitive.byteStride )
155 primitivePtr += bvPrimitive.byteStride;
157 primitivePtr += 3 *
sizeof(
unsigned short );
159 index1 = usPtrPrimitive[0];
160 index2 = usPtrPrimitive[1];
161 index3 = usPtrPrimitive[2];
163 else if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE )
165 const unsigned char *usPtrPrimitive =
reinterpret_cast<const unsigned char *
>( primitivePtr );
166 if ( bvPrimitive.byteStride )
167 primitivePtr += bvPrimitive.byteStride;
169 primitivePtr += 3 *
sizeof(
unsigned char );
171 index1 = usPtrPrimitive[0];
172 index2 = usPtrPrimitive[1];
173 index3 = usPtrPrimitive[2];
177 const unsigned int *uintPtrPrimitive =
reinterpret_cast<const unsigned int *
>( primitivePtr );
178 if ( bvPrimitive.byteStride )
179 primitivePtr += bvPrimitive.byteStride;
181 primitivePtr += 3 *
sizeof(
unsigned int );
183 index1 = uintPtrPrimitive[0];
184 index2 = uintPtrPrimitive[1];
185 index3 = uintPtrPrimitive[2];
189 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] ) }
196std::unique_ptr<QgsAbstractGeometry> extractLines(
197 const tinygltf::Model &model,
198 const tinygltf::Primitive &primitive,
201 const QMatrix4x4 *gltfLocalTransform,
205 auto posIt = primitive.attributes.find(
"POSITION" );
206 if ( posIt == primitive.attributes.end() )
208 feedback->
reportError( QObject::tr(
"Could not find POSITION attribute for primitive" ) );
211 int positionAccessorIndex = posIt->second;
216 QgsGltfUtils::accessorToMapCoordinates( model, positionAccessorIndex,
QgsMatrix4x4(), &ecefTransform, tileTranslationEcef, gltfLocalTransform,
Qgis::Axis::Y, x, y, z );
218 auto ml = std::make_unique<QgsMultiLineString>();
220 if ( primitive.indices == -1 )
222 Q_ASSERT( x.size() % 2 == 0 );
224 ml->reserve( x.size() );
225 for (
int i = 0; i < x.size(); i += 2 )
232 const tinygltf::Accessor &primitiveAccessor = model.accessors[primitive.indices];
233 const tinygltf::BufferView &bvPrimitive = model.bufferViews[primitiveAccessor.bufferView];
234 const tinygltf::Buffer &bPrimitive = model.buffers[bvPrimitive.buffer];
237 ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT
238 || primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT
239 || primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE )
240 && primitiveAccessor.type == TINYGLTF_TYPE_SCALAR
243 const char *primitivePtr =
reinterpret_cast<const char *
>( bPrimitive.data.data() ) + bvPrimitive.byteOffset + primitiveAccessor.byteOffset;
245 ml->reserve( primitiveAccessor.count / 2 );
246 for ( std::size_t i = 0; i < primitiveAccessor.count / 2; i++ )
248 unsigned int index1 = 0;
249 unsigned int index2 = 0;
251 if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT )
253 const unsigned short *usPtrPrimitive =
reinterpret_cast<const unsigned short *
>( primitivePtr );
254 if ( bvPrimitive.byteStride )
255 primitivePtr += bvPrimitive.byteStride;
257 primitivePtr += 2 *
sizeof(
unsigned short );
259 index1 = usPtrPrimitive[0];
260 index2 = usPtrPrimitive[1];
262 else if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE )
264 const unsigned char *usPtrPrimitive =
reinterpret_cast<const unsigned char *
>( primitivePtr );
265 if ( bvPrimitive.byteStride )
266 primitivePtr += bvPrimitive.byteStride;
268 primitivePtr += 2 *
sizeof(
unsigned char );
270 index1 = usPtrPrimitive[0];
271 index2 = usPtrPrimitive[1];
275 const unsigned int *uintPtrPrimitive =
reinterpret_cast<const unsigned int *
>( primitivePtr );
276 if ( bvPrimitive.byteStride )
277 primitivePtr += bvPrimitive.byteStride;
279 primitivePtr += 2 *
sizeof(
unsigned int );
281 index1 = uintPtrPrimitive[0];
282 index2 = uintPtrPrimitive[1];
285 ml->addGeometry(
new QgsLineString( QVector<QgsPoint> {
QgsPoint( x[index1], y[index1], z[index1] ),
QgsPoint( x[index2], y[index2], z[index2] ) } ) );
293 const QString path = parameterAsFile( parameters, u
"INPUT"_s, context );
299 std::unique_ptr<QgsFeatureSink> polygonSink( parameterAsSink( parameters, u
"OUTPUT_POLYGONS"_s, context, polygonDest, fields,
Qgis::WkbType::MultiPolygonZ, destCrs ) );
300 if ( !polygonSink && parameters.value( u
"OUTPUT_POLYGONS"_s ).isValid() )
303 std::unique_ptr<QgsFeatureSink> lineSink( parameterAsSink( parameters, u
"OUTPUT_LINES"_s, context, lineDest, fields,
Qgis::WkbType::MultiLineStringZ, destCrs ) );
304 if ( !lineSink && parameters.value( u
"OUTPUT_LINES"_s ).isValid() )
308 QByteArray fileContent;
309 if ( f.open( QIODevice::ReadOnly ) )
311 fileContent = f.readAll();
320 tinygltf::Model model;
323 if ( !QgsGltfUtils::loadGltfModel( fileContent, model, &errors, &warnings, QFileInfo( path ).absolutePath() ) )
327 if ( !warnings.isEmpty() )
331 feedback->
pushDebugInfo( QObject::tr(
"Found %1 scenes" ).arg( model.scenes.size() ) );
333 bool sceneOk =
false;
334 const std::size_t sceneIndex = QgsGltfUtils::sourceSceneForModel( model, sceneOk );
340 const tinygltf::Scene &scene = model.scenes[sceneIndex];
341 feedback->
pushDebugInfo( QObject::tr(
"Found %1 nodes in default scene [%2]" ).arg( scene.nodes.size() ).arg( sceneIndex ) );
343 QSet<int> warnedPrimitiveTypes;
345 const QgsVector3D tileTranslationEcef = QgsGltfUtils::extractTileTranslation( model );
346 std::function<void(
int nodeIndex,
const QMatrix4x4 &transform )> traverseNode;
347 traverseNode = [&model, feedback, &polygonSink, &lineSink, &warnedPrimitiveTypes, &ecefTransform, &tileTranslationEcef, &traverseNode, ¶meters](
int nodeIndex,
const QMatrix4x4 &parentTransform ) {
348 const tinygltf::Node &gltfNode = model.nodes[nodeIndex];
349 std::unique_ptr<QMatrix4x4> gltfLocalTransform = QgsGltfUtils::parseNodeTransform( gltfNode );
350 if ( !parentTransform.isIdentity() )
352 if ( gltfLocalTransform )
353 *gltfLocalTransform = parentTransform * *gltfLocalTransform;
356 gltfLocalTransform = std::make_unique<QMatrix4x4>( parentTransform );
360 if ( gltfNode.mesh >= 0 )
362 const tinygltf::Mesh &mesh = model.meshes[gltfNode.mesh];
363 feedback->pushDebugInfo( QObject::tr(
"Found %1 primitives in node [%2]" ).arg( mesh.primitives.size() ).arg( nodeIndex ) );
365 for (
const tinygltf::Primitive &primitive : mesh.primitives )
367 switch ( primitive.mode )
369 case TINYGLTF_MODE_TRIANGLES:
373 std::unique_ptr<QgsAbstractGeometry> geometry = extractTriangles( model, primitive, ecefTransform, tileTranslationEcef, gltfLocalTransform.get(), feedback );
385 case TINYGLTF_MODE_LINE:
389 std::unique_ptr<QgsAbstractGeometry> geometry = extractLines( model, primitive, ecefTransform, tileTranslationEcef, gltfLocalTransform.get(), feedback );
401 case TINYGLTF_MODE_POINTS:
402 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_POINTS ) )
404 feedback->reportError( QObject::tr(
"Point objects are not supported" ) );
405 warnedPrimitiveTypes.insert( TINYGLTF_MODE_POINTS );
409 case TINYGLTF_MODE_LINE_LOOP:
410 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_LINE_LOOP ) )
412 feedback->reportError( QObject::tr(
"Line loops in are not supported" ) );
413 warnedPrimitiveTypes.insert( TINYGLTF_MODE_LINE_LOOP );
417 case TINYGLTF_MODE_LINE_STRIP:
418 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_LINE_STRIP ) )
420 feedback->reportError( QObject::tr(
"Line strips in are not supported" ) );
421 warnedPrimitiveTypes.insert( TINYGLTF_MODE_LINE_STRIP );
425 case TINYGLTF_MODE_TRIANGLE_STRIP:
426 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_TRIANGLE_STRIP ) )
428 feedback->reportError( QObject::tr(
"Triangular strips are not supported" ) );
429 warnedPrimitiveTypes.insert( TINYGLTF_MODE_TRIANGLE_STRIP );
433 case TINYGLTF_MODE_TRIANGLE_FAN:
434 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_TRIANGLE_FAN ) )
436 feedback->reportError( QObject::tr(
"Triangular fans are not supported" ) );
437 warnedPrimitiveTypes.insert( TINYGLTF_MODE_TRIANGLE_FAN );
442 if ( !warnedPrimitiveTypes.contains( primitive.mode ) )
444 feedback->reportError( QObject::tr(
"Primitive type %1 are not supported" ).arg( primitive.mode ) );
445 warnedPrimitiveTypes.insert( primitive.mode );
452 for (
int childNode : gltfNode.children )
454 traverseNode( childNode, gltfLocalTransform ? *gltfLocalTransform : QMatrix4x4() );
458 if ( !scene.nodes.empty() )
460 for (
const int nodeIndex : scene.nodes )
462 traverseNode( nodeIndex, QMatrix4x4() );
469 polygonSink->finalize();
470 outputs.insert( u
"OUTPUT_POLYGONS"_s, polygonDest );
474 lineSink->finalize();
475 outputs.insert( u
"OUTPUT_LINES"_s, 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...