1#include "OGC3DTilesSubtree.h"
2#include "OGC3DTilesUtils.h"
4#include "Systems/OGC3DTilesSystem.h"
5#include "ExtensionRegistry.h"
10#include <Foundation/Logging/Logger.h>
11#include <Foundation/Platform/IO.h>
12#include <Foundation/BitTwiddling/MortonCode.h>
14#include <Serialization/JsonParser.h>
21using namespace Cogs::Core::OGC3DTilesSubtree;
22using namespace std::chrono_literals;
36 uint64_t bitstreamLength;
40 inline uint32_t interleaveBits(uint16_t a, uint16_t b)
46 inline uint64_t interleaveBits(uint16_t a, uint16_t b, uint16_t c)
57 inline uint32_t concatBits(uint16_t a, uint16_t b,
int numBitsFromB)
59 b = b << (16 - numBitsFromB);
60 uint32_t ax = uint32_t(a) << 16;
62 return ax >> (16 - numBitsFromB);
66 inline uint32_t removeTwoSpacing(uint32_t v) {
68 v = (v ^ (v >> 2)) & 0x030c30c3;
69 v = (v ^ (v >> 4)) & 0x0300f00f;
70 v = (v ^ (v >> 8)) & 0xff0000ff;
71 v = (v ^ (v >> 16)) & 0x000003ff;
74 inline uint32_t removeOneSpacing(uint32_t x)
77 x = (x ^ (x >> 1)) & 0x33333333;
78 x = (x ^ (x >> 2)) & 0x0f0f0f0f;
79 x = (x ^ (x >> 4)) & 0x00ff00ff;
80 x = (x ^ (x >> 8)) & 0x0000ffff;
89printAvailableSubtrees(
const Subtree* subtree)
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;
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);
100 if (subtree->childSubtreeAvailability.availableCount == 0) {
101 LOG_DEBUG(logger,
" => No subtrees available\n");
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;
110 mortonIndex = interleaveBits(x, y, z);
113 mortonIndex = interleaveBits(x, y);
116 uint32_t levelOffset = 0;
117 uint32_t offset = levelOffset + mortonIndex;
119 assert(bitstreamIdx < subtree->bitstreamSections.size() &&
"Index out of bounds");
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);
127 int coarseIdx = offset >> 3;
128 int fineIdx = offset & 0b111;
130 assert(level >= 0 &&
"Internal error");
131 assert(coarseIdx < buf.size() &&
"Index out of bounds");
133 unsigned char val = buf[coarseIdx];
134 std::bitset<8> valBits(val);
136 bool isSet = valBits.test(fineIdx);
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);
141 LOG_DEBUG(logger,
" parent?: %d:%d|%d\n", level - 1, x >> 1, y >> 1);
154parseAvailabilityJSON(
const Value& value,
Availability* target)
156 target->allIsAvailable =
false;
157 target->availableCount = 0;
159 if (value.HasMember(
"constant")) {
160 target->allIsAvailable = value[
"constant"].GetInt() >= 1;
163 if (!target->allIsAvailable && value.MemberCount() > 1) {
164 if (value.HasMember(
"availableCount")) {
165 target->availableCount = value[
"availableCount"].GetInt();
168 LOG_ERROR(logger,
"Availability struct must contain a 'availableCount' member if 'constant=0' or undefined.");
172 if (value.HasMember(
"bitstream")) {
173 target->bitstreamIdx = value[
"bitstream"].GetInt();
176 if (target->availableCount != 0) {
177 LOG_ERROR(logger,
"Availability struct must contain a 'bitstream' member if 'constant==0' or undefined and 'availibilityCount != 0'.");
192fetchSubtreeBufferFile(
Context* context,
const std::string& URI,
const std::string& originURL,
Subtree* target,
int bufferIdx, uint32_t uniqueComponentId)
194 assert(bufferIdx <
int(target->bitstreamSections.size()));
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;
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();
209 std::string accessToken = originURL.substr(accessTokenPos, endPos - accessTokenPos);
210 newURL +=
"?" + accessToken;
213 target->bitstreams.resize(target->bitstreamSections.size());
217 target->pendingRequests += 1;
219 (void)DataFetcherManager::fetchAsync(context, newURL,
220 [context, target, bufferIdx, uniqueComponentId](std::unique_ptr<Cogs::FileContents> inData) {
225 auto data = inData.release();
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]);
232 target->pendingRequests -= 1;
236 OGC3DTilesSystem* system = ExtensionRegistry::getExtensionSystem<OGC3DTilesSystem>(context);
237 for (
auto& comp : system->
pool) {
240 if (componentState->uniqueComponentId == uniqueComponentId) {
241 context->
engine->setDirty();
251parseSubtreeBufferJSON(
Context* context, std::string_view json,
Subtree* target,
const std::string& originURL, uint32_t uniqueComponentId)
253 Document doc = parseJson(json, JsonParseFlags::None);
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");
264 (uint8_t) bv[
"buffer"].GetUint(),
265 bv[
"byteOffset"].GetUint(),
266 bv[
"byteLength"].GetUint()
268 target->bitstreamSections.emplace_back(bss);
273 if (doc.HasMember(
"tileAvailability")) {
274 ok &= parseAvailabilityJSON(doc[
"tileAvailability"], &target->tileAvailability);
276 LOG_ERROR(logger,
"Could not parse 'tileAvailability'");
279 if (!target->tileAvailability.allIsAvailable && target->tileAvailability.availableCount != 0) {
280 assert(target->tileAvailability.bitstreamIdx < target->bitstreams.size() &&
"Bitstream index out of bounds");
284 LOG_ERROR(logger,
"Subtree JSON did not contain a 'tileAvailability' section.");
288 if (doc.HasMember(
"contentAvailability")) {
289 GenericArray array = doc[
"contentAvailability"].GetArray();
290 target->contentAvailability.resize(array.Size());
292 for (Value& c : array) {
293 ok &= parseAvailabilityJSON(c, &target->contentAvailability[i]);
295 LOG_ERROR(logger,
"Could not parse 'contentAvailability' nr %d", (i + 1));
301 LOG_ERROR(logger,
"Subtree JSON did not contain a 'contentAvailability' section.");
305 if (doc.HasMember(
"childSubtreeAvailability")) {
306 ok &= parseAvailabilityJSON(doc[
"childSubtreeAvailability"], &target->childSubtreeAvailability);
308 LOG_ERROR(logger,
"Could not parse 'childSubtreeAvailability'");
312 LOG_ERROR(logger,
"Subtree JSON did not contain a 'childSubtreeAvailability' section.");
316 if (doc.HasMember(
"buffers") && target->tileAvailability.availableCount > 0) {
317 GenericArray array = doc[
"buffers"].GetArray();
319 for (Value& buf : array) {
320 assert(buf.HasMember(
"byteLength"));
322 if (buf.HasMember(
"uri")) {
324 fetchSubtreeBufferFile(context, buf[
"uri"].GetString(), originURL, target, bufferIdx, uniqueComponentId);
328 assert(bufferIdx == 0 &&
"Internal buffer must always be index 0");
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.");
346 const uint8_t* data =
static_cast<const uint8_t*
>(buf->data());
347 const Header* h = (
const Header*)data;
349 if (h->magic != 0x74627573) {
350 LOG_ERROR(logger,
"Subtree header magic value was not 0x74627573 (was 0x%x)", h->magic);
354 if (h->version != 1) {
355 LOG_ERROR(logger,
"Subtree version was not '1' (was %d)", h->version);
360 if (h->bitstreamLength > 0) {
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));
368 LOG_ERROR(logger,
"parseSubtreeBufferBinary(): BitstreamLength value in header <= 0");
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);
380 if (target->bitstreamSections.size() > 1) {
381 LOG_DEBUG(logger,
"parseSubtreeBufferBinary(): Bitstream sections:");
382 for (
int x = 1; x < target->bitstreamSections.size(); ++x) {
384 int bufIdx = section.bitstreamIdx;
385 int start = section.startIdx;
386 int len = section.length;
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 <<
' ';
393 LOG_DEBUG(logger,
"] (NB: read backwards)\n");
404std::vector<OGC3DTiles::Coord>
407 std::vector<OGC3DTiles::Coord> coords;
409 if (subtree->childSubtreeAvailability.allIsAvailable) {
410 return getAllChildren(subtree, leafNodeCoord);
413 if (subtree->childSubtreeAvailability.availableCount == 0 || subtree->pendingRequests > 0) {
417 const int bitstreamIdx = subtree->childSubtreeAvailability.bitstreamIdx;
418 assert(bitstreamIdx <
int(subtree->bitstreamSections.size()) &&
"Index out of bounds");
421 const bool isOctTree = subtree->globalCoord.z >= 0;
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));
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));
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);
436 const int num = isOctTree ? 8 : 4;
439 const std::vector<uint8_t>& bitarray = subtree->bitstreams[section.bitstreamIdx];
440 const size_t bufSize = bitarray.size();
442 for (
int i = 0; i < num; ++i) {
443 const uint32_t offset = localOffset + i;
444 const uint32_t coarseIdx = offset >> 3;
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");
449 const uint8_t val = bitarray[section.startIdx + coarseIdx];
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);
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);
476std::vector<OGC3DTiles::Coord>
479 std::vector<OGC3DTiles::Coord> children;
481 const bool isOctTree = subtree->globalCoord.z >= 0;
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));
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);
490 const int num = isOctTree ? 8 : 4;
491 children.resize(num);
493 for (
int i = 0; i < num; ++i) {
494 children[i] = mortonCodeToCoord(isOctTree, parent.level + 1, offset + i);
506std::vector<OGC3DTiles::Coord>
509 std::vector<OGC3DTiles::Coord> children;
510 if (subtree->pendingRequests > 0) {
514 int bitstreamIdx = subtree->tileAvailability.bitstreamIdx;
515 assert(bitstreamIdx <
int(subtree->bitstreamSections.size()) &&
"Index out of bounds");
517 const std::vector<uint8_t>& bitarray = subtree->bitstreams[section.bitstreamIdx];
519 const bool isOctTree = subtree->globalCoord.z >= 0;
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);
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));
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);
537 const int num = isOctTree ? 8 : 4;
538 children.reserve(num);
540 for (
int i = 0; i < num; ++i) {
541 int offset = levelOffset + localOffset + i;
542 const uint32_t coarseIdx = offset >> 3;
543 assert(coarseIdx < section.length &&
"Index out of bounds for bitstream section");
544 const int fineIdx = offset & 0b111;
546 assert(section.startIdx + coarseIdx < bitarray.size() &&
"Index out of bounds");
547 const uint8_t val = bitarray[section.startIdx + coarseIdx];
549 std::bitset<8> valBits(val);
550 if (valBits.test(fineIdx)) {
551 children.emplace_back(mortonCodeToCoord(isOctTree, parent.level + 1, globalOffset + i));
567Cogs::Core::DataFetcherManager::FetchId
568OGC3DTilesSubtree::fetchSubtree(
Cogs::Core::Context* context,
const std::string& URL, uint32_t uniqueComponentId, FetchSubtreeCallback callback)
570 Cogs::Core::DataFetcherManager::FetchId fetchId = DataFetcherManager::fetchAsync(context, URL,
571 [context, URL, callback, uniqueComponentId](std::unique_ptr<Cogs::FileContents> inData) {
578 bool success =
false;
580 if (data && data->data()) {
581 subtree = new Subtree;
582 subtree->pendingRequests = 0;
583 subtree->tileAvailability.allIsAvailable = false;
584 subtree->childSubtreeAvailability.allIsAvailable = false;
586 size_t paramPos = URL.find_last_of(
'?');
587 const std::string fileURL = URL.substr(0, paramPos == std::string::npos ? URL.size() : paramPos);
589 Memory::MemoryBuffer contentsBuff = inData->take();
590 if (IO::extension(fileURL) ==
".subtree" || IO::extension(fileURL) ==
".SUBTREE") {
591 success = parseSubtreeBufferBinary(context, &contentsBuff, subtree, URL, uniqueComponentId);
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);
598 LOG_ERROR(logger,
"fetchSubtree(): Unknown subtree extension for '%s'", fileURL.c_str());
607 LOG_ERROR(logger,
"fetchSubtree(): Could not fetch subtree '%s'", URL.c_str());
610 auto finishTask = [callback, subtree]() {
614#if defined(EMSCRIPTEN)
619 context->
engine->runTaskInMainThread(std::move(finishTask));
627checkAvailability(
const Subtree* subtree,
int bitstreamSectionIdx,
int localLevel,
int localX,
int localY,
int localZ)
629 if (subtree->pendingRequests > 0) {
633 uint32_t levelOffset = 0;
634 uint64_t mortonIndex = 0;
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)));
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));
643 assert(mortonIndex < 0xFFFFFFFF &&
"Internal error. Morton index is way too large.");
645 uint32_t offset = levelOffset + ((uint32_t) mortonIndex);
647 assert(bitstreamSectionIdx <
int(subtree->bitstreamSections.size()) &&
"Index out of bounds");
648 const BitstreamSection& section = subtree->bitstreamSections[bitstreamSectionIdx];
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());
654 assert(section.bitstreamIdx < subtree->bitstreams.size());
656 const std::vector<uint8_t>& bitarray = subtree->bitstreams[section.bitstreamIdx];
658 uint32_t coarseIdx = offset >> 3;
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");
663 unsigned char val = bitarray[section.startIdx + coarseIdx];
664 std::bitset<8> valBits(val);
665 return valBits.test(fineIdx);
673 bool hasContent =
false;
674 for (
const Availability& c : subtree->contentAvailability) {
675 if (c.allIsAvailable) {
679 else if (c.availableCount > 0) {
680 hasContent = checkAvailability(subtree, c.bitstreamIdx, localCoord.level, localCoord.x, localCoord.y, localCoord.z);
697OGC3DTilesSubtree::getSubtreesRootCoord(
int levelsPerSubtree,
const OGC3DTiles::Coord& coord)
699 int subtreeLevel = coord.level / levelsPerSubtree;
700 int levelsLeft = coord.level % levelsPerSubtree;
703 coord.x >> levelsLeft,
704 coord.y >> levelsLeft,
705 coord.z >= 0 ? coord.z >> levelsLeft : -1 };
711 const int relativeLevel = globalCoord.level - subtree->globalCoord.level;
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
721OGC3DTilesSubtree::mortonCodeToCoord(
bool isOctTree,
int level, uint32_t mortonCode)
727 coord.x = removeTwoSpacing(mortonCode);
728 coord.y = removeTwoSpacing(mortonCode >> 1);
729 coord.z = removeTwoSpacing(mortonCode >> 2);
732 coord.x = removeOneSpacing(mortonCode);
733 coord.y = removeOneSpacing(mortonCode >> 1);
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.
std::unique_ptr< class Engine > engine
Engine instance.
static bool componentIsStale(Context *context, uint32_t id)
Log implementation class.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
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.
Abstract base class storing data read from a file.