QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
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 = 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.push_back( 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.push_back( 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 vertexAccessor.type = TINYGLTF_TYPE_VEC3;
408 vertexAccessor.minValues = coordMinimums;
409 vertexAccessor.maxValues = coordMaximums;
410 model.accessors.push_back( vertexAccessor );
411
412 tinygltf::Accessor triangleAccessor;
413 triangleAccessor.bufferView = 1;
414 triangleAccessor.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
415 triangleAccessor.count = triangleBuffer.data.size() / sizeof( uint32_t );
416 triangleAccessor.type = TINYGLTF_TYPE_SCALAR;
417 model.accessors.push_back( triangleAccessor );
418
419 tinygltf::Mesh mesh;
420 tinygltf::Primitive primitive;
421 primitive.attributes["POSITION"] = 0;
422 primitive.indices = 1;
423 primitive.mode = TINYGLTF_MODE_TRIANGLES;
424
425 if ( mNormalCoords.size() )
426 {
427 tinygltf::Buffer normalBuffer;
428 normalBuffer.data.resize( vertexBuffer.data.size() );
429 // Copy explicit normals, leave rest zeroed
430 size_t explicitNormalsBytes = mNormalCoords.size() * sizeof( float );
431 Q_ASSERT( vertexBuffer.data.size() >= explicitNormalsBytes );
432 const char *normData = reinterpret_cast<const char *>( mNormalCoords.data() );
433 std::copy( normData, normData + explicitNormalsBytes, normalBuffer.data.begin() );
434 memset( normalBuffer.data.data() + explicitNormalsBytes, 0, normalBuffer.data.size() - explicitNormalsBytes );
435 model.buffers.push_back( normalBuffer );
436
437 tinygltf::BufferView normalBufferView;
438 normalBufferView.buffer = model.buffers.size() - 1;
439 normalBufferView.byteLength = normalBuffer.data.size();
440 normalBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
441 model.bufferViews.push_back( normalBufferView );
442
443 std::vector<double> normalMinimums = {1, 1, 1};
444 std::vector<double> normalMaximums = {-1, -1, -1};
445
446 for ( size_t i = 0; i < mNormalCoords.size(); i++ )
447 {
448 float coord = mNormalCoords[i];
449 if ( normalMinimums[i % 3] > coord )
450 normalMinimums[i % 3] = coord;
451 if ( normalMaximums[i % 3] < coord )
452 normalMaximums[i % 3] = coord;
453 }
454
455 tinygltf::Accessor normalAccessor;
456 normalAccessor.bufferView = model.bufferViews.size() - 1;
457 normalAccessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
458 normalAccessor.count = normalBuffer.data.size() / sizeof( float ) / 3;
459 normalAccessor.type = TINYGLTF_TYPE_VEC3;
460 normalAccessor.minValues = normalMinimums;
461 normalAccessor.maxValues = normalMaximums;
462 model.accessors.push_back( normalAccessor );
463
464 primitive.attributes["NORMAL"] = model.accessors.size() - 1;
465 }
466
467 if ( withTextureCoords )
468 {
469 // Create texture coordinates matching X, Y
470
471 tinygltf::Buffer textureCoordBuffer;
472 textureCoordBuffer.data.resize( vertexBuffer.data.size() / 3 * 2 );
473 std::vector<double> texCoordMinimums = {1.0, 1.0};
474 std::vector<double> texCoordMaximums = {0.0, 0.0};
475 auto textureCoordFloats = reinterpret_cast<float *>( textureCoordBuffer.data.data() );
476
477 auto addTexCoordForVertex = [&]( size_t vertexIdx )
478 {
479 double u = mVertexCoords[vertexIdx * 3] / 32767.0;
480 // V coord needs to be flipped for terrain for some reason
481 double v = 1.0 - ( mVertexCoords[vertexIdx * 3 + 1] / 32767.0 );
482 if ( texCoordMinimums[0] > u ) texCoordMinimums[0] = u;
483 if ( texCoordMinimums[1] > v ) texCoordMinimums[1] = v;
484 if ( texCoordMaximums[0] < u ) texCoordMaximums[0] = u;
485 if ( texCoordMaximums[1] < v ) texCoordMaximums[1] = v;
486 *textureCoordFloats++ = u;
487 *textureCoordFloats++ = v;
488 };
489
490 for ( size_t i = 0; i < mVertexCoords.size() / 3; i++ )
491 {
492 addTexCoordForVertex( i );
493 }
494
495 if ( addSkirt )
496 {
497 // Add UV for generated bottom vertices matching the top edge vertices
498 auto addSkirtVertexUVs = [&]( const std::vector<uint32_t> &idxs )
499 {
500 for ( uint32_t idx : idxs )
501 {
502 addTexCoordForVertex( idx );
503 }
504
505 if ( idxs.size() > 1 )
506 {
507 // The two bottom corner vertices get UVs too
508 addTexCoordForVertex( idxs[0] );
509 addTexCoordForVertex( idxs[idxs.size() - 1] );
510 }
511 };
512 addSkirtVertexUVs( mWestVertices );
513 addSkirtVertexUVs( mSouthVertices );
514 addSkirtVertexUVs( mEastVertices );
515 addSkirtVertexUVs( mNorthVertices );
516 }
517
518 Q_ASSERT( textureCoordFloats == ( float * )( textureCoordBuffer.data.data() + textureCoordBuffer.data.size() ) );
519
520 model.buffers.push_back( textureCoordBuffer );
521
522 tinygltf::BufferView textureCoordBufferView;
523 textureCoordBufferView.buffer = model.buffers.size() - 1;
524 //textureCoordBufferView.buffer = vertexBufferView.buffer; // Reuse vertex coords
525 textureCoordBufferView.byteLength = textureCoordBuffer.data.size();
526 //textureCoordBufferView.byteLength = vertexBuffer.data.size();
527 //textureCoordBufferView.byteStride = sizeof(float) * 3;
528 textureCoordBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
529 model.bufferViews.push_back( textureCoordBufferView );
530
531 tinygltf::Accessor textureCoordAccessor;
532 textureCoordAccessor.bufferView = model.bufferViews.size() - 1;
533 textureCoordAccessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
534 textureCoordAccessor.count = vertexAccessor.count;
535 textureCoordAccessor.type = TINYGLTF_TYPE_VEC2;
536 textureCoordAccessor.minValues = { texCoordMinimums[0], texCoordMinimums[1] };
537 textureCoordAccessor.maxValues = { texCoordMaximums[0], texCoordMaximums[1] };
538 model.accessors.push_back( textureCoordAccessor );
539
540 primitive.attributes["TEXCOORD_0"] = model.accessors.size() - 1;
541 }
542
543 mesh.primitives.push_back( primitive );
544 model.meshes.push_back( mesh );
545
546 tinygltf::Node node;
547 node.mesh = 0;
548 model.nodes.push_back( node );
549
550 tinygltf::Scene scene;
551 scene.nodes.push_back( 0 );
552 model.scenes.push_back( scene );
553 model.defaultScene = 0;
554
555 return model;
556}
557
559{
560 QgsMesh mesh;
561
562 mesh.vertices.reserve( mVertexCoords.size() / 3 );
563 for ( size_t i = 0; i < mVertexCoords.size(); i += 3 )
564 {
565 // Rescale to X,Y in CRS and Z in meters
566 double x = ( mVertexCoords[i] / 32767.0 ) * ( tileBounds.width() ) + tileBounds.xMinimum();
567 double y = ( mVertexCoords[i + 1] / 32767.0 ) * ( tileBounds.height() ) + tileBounds.yMinimum();
568 double z = ( mVertexCoords[i + 2] / 32767.0 ) * ( mHeader.MaximumHeight - mHeader.MinimumHeight ) + mHeader.MinimumHeight;
569 mesh.vertices.push_back( {x, y, z} );
570 }
571
572 mesh.faces.reserve( mTriangleIndices.size() / 3 );
573 for ( size_t i = 0; i < mTriangleIndices.size(); i += 3 )
574 {
575 mesh.faces.push_back(
576 {
577 static_cast<int>( mTriangleIndices[i] ),
578 static_cast<int>( mTriangleIndices[i + 1] ),
579 static_cast<int>( mTriangleIndices[i + 2] ),
580 } );
581 }
582
583 return mesh;
584}
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