26#include <QRegularExpression> 
   28#define TINYGLTF_IMPLEMENTATION        
   33#define TINYGLTF_ENABLE_DRACO 
   36#define TINYGLTF_NO_STB_IMAGE          
   37#define TINYGLTF_NO_STB_IMAGE_WRITE    
   46bool QgsGltfUtils::accessorToMapCoordinates( 
const tinygltf::Model &model, 
int accessorIndex, 
const QgsMatrix4x4 &tileTransform, 
const QgsCoordinateTransform *ecefToTargetCrs, 
const QgsVector3D &tileTranslationEcef, 
const QMatrix4x4 *nodeTransform, 
Qgis::Axis gltfUpAxis, QVector<double> &vx, QVector<double> &vy, QVector<double> &vz )
 
   48  const tinygltf::Accessor &accessor = model.accessors[accessorIndex];
 
   49  const tinygltf::BufferView &bv = model.bufferViews[accessor.bufferView];
 
   50  const tinygltf::Buffer &b = model.buffers[bv.buffer];
 
   52  if ( accessor.componentType != TINYGLTF_PARAMETER_TYPE_FLOAT || accessor.type != TINYGLTF_TYPE_VEC3 )
 
   58  const unsigned char *ptr = b.data.data() + bv.byteOffset + accessor.byteOffset;
 
   60  vx.resize( accessor.count );
 
   61  vy.resize( accessor.count );
 
   62  vz.resize( accessor.count );
 
   63  double *vxOut = vx.data();
 
   64  double *vyOut = vy.data();
 
   65  double *vzOut = vz.data();
 
   66  for ( 
int i = 0; i < static_cast<int>( accessor.count ); ++i )
 
   68    const float *fptr = 
reinterpret_cast<const float *
>( ptr );
 
   69    QVector3D vOrig( fptr[0], fptr[1], fptr[2] );
 
   72      vOrig = nodeTransform->map( vOrig );
 
   79        QgsDebugError( QStringLiteral( 
"X up translation not yet supported" ) );
 
   80        v = tileTransform.
map( tileTranslationEcef );
 
   87        QVector3D vFlip( vOrig.x(), -vOrig.z(), vOrig.y() );
 
   88        v = tileTransform.
map( 
QgsVector3D( vFlip ) + tileTranslationEcef );
 
   94        v = tileTransform.
map( 
QgsVector3D( vOrig ) + tileTranslationEcef );
 
  104      ptr += bv.byteStride;
 
  106      ptr += 3 * 
sizeof( float );
 
  109  if ( ecefToTargetCrs )
 
  113      ecefToTargetCrs->
transformCoords( accessor.count, vx.data(), vy.data(), vz.data() );
 
  124bool QgsGltfUtils::extractTextureCoordinates( 
const tinygltf::Model &model, 
int accessorIndex, QVector<float> &x, QVector<float> &y )
 
  126  const tinygltf::Accessor &accessor = model.accessors[accessorIndex];
 
  127  const tinygltf::BufferView &bv = model.bufferViews[accessor.bufferView];
 
  128  const tinygltf::Buffer &b = model.buffers[bv.buffer];
 
  130  if ( accessor.componentType != TINYGLTF_PARAMETER_TYPE_FLOAT || accessor.type != TINYGLTF_TYPE_VEC2 )
 
  135  const unsigned char *ptr = b.data.data() + bv.byteOffset + accessor.byteOffset;
 
  136  x.resize( accessor.count );
 
  137  y.resize( accessor.count );
 
  139  float *xOut = x.data();
 
  140  float *yOut = y.data();
 
  142  for ( std::size_t i = 0; i < accessor.count; i++ )
 
  144    const float *fptr = 
reinterpret_cast< const float * 
>( ptr );
 
  150      ptr += bv.byteStride;
 
  152      ptr += 2 * 
