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