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)