sizeof( float );
 
  157QgsGltfUtils::ResourceType QgsGltfUtils::imageResourceType( 
const tinygltf::Model &model, 
int index )
 
  159  const tinygltf::Image &img = model.images[index];
 
  161  if ( !img.image.empty() )
 
  163    return ResourceType::Embedded;
 
  167    return ResourceType::Linked;
 
  171QImage QgsGltfUtils::extractEmbeddedImage( 
const tinygltf::Model &model, 
int index )
 
  173  const tinygltf::Image &img = model.images[index];
 
  174  if ( !img.image.empty() )
 
  175    return QImage( img.image.data(), img.width, img.height, QImage::Format_ARGB32 );
 
  180QString QgsGltfUtils::linkedImagePath( 
const tinygltf::Model &model, 
int index )
 
  182  const tinygltf::Image &img = model.images[index];
 
  183  return QString::fromStdString( img.uri );
 
  186std::unique_ptr<QMatrix4x4> QgsGltfUtils::parseNodeTransform( 
const tinygltf::Node &node )
 
  190  std::unique_ptr<QMatrix4x4> matrix;
 
  191  if ( !node.matrix.empty() )
 
  193    matrix.reset( 
new QMatrix4x4 );
 
  194    float *mdata = matrix->data();
 
  195    for ( 
int i = 0; i < 16; ++i )
 
  196      mdata[i] = 
static_cast< float >( node.matrix[i] );
 
  198  else if ( node.translation.size() || node.rotation.size() || node.scale.size() )
 
  200    matrix.reset( 
new QMatrix4x4 );
 
  201    if ( node.scale.size() )
 
  203      matrix->scale( 
static_cast< float >( node.scale[0] ), 
static_cast< float >( node.scale[1] ), 
static_cast< float >( node.scale[2] ) );
 
  205    if ( node.rotation.size() )
 
  207      matrix->rotate( QQuaternion( 
static_cast< float >( node.rotation[3] ), 
static_cast< float >( node.rotation[0] ), 
static_cast< float >( node.rotation[1] ), 
static_cast< float >( node.rotation[2] ) ) );
 
  209    if ( node.translation.size() )
 
  211      matrix->translate( 
static_cast< float >( node.translation[0] ), 
static_cast< float >( node.translation[1] ), 
static_cast< float >( node.translation[2] ) );
 
  220  bool sceneOk = 
false;
 
  221  const std::size_t sceneIndex = QgsGltfUtils::sourceSceneForModel( model, sceneOk );
 
  227  const tinygltf::Scene &scene = model.scenes[sceneIndex];
 
  230  auto it = model.extensions.find( 
"CESIUM_RTC" );
 
  231  if ( it != model.extensions.end() )
 
  233    const tinygltf::Value v = it->second;
 
  234    if ( v.IsObject() && v.Has( 
"center" ) )
 
  236      const tinygltf::Value center = v.Get( 
"center" );
 
  237      if ( center.IsArray() && center.Size() == 3 )
 
  239        tileTranslationEcef = 
QgsVector3D( center.Get( 0 ).GetNumberAsDouble(), center.Get( 1 ).GetNumberAsDouble(), center.Get( 2 ).GetNumberAsDouble() );
 
  244  if ( scene.nodes.size() == 0 )
 
  247  int rootNodeIndex = scene.nodes[0];
 
  248  tinygltf::Node &rootNode = model.nodes[rootNodeIndex];
 
  250  if ( tileTranslationEcef.
isNull() && rootNode.translation.size() )
 
  252    QgsVector3D rootTranslation( rootNode.translation[0], rootNode.translation[1], rootNode.translation[2] );
 
  256    if ( rootTranslation.length() > 1e6 )
 
  261          QgsDebugError( QStringLiteral( 
"X up translation not yet supported" ) );
 
  266          tileTranslationEcef = 
QgsVector3D( rootTranslation.x(), -rootTranslation.z(), rootTranslation.y() );
 
  267          rootNode.translation[0] = rootNode.translation[1] = rootNode.translation[2] = 0;
 
  272          tileTranslationEcef = 
