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 & )
79std::unique_ptr<QgsAbstractGeometry> extractTriangles(
80 const tinygltf::Model &model,
81 const tinygltf::Primitive &primitive,
84 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 || primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT || primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE ) && primitiveAccessor.type == TINYGLTF_TYPE_SCALAR );
128 const char *primitivePtr =
reinterpret_cast<const char *
>( bPrimitive.data.data() ) + bvPrimitive.byteOffset + primitiveAccessor.byteOffset;
130 mp->reserve( primitiveAccessor.count / 3 );
131 for ( std::size_t i = 0; i < primitiveAccessor.count / 3; i++ )
133 unsigned int index1 = 0;
134 unsigned int index2 = 0;
135 unsigned int index3 = 0;
137 if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT )
139 const unsigned short *usPtrPrimitive =
reinterpret_cast<const unsigned short *
>( primitivePtr );
140 if ( bvPrimitive.byteStride )
141 primitivePtr += bvPrimitive.byteStride;
143 primitivePtr += 3 *
sizeof(
unsigned short );
145 index1 = usPtrPrimitive[0];
146 index2 = usPtrPrimitive[1];
147 index3 = usPtrPrimitive[2];
149 else if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE )
151 const unsigned char *usPtrPrimitive =
reinterpret_cast<const unsigned char *
>( primitivePtr );
152 if ( bvPrimitive.byteStride )
153 primitivePtr += bvPrimitive.byteStride;
155 primitivePtr += 3 *
sizeof(
unsigned char );
157 index1 = usPtrPrimitive[0];
158 index2 = usPtrPrimitive[1];
159 index3 = usPtrPrimitive[2];
163 const unsigned int *uintPtrPrimitive =
reinterpret_cast<const unsigned int *
>( primitivePtr );
164 if ( bvPrimitive.byteStride )
165 primitivePtr += bvPrimitive.byteStride;
167 primitivePtr += 3 *
sizeof(
unsigned int );
169 index1 = uintPtrPrimitive[0];
170 index2 = uintPtrPrimitive[1];
171 index3 = uintPtrPrimitive[2];
174 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] ) } ) ) );
180std::unique_ptr<QgsAbstractGeometry> extractLines(
181 const tinygltf::Model &model,
182 const tinygltf::Primitive &primitive,
185 const QMatrix4x4 *gltfLocalTransform,
189 auto posIt = primitive.attributes.find(
"POSITION" );
190 if ( posIt == primitive.attributes.end() )
192 feedback->
reportError( QObject::tr(
"Could not find POSITION attribute for primitive" ) );
195 int positionAccessorIndex = posIt->second;
200 QgsGltfUtils::accessorToMapCoordinates(
209 std::unique_ptr<QgsMultiLineString> ml = std::make_unique<QgsMultiLineString>();
211 if ( primitive.indices == -1 )
213 Q_ASSERT( x.size() % 2 == 0 );
215 ml->reserve( x.size() );
216 for (
int i = 0; i < x.size(); i += 2 )
223 const tinygltf::Accessor &primitiveAccessor = model.accessors[primitive.indices];
224 const tinygltf::BufferView &bvPrimitive = model.bufferViews[primitiveAccessor.bufferView];
225 const tinygltf::Buffer &bPrimitive = model.buffers[bvPrimitive.buffer];
227 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 );
229 const char *primitivePtr =
reinterpret_cast<const char *
>( bPrimitive.data.data() ) + bvPrimitive.byteOffset + primitiveAccessor.byteOffset;
231 ml->reserve( primitiveAccessor.count / 2 );
232 for ( std::size_t i = 0; i < primitiveAccessor.count / 2; i++ )
234 unsigned int index1 = 0;
235 unsigned int index2 = 0;
237 if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT )
239 const unsigned short *usPtrPrimitive =
reinterpret_cast<const unsigned short *
>( primitivePtr );
240 if ( bvPrimitive.byteStride )
241 primitivePtr += bvPrimitive.byteStride;
243 primitivePtr += 2 *
sizeof(
unsigned short );
245 index1 = usPtrPrimitive[0];
246 index2 = usPtrPrimitive[1];
248 else if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE )
250 const unsigned char *usPtrPrimitive =
reinterpret_cast<const unsigned char *
>( primitivePtr );
251 if ( bvPrimitive.byteStride )
252 primitivePtr += bvPrimitive.byteStride;
254 primitivePtr += 2 *
sizeof(
unsigned char );
256 index1 = usPtrPrimitive[0];
257 index2 = usPtrPrimitive[1];
261 const unsigned int *uintPtrPrimitive =
reinterpret_cast<const unsigned int *
>( primitivePtr );
262 if ( bvPrimitive.byteStride )
263 primitivePtr += bvPrimitive.byteStride;
265 primitivePtr += 2 *
sizeof(
unsigned int );
267 index1 = uintPtrPrimitive[0];
268 index2 = uintPtrPrimitive[1];
271 ml->addGeometry(
new QgsLineString( QVector<QgsPoint> {
QgsPoint( x[index1], y[index1], z[index1] ),
QgsPoint( x[index2], y[index2], z[index2] ) } ) );
279 const QString path = parameterAsFile( parameters, QStringLiteral(
"INPUT" ), context );
285 std::unique_ptr<QgsFeatureSink> polygonSink( parameterAsSink( parameters, QStringLiteral(
"OUTPUT_POLYGONS" ), context, polygonDest, fields,
Qgis::WkbType::MultiPolygonZ, destCrs ) );
286 if ( !polygonSink && parameters.value( QStringLiteral(
"OUTPUT_POLYGONS" ) ).isValid() )
289 std::unique_ptr<QgsFeatureSink> lineSink( parameterAsSink( parameters, QStringLiteral(
"OUTPUT_LINES" ), context, lineDest, fields,
Qgis::WkbType::MultiLineStringZ, destCrs ) );
290 if ( !lineSink && parameters.value( QStringLiteral(
"OUTPUT_LINES" ) ).isValid() )
294 QByteArray fileContent;
295 if ( f.open( QIODevice::ReadOnly ) )
297 fileContent = f.readAll();
306 tinygltf::Model model;
309 if ( !QgsGltfUtils::loadGltfModel( fileContent, model, &errors, &warnings ) )
313 if ( !warnings.isEmpty() )
317 feedback->
pushDebugInfo( QObject::tr(
"Found %1 scenes" ).arg( model.scenes.size() ) );
319 bool sceneOk =
false;
320 const std::size_t sceneIndex = QgsGltfUtils::sourceSceneForModel( model, sceneOk );
326 const tinygltf::Scene &scene = model.scenes[sceneIndex];
327 feedback->
pushDebugInfo( QObject::tr(
"Found %1 nodes in default scene [%2]" ).arg( scene.nodes.size() ).arg( sceneIndex ) );
329 QSet<int> warnedPrimitiveTypes;
331 const QgsVector3D tileTranslationEcef = QgsGltfUtils::extractTileTranslation( model );
332 std::function<void(
int nodeIndex,
const QMatrix4x4 &transform )> traverseNode;
333 traverseNode = [&model, feedback, &polygonSink, &lineSink, &warnedPrimitiveTypes, &ecefTransform, &tileTranslationEcef, &traverseNode, ¶meters](
int nodeIndex,
const QMatrix4x4 &parentTransform ) {
334 const tinygltf::Node &gltfNode = model.nodes[nodeIndex];
335 std::unique_ptr<QMatrix4x4> gltfLocalTransform = QgsGltfUtils::parseNodeTransform( gltfNode );
336 if ( !parentTransform.isIdentity() )
338 if ( gltfLocalTransform )
339 *gltfLocalTransform = parentTransform * *gltfLocalTransform;
342 gltfLocalTransform.reset(
new QMatrix4x4( parentTransform ) );
346 if ( gltfNode.mesh >= 0 )
348 const tinygltf::Mesh &mesh = model.meshes[gltfNode.mesh];
349 feedback->pushDebugInfo( QObject::tr(
"Found %1 primitives in node [%2]" ).arg( mesh.primitives.size() ).arg( nodeIndex ) );
351 for (
const tinygltf::Primitive &primitive : mesh.primitives )
353 switch ( primitive.mode )
355 case TINYGLTF_MODE_TRIANGLES:
359 std::unique_ptr<QgsAbstractGeometry> geometry = extractTriangles( model, primitive, ecefTransform, tileTranslationEcef, gltfLocalTransform.get(), feedback );
365 throw QgsProcessingException( writeFeatureError( polygonSink.get(), parameters, QStringLiteral(
"OUTPUT_POLYGONS" ) ) );
371 case TINYGLTF_MODE_LINE:
375 std::unique_ptr<QgsAbstractGeometry> geometry = extractLines( model, primitive, ecefTransform, tileTranslationEcef, gltfLocalTransform.get(), feedback );
381 throw QgsProcessingException( writeFeatureError( lineSink.get(), parameters, QStringLiteral(
"OUTPUT_LINES" ) ) );
387 case TINYGLTF_MODE_POINTS:
388 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_POINTS ) )
390 feedback->reportError( QObject::tr(
"Point objects are not supported" ) );
391 warnedPrimitiveTypes.insert( TINYGLTF_MODE_POINTS );
395 case TINYGLTF_MODE_LINE_LOOP:
396 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_LINE_LOOP ) )
398 feedback->reportError( QObject::tr(
"Line loops in are not supported" ) );
399 warnedPrimitiveTypes.insert( TINYGLTF_MODE_LINE_LOOP );
403 case TINYGLTF_MODE_LINE_STRIP:
404 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_LINE_STRIP ) )
406 feedback->reportError( QObject::tr(
"Line strips in are not supported" ) );
407 warnedPrimitiveTypes.insert( TINYGLTF_MODE_LINE_STRIP );
411 case TINYGLTF_MODE_TRIANGLE_STRIP:
412 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_TRIANGLE_STRIP ) )
414 feedback->reportError( QObject::tr(
"Triangular strips are not supported" ) );
415 warnedPrimitiveTypes.insert( TINYGLTF_MODE_TRIANGLE_STRIP );
419 case TINYGLTF_MODE_TRIANGLE_FAN:
420 if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_TRIANGLE_FAN ) )
422 feedback->reportError( QObject::tr(
"Triangular fans are not supported" ) );
423 warnedPrimitiveTypes.insert( TINYGLTF_MODE_TRIANGLE_FAN );
428 if ( !warnedPrimitiveTypes.contains( primitive.mode ) )
430 feedback->reportError( QObject::tr(
"Primitive type %1 are not supported" ).arg( primitive.mode ) );
431 warnedPrimitiveTypes.insert( primitive.mode );
438 for (
int childNode : gltfNode.children )
440 traverseNode( childNode, gltfLocalTransform ? *gltfLocalTransform : QMatrix4x4() );
444 if ( !scene.nodes.empty() )
446 for (
const int nodeIndex : scene.nodes )
448 traverseNode( nodeIndex, QMatrix4x4() );
455 polygonSink->finalize();
456 outputs.insert( QStringLiteral(
"OUTPUT_POLYGONS" ), polygonDest );
460 lineSink->finalize();
461 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...