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( 
"This algorithm converts GLTF content to standard vector layer formats." );
 
   66QString QgsGltfToVectorFeaturesAlgorithm::shortDescription()
 const 
   68  return QObject::tr( 
"Converts GLTF content to standard vector layer formats." );
 
   71QgsGltfToVectorFeaturesAlgorithm *QgsGltfToVectorFeaturesAlgorithm::createInstance()
 const 
   73  return new QgsGltfToVectorFeaturesAlgorithm();
 
   76void QgsGltfToVectorFeaturesAlgorithm::initAlgorithm( 
const QVariantMap & )
 
   84std::unique_ptr<QgsAbstractGeometry> extractTriangles(
 
   85  const tinygltf::Model &model,
 
   86  const tinygltf::Primitive &primitive,
 
   89  const QMatrix4x4 *gltfLocalTransform,
 
   93  auto posIt = primitive.attributes.find( 
"POSITION" );
 
   94  if ( posIt == primitive.attributes.end() )
 
   96    feedback->
reportError( QObject::tr( 
"Could not find POSITION attribute for primitive" ) );
 
   99  int positionAccessorIndex = posIt->second;
 
  104  QgsGltfUtils::accessorToMapCoordinates(
 
  113  auto mp = std::make_unique<QgsMultiPolygon>();
 
  115  if ( primitive.indices == -1 )
 
  117    Q_ASSERT( x.size() % 3 == 0 );
 
  119    mp->reserve( x.size() );
 
  120    for ( 
int i = 0; i < x.size(); i += 3 )
 
  122      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] ) } ) ) );
 
  127    const tinygltf::Accessor &primitiveAccessor = model.accessors[primitive.indices];
 
  128    const tinygltf::BufferView &bvPrimitive = model.bufferViews[primitiveAccessor.bufferView];
 
  129    const tinygltf::Buffer &bPrimitive = model.buffers[bvPrimitive.buffer];
 
  131    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 );
 
  133    const char *primitivePtr = 
reinterpret_cast<const char *
>( bPrimitive.data.data() ) + bvPrimitive.byteOffset + primitiveAccessor.byteOffset;
 
  135    mp->reserve( primitiveAccessor.count / 3 );
 
  136    for ( std::size_t i = 0; i < primitiveAccessor.count / 3; i++ )
 
  138      unsigned int index1 = 0;
 
  139      unsigned int index2 = 0;
 
  140      unsigned int index3 = 0;
 
  142      if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT )
 
  144        const unsigned short *usPtrPrimitive = 
reinterpret_cast<const unsigned short *
>( primitivePtr );
 
  145        if ( bvPrimitive.byteStride )
 
  146          primitivePtr += bvPrimitive.byteStride;
 
  148          primitivePtr += 3 * 
sizeof( 
unsigned short );
 
  150        index1 = usPtrPrimitive[0];
 
  151        index2 = usPtrPrimitive[1];
 
  152        index3 = usPtrPrimitive[2];
 
  154      else if ( primitiveAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE )
 
  156        const unsigned char *usPtrPrimitive = 
reinterpret_cast<const unsigned char *
>( primitivePtr );
 
  157        if ( bvPrimitive.byteStride )
 
  158          primitivePtr += bvPrimitive.byteStride;
 
  160          primitivePtr += 3 * 
sizeof( 
unsigned char );
 
  162        index1 = usPtrPrimitive[0];
 
  163        index2 = usPtrPrimitive[1];
 
  164        index3 = usPtrPrimitive[2];
 
  168        const unsigned int *uintPtrPrimitive = 
reinterpret_cast<const unsigned int *
>( primitivePtr );
 
  169        if ( bvPrimitive.byteStride )
 
  170          primitivePtr += bvPrimitive.byteStride;
 
  172          primitivePtr += 3 * 
