Cogs.Core
OGC3DTilesSubtree.cpp
1#include "OGC3DTilesSubtree.h"
2#include "OGC3DTilesUtils.h"
3
4#include "Systems/OGC3DTilesSystem.h"
5#include "ExtensionRegistry.h"
6
7#include <Context.h>
8#include <Engine.h>
9
10#include <Foundation/Logging/Logger.h>
11#include <Foundation/Platform/IO.h>
12#include <Foundation/BitTwiddling/MortonCode.h>
13
14#include <Serialization/JsonParser.h>
15
16#include <tuple>
17#include <cassert>
18#include <bitset>
19
20using namespace Cogs::Core;
21using namespace Cogs::Core::OGC3DTilesSubtree;
22using namespace std::chrono_literals;
23
28namespace {
29 Cogs::Logging::Log logger = Cogs::Logging::getLogger("OGC3DTilesSubtree");
30
31 // Ref: https://github.com/CesiumGS/3d-tiles/tree/draft-1.1/specification/ImplicitTiling#subtree-binary-format
32 struct Header {
33 uint32_t magic;
34 uint32_t version;
35 uint64_t jsonLength;
36 uint64_t bitstreamLength;
37 };
38
39 // Will interleave A and B, bitwise.
40 inline uint32_t interleaveBits(uint16_t a, uint16_t b)
41 {
42 return Cogs::mortonCode(a, b);
43 }
44
45 // Will interleave A, B and C, bitwise.
46 inline uint64_t interleaveBits(uint16_t a, uint16_t b, uint16_t c)
47 {
48 // FIXME: We should do some checking here to see if the result will exceed 32bit
49 // and maybe print some warnings. If not, prepare the code for 64-bits morton codes
50 // in OctTree-mode.
51 // NOTE: I assume that a Morton code which exceeds 32bit is not realistic in our scenario,
52 // expecially when it comes to checking the content/subtree availability bitset. A 32bit+ index
53 // would mean that the subtree-bitset would exceed 0xffffff+ bytes in length, ie. 16+ MB.
54 return Cogs::mortonCode(a, b, c);
55 }
56
57 inline uint32_t concatBits(uint16_t a, uint16_t b, int numBitsFromB)
58 {
59 b = b << (16 - numBitsFromB);
60 uint32_t ax = uint32_t(a) << 16;
61 ax = ax + b;
62 return ax >> (16 - numBitsFromB);
63 }
64
65 // Ref: https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
66 inline uint32_t removeTwoSpacing(uint32_t v) {
67 v &= 0x09249249;
68 v = (v ^ (v >> 2)) & 0x030c30c3;
69 v = (v ^ (v >> 4)) & 0x0300f00f;
70 v = (v ^ (v >> 8)) & 0xff0000ff;
71 v = (v ^ (v >> 16)) & 0x000003ff;
72 return v;
73 }
74 inline uint32_t removeOneSpacing(uint32_t x)
75 {
76 x &= 0x55555555;
77 x = (x ^ (x >> 1)) & 0x33333333;
78 x = (x ^ (x >> 2)) & 0x0f0f0f0f;
79 x = (x ^ (x >> 4)) & 0x00ff00ff;
80 x = (x ^ (x >> 8)) & 0x0000ffff;
81 return x;
82 }
83}
84
85#if 0
86// Debug utility
87// NOTE: The Subtree-object needs to have been given a correct 'levelsPerSubtree' value from the tileset
88void
89printAvailableSubtrees(const Subtree* subtree)
90{
91 int bitstreamIdx = subtree->childSubtreeAvailability.bitstreamIdx;
92 int level = subtree->globalCoord.level + subtree->levelsPerSubtree;
93 int range = (int)OGC3DTilesUtils::fastPow2(level);
94 bool isOctTree = subtree->globalCoord.z >= 0;
95
96 printf("subtree->globalCoord.level=%d, levelsPerSubtree=%d\n", subtree->globalCoord.level, subtree->levelsPerSubtree);
97 LOG_DEBUG(logger, "printAvailableSubtrees(): level=%d, range=%d, section=%d (x=%d, y=%d, z=%d):",
98 level, range, bitstreamIdx, subtree->globalCoord.x, subtree->globalCoord.y, subtree->globalCoord.z);
99
100 if (subtree->childSubtreeAvailability.availableCount == 0) {
101 LOG_DEBUG(logger, " => No subtrees available\n");
102 return;
103 }
104
105 for (int x = 0; x < range; ++x) {
106 for (int y = 0; y < range; ++y) {
107 for (int z = 0; z < range; ++z) {
108 uint32_t mortonIndex = 0;
109 if (isOctTree) {
110 mortonIndex = interleaveBits(x, y, z);
111 }
112 else {
113 mortonIndex = interleaveBits(x, y);
114 }
115
116 uint32_t levelOffset = 0; // The subtree-array is always a single array/level.
117 uint32_t offset = levelOffset + mortonIndex;
118
119 assert(bitstreamIdx < subtree->bitstreamSections.size() && "Index out of bounds");
120 const BitstreamSection& section = subtree->bitstreamSections[bitstreamIdx];
121
122 auto bsBegin = subtree->bitstreams[section.bitstreamIdx].cbegin();
123 std::vector<unsigned char>::const_iterator start = bsBegin + section.startIdx;
124 std::vector<unsigned char>::const_iterator end = bsBegin + section.startIdx + section.length;
125 std::vector<unsigned char> buf(start, end);
126
127 int coarseIdx = offset >> 3; // Divide by 8
128 int fineIdx = offset & 0b111;
129
130 assert(level >= 0 && "Internal error");
131 assert(coarseIdx < buf.size() && "Index out of bounds");
132
133 unsigned char val = buf[coarseIdx];
134 std::bitset<8> valBits(val);
135
136 bool isSet = valBits.test(fineIdx);
137 if (isSet) {
138 if (isOctTree) LOG_DEBUG(logger, " %d.%d.%d.%d.subtree\n", level, x, y, z);
139 else LOG_DEBUG(logger, " %d.%d.%d.subtree\n", level, x, y);
140
141 LOG_DEBUG(logger, " parent?: %d:%d|%d\n", level - 1, x >> 1, y >> 1);
142 }
143
144 if (!isOctTree) {
145 break;
146 }
147 }
148 }
149 }
150}
151#endif
152
153bool
154parseAvailabilityJSON(const Value& value, Availability* target)
155{
156 target->allIsAvailable = false; // We'll assume all tiles are available unless specified
157 target->availableCount = 0;
158
159 if (value.HasMember("constant")) {
160 target->allIsAvailable = value["constant"].GetInt() >= 1;
161 }
162
163 if (!target->allIsAvailable && value.MemberCount() > 1) {
164 if (value.HasMember("availableCount")) {
165 target->availableCount = value["availableCount"].GetInt();
166 }
167 else {
168 LOG_ERROR(logger, "Availability struct must contain a 'availableCount' member if 'constant=0' or undefined.");
169 return false;
170 }
171
172 if (value.HasMember("bitstream")) {
173 target->bitstreamIdx = value["bitstream"].GetInt();
174 }
175 else {
176 if (target->availableCount != 0) {
177 LOG_ERROR(logger, "Availability struct must contain a 'bitstream' member if 'constant==0' or undefined and 'availibilityCount != 0'.");
178 return false;
179 }
180 }
181 }
182
183 return true;
184}
185
191void
192fetchSubtreeBufferFile(Context* context, const std::string& URI, const std::string& originURL, Subtree* target, int bufferIdx, uint32_t uniqueComponentId)
193{
194 assert(bufferIdx < int(target->bitstreamSections.size()));
195
196 size_t pos = originURL.rfind("/");
197 assert(pos != std::string::npos && "Invalid originURL");
198 std::string baseURL = originURL.substr(0, pos);
199 std::string newURL = baseURL + "/" + URI;
200
201 // Does the originURL contain an "access_token" parameter?
202 // FIXME: Do we need to do the same with the "key=" session keys used by some servers?
203 size_t accessTokenPos = originURL.find("access_token");
204 if (accessTokenPos != std::string::npos) {
205 size_t endPos = originURL.find("&", accessTokenPos);
206 if (endPos == std::string::npos) {
207 endPos = originURL.size();
208 }
209 std::string accessToken = originURL.substr(accessTokenPos, endPos - accessTokenPos);
210 newURL += "?" + accessToken;
211 }
212
213 target->bitstreams.resize(target->bitstreamSections.size());
214
215 // FIXME: We should explicitly register WHICH subtrees we are waiting for. This will make it possible
216 // to start processing before all subtrees are loaded (see OGC3DTilesSubtree::getChildren() f.ex).
217 target->pendingRequests += 1;
218
219 (void)DataFetcherManager::fetchAsync(context, newURL,
220 [context, target, bufferIdx, uniqueComponentId](std::unique_ptr<Cogs::FileContents> inData) {
221 if (OGC3DTilesSystem::componentIsStale(context, uniqueComponentId) || !inData.get()) {
222 return;
223 }
224
225 auto data = inData.release();
226 BitstreamSection section = target->bitstreamSections[bufferIdx];
227
228 target->bitstreams[section.bitstreamIdx].reserve(section.length);
229 for (size_t j = 0; j < section.length; ++j) {
230 target->bitstreams[section.bitstreamIdx].emplace_back(data->ptr[section.startIdx + j]);
231 }
232 target->pendingRequests -= 1;
233
234 // As this request is an async prosess independent of the original subtree-request,
235 // we'll have to flag that the 3DTiles component needs an update.
236 OGC3DTilesSystem* system = ExtensionRegistry::getExtensionSystem<OGC3DTilesSystem>(context);
237 for (auto& comp : system->pool) {
238 OGC3DTilesDataHolder& h = system->getData(&comp);
239 OGC3DTilesData* componentState = h.data.get();
240 if (componentState->uniqueComponentId == uniqueComponentId) {
241 context->engine->setDirty();
242 break;
243 }
244 }
245
246 delete data;
247 });
248}
249
250bool
251parseSubtreeBufferJSON(Context* context, std::string_view json, Subtree* target, const std::string& originURL, uint32_t uniqueComponentId)
252{
253 Document doc = parseJson(json, JsonParseFlags::None);
254
255 std::vector<std::vector<std::pair<uint32_t, uint32_t>>> bufferSections;
256 if (doc.HasMember("bufferViews")) {
257 GenericArray array = doc["bufferViews"].GetArray();
258 for (Value& bv : array) {
259 if (!(bv.HasMember("buffer") && bv.HasMember("byteOffset") && bv.HasMember("byteLength"))) {
260 LOG_ERROR(logger, "No 'buffer', 'byteOffset' and 'byteLength' in 'bufferViews' section");
261 return false;
262 }
264 (uint8_t) bv["buffer"].GetUint(), // "bitstreamIdx"
265 bv["byteOffset"].GetUint(), // "startIdx"
266 bv["byteLength"].GetUint() // "length"
267 };
268 target->bitstreamSections.emplace_back(bss);
269 }
270 }
271
272 bool ok = true;
273 if (doc.HasMember("tileAvailability")) {
274 ok &= parseAvailabilityJSON(doc["tileAvailability"], &target->tileAvailability);
275 if (!ok) {
276 LOG_ERROR(logger, "Could not parse 'tileAvailability'");
277 }
278
279 if (!target->tileAvailability.allIsAvailable && target->tileAvailability.availableCount != 0) {
280 assert(target->tileAvailability.bitstreamIdx < target->bitstreams.size() && "Bitstream index out of bounds");
281 }
282 }
283 else {
284 LOG_ERROR(logger, "Subtree JSON did not contain a 'tileAvailability' section.");
285 return false;
286 }
287
288 if (doc.HasMember("contentAvailability")) {
289 GenericArray array = doc["contentAvailability"].GetArray();
290 target->contentAvailability.resize(array.Size());
291 int i = 0;
292 for (Value& c : array) {
293 ok &= parseAvailabilityJSON(c, &target->contentAvailability[i]);
294 if (!ok) {
295 LOG_ERROR(logger, "Could not parse 'contentAvailability' nr %d", (i + 1));
296 }
297 i += 1;
298 }
299 }
300 else {
301 LOG_ERROR(logger, "Subtree JSON did not contain a 'contentAvailability' section.");
302 return false;
303 }
304
305 if (doc.HasMember("childSubtreeAvailability")) {
306 ok &= parseAvailabilityJSON(doc["childSubtreeAvailability"], &target->childSubtreeAvailability);
307 if (!ok) {
308 LOG_ERROR(logger, "Could not parse 'childSubtreeAvailability'");
309 }
310 }
311 else {
312 LOG_ERROR(logger, "Subtree JSON did not contain a 'childSubtreeAvailability' section.");
313 return false;
314 }
315
316 if (doc.HasMember("buffers") && target->tileAvailability.availableCount > 0) {
317 GenericArray array = doc["buffers"].GetArray();
318 int bufferIdx = 0;
319 for (Value& buf : array) {
320 assert(buf.HasMember("byteLength"));
321
322 if (buf.HasMember("uri")) {
323 //LOG_DEBUG(logger, "Subtree.buffer is not of type 'Internal Buffer'. Fetching '%s'", buf["uri"].GetString());
324 fetchSubtreeBufferFile(context, buf["uri"].GetString(), originURL, target, bufferIdx, uniqueComponentId);
325 }
326 else {
327 // Default/internal buffer which is already loaded
328 assert(bufferIdx == 0 && "Internal buffer must always be index 0");
329 }
330 ++bufferIdx;
331 }
332 }
333 else {
334 if (!target->tileAvailability.allIsAvailable && target->tileAvailability.availableCount > 0) {
335 LOG_ERROR(logger, "Subtree JSON has sparse tile-availibility but did not contain a 'buffers' section.");
336 return false;
337 }
338 }
339
340 return ok;
341}
342
343bool
344parseSubtreeBufferBinary(Context* context, const Cogs::Memory::MemoryBuffer* buf, Subtree* target, const std::string& originURL, uint32_t uniqueComponentId)
345{
346 const uint8_t* data = static_cast<const uint8_t*>(buf->data());
347 const Header* h = (const Header*)data;
348
349 if (h->magic != 0x74627573) {
350 LOG_ERROR(logger, "Subtree header magic value was not 0x74627573 (was 0x%x)", h->magic);
351 return false;
352 }
353
354 if (h->version != 1) {
355 LOG_ERROR(logger, "Subtree version was not '1' (was %d)", h->version);
356 return false;
357 }
358
359 // Read bitstreams before we parse the JSON for easier validation.
360 if (h->bitstreamLength > 0) {
361 // As a default we'll always load the data into bitstream #1.
362 uint32_t paddedJSONLength = static_cast<uint32_t>(h->jsonLength);
363 uint32_t offset = sizeof(Header) + paddedJSONLength;
364 const uint8_t* bitstreamStartPtr = &(static_cast<const uint8_t*>(buf->data())[offset]);
365 target->bitstreams.emplace_back(std::vector<uint8_t>(bitstreamStartPtr, bitstreamStartPtr + h->bitstreamLength));
366 }
367 else {
368 LOG_ERROR(logger, "parseSubtreeBufferBinary(): BitstreamLength value in header <= 0");
369 return false;
370 }
371
372 const char* jsonStartPtr = &(static_cast<const char*>(buf->data())[sizeof(Header)]);
373 const std::string json = std::string(jsonStartPtr, h->jsonLength);
374 bool ok = parseSubtreeBufferJSON(context, json, target, originURL, uniqueComponentId);
375 if (!ok) {
376 return false;
377 }
378
379#if 0
380 if (target->bitstreamSections.size() > 1) {
381 LOG_DEBUG(logger, "parseSubtreeBufferBinary(): Bitstream sections:");
382 for (int x = 1; x < target->bitstreamSections.size(); ++x) {
383 BitstreamSection section = target->bitstreamSections[x];
384 int bufIdx = section.bitstreamIdx;
385 int start = section.startIdx;
386 int len = section.length;
387
388 LOG_DEBUG(logger, " #%d: BufferIdx=%d, start=%d, len=%d => [ ", x, bufIdx, start, len);
389 for (int i = (len - 1); i >= 0; --i) {
390 std::bitset<8> x(target->bitstreams[bufIdx][i]);
391 std::cout << x << ' ';
392 }
393 LOG_DEBUG(logger, "] (NB: read backwards)\n");
394 }
395 }
396#endif
397 return true;
398}
399
404std::vector<OGC3DTiles::Coord>
405OGC3DTilesSubtree::getLeafNodesSubtreeCoords(const Subtree* subtree, const OGC3DTiles::Coord& leafNodeCoord)
406{
407 std::vector<OGC3DTiles::Coord> coords;
408
409 if (subtree->childSubtreeAvailability.allIsAvailable) {
410 return getAllChildren(subtree, leafNodeCoord);
411 }
412
413 if (subtree->childSubtreeAvailability.availableCount == 0 || subtree->pendingRequests > 0) {
414 return coords;
415 }
416
417 const int bitstreamIdx = subtree->childSubtreeAvailability.bitstreamIdx;
418 assert(bitstreamIdx < int(subtree->bitstreamSections.size()) && "Index out of bounds");
419 const OGC3DTilesSubtree::BitstreamSection& section = subtree->bitstreamSections[bitstreamIdx];
420
421 const bool isOctTree = subtree->globalCoord.z >= 0;
422 const OGC3DTiles::Coord localCoord = globalToLocal(subtree, leafNodeCoord);
423
424 const uint64_t leafNodeMortonIndex = isOctTree ?
425 interleaveBits(uint16_t(leafNodeCoord.x), uint16_t(leafNodeCoord.y), uint16_t(leafNodeCoord.z)) :
426 interleaveBits(uint16_t(leafNodeCoord.x), uint16_t(leafNodeCoord.y));
427
428 const uint64_t localMortonIndex = isOctTree ?
429 interleaveBits(uint16_t(localCoord.x), uint16_t(localCoord.y), uint16_t(localCoord.z)) :
430 interleaveBits(uint16_t(localCoord.x), uint16_t(localCoord.y));
431
432 assert(leafNodeMortonIndex * (isOctTree ? 8 : 4) <= 0xFFFFFFFF && "Morton code for leaf-node is out of 32bit range");
433 const uint32_t leafNodeOffset = uint32_t(isOctTree ? leafNodeMortonIndex * 8 : leafNodeMortonIndex * 4);
434 const uint32_t localOffset = uint32_t(isOctTree ? localMortonIndex * 8 : localMortonIndex * 4);
435
436 const int num = isOctTree ? 8 : 4;
437 coords.reserve(num);
438
439 const std::vector<uint8_t>& bitarray = subtree->bitstreams[section.bitstreamIdx];
440 const size_t bufSize = bitarray.size();
441
442 for (int i = 0; i < num; ++i) {
443 const uint32_t offset = localOffset + i;
444 const uint32_t coarseIdx = offset >> 3; // Divide by 8
445 const int fineIdx = offset & 0b111;
446 assert(coarseIdx < section.length && "Buffer index out of bounds for bitstream section");
447 assert(section.startIdx + coarseIdx < bufSize && "Buffer index out of bounds for bitstream");
448
449 const uint8_t val = bitarray[section.startIdx + coarseIdx];
450 if (val) {
451 std::bitset<8> valBits(val);
452 if (valBits.test(fineIdx)) {
453 OGC3DTiles::Coord subtreeCoord = mortonCodeToCoord(isOctTree, leafNodeCoord.level + 1, leafNodeOffset + i);
454 coords.emplace_back(subtreeCoord);
455 }
456 }
457 }
458
459 return coords;
460}
461
462bool
463OGC3DTilesSubtree::isLeafNodeInSubtree(const Subtree* subtree, const OGC3DTiles::Coord& coord)
464{
465 assert(coord.level <= subtree->globalCoord.level + subtree->levelsPerSubtree && "Coord level is deeper into subtree than allowed max depth.");
466 const bool isLeaf = coord.level == subtree->globalCoord.level + (subtree->levelsPerSubtree - 1);
467 return isLeaf;
468}
469
476std::vector<OGC3DTiles::Coord>
477OGC3DTilesSubtree::getAllChildren(const Subtree* subtree, const OGC3DTiles::Coord& parent)
478{
479 std::vector<OGC3DTiles::Coord> children;
480
481 const bool isOctTree = subtree->globalCoord.z >= 0;
482
483 const uint64_t parentMortonCode = isOctTree ?
484 interleaveBits(uint16_t(parent.x), uint16_t(parent.y), uint16_t(parent.z)) :
485 interleaveBits(uint16_t(parent.x), uint16_t(parent.y));
486
487 assert(parentMortonCode * (isOctTree ? 8 : 4) <= 0xFFFFFFFF && "Morton code for parent is out of 32bit range");
488 const uint32_t offset = uint32_t(isOctTree ? parentMortonCode * 8 : parentMortonCode * 4);
489
490 const int num = isOctTree ? 8 : 4;
491 children.resize(num);
492
493 for (int i = 0; i < num; ++i) {
494 children[i] = mortonCodeToCoord(isOctTree, parent.level + 1, offset + i);
495 }
496
497 return children;
498}
499
506std::vector<OGC3DTiles::Coord>
507OGC3DTilesSubtree::getChildren(const Subtree* subtree, const OGC3DTiles::Coord& parent)
508{
509 std::vector<OGC3DTiles::Coord> children;
510 if (subtree->pendingRequests > 0) {
511 return children;
512 }
513
514 int bitstreamIdx = subtree->tileAvailability.bitstreamIdx;
515 assert(bitstreamIdx < int(subtree->bitstreamSections.size()) && "Index out of bounds");
516 const OGC3DTilesSubtree::BitstreamSection& section = subtree->bitstreamSections[bitstreamIdx];
517 const std::vector<uint8_t>& bitarray = subtree->bitstreams[section.bitstreamIdx];
518
519 const bool isOctTree = subtree->globalCoord.z >= 0;
520 const OGC3DTiles::Coord localCoord = globalToLocal(subtree, parent);
521
522 const uint64_t parentMortonCode = isOctTree ?
523 interleaveBits(uint16_t(parent.x), uint16_t(parent.y), uint16_t(parent.z)) :
524 interleaveBits(uint16_t(parent.x), uint16_t(parent.y));
525 assert(parentMortonCode * (isOctTree ? 8 : 4) <= 0xFFFFFFFF && "Morton code for parent is out of 32bit range");
526 const uint32_t globalOffset = uint32_t(isOctTree ? parentMortonCode * 8 : parentMortonCode * 4);
527
528 const uint32_t levelOffset = isOctTree ?
529 static_cast<uint32_t>((OGC3DTilesUtils::fastPow8(localCoord.level + 1) - 1) / (8 - 1)) :
530 static_cast<uint32_t>((OGC3DTilesUtils::fastPow4(localCoord.level + 1) - 1) / (4 - 1));
531
532 const uint64_t localMortonCode = isOctTree ?
533 interleaveBits(uint16_t(localCoord.x), uint16_t(localCoord.y), uint16_t(localCoord.z)) :
534 interleaveBits(uint16_t(localCoord.x), uint16_t(localCoord.y));
535 const uint32_t localOffset = uint32_t(isOctTree ? localMortonCode * 8 : localMortonCode * 4);
536
537 const int num = isOctTree ? 8 : 4;
538 children.reserve(num);
539
540 for (int i = 0; i < num; ++i) {
541 int offset = levelOffset + localOffset + i;
542 const uint32_t coarseIdx = offset >> 3; // Divide by 8
543 assert(coarseIdx < section.length && "Index out of bounds for bitstream section");
544 const int fineIdx = offset & 0b111;
545
546 assert(section.startIdx + coarseIdx < bitarray.size() && "Index out of bounds");
547 const uint8_t val = bitarray[section.startIdx + coarseIdx];
548 if (val) {
549 std::bitset<8> valBits(val);
550 if (valBits.test(fineIdx)) {
551 children.emplace_back(mortonCodeToCoord(isOctTree, parent.level + 1, globalOffset + i));
552 }
553 }
554 }
555
556 return children;
557}
558
567Cogs::Core::DataFetcherManager::FetchId
568OGC3DTilesSubtree::fetchSubtree(Cogs::Core::Context* context, const std::string& URL, uint32_t uniqueComponentId, FetchSubtreeCallback callback)
569{
570 Cogs::Core::DataFetcherManager::FetchId fetchId = DataFetcherManager::fetchAsync(context, URL,
571 [context, URL, callback, uniqueComponentId](std::unique_ptr<Cogs::FileContents> inData) {
572 if (OGC3DTilesSystem::componentIsStale(context, uniqueComponentId)) {
573 return;
574 }
575
576 Cogs::FileContents* data = inData.get();
577 Subtree* subtree = nullptr;
578 bool success = false;
579
580 if (data && data->data()) {
581 subtree = new Subtree;
582 subtree->pendingRequests = 0;
583 subtree->tileAvailability.allIsAvailable = false;
584 subtree->childSubtreeAvailability.allIsAvailable = false;
585
586 size_t paramPos = URL.find_last_of('?');
587 const std::string fileURL = URL.substr(0, paramPos == std::string::npos ? URL.size() : paramPos);
588
589 Memory::MemoryBuffer contentsBuff = inData->take();
590 if (IO::extension(fileURL) == ".subtree" || IO::extension(fileURL) == ".SUBTREE") {
591 success = parseSubtreeBufferBinary(context, &contentsBuff, subtree, URL, uniqueComponentId);
592 }
593 else if (IO::extension(fileURL) == ".json" || IO::extension(fileURL) == ".JSON") {
594 std::string_view jsonStr = std::string_view(static_cast<const char*>(contentsBuff.data()), contentsBuff.size());
595 success = parseSubtreeBufferJSON(context, jsonStr, subtree, URL, uniqueComponentId);
596 }
597 else {
598 LOG_ERROR(logger, "fetchSubtree(): Unknown subtree extension for '%s'", fileURL.c_str());
599 }
600
601 if (!success) {
602 delete subtree;
603 subtree = nullptr;
604 }
605 }
606 else {
607 LOG_ERROR(logger, "fetchSubtree(): Could not fetch subtree '%s'", URL.c_str());
608 }
609
610 auto finishTask = [callback, subtree]() {
611 callback(subtree);
612 };
613
614#if defined(EMSCRIPTEN)
615 // Cogs.js support - no threading
616 (void)context; // Silence unused capture warning.
617 finishTask();
618#else
619 context->engine->runTaskInMainThread(std::move(finishTask));
620#endif
621 });
622
623 return fetchId;
624}
625
626bool
627checkAvailability(const Subtree* subtree, int bitstreamSectionIdx, int localLevel, int localX, int localY, /*opt*/ int localZ)
628{
629 if (subtree->pendingRequests > 0) {
630 return false;
631 }
632
633 uint32_t levelOffset = 0;
634 uint64_t mortonIndex = 0;
635 if (localZ >= 0) { // We are in Octree mode
636 levelOffset = static_cast<uint32_t>((OGC3DTilesUtils::fastPow8(localLevel) - 1) / (8 - 1));
637 mortonIndex = static_cast<uint32_t>(interleaveBits(static_cast<uint16_t>(localX), static_cast<uint16_t>(localY), static_cast<uint16_t>(localZ)));
638 }
639 else {
640 levelOffset = static_cast<uint32_t>((OGC3DTilesUtils::fastPow4(localLevel) - 1) / (4 - 1));
641 mortonIndex = interleaveBits(static_cast<uint16_t>(localX), static_cast<uint16_t>(localY));
642 }
643 assert(mortonIndex < 0xFFFFFFFF && "Internal error. Morton index is way too large.");
644
645 uint32_t offset = levelOffset + ((uint32_t) mortonIndex);
646
647 assert(bitstreamSectionIdx < int(subtree->bitstreamSections.size()) && "Index out of bounds");
648 const BitstreamSection& section = subtree->bitstreamSections[bitstreamSectionIdx];
649
650 if (section.bitstreamIdx >= subtree->bitstreams.size()) {
651 LOG_ERROR(logger, "checkAvailibility(): Bitstream index > number of available bitstreams (%d vs %d)", section.bitstreamIdx, (int)subtree->bitstreams.size());
652 return false;
653 }
654 assert(section.bitstreamIdx < subtree->bitstreams.size());
655
656 const std::vector<uint8_t>& bitarray = subtree->bitstreams[section.bitstreamIdx];
657
658 uint32_t coarseIdx = offset >> 3; // Divide by 8
659 uint32_t fineIdx = offset & 0b111;
660 assert(coarseIdx < section.length && "Index out of bounds for section bitstream");
661 assert(section.startIdx + coarseIdx < bitarray.size() && "Index out of bounds to bitstream");
662
663 unsigned char val = bitarray[section.startIdx + coarseIdx];
664 std::bitset<8> valBits(val);
665 return valBits.test(fineIdx);
666}
667
668bool
669OGC3DTilesSubtree::hasContent(const Subtree* subtree, const OGC3DTiles::Coord& coord)
670{
671 const OGC3DTiles::Coord & localCoord = globalToLocal(subtree, coord);
672
673 bool hasContent = false;
674 for (const Availability& c : subtree->contentAvailability) {
675 if (c.allIsAvailable) {
676 hasContent = true;
677 break;
678 }
679 else if (c.availableCount > 0) {
680 hasContent = checkAvailability(subtree, c.bitstreamIdx, localCoord.level, localCoord.x, localCoord.y, localCoord.z);
681 if (hasContent) {
682 break;
683 }
684 }
685 }
686
687 return hasContent;
688}
689
697OGC3DTilesSubtree::getSubtreesRootCoord(int levelsPerSubtree, const OGC3DTiles::Coord& coord)
698{
699 int subtreeLevel = coord.level / levelsPerSubtree;
700 int levelsLeft = coord.level % levelsPerSubtree;
701
702 return OGC3DTiles::Coord{ subtreeLevel * levelsPerSubtree,
703 coord.x >> levelsLeft,
704 coord.y >> levelsLeft,
705 coord.z >= 0 ? coord.z >> levelsLeft : -1 };
706}
707
709OGC3DTilesSubtree::globalToLocal(const Subtree* subtree, const OGC3DTiles::Coord& globalCoord)
710{
711 const int relativeLevel = globalCoord.level - subtree->globalCoord.level;
712 return OGC3DTiles::Coord{
713 relativeLevel,
714 globalCoord.x - (subtree->globalCoord.x << relativeLevel),
715 globalCoord.y - (subtree->globalCoord.y << relativeLevel) ,
716 globalCoord.z >= 0 ? globalCoord.z - (subtree->globalCoord.z << relativeLevel) : -1
717 };
718}
719
721OGC3DTilesSubtree::mortonCodeToCoord(bool isOctTree, int level, uint32_t mortonCode)
722{
723 OGC3DTiles::Coord coord;
724 coord.level = level;
725
726 if (isOctTree) {
727 coord.x = removeTwoSpacing(mortonCode);
728 coord.y = removeTwoSpacing(mortonCode >> 1);
729 coord.z = removeTwoSpacing(mortonCode >> 2);
730 }
731 else {
732 coord.x = removeOneSpacing(mortonCode);
733 coord.y = removeOneSpacing(mortonCode >> 1);
734 coord.z = -1;
735 }
736
737 return coord;
738}
ComponentPool< ComponentType > pool
Pool of components managed by the system.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class Engine > engine
Engine instance.
Definition: Context.h:222
static bool componentIsStale(Context *context, uint32_t id)
Log implementation class.
Definition: LogManager.h:140
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
constexpr uint64_t mortonCode(uint16_t i, uint16_t j, uint16_t k)
Interleave bits of 3 values to form the 3-way Morton code.
Definition: MortonCode.h:10
Abstract base class storing data read from a file.
Definition: FileContents.h:20