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