sizeof( 
unsigned int );
 
  174        index1 = uintPtrPrimitive[0];
 
  175        index2 = uintPtrPrimitive[1];
 
  176        index3 = uintPtrPrimitive[2];
 
  179      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] ) } ) ) );
 
  185std::unique_ptr<QgsAbstractGeometry> extractLines(
 
  186  const tinygltf::Model &model,
 
  187  const tinygltf::Primitive &primitive,
 
  190  const QMatrix4x4 *gltfLocalTransform,
 
  194  auto posIt = primitive.attributes.find( 
"POSITION" );
 
  195  if ( posIt == primitive.attributes.end() )
 
  197    feedback->
reportError( QObject::tr( 
"Could not find POSITION attribute for primitive" ) );
 
  200  int positionAccessorIndex = posIt->second;
 
  205  QgsGltfUtils::accessorToMapCoordinates(
 
  214  auto ml = std::make_unique<QgsMultiLineString>();
 
  216  if ( primitive.indices == -1 )
 
  218    Q_ASSERT( x.size() % 2 == 0 );
 
  220    ml->reserve( x.size() );
 
  221    for ( 
int i = 0; i < x.size(); i += 2 )
 
  228    const tinygltf::Accessor &primitiveAccessor = model.accessors[primitive.indices];
 
  229    const tinygltf::BufferView &bvPrimitive = model.bufferViews[primitiveAccessor.bufferView];
 
  230    const tinygltf::Buffer &bPrimitive = model.buffers[bvPrimitive.buffer];
 
  232    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 );
 
  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, 
Qgis::WkbType::MultiPolygonZ, destCrs ) );
 
  291  if ( !polygonSink && parameters.value( QStringLiteral( 
"OUTPUT_POLYGONS" ) ).isValid() )
 
  294  std::unique_ptr<QgsFeatureSink> lineSink( parameterAsSink( parameters, QStringLiteral( 
"OUTPUT_LINES" ), context, lineDest, fields, 
Qgis::WkbType::MultiLineStringZ, destCrs ) );
 
  295  if ( !lineSink && parameters.value( QStringLiteral( 
"OUTPUT_LINES" ) ).isValid() )
 
  299  QByteArray fileContent;
 
  300  if ( f.open( QIODevice::ReadOnly ) )
 
  302    fileContent = f.readAll();
 
  311  tinygltf::Model model;
 
  314  if ( !QgsGltfUtils::loadGltfModel( fileContent, model, &errors, &warnings ) )
 
  318  if ( !warnings.isEmpty() )
 
  322  feedback->
pushDebugInfo( QObject::tr( 
"Found %1 scenes" ).arg( model.scenes.size() ) );
 
  324  bool sceneOk = 
false;
 
  325  const std::size_t sceneIndex = QgsGltfUtils::sourceSceneForModel( model, sceneOk );
 
  331  const tinygltf::Scene &scene = model.scenes[sceneIndex];
 
  332  feedback->