QgsVector3D( rootTranslation.x(), rootTranslation.y(), rootTranslation.z() );
 
  273          rootNode.translation[0] = rootNode.translation[1] = rootNode.translation[2] = 0;
 
  280  return tileTranslationEcef;
 
  284bool QgsGltfUtils::loadImageDataWithQImage(
 
  285  tinygltf::Image *image, 
const int image_idx, std::string *err,
 
  286  std::string *warn, 
int req_width, 
int req_height,
 
  287  const unsigned char *bytes, 
int size, 
void *user_data )
 
  290  if ( req_width != 0 || req_height != 0 )
 
  294      ( *err ) += 
"Expecting zero req_width/req_height.\n";
 
  303  if ( !img.loadFromData( bytes, size ) )
 
  308        "Unknown image format. QImage cannot decode image data for image[" +
 
  309        std::to_string( image_idx ) + 
"] name = \"" + image->name + 
"\".\n";
 
  314  if ( img.format() != QImage::Format_RGB32 && img.format() != QImage::Format_ARGB32 )
 
  317    img.convertTo( QImage::Format_RGB32 );
 
  320  image->width = img.width();
 
  321  image->height = img.height();
 
  322  image->component = 4;
 
  324  image->pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
 
  326  image->image.resize( 
static_cast<size_t>( image->width * image->height * image->component ) * 
size_t( image->bits / 8 ) );
 
  327  std::copy( img.constBits(), img.constBits() + 
static_cast< std::size_t 
>( image->width ) * image->height * image->component * ( image->bits / 8 ), image->image.begin() );
 
  332bool QgsGltfUtils::loadGltfModel( 
const QByteArray &data, tinygltf::Model &model, QString *errors, QString *warnings )
 
  334  tinygltf::TinyGLTF loader;
 
  336  loader.SetImageLoader( QgsGltfUtils::loadImageDataWithQImage, 
nullptr );
 
  342  loader.SetParseStrictness( tinygltf::ParseStrictness::Permissive );
 
  345  std::string err, warn;
 
  348  if ( data.startsWith( 
"glTF" ) )   
 
  350    if ( data.at( 4 ) == 1 )
 
  352      *errors = QObject::tr( 
"GLTF version 1 tiles cannot be loaded" );
 
  355    res = loader.LoadBinaryFromMemory( &model, &err, &warn,
 
  356                                       ( 
const unsigned char * )data.constData(), data.size(), baseDir );
 
  360    res = loader.LoadASCIIFromString( &model, &err, &warn,
 
  361                                      data.constData(), data.size(), baseDir );
 
  365    *errors = QString::fromStdString( err );
 
  368    *warnings = QString::fromStdString( warn );
 
  371    const thread_local QRegularExpression rxFailedToLoadExternalUriForImage( QStringLiteral( 
"Failed to load external 'uri' for image\\[\\d+\\] name = \".*?\"\\n?" ) );
 
  372    warnings->replace( rxFailedToLoadExternalUriForImage, QString() );
 
  373    const thread_local QRegularExpression rxFileNotFound( QStringLiteral( 
"File not found : .*?\\n" ) );
 
  374    warnings->replace( rxFileNotFound, QString() );
 
  380std::size_t QgsGltfUtils::sourceSceneForModel( 
const tinygltf::Model &model, 
bool &ok )
 
  383  if ( model.scenes.empty() )
 
  389  int index = model.defaultScene;
 
  390  if ( index >= 0 && 
static_cast< std::size_t
>( index ) < model.scenes.size() )
 
Custom exception class for Coordinate Reference System related exceptions.
 
A simple 4x4 matrix implementation useful for transformation in 3D space.
 
QgsVector3D map(const QgsVector3D &vector) const
Matrix-vector multiplication (vector is converted to homogeneous coordinates [X,Y,...
 
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
 
double y() const
Returns Y coordinate.
 
double z() const
Returns Z coordinate.
 
bool isNull() const
Returns true if all three coordinates are zero.
 
double x() const
Returns X coordinate.
 
#define QgsDebugError(str)