QGIS API Documentation 3.43.0-Master (58029bba303)
qgsquantizedmeshtiles.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsquantizedmeshtiles.cpp
3 ----------------------------
4 begin : May 2024
5 copyright : (C) 2024 by David Koňařík
6 email : dvdkon at konarici dot cz
7
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
18
19#include "qgsexception.h"
20#include <algorithm>
21#include <cstddef>
22#include <qdebug.h>
23#include <qglobal.h>
24#include <qstringliteral.h>
25#include <qvector3d.h>
26
28
32class VectorStream
33{
34 public:
35 size_t mOffset = 0;
36 VectorStream( const QByteArray &vec ) : mVec( vec ) {}
37 const void *read( size_t bytes )
38 {
39 if ( ( size_t ) mVec.size() < mOffset + bytes )
40 {
41 throw QgsQuantizedMeshParsingException( "Tried to read beyond EOF when parsing quantized mesh tile" );
42 }
43 const void *data = mVec.data() + mOffset;
44 mOffset += bytes;
45 return data;
46 }
47 size_t remaining() const
48 {
49 return mVec.size() - mOffset;
50 }
51 private:
52 const QByteArray &mVec;
53};
54
56
57constexpr uint8_t normalsExtensionId = 1;
58
59// Algorithm from http://jcgt.org/published/0003/02/01/
60static QVector3D oct16Decode( uint8_t x, uint8_t y )
61{
62 if ( x == 0 && y == 0 )
63 return QVector3D( 0, 0, 0 );
64 float fx = x / 255.0f * 2.0f - 1.0f;
65 float fy = y / 255.0f * 2.0f - 1.0f;
66 QVector3D decoded( fx, fy, 1.0f - abs( fx ) - abs( fy ) );
67 if ( decoded.z() < 0 )
68 {
69 decoded.setX( ( 1.0f - abs( fy ) ) * ( fx >= 0 ? 1.0f : -1.0f ) );
70 decoded.setY( ( 1.0f - abs( fx ) ) * ( fy >= 0 ? 1.0f : -1.0f ) );
71 }
72 decoded.normalize();
73 return decoded;
74}
75
76// Copied from specification
77static uint16_t zigZagDecode( uint16_t value )
78{
79 return ( value >> 1 ) ^ ( -( value & 1 ) );
80}
81
82static std::vector<uint32_t> parseU32OrU16Array( VectorStream &stream,
83 bool isU32, size_t countMult )
84{
85 std::vector<uint32_t> values;
86 if ( isU32 )
87 {
88 // Pad to multiple of 4
89 stream.read( stream.mOffset % 4 );
90 const uint32_t *countBase =
91 reinterpret_cast<const uint32_t *>( stream.read( sizeof( uint32_t ) ) );
92 size_t count = static_cast<size_t>( *countBase ) * countMult;
93 values.resize( count );
94 const uint32_t *valuesRaw = reinterpret_cast<const uint32_t *>(
95 stream.read( sizeof( uint32_t ) * count ) );
96 std::copy( valuesRaw, valuesRaw + count, values.begin() );
97 }
98 else
99 {
100 const uint32_t *countBase =
101 reinterpret_cast<const uint32_t *>( stream.read( sizeof( uint32_t ) ) );
102 size_t count = static_cast<size_t>( *countBase ) * countMult;
103 values.resize( count );
104 const uint16_t *valuesRaw = reinterpret_cast<const uint16_t *>(
105 stream.read( sizeof( uint16_t ) * count ) );
106 std::copy( valuesRaw, valuesRaw + count, values.begin() );
107 }
108 return values;
109}
110
112{
113 // Check if machine is big endian
114 uint16_t endiannessCheck = 0x1020;
115 if ( reinterpret_cast<char *>( &endiannessCheck )[0] == 0x10 )
117 "Parsing quantized mesh tiles on big endian machines is not supported" );
118
119 VectorStream stream( data );
120 mHeader = *reinterpret_cast<const QgsQuantizedMeshHeader *>(
121 stream.read( sizeof( QgsQuantizedMeshHeader ) ) );
122
123 auto uArr = reinterpret_cast<const uint16_t *>(
124 stream.read( sizeof( uint16_t ) * mHeader.vertexCount ) );
125 auto vArr = reinterpret_cast<const uint16_t *>(
126 stream.read( sizeof( uint16_t ) * mHeader.vertexCount ) );
127 auto heightArr = reinterpret_cast<const uint16_t *>(
128 stream.read( sizeof( uint16_t ) * mHeader.vertexCount ) );
129
130 uint16_t u = 0, v = 0, height = 0;
131 mVertexCoords.resize( static_cast<size_t>( mHeader.vertexCount ) * 3 );
132 for ( size_t i = 0; i < mHeader.vertexCount; i++ )
133 {
134 u += zigZagDecode( uArr[i] );
135 v += zigZagDecode( vArr[i] );
136 height += zigZagDecode( heightArr[i] );
137
138 mVertexCoords[i * 3] = u;
139 mVertexCoords[i * 3 + 1] = v;
140 mVertexCoords[i * 3 + 2] = height;
141 }
142
143 bool vertexIndices32Bit = mHeader.vertexCount > 65536;
145 parseU32OrU16Array( stream, vertexIndices32Bit, 3 );
146 uint32_t highest = 0;
147 for ( auto &idx : mTriangleIndices )
148 {
149 uint32_t code = idx;
150 idx = highest - code;
151 if ( code == 0 )
152 highest++;
153 }
154
156 parseU32OrU16Array( stream, vertexIndices32Bit, 1 );
158 parseU32OrU16Array( stream, vertexIndices32Bit, 1 );
160 parseU32OrU16Array( stream, vertexIndices32Bit, 1 );
162 parseU32OrU16Array( stream, vertexIndices32Bit, 1 );
163
164 while ( stream.remaining() > 0 )
165 {
166 uint8_t extensionId =
167 *reinterpret_cast<const uint8_t *>( stream.read( sizeof( char ) ) );
168 uint32_t length =
169 *reinterpret_cast<const uint32_t *>( stream.read( sizeof( uint32_t ) ) );
170
171 if ( extensionId == normalsExtensionId )
172 {
173 mNormalCoords.reserve( mHeader.vertexCount * 3 );
174 for ( size_t i = 0; i < mHeader.vertexCount; i++ )
175 {
176 auto normal = oct16Decode(
177 *reinterpret_cast<const uint8_t *>( stream.read( sizeof( char ) ) ),
178 *reinterpret_cast<const uint8_t *>( stream.read( sizeof( char ) ) ) );
179 mNormalCoords.insert( mNormalCoords.end(), {normal.x(), normal.y(), normal.z()} );
180 }
181 continue;
182 }
183
184 std::vector<char> data( length );
185 const char *dataPtr = reinterpret_cast<const char *>( stream.read( length ) );
186 std::copy( dataPtr, dataPtr + length, data.begin() );
187 mExtensions[extensionId] = std::move( data );
188 }
189}
190
192{
193 std::vector<uint32_t> newTriangleIndices;
194 for ( size_t i = 0; i < mTriangleIndices.size(); i += 3 )
195 {
196 uint32_t a = mTriangleIndices[i];
197 uint32_t b = mTriangleIndices[i + 1];
198 uint32_t c = mTriangleIndices[i + 2];
199 if ( a != b && b != c && a != c )
200 {
201 newTriangleIndices.insert( newTriangleIndices.end(), {a, b, c} );
202 }
203 }
204 mTriangleIndices = std::move( newTriangleIndices );
205}
206
208{
209 auto vertexAsVector = [ this ]( size_t idx )
210 {
211 Q_ASSERT( idx * 3 + 2 < mVertexCoords.size() );
212 return QVector3D( mVertexCoords[idx * 3], mVertexCoords[idx * 3 + 1], mVertexCoords[idx * 3 + 2] );
213 };
214
215 Q_ASSERT( mNormalCoords.size() == 0 );
216 mNormalCoords.resize( mVertexCoords.size(), 0.0 );
217
218 // Sum up contributing normals from all triangles
219 for ( size_t i = 0; i < mTriangleIndices.size(); i += 3 )
220 {
221 std::array<size_t, 3> vtxIdxs {mTriangleIndices[i], mTriangleIndices[i + 1], mTriangleIndices[i + 2]};
222 auto a = vertexAsVector( vtxIdxs[0] );
223 auto b = vertexAsVector( vtxIdxs[1] );
224 auto c = vertexAsVector( vtxIdxs[2] );
225 auto n = QVector3D::crossProduct( b - a, c - a );
226 n.normalize();
227 for ( auto vtx : vtxIdxs )
228 {
229 mNormalCoords[vtx * 3] += n.x();
230 mNormalCoords[vtx * 3 + 1] += n.y();
231 mNormalCoords[vtx * 3 + 2] += n.z();
232 }
233 }
234
235 // Normalize (average over triangles)
236 for ( size_t i = 0; i < mNormalCoords.size(); i += 3 )
237 {
238 QVector3D n( mNormalCoords[i], mNormalCoords[i + 1], mNormalCoords[i + 2] );
239 n.normalize();
240 mNormalCoords[i] = n.x();
241 mNormalCoords[i + 1] = n.y();
242 mNormalCoords[i + 2] = n.z();
243 }
244}
245
246tinygltf::Model QgsQuantizedMeshTile::toGltf( bool addSkirt, double skirtDepth, bool withTextureCoords )
247{
248 tinygltf::Model model;
249
250 tinygltf::Buffer vertexBuffer;
251 vertexBuffer.data.resize( mVertexCoords.size() * sizeof( float ) );
252 std::vector<double> coordMinimums = {32767, 32767, mHeader.MaximumHeight};
253 std::vector<double> coordMaximums = {0, 0, mHeader.MinimumHeight};
254
255 for ( size_t i = 0; i < mVertexCoords.size(); i++ )
256 {
257 double coord = mVertexCoords[i] / 32767.0; // Rescale to 0.0 -- 1.0;
258 // Rescale Z to height in meters
259 if ( i % 3 == 2 )
260 {
262 }
263 ( ( float * ) vertexBuffer.data.data() )[i] = ( float )coord;
264 if ( coordMinimums[i % 3] > coord )
265 coordMinimums[i % 3] = coord;
266 if ( coordMaximums[i % 3] < coord )
267 coordMaximums[i % 3] = coord;
268 }
269 tinygltf::Buffer triangleBuffer;
270 triangleBuffer.data.resize( mTriangleIndices.size() * sizeof( uint32_t ) );
271 const char *triData = reinterpret_cast<const char *>( mTriangleIndices.data() );
272 std::copy( triData, triData + triangleBuffer.data.size(), triangleBuffer.data.begin() );
273
274 if ( addSkirt )
275 {
276 // We first need to sort the edge-indices by coordinate to later create a quad for each "gap"
277 std::sort( mWestVertices.begin(), mWestVertices.end(), [&]( uint32_t a, uint32_t b )
278 {
279 return mVertexCoords[a * 3 + 1] < mVertexCoords[b * 3 + 1];
280 } );
281 std::sort( mSouthVertices.begin(), mSouthVertices.end(), [&]( uint32_t a, uint32_t b )
282 {
283 return mVertexCoords[a * 3] > mVertexCoords[b * 3];
284 } );
285 std::sort( mEastVertices.begin(), mEastVertices.end(), [&]( uint32_t a, uint32_t b )
286 {
287 return mVertexCoords[a * 3 + 1] > mVertexCoords[b * 3 + 1];
288 } );
289 std::sort( mNorthVertices.begin(), mNorthVertices.end(), [&]( uint32_t a, uint32_t b )
290 {
291 return mVertexCoords[a * 3] < mVertexCoords[b * 3];
292 } );
293
294 size_t edgeVertexCount = mWestVertices.size() + mSouthVertices.size() + mEastVertices.size() + mNorthVertices.size();
295 size_t skirtBottomCoordCount =
296 ( ( mWestVertices.size() > 1 ) +
297 ( mSouthVertices.size() > 1 ) +
298 ( mEastVertices.size() > 1 ) +
299 ( mNorthVertices.size() > 1 ) ) * 6;
300 // Add new vertex for each existing edge vertex, projected to Z = minHeight
301 coordMinimums[2] = mHeader.MinimumHeight - skirtDepth;
302 size_t skirtVerticesIdxStart = mVertexCoords.size() / 3;
303 vertexBuffer.data.resize( vertexBuffer.data.size() + ( edgeVertexCount * 3 + skirtBottomCoordCount ) * sizeof( float ) );
304 float *skirtVertexCoords = ( float * )( vertexBuffer.data.data() + ( skirtVerticesIdxStart * 3 * sizeof( float ) ) );
305 auto addSkirtVertices = [&]( const std::vector<uint32_t> &idxs )
306 {
307 size_t startIdx = ( ( uint8_t * ) skirtVertexCoords - vertexBuffer.data.data() ) / sizeof( float ) / 3;
308 for ( uint32_t idx : idxs )
309 {
310 *skirtVertexCoords++ = mVertexCoords[idx * 3] / 32767.0f;
311 *skirtVertexCoords++ = mVertexCoords[idx * 3 + 1] / 32767.0f;
312 *skirtVertexCoords++ = mHeader.MinimumHeight;
313 }
314
315 if ( idxs.size() > 1 )
316 {
317 // Add two vertices at two corners, skirtDepth below the tile bottom
318 *skirtVertexCoords++ = mVertexCoords[idxs[0] * 3] / 32767.0f;
319 *skirtVertexCoords++ = mVertexCoords[idxs[0] * 3 + 1] / 32767.0f;
320 *skirtVertexCoords++ = mHeader.MinimumHeight - skirtDepth;
321
322 *skirtVertexCoords++ = mVertexCoords[idxs[idxs.size() - 1] * 3] / 32767.0f;
323 *skirtVertexCoords++ = mVertexCoords[idxs[idxs.size() - 1] * 3 + 1] / 32767.0f;
324 *skirtVertexCoords++ = mHeader.MinimumHeight - skirtDepth;
325 }
326
327 return startIdx;
328 };
329 size_t westBottomVerticesIdx = addSkirtVertices( mWestVertices );
330 size_t southBottomVerticesIdx = addSkirtVertices( mSouthVertices );
331 size_t eastBottomVerticesIdx = addSkirtVertices( mEastVertices );
332 size_t northBottomVerticesIdx = addSkirtVertices( mNorthVertices );
333 // Check that we didn't miscalculate buffer size
334 Q_ASSERT( skirtVertexCoords == ( float * )( vertexBuffer.data.data() + vertexBuffer.data.size() ) );
335
336 // Add skirt triangles (a trapezoid for each pair of edge vertices)
337 size_t skirtTrianglesStartIdx = triangleBuffer.data.size();
338 size_t edgeQuadCount =
339 // For 0/1 point we have 0 quads, for N we have N-1, and an additional one for skirtDepth
340 ( mWestVertices.size() > 1 ? mWestVertices.size() : 0 ) +
341 ( mSouthVertices.size() > 1 ? mSouthVertices.size() : 0 ) +
342 ( mEastVertices.size() > 1 ? mEastVertices.size() : 0 ) +
343 ( mNorthVertices.size() > 1 ? mNorthVertices.size() : 0 );
344 triangleBuffer.data.resize( triangleBuffer.data.size() + edgeQuadCount * 6 * sizeof( uint32_t ) );
345 uint32_t *skirtTriangles = ( uint32_t * )( triangleBuffer.data.data() + skirtTrianglesStartIdx );
346 auto addSkirtTriangles = [&]( const std::vector<uint32_t> &topIdxs, size_t bottomVertexIdxStart )
347 {
348 size_t bottomVertexIdx = bottomVertexIdxStart;
349 for ( size_t i = 1; i < topIdxs.size(); i++ )
350 {
351 uint32_t topVertex1 = topIdxs[i - 1];
352 uint32_t topVertex2 = topIdxs[i];
353 uint32_t bottomVertex1 = bottomVertexIdx;
354 uint32_t bottomVertex2 = ++bottomVertexIdx;
355
356 *skirtTriangles++ = bottomVertex1;
357 *skirtTriangles++ = topVertex1;
358 *skirtTriangles++ = topVertex2;
359
360 *skirtTriangles++ = bottomVertex2;
361 *skirtTriangles++ = bottomVertex1;
362 *skirtTriangles++ = topVertex2;
363 }
364
365 if ( topIdxs.size() > 1 )
366 {
367 uint32_t topVertex1 = bottomVertexIdxStart;
368 uint32_t topVertex2 = bottomVertexIdxStart + topIdxs.size() - 1;
369 uint32_t bottomVertex1 = bottomVertexIdxStart + topIdxs.size();
370 uint32_t bottomVertex2 = bottomVertexIdxStart + topIdxs.size() + 1;
371
372 *skirtTriangles++ = bottomVertex1;
373 *skirtTriangles++ = topVertex1;
374 *skirtTriangles++ = topVertex2;
375
376 *skirtTriangles++ = bottomVertex2;
377 *skirtTriangles++ = bottomVertex1;
378 *skirtTriangles++ = topVertex2;
379 }
380 };
381 addSkirtTriangles( mWestVertices, westBottomVerticesIdx );
382 addSkirtTriangles( mSouthVertices, southBottomVerticesIdx );
383 addSkirtTriangles( mEastVertices, eastBottomVerticesIdx );
384 addSkirtTriangles( mNorthVertices, northBottomVerticesIdx );
385 Q_ASSERT( skirtTriangles == ( uint32_t * )( triangleBuffer.data.data() + triangleBuffer.data.size() ) );
386 }
387
388 model.buffers.push_back( vertexBuffer );
389 model.buffers.push_back( triangleBuffer );
390
391 tinygltf::BufferView vertexBufferView;
392 vertexBufferView.buffer = 0;
393 vertexBufferView.byteLength = vertexBuffer.data.size();
394 vertexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
395 model.bufferViews.emplace_back( std::move( vertexBufferView ) );
396
397 tinygltf::BufferView triangleBufferView;
398 triangleBufferView.buffer = 1;
399 triangleBufferView.byteLength = triangleBuffer.data.size();
400 triangleBufferView.target = TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
401 model.bufferViews.emplace_back( std::move( triangleBufferView ) );
402
403 tinygltf::Accessor vertexAccessor;
404 vertexAccessor.bufferView = 0;
405 vertexAccessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
406 vertexAccessor.count = vertexBuffer.data.size() / sizeof( float ) / 3;
407 const std::size_t vertexAccessorCount = vertexAccessor.count;
408 vertexAccessor.type = TINYGLTF_TYPE_VEC3;
409 vertexAccessor.minValues = std::move( coordMinimums );
410 vertexAccessor.maxValues = std::move( coordMaximums );
411 model.accessors.emplace_back( std::move( vertexAccessor ) );
412
413 tinygltf::Accessor triangleAccessor;
414 triangleAccessor.bufferView = 1;
415 triangleAccessor.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
416 triangleAccessor.count = triangleBuffer.data.size() / sizeof( uint32_t );
417 triangleAccessor.type = TINYGLTF_TYPE_SCALAR;
418 model.accessors.emplace_back( std::move( triangleAccessor ) );
419
420 tinygltf::Mesh mesh;
421 tinygltf::Primitive primitive;
422 primitive.attributes["POSITION"] = 0;
423 primitive.indices = 1;
424 primitive.mode = TINYGLTF_MODE_TRIANGLES;
425
426 if ( mNormalCoords.size() )
427 {
428 tinygltf::Buffer normalBuffer;
429 normalBuffer.data.resize( vertexBuffer.data.size() );
430 // Copy explicit normals, leave rest zeroed
431 size_t explicitNormalsBytes = mNormalCoords.size() * sizeof( float );
432 Q_ASSERT( vertexBuffer.data.size() >= explicitNormalsBytes );
433 const char *normData = reinterpret_cast<const char *>( mNormalCoords.data() );
434 std::copy( normData, normData + explicitNormalsBytes, normalBuffer.data.begin() );
435 memset( normalBuffer.data.data() + explicitNormalsBytes, 0, normalBuffer.data.size() - explicitNormalsBytes );
436 model.buffers.push_back( normalBuffer );
437
438 tinygltf::BufferView normalBufferView;
439 normalBufferView.buffer = model.buffers.size() - 1;
440 normalBufferView.byteLength = normalBuffer.data.size();
441 normalBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
442 model.bufferViews.push_back( normalBufferView );
443
444 std::vector<double> normalMinimums = {1, 1, 1};
445 std::vector<double> normalMaximums = {-1, -1, -1};
446
447 for ( size_t i = 0; i < mNormalCoords.size(); i++ )
448 {
449 float coord = mNormalCoords[i];
450 if ( normalMinimums[i % 3] > coord )
451 normalMinimums[i % 3] = coord;
452 if ( normalMaximums[i % 3] < coord )
453 normalMaximums[i % 3] = coord;
454 }
455
456 tinygltf::Accessor normalAccessor;
457 normalAccessor.bufferView = model.bufferViews.size() - 1;
458 normalAccessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
459 normalAccessor.count = normalBuffer.data.size() / sizeof( float ) / 3;
460 normalAccessor.type = TINYGLTF_TYPE_VEC3;
461 normalAccessor.minValues = std::move( normalMinimums );
462 normalAccessor.maxValues = std::move( normalMaximums );
463 model.accessors.emplace_back( std::move( normalAccessor ) );
464
465 primitive.attributes["NORMAL"] = model.accessors.size() - 1;
466 }
467
468 if ( withTextureCoords )
469 {
470 // Create texture coordinates matching X, Y
471
472 tinygltf::Buffer textureCoordBuffer;
473 textureCoordBuffer.data.resize( vertexBuffer.data.size() / 3 * 2 );
474 std::vector<double> texCoordMinimums = {1.0, 1.0};
475 std::vector<double> texCoordMaximums = {0.0, 0.0};
476 auto textureCoordFloats = reinterpret_cast<float *>( textureCoordBuffer.data.data() );
477
478 auto addTexCoordForVertex = [&]( size_t vertexIdx )
479 {
480 double u = mVertexCoords[vertexIdx * 3] / 32767.0;
481 // V coord needs to be flipped for terrain for some reason
482 double v = 1.0 - ( mVertexCoords[vertexIdx * 3 + 1] / 32767.0 );
483 if ( texCoordMinimums[0] > u ) texCoordMinimums[0] = u;
484 if ( texCoordMinimums[1] > v ) texCoordMinimums[1] = v;
485 if ( texCoordMaximums[0] < u ) texCoordMaximums[0] = u;
486 if ( texCoordMaximums[1] < v ) texCoordMaximums[1] = v;
487 *textureCoordFloats++ = u;
488 *textureCoordFloats++ = v;
489 };
490
491 for ( size_t i = 0; i < mVertexCoords.size() / 3; i++ )
492 {
493 addTexCoordForVertex( i );
494 }
495
496 if ( addSkirt )
497 {
498 // Add UV for generated bottom vertices matching the top edge vertices
499 auto addSkirtVertexUVs = [&]( const std::vector<uint32_t> &idxs )
500 {
501 for ( uint32_t idx : idxs )
502 {
503 addTexCoordForVertex( idx );
504 }
505
506 if ( idxs.size() > 1 )
507 {
508 // The two bottom corner vertices get UVs too
509 addTexCoordForVertex( idxs[0] );
510 addTexCoordForVertex( idxs[idxs.size() - 1] );
511 }
512 };
513 addSkirtVertexUVs( mWestVertices );
514 addSkirtVertexUVs( mSouthVertices );
515 addSkirtVertexUVs( mEastVertices );
516 addSkirtVertexUVs( mNorthVertices );
517 }
518
519 Q_ASSERT( textureCoordFloats == ( float * )( textureCoordBuffer.data.data() + textureCoordBuffer.data.size() ) );
520
521 model.buffers.push_back( textureCoordBuffer );
522
523 tinygltf::BufferView textureCoordBufferView;
524 textureCoordBufferView.buffer = model.buffers.size() - 1;
525 //textureCoordBufferView.buffer = vertexBufferView.buffer; // Reuse vertex coords
526 textureCoordBufferView.byteLength = textureCoordBuffer.data.size();
527 //textureCoordBufferView.byteLength = vertexBuffer.data.size();
528 //textureCoordBufferView.byteStride = sizeof(float) * 3;
529 textureCoordBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
530 model.bufferViews.push_back( textureCoordBufferView );
531
532 tinygltf::Accessor textureCoordAccessor;
533 textureCoordAccessor.bufferView = model.bufferViews.size() - 1;
534 textureCoordAccessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
535 textureCoordAccessor.count = vertexAccessorCount;
536 textureCoordAccessor.type = TINYGLTF_TYPE_VEC2;
537 textureCoordAccessor.minValues = { texCoordMinimums[0], texCoordMinimums[1] };
538 textureCoordAccessor.maxValues = { texCoordMaximums[0], texCoordMaximums[1] };
539 model.accessors.push_back( textureCoordAccessor );
540
541 primitive.attributes["TEXCOORD_0"] = model.accessors.size() - 1;
542 }
543
544 mesh.primitives.push_back( primitive );
545 model.meshes.push_back( mesh );
546
547 tinygltf::Node node;
548 node.mesh = 0;
549 model.nodes.push_back( node );
550
551 tinygltf::Scene scene;
552 scene.nodes.push_back( 0 );
553 model.scenes.push_back( scene );
554 model.defaultScene = 0;
555
556 return model;
557}
558
560{
561 QgsMesh mesh;
562
563 mesh.vertices.reserve( mVertexCoords.size() / 3 );
564 for ( size_t i = 0; i < mVertexCoords.size(); i += 3 )
565 {
566 // Rescale to X,Y in CRS and Z in meters
567 double x = ( mVertexCoords[i] / 32767.0 ) * ( tileBounds.width() ) + tileBounds.xMinimum();
568 double y = ( mVertexCoords[i + 1] / 32767.0 ) * ( tileBounds.height() ) + tileBounds.yMinimum();
569 double z = ( mVertexCoords[i + 2] / 32767.0 ) * ( mHeader.MaximumHeight - mHeader.MinimumHeight ) + mHeader.MinimumHeight;
570 mesh.vertices.push_back( {x, y, z} );
571 }
572
573 mesh.faces.reserve( mTriangleIndices.size() / 3 );
574 for ( size_t i = 0; i < mTriangleIndices.size(); i += 3 )
575 {
576 mesh.faces.push_back(
577 {
578 static_cast<int>( mTriangleIndices[i] ),
579 static_cast<int>( mTriangleIndices[i + 1] ),
580 static_cast<int>( mTriangleIndices[i + 2] ),
581 } );
582 }
583
584 return mesh;
585}
Custom exception class which is raised when an operation is not supported.
Exception thrown on failure to parse Quantized Mesh tile (malformed data).
A rectangle specified with double values.
double xMinimum
double yMinimum
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
constexpr uint8_t normalsExtensionId
Mesh - vertices, edges and faces.
QVector< QgsMeshVertex > vertices
QVector< QgsMeshFace > faces
std::vector< float > mNormalCoords
std::vector< uint32_t > mTriangleIndices
tinygltf::Model toGltf(bool addSkirt=false, double skirtDepth=0, bool withTextureCoords=false)
QgsQuantizedMeshTile(const QByteArray &data)
std::vector< uint32_t > mNorthVertices
std::vector< uint32_t > mWestVertices
QgsMesh toMesh(QgsRectangle tileBounds)
QgsQuantizedMeshHeader mHeader
std::vector< uint32_t > mEastVertices
std::map< uint8_t, std::vector< char > > mExtensions
std::vector< uint16_t > mVertexCoords
std::vector< uint32_t > mSouthVertices