pushDebugInfo( QObject::tr( 
"Found %1 nodes in default scene [%2]" ).arg( scene.nodes.size() ).arg( sceneIndex ) );
 
  334  QSet<int> warnedPrimitiveTypes;
 
  336  const QgsVector3D tileTranslationEcef = QgsGltfUtils::extractTileTranslation( model );
 
  337  std::function<void( 
int nodeIndex, 
const QMatrix4x4 &transform )> traverseNode;
 
  338  traverseNode = [&model, feedback, &polygonSink, &lineSink, &warnedPrimitiveTypes, &ecefTransform, &tileTranslationEcef, &traverseNode, ¶meters]( 
int nodeIndex, 
const QMatrix4x4 &parentTransform ) {
 
  339    const tinygltf::Node &gltfNode = model.nodes[nodeIndex];
 
  340    std::unique_ptr<QMatrix4x4> gltfLocalTransform = QgsGltfUtils::parseNodeTransform( gltfNode );
 
  341    if ( !parentTransform.isIdentity() )
 
  343      if ( gltfLocalTransform )
 
  344        *gltfLocalTransform = parentTransform * *gltfLocalTransform;
 
  347        gltfLocalTransform.reset( 
new QMatrix4x4( parentTransform ) );
 
  351    if ( gltfNode.mesh >= 0 )
 
  353      const tinygltf::Mesh &mesh = model.meshes[gltfNode.mesh];
 
  354      feedback->pushDebugInfo( QObject::tr( 
"Found %1 primitives in node [%2]" ).arg( mesh.primitives.size() ).arg( nodeIndex ) );
 
  356      for ( 
const tinygltf::Primitive &primitive : mesh.primitives )
 
  358        switch ( primitive.mode )
 
  360          case TINYGLTF_MODE_TRIANGLES:
 
  364              std::unique_ptr<QgsAbstractGeometry> geometry = extractTriangles( model, primitive, ecefTransform, tileTranslationEcef, gltfLocalTransform.get(), feedback );
 
  370                  throw QgsProcessingException( writeFeatureError( polygonSink.get(), parameters, QStringLiteral( 
"OUTPUT_POLYGONS" ) ) );
 
  376          case TINYGLTF_MODE_LINE:
 
  380              std::unique_ptr<QgsAbstractGeometry> geometry = extractLines( model, primitive, ecefTransform, tileTranslationEcef, gltfLocalTransform.get(), feedback );
 
  386                  throw QgsProcessingException( writeFeatureError( lineSink.get(), parameters, QStringLiteral( 
"OUTPUT_LINES" ) ) );
 
  392          case TINYGLTF_MODE_POINTS:
 
  393            if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_POINTS ) )
 
  395              feedback->reportError( QObject::tr( 
"Point objects are not supported" ) );
 
  396              warnedPrimitiveTypes.insert( TINYGLTF_MODE_POINTS );
 
  400          case TINYGLTF_MODE_LINE_LOOP:
 
  401            if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_LINE_LOOP ) )
 
  403              feedback->reportError( QObject::tr( 
"Line loops in are not supported" ) );
 
  404              warnedPrimitiveTypes.insert( TINYGLTF_MODE_LINE_LOOP );
 
  408          case TINYGLTF_MODE_LINE_STRIP:
 
  409            if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_LINE_STRIP ) )
 
  411              feedback->reportError( QObject::tr( 
"Line strips in are not supported" ) );
 
  412              warnedPrimitiveTypes.insert( TINYGLTF_MODE_LINE_STRIP );
 
  416          case TINYGLTF_MODE_TRIANGLE_STRIP:
 
  417            if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_TRIANGLE_STRIP ) )
 
  419              feedback->reportError( QObject::tr( 
"Triangular strips are not supported" ) );
 
  420              warnedPrimitiveTypes.insert( TINYGLTF_MODE_TRIANGLE_STRIP );
 
  424          case TINYGLTF_MODE_TRIANGLE_FAN:
 
  425            if ( !warnedPrimitiveTypes.contains( TINYGLTF_MODE_TRIANGLE_FAN ) )
 
  427              feedback->reportError( QObject::tr( 
"Triangular fans are not supported" ) );
 
  428              warnedPrimitiveTypes.insert( TINYGLTF_MODE_TRIANGLE_FAN );
 
  433            if ( !warnedPrimitiveTypes.contains( primitive.mode ) )
 
  435              feedback->reportError( QObject::tr( 
"Primitive type %1 are not supported" ).arg( primitive.mode ) );
 
  436              warnedPrimitiveTypes.insert( primitive.mode );
 
  443    for ( 
int childNode : gltfNode.children )
 
  445      traverseNode( childNode, gltfLocalTransform ? *gltfLocalTransform : QMatrix4x4() );
 
  449  if ( !scene.nodes.empty() )
 
  451    for ( 
const int nodeIndex : scene.nodes )
 
  453      traverseNode( nodeIndex, QMatrix4x4() );
 
  460    polygonSink->finalize();
 
  461    outputs.insert( QStringLiteral( 
"OUTPUT_POLYGONS" ), polygonDest );
 
  465    lineSink->finalize();
 
  466    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...