1#include "OGC3DTilesSystem.h"
2#include "OGC3DTilesBounds.h"
4#include "../OGC3DTilesUtils.h"
5#include "../OGC3DTilesAccessParser.h"
6#include "../OGC3DTilesAssetsParser.h"
7#include "../OGC3DTilesTileset.h"
8#include "../OGC3DTilesSubtree.h"
9#include "../Components/OGC3DTilesComponent.h"
12#include "ViewContext.h"
13#include "EntityStore.h"
14#include "ExtensionRegistry.h"
16#include "Services/Time.h"
17#include "Services/QualityService.h"
18#include "Services/Variables.h"
20#include "Components/Core/ModelComponent.h"
21#include "Components/Core/MeshComponent.h"
22#include "Components/Core/SceneComponent.h"
23#include "Components/Core/SubMeshRenderComponent.h"
25#include "Systems/Core/CameraSystem.h"
26#include "Systems/Core/TransformSystem.h"
28#include "Resources/MeshManager.h"
29#include "Resources/ModelManager.h"
30#include "Resources/VertexFormats.h"
31#include "Resources/MaterialManager.h"
32#include "Resources/MaterialInstance.h"
33#include "Resources/DefaultMaterial.h"
34#include "Resources/Texture.h"
36#include "Renderer/RenderResource.h"
38#include "Foundation/Logging/Logger.h"
39#include "Foundation/HashFunctions.h"
41#include <glm/gtc/matrix_transform.hpp>
42#include <glm/gtx/quaternion.hpp>
52constexpr int MAX_NUM_ACTIVE_MODEL_REQUESTS = 64;
53constexpr int MAX_NUM_ACTIVE_SUBTREES_REQUESTS = 8;
54constexpr int MAX_NUM_ACTIVE_SUBTILESET_REQUESTS = 8;
55constexpr int MAX_NUM_MODEL_REQUESTS_PER_FRAME = 24;
56constexpr float DEFAULT_GRACE_PERIOD_BEFORE_TREE_TRAVERSAL = 0.0;
57constexpr float DEFAULT_GRACE_PERIOD_BEFORE_TILE_REMOVAL = 2.0;
60#define BBOX_BASE_COLOR_FORMAT glm::vec4(r, 1.0, r, 0.5)
63#define DEBUG_ADD_URL_TO_MODEL_NAMES false
65#define DEBUG_CHECK_IF_INCOMING_MODEL_EXISTS false
71 const Cogs::StringView colorTilesAccordingToTreeDepthName =
"OGC3DTiles.debug.colorTilesAccordingToTreeDepth";
73 const Cogs::StringView maxNumActiveModelRequestsName =
"OGC3DTiles.requests.maxConcurrent";
75 const Cogs::StringView maxNumModelRequestsPerFrameName =
"OGC3DTiles.requests.maxPerFrame";
86 const Cogs::StringView gracePeriodBeforeTreeTraversalName =
"OGC3DTiles.timing.gracePeriodBeforeTreeTraversal";
88 const Cogs::StringView gracePeriodBeforeTileRemovalName =
"OGC3DTiles.timing.gracePeriodBeforeTileRemoval";
92 uint32_t createUniqueComponentId() {
93 static uint32_t idCounter = 1;
94 if (idCounter == 0) idCounter++;
104OGC3DTilesSystem::~OGC3DTilesSystem() =
default;
112 context->
variables->set(maxNumActiveModelRequestsName, MAX_NUM_ACTIVE_MODEL_REQUESTS);
116 context->
variables->set(maxNumModelRequestsPerFrameName, MAX_NUM_MODEL_REQUESTS_PER_FRAME);
120 context->
variables->set(gracePeriodBeforeTreeTraversalName, DEFAULT_GRACE_PERIOD_BEFORE_TREE_TRAVERSAL);
124 context->
variables->set(gracePeriodBeforeTileRemovalName, DEFAULT_GRACE_PERIOD_BEFORE_TILE_REMOVAL);
138 context->materialManager->processLoading();
140 for (
int i = 1; i < 8; ++i) {
141 glm::vec4 color = glm::vec4(i & 0b1 ? 1.0 : 0.0, i & 0b10 ? 1.0 : 0.0, i & 0b100 ? 1.0 : 0.0, 1.0);
143 materialInstance->setPermutation(
"FlatShaded");
144 if (
VariableKey key = materialInstance->
material->getVec4Key(
"diffuseColor"); key != NoProperty) {
148 debugTileTreeDepthColorMaterials.push_back(materialInstance);
162 for (
auto& comp : system->
pool) {
165 if (componentState->uniqueComponentId ==
id) {
174OGC3DTilesSystem::compileURL(
const std::string& baseURL,
const std::string& tilesetPath,
const std::string& modelPath)
176 return baseURL + (baseURL.empty() ?
"" :
"/") +
177 tilesetPath + (tilesetPath.empty() ?
"" :
"/") +
182OGC3DTilesSystem::printDebugStats(
const OGC3DTilesData* componentState)
184 LOG_DEBUG(logger,
"== OGC3DTiles debug stats ['%s'] ==", componentState->baseURL.c_str());
186 int totalNumTiles = 0;
187 int totalNumRequests = 0;
188 for (
auto const& [tileset, state] : componentState->states) {
189 totalNumTiles += (int)state.tileCache.size();
190 totalNumRequests += (int)state.modelRequests.size();
193 LOG_DEBUG(logger,
" Total num tiles in cache(s): %d", totalNumTiles);
194 LOG_DEBUG(logger,
" Num models in request-queue: %d", totalNumRequests);
195 LOG_DEBUG(logger,
" Max traversal depth: %d, Num canceled model requests: %d",
196 componentState->statistics.traversedDepth, componentState->statistics.numberOfCancelledRequests);
202 for (
auto tileset : componentState->tilesets) {
203 for (
auto const& [tileId, models] : componentState->states.at(tileset).tileCache) {
204 for (
const LoadedModel& model : models) {
205 applyMaterialToModel(model, materialHandle);
226 CpuInstrumentationScope(SCOPE_OGC3DTILES,
"OGC3DTilesSystem::update");
234 const CameraData& cameraData = this->context->cameraSystem->getMainCameraData();
238 bool cameraMoved = cameraData.rawViewProjection != this->lastCameraViewProjection;
244 if (componentState->overrideMaterial != component.overrideMaterial) {
246 applyMaterialToAllModels(componentState, component.overrideMaterial);
247 componentState->overrideMaterial = component.overrideMaterial;
250 if (componentState->detailFactor < 0.0) {
251 componentState->detailFactor = 0.0;
254 if (component.baseURL != componentState->baseURL && componentState->tilesets.empty()) {
255 componentState->parentEntity = component.getContainer();
256 initializeTileset(&component, componentState);
260 if (componentState->showTileBBoxes && !component.showTileBBoxes) {
262 for (
auto tileset : componentState->tilesets) {
263 for (
auto const& [tileId, entity] : componentState->states[tileset].bboxIndicatorCache) {
264 this->context->
store->
removeChild(componentState->parentEntity, entity.get());
267 componentState->states[tileset].bboxIndicatorCache.clear();
271 componentState->showTileBBoxes = component.showTileBBoxes;
272 componentState->neverDiscardRootTiles = component.neverDiscardRootTiles;
273 componentState->detailFactor = component.detailFactor;
274 componentState->waitForTileSiblings = component.waitForTileSiblings;
275 componentState->enableCaching = component.enableCaching;
276 componentState->colorTileAccordingToTreeDepth =
context->
variables->get(colorTilesAccordingToTreeDepthName,
false);
277 componentState->dontRenderLeaves = component.dontRenderLeaves;
278 componentState->loadAllSiblings = component.loadAllSiblings;
281 const double now = this->context->
time->getAnimationTime();
283 if (!componentState->tilesets.empty()) {
284 float gracePeriodBeforeTreeTraversal =
context->
variables->get(gracePeriodBeforeTreeTraversalName, DEFAULT_GRACE_PERIOD_BEFORE_TREE_TRAVERSAL);
286 bool needsTraversal =
false;
287 if (gracePeriodBeforeTreeTraversal > 0) {
288 const double delta = now - this->timestampOfLastCameraMovement;
289 needsTraversal = delta > gracePeriodBeforeTreeTraversal;
290 if (needsTraversal) {
295 needsTraversal = cameraMoved;
298 if (needsTraversal || hasPendingRequests(componentState) || componentState->requiresTraversal) {
299 float gracePeriodBeforeTileRemoval =
context->
variables->get(gracePeriodBeforeTileRemovalName, DEFAULT_GRACE_PERIOD_BEFORE_TILE_REMOVAL);
301 componentState->statistics.traversedDepth = 0;
302 componentState->statistics.numberOfCancelledRequests = 0;
303 componentState->statistics.numberOfPendingRequests = 0;
304 componentState->statistics.numberOfVisitedSubTilesets = 0;
305 componentState->numberOfModelsBeingPrepared = 0;
306 componentState->requiresTraversal =
false;
309 traverseTileset(&component, componentState, componentState->tilesets[0], gracePeriodBeforeTileRemoval);
313 for (
auto const& tileset : componentState->tilesets) {
314 double timestamp = componentState->states[tileset].timestampOfLastTraversal;
315 if (tileset != componentState->tilesets[0] &&
316 componentState->states[tileset].candidateForDelayedTraversal &&
317 (now - timestamp) >= gracePeriodBeforeTileRemoval) {
318 traverseTileset(&component, componentState, tileset, gracePeriodBeforeTileRemoval);
319 componentState->states[tileset].candidateForDelayedTraversal =
false;
324 this->processSubTilesetRequests(&component, componentState);
328 LOG_DEBUG(logger,
" Max traversal depth: %d, Num requests: %d, Num cancelled: %d, Num pending: %d",
329 componentState->statistics.traversedDepth,
330 componentState->modelRequests.size(),
331 componentState->statistics.numberOfCancelledRequests,
332 componentState->statistics.numberOfPendingRequests);
336 this->timestampOfLastCameraMovement = now;
337 this->lastCameraViewProjection = cameraData.rawViewProjection;
342 if (this->hasPendingRequests(componentState)) {
343 this->context->
engine->setDirty();
354 std::vector<std::string> toBeCancelled;
355 toBeCancelled.reserve(node.tile.URLs.size());
358 for (
auto const& url : node.tile.URLs) {
359 if (!state->modelRequests.contains(url)) {
361 for (
auto const& model : state->tileCache[node.tile.tileId]) {
362 if (model.URL == url) {
369 this->
context->modelManager->cancelModelLoad(state->modelRequests[url].second);
370 componentState->statistics.numberOfCancelledRequests += 1;
371 toBeCancelled.emplace_back(url);
376 for (
auto const& url : toBeCancelled) {
377 state->modelRequests.erase(url);
380 for (
auto const& [tileId, child] : node.children) {
381 cancelStaleModelRequests(componentState, tileset, child);
394 const std::unordered_map<std::string, OGC3DTiles::Coord> subtreeRequestsCopy = state->subtreeRequests;
396 for (
const auto& [subtreeURL, coord] : subtreeRequestsCopy) {
398 if (state->subtreeCache.contains(coord.level) && state->subtreeCache[coord.level].contains(key) &&
399 state->subtreeCache[coord.level][key]->pendingRequests == 0) {
400 const uint64_t subtreeGUID =
Cogs::hash(subtreeURL);
401 state->subtreeRequests.erase(subtreeURL);
402 state->subtreeFetchIds.erase(subtreeGUID);
408OGC3DTilesSystem::hasPendingRequests(
const OGC3DTilesData* componentState)
const
410 const bool subTilesetsState = !componentState->subTilesetRequests.empty();
411 const bool modelsPreparedState = componentState->numberOfModelsBeingPrepared > 0;
412 const bool tilesetFetches = !componentState->tilesetFetchIds.empty();
414 if (subTilesetsState || modelsPreparedState || tilesetFetches) {
418 for (
auto tileset : componentState->tilesets) {
419 const bool modelRequestsState = !componentState->states.at(tileset).pendingModelRequests.empty() || !componentState->states.at(tileset).modelRequests.empty();
420 const bool subtreeRequestsState = !componentState->states.at(tileset).subtreeRequests.empty();
422 if (modelRequestsState || subtreeRequestsState) {
433 std::unordered_set<const OGC3DTilesTileset::Tileset*> visitedTilesets;
436 rootNode.tile.depth = 0;
438 componentState->states[tileset].pendingModelRequests.clear();
441 const glm::mat4 globalTransform = this->
context->transformSystem->getLocalToWorld(componentTransform);
446 if (tileset->root.useImplicitTiling) {
448 OGC3DTiles::Coord rootCoord{ 0, 0, 0, tileset->root.implicitTiling.isOctTree ? 0 : -1 };
451 rootNode =
buildImplicitTree(componentState, tileset, rootCoord, globalTransform);
455 rootNode =
buildExplicitTree(componentState, tileset, &tileset->root,
"", tileset->root.transform, globalTransform, visitedTilesets);
458 if (rootNode.valid) {
459 std::unordered_map<uint64_t, OGC3DTilesSystem::TileCandidate> tilesInView;
463 LOG_DEBUG(logger,
"Tiles in view: %d\n", (
int)tilesInView.size());
464 OGC3DTilesSystem::printDebugStats(componentState);
476 cancelStaleModelRequests(componentState, tileset, rootNode);
478 const size_t numReleased =
pruneTileCache(componentState, tileset, gracePeriodBeforeTileRemoval);
480 if (numReleased != 0) {
481 LOG_DEBUG(logger,
"Released %zu model(s) (frame #%d)", numReleased,
context->
time->getFrame());
487 processModelRequests(componentState, tileset,
context->
time->getFrame());
489 componentState->states[tileset].timestampOfLastTraversal = this->
context->
time->getAnimationTime();
490 componentState->states[tileset].candidateForDelayedTraversal =
true;
492 if (tileset->root.useImplicitTiling) {
493 processSubtreeRequests(component, componentState, tileset);
497 for (
auto subTileset : visitedTilesets) {
498 assert(subTileset != componentState->tilesets[0] &&
"Don't traverse the main tileset twice!");
499 traverseTileset(component, componentState, subTileset, gracePeriodBeforeTileRemoval);
509 const std::string_view sessionToken =
"session=";
510 const std::string_view keyToken =
"key=";
512 size_t sessionTokenPos = url.find(sessionToken);
513 size_t sessionTokenEndPos = url.find(
"&");
514 if (sessionTokenPos != std::string::npos) {
515 size_t len = sessionTokenEndPos == std::string::npos ? std::string::npos : (sessionTokenEndPos - (sessionTokenPos + sessionToken.size()));
516 const std::string session = url.substr(sessionTokenPos + sessionToken.size(), len);
518 if (!componentState->optionalSessionKey.empty() && componentState->optionalSessionKey != session) {
519 LOG_WARNING(logger,
"Multiple session keys ('session=') for one domain is not supported.");
521 componentState->optionalSessionKey =
"session="+session;
522 LOG_DEBUG(logger,
"Found session key '%s' in URL", session.c_str());
526 LOG_DEBUG(logger,
"No optional session key in URL '%s'", url.c_str());
531 size_t keyTokenPos = url.find(keyToken);
532 size_t keyTokenEndPos = url.find(
"&");
533 if (keyTokenPos != std::string::npos) {
534 size_t len = keyTokenEndPos == std::string::npos ? std::string::npos : (keyTokenEndPos - (keyTokenPos + keyToken.size()));
535 const std::string key = url.substr(keyTokenPos + keyToken.size(), len);
537 if (!componentState->optionalAccessKey.empty() && componentState->optionalAccessKey != key) {
538 LOG_WARNING(logger,
"Multiple access-keys ('key=') for one domain is not supported.");
540 componentState->optionalAccessKey =
"key="+key;
541 LOG_DEBUG(logger,
"Found access key '%s' in URL", key.c_str());
545 LOG_DEBUG(logger,
"No optional access key in URL '%s'", url.c_str());
552OGC3DTilesSystem::addOptionalURLParameters(
const OGC3DTilesData* componentState,
const std::string& url)
const
554 std::string result = url;
555 bool hasParams = url.find(
"?") != std::string::npos;
558 if (result.find(componentState->optionalSessionKey) == std::string::npos) {
559 result += hasParams ?
"&" :
"?";
560 result += componentState->optionalSessionKey;
565 if (result.find(componentState->optionalAccessKey) == std::string::npos) {
566 result += hasParams ?
"&" :
"?";
567 result += componentState->optionalAccessKey;
571 if (!componentState->additionalURLParameters.empty()) {
572 result += hasParams ?
"&" :
"?";
573 result += componentState->additionalURLParameters;
582 LOG_INFO(logger,
"Initializing a OGC3DTilesComponent with baseURL='%s'.", component->
baseURL.c_str());
583 componentState->baseURL = component->
baseURL;
589 componentState->assetsFetchId = Cogs::Core::DataFetcherManager::NoFetchId;
591 LOG_DEBUG(logger,
"Fetching assets:\n URL='%s'\n accessToken='%s'\n AssetID=%d", component->
baseURL.c_str(), component->
accessToken.c_str(), component->
assetId);
592 componentState->assetsFetchId = fetchAndInitializeAsset(component, componentState->uniqueComponentId);
595 std::string tilesetURL = component->
baseURL +
"/tileset.json";
596 tilesetURL = addOptionalURLParameters(componentState, tilesetURL);
597 const uint64_t tilesetGUID =
Cogs::hash(tilesetURL);
599 if (componentState->tilesetFetchIds.contains(tilesetGUID)) {
600 LOG_ERROR(logger,
"Tileset '%s' is already being fetched [%lu]", tilesetURL.c_str(), componentState->tilesetFetchIds[tilesetGUID]);
603 tilesetURL = this->addOptionalURLParameters(componentState, tilesetURL);
604 componentState->tileBaseURL = component->
baseURL;
605 componentState->tilesetFetchIds[tilesetGUID] = fetchAndInitializeTileset(tilesetGUID, tilesetURL, component, componentState->uniqueComponentId);
609Cogs::Core::DataFetcherManager::FetchId
610OGC3DTilesSystem::fetchAndInitializeAsset(
OGC3DTilesComponent * component, uint32_t uniqueComponentId)
614 if (OGC3DTilesSystem::componentIsStale(thisp->context, uniqueComponentId)) {
622 std::string tilesetURL = component->
baseURL +
"/tileset.json";
623 componentState->tileBaseURL = component->
baseURL;
625 if (tilesAsset !=
nullptr) {
626 LOG_DEBUG(logger,
"Got asset data URL: '%s'", tilesAsset->url.c_str());
627 tilesetURL = tilesAsset->url;
629 thisp->extractAndStoreOptionalSessionKeys(componentState, tilesetURL);
632 size_t pos = tilesetURL.find_first_of(
'/', std::string(
"https://").
size()+1);
633 if (pos == std::string::npos) {
634 pos = tilesetURL.size();
636 componentState->tileBaseURL = tilesetURL.substr(0, pos);
639 if (!tilesAsset->bearerToken.empty()) {
641 LOG_DEBUG(logger,
"Appending bearer-token auth to tile requests URLs ('%s')", tilesAsset->bearerToken.c_str());
642 componentState->optionalAccessKey =
"access_token=" + tilesAsset->bearerToken;
645 LOG_DEBUG(logger,
"Tiles URL base is: '%s'", componentState->tileBaseURL.c_str());
649 LOG_ERROR(logger,
"Could not fetch asset data from '%s'\n", component->
baseURL.c_str());
653 tilesetURL = thisp->addOptionalURLParameters(componentState, tilesetURL);
658 const uint64_t tilesetGUID =
Cogs::hash(tilesetURL);
659 if (componentState->tilesetFetchIds.contains(tilesetGUID)) {
660 LOG_ERROR(logger,
"Tileset '%s' is already being fetched [%lu]", tilesetURL.c_str(), componentState->tilesetFetchIds[tilesetGUID]);
664 componentState->tilesetFetchIds[tilesetGUID] = thisp->fetchAndInitializeTileset(tilesetGUID, tilesetURL, component, uniqueComponentId);
665 componentState->assetsFetchId = Cogs::Core::DataFetcherManager::NoFetchId;
670OGC3DTilesSystem::fetchAndInitializeTileset(
const uint64_t tilesetGUID,
const std::string & tilesetURL,
OGC3DTilesComponent * component, uint32_t uniqueComponentId)
672 return Cogs::Core::OGC3DTilesTileset::fetch(this->
context, tilesetURL,
681 if (mainTileset !=
nullptr) {
683 componentState->originalRootTransform = mainTileset->root.transform;
684 mainTileset->root.transform = glm::mat4(1.0);
685 LOG_INFO(logger,
"Loaded tileset is of type '%s' [%p]", mainTileset->root.useImplicitTiling ?
"IMPLICIT" :
"EXPLICIT", mainTileset);
687 Cogs::Geometry::DBoundingBox bbox =
688 OGC3DTilesUtils::boundingVolumeConverter(mainTileset->root.boundingVolume, mainTileset->root.transform);
689 glm::vec3 center = (bbox.max - bbox.min) * 0.5 + bbox.min;
691 LOG_INFO(logger,
"Tileset center: <%.2f, %.2f, %.2f> [%p]", center.x, center.y, center.z, mainTileset);
692 LOG_INFO(logger,
"Tileset bbox: <%.2f, %.2f, %.2f>|<%.2f, %.2f, %.2f> [%p]", bbox.min.x, bbox.min.y, bbox.min.z, bbox.max.x, bbox.max.y, bbox.max.z, mainTileset);
693 LOG_INFO(logger,
"Tileset bounding volume: %s", mainTileset->root.boundingVolume.toString().c_str());
699 Cogs::Geometry::DBoundingBox globalBBox =
700 OGC3DTilesUtils::boundingVolumeConverter(mainTileset->root.boundingVolume, mainTileset->root.transform);
701 thisp->addBBoxIndicatorToScene(componentState, mainTileset, 0, globalBBox);
706 if (mainTileset->root.useImplicitTiling) {
707 LOG_INFO(logger,
" Tree-structure is of type '%s' [%p]", mainTileset->root.implicitTiling.isOctTree ?
"OCT" :
"QUAD", mainTileset);
709 thisp->requestSubtree(componentState, mainTileset, rootCoord);
712 componentState->tilesets.push_back(mainTileset);
715 thisp->context->engine->invokeComponentNotifyCallback(*component,
717 reinterpret_cast<const double *
>(glm::value_ptr(componentState->originalRootTransform)),
721 LOG_ERROR(logger,
"Failed to initialize OGC3DTilesComponent (baseURL='%s')", component->
baseURL.c_str());
724 componentState->tilesetFetchIds.erase(tilesetGUID);
726 LOG_DEBUG(logger,
"Tileset fetched, initialized and ready");
727 thisp->context->engine->setDirty();
728 componentState->requiresTraversal =
true;
745 const float qualityDelta = componentState->enableCaching ?
748 if (qualityDelta >= 0.0) {
753 gracePeriodBeforeTileRemoval = std::max(2.0f/60.0f, gracePeriodBeforeTileRemoval);
755 std::set<std::pair<double, uint64_t>> toBeRemoved;
756 const double now = this->
context->
time->getAnimationTime();
759 bool tileHidden =
false;
760 for (
const auto& [tileId, loadedModels] : componentState->states[tileset].tileCache) {
761 if (componentState->neverDiscardRootTiles &&
762 tileset == componentState->tilesets[0] &&
769 if (lm.timestampLastUsed + gracePeriodBeforeTileRemoval < now) {
770 toBeRemoved.emplace(std::make_pair(lm.timestampLastUsed, tileId));
777 if (lm.timestampLastUsed < now) {
781 sceneComponent->
visible =
false;
793 if (toBeRemoved.empty()) {
797 std::list<std::pair<double, uint64_t>> sortedByTimestamp(toBeRemoved.begin(), toBeRemoved.end());
798 sortedByTimestamp.sort([](
const std::pair<double, uint64_t>& a,
const std::pair<double, uint64_t>& b) {
return a.first > b.first; });
801 size_t numRemoved = 0;
803 const size_t numToBeRemoved = sortedByTimestamp.size() * size_t(-qualityDelta);
805 for (
const auto& [timestamp, tileId] : sortedByTimestamp) {
806 removeTileFromScene(componentState, tileset, tileId);
809 if (numRemoved >= numToBeRemoved) {
818OGC3DTilesSystem::setVisibilityForAllTiles(
const OGC3DTilesData* componentState,
bool onoff)
const
821 for (
auto tileset : componentState->tilesets) {
822 for (
const auto& [tileId, loadedModels] : componentState->states.at(tileset).tileCache) {
823 setTileVisibility(componentState, tileset, tileId, onoff);
831 if (!componentState->states.at(tileset).tileCache.contains(tileId)) {
836 const std::vector<LoadedModel>& loadedModels = componentState->states.at(tileset).tileCache.at(tileId);
837 for (
const LoadedModel& lm : loadedModels) {
838 if (!modelIsReady(lm.modelHandle)) {
849 if (sceneComponent->
visible != onoff) {
852 sceneComponent->
visible = onoff;
855 LOG_WARNING(logger,
"Could not find '%s' for visibility toggling (tileset %p)", lm.URL.c_str(), tileset);
861OGC3DTilesSystem::modelIsReady(
const ModelHandle& modelhandle)
const
863 if (!modelhandle->isResident()) {
868 for (
const auto& mesh : modelhandle->meshes) {
869 if (!mesh->isActive()) {
873 if (mesh->hasAttachedResource()) {
874 const auto r = mesh->getAttachedResource();
875 if (!r->isActive()) {
882 for (
const auto& material : modelhandle->materials) {
883 if (!material->isResident()) {
887 for (
const auto& tv : material->textureVariables) {
888 if (!tv.texture.handle->isResident()) {
900 const std::vector<LoadedModel>& loadedModels = componentState->states[tileset].tileCache[tileId];
901 for (
const LoadedModel& lm : loadedModels) {
914 LOG_WARNING(logger,
"removeTileFromCache(): Entity '%s' (%s) not found.", lm.URL.c_str(), lm.uniqueName.c_str());
918 if (componentState->showTileBBoxes) {
919 removeBBoxIndicatorFromScene(componentState, tileset, tileId);
922 componentState->states[tileset].tileCache.erase(tileId);
928 std::vector<const std::string*> finished;
929 const double now = this->
context->
time->getAnimationTime();
931 for (
const auto& [URL, pair] : componentState->states[tileset].modelRequests) {
932 TileCandidate tileCandidate = pair.first;
934 if (this->modelIsReady(modelHandle)) {
935 finished.emplace_back(&URL);
937#if DEBUG_CHECK_IF_INCOMING_MODEL_EXISTS
938 const std::vector<LoadedModel>& loadedModels = componentState->tileCache[tileCandidate.tileId];
939 bool alreadyLoaded = std::any_of(loadedModels.begin(), loadedModels.end(), [&url=URL](
const LoadedModel & item) { return item.URL == url; });
941 LOG_ERROR(logger,
"Incoming model '%s' is already loaded", URL.c_str());
942 assert(alreadyLoaded &&
"Incoming model is already loaded for this tile!");
947#if DEBUG_ADD_URL_TO_MODEL_NAMES
949 const std::string uniqueName = (tileset->root.useImplicitTiling ? OGC3DTiles::Coord::fromHash(tileCandidate.tileId).toString() : std::to_string(tileCandidate.tileId)) +
" - " + URL;
952 std::string uniqueName = tileset->root.useImplicitTiling ? OGC3DTiles::Coord::fromHash(tileCandidate.tileId).toString() : std::to_string(tileCandidate.tileId);
954 uniqueName += std::to_string(uint64_t(tileset));
964 LoadedModel loadedModel;
965 loadedModel.modelHandle = modelHandle;
966 loadedModel.timestampLastUsed = now;
967 loadedModel.URL = URL;
968 loadedModel.uniqueName = uniqueName;
969 loadedModel.frameNumberAtArrival = frameNumber;
971 if (!OGC3DTilesSystem::modelIsEmpty(modelHandle)) {
972 addModelToScene(loadedModel.uniqueName, tileCandidate, modelHandle, componentState);
973 loadedModel.isValid =
true;
974 componentState->requiresTraversal =
true;
977 componentState->states[tileset].tileCache[tileCandidate.tileId].emplace_back(loadedModel);
979 else if (modelHandle->hasFailedLoad()) {
980 LOG_ERROR(logger,
"processModelRequests(): Could not load '%s'", URL.c_str());
981 finished.emplace_back(&URL);
983 else if (modelHandle->hasFailedActivation()) {
985 LOG_ERROR(logger,
"processModelRequests(): Model failed to activate!");
987 finished.emplace_back(&URL);
991 for (
const std::string* URL : finished) {
992 componentState->states[tileset].modelRequests.erase(*URL);
1007 if (URL.find(
".json") != std::string::npos) {
1008 LOG_ERROR(logger,
"Internal error: A sub-tileset was requested as model; '%s'.", URL.c_str());
1009 assert(
false &&
"Internal error");
1014 if (!componentState->states[tileset].modelRequests.contains(URL)) {
1015 const std::vector<LoadedModel>& loadedModels = componentState->states[tileset].tileCache[tile.tileId];
1016 bool alreadyLoaded = std::ranges::any_of(loadedModels.cbegin(), loadedModels.cend(), [&URL](
LoadedModel const& item) { return item.URL == URL; });
1017 if (!alreadyLoaded) {
1019 componentState->states[tileset].modelRequests[URL] = std::pair(tile, handle);
1041 std::vector<const TileCandidate*> sortedTilesInView;
1042 sortedTilesInView.reserve(tilesInView.size());
1043 for (
auto it = tilesInView.cbegin(); it != tilesInView.cend(); ++it) {
1044 sortedTilesInView.emplace_back(&it->second);
1049 const glm::vec3 cameraPos = getCurrentCameraPosition();
1050 std::ranges::sort(sortedTilesInView.begin(), sortedTilesInView.end(),
1052 const double distA = OGC3DTilesUtils::distanceFromBBox(cameraPos, a->bbox);
1053 const double distB = OGC3DTilesUtils::distanceFromBBox(cameraPos, b->bbox);
1054 return distA < distB;
1058 std::ranges::sort(sortedTilesInView.begin(), sortedTilesInView.end(),
1060 return a->depth < b->depth;
1064 const double now = this->
context->
time->getAnimationTime();
1065 int numScheduled = 0;
1067 int maxNumRequestsPerFrame =
context->
variables->get(maxNumModelRequestsPerFrameName, MAX_NUM_MODEL_REQUESTS_PER_FRAME);
1068 if (maxNumRequestsPerFrame <= 0) {
1069 LOG_WARNING_ONCE(logger,
"Variable '%s' is <= 0. Resetting to %d.",
1070 maxNumModelRequestsPerFrameName.
to_string().c_str(), MAX_NUM_MODEL_REQUESTS_PER_FRAME);
1071 maxNumRequestsPerFrame = MAX_NUM_MODEL_REQUESTS_PER_FRAME;
1072 context->
variables->set(maxNumModelRequestsPerFrameName, MAX_NUM_MODEL_REQUESTS_PER_FRAME);
1075 int maxNumActiveRequests =
context->
variables->get(maxNumActiveModelRequestsName, MAX_NUM_ACTIVE_MODEL_REQUESTS);
1076 if (maxNumActiveRequests <= 0) {
1077 LOG_WARNING_ONCE(logger,
"Variable '%s' is <= 0. Resetting to %d.",
1078 maxNumActiveModelRequestsName.
to_string().c_str(), MAX_NUM_ACTIVE_MODEL_REQUESTS);
1079 maxNumActiveRequests = MAX_NUM_ACTIVE_MODEL_REQUESTS;
1080 context->
variables->set(maxNumActiveModelRequestsName, MAX_NUM_ACTIVE_MODEL_REQUESTS);
1084 state->pendingModelRequests.reserve(sortedTilesInView.size());
1086 for (
const auto* tilecandidate : sortedTilesInView) {
1087 for (std::string URL : tilecandidate->URLs) {
1089 if (state->modelRequests.size() >=
size_t(maxNumActiveRequests)) {
1091 state->pendingModelRequests.emplace(tilecandidate->tileId);
1094 if (numScheduled < maxNumRequestsPerFrame) {
1095 if (
requestModel(componentState, tileset, *tilecandidate, addOptionalURLParameters(componentState, URL))) {
1097 if (componentState->showTileBBoxes) {
1098 addBBoxIndicatorToScene(componentState, tileset, tilecandidate->tileId, tilecandidate->bbox);
1104 state->pendingModelRequests.emplace(tilecandidate->tileId);
1110 if (state->tileCache.contains(tilecandidate->tileId)) {
1111 std::vector<LoadedModel>& loadedModels = state->tileCache[tilecandidate->tileId];
1113 m.timestampLastUsed = now;
1118 componentState->statistics.numberOfPendingRequests = (int) componentState->states[tileset].pendingModelRequests.size();
1125 std::string path = std::string(tileset->root.implicitTiling.subTreeURLScheme);
1126 path = path.replace(path.find(
"{level}"), 7, std::to_string(coord.level));
1127 path = path.replace(path.find(
"{x}"), 3, std::to_string(coord.x));
1128 path = path.replace(path.find(
"{y}"), 3, std::to_string(coord.y));
1130 path = path.replace(path.find(
"{z}"), 3, std::to_string(coord.z));
1133 return componentState->tileBaseURL +
"/" + path;
1145 assert(coord.level <= 0xFFFF && coord.x <= 0xFFFF && coord.y <= 0xFFFF &&
"Coord values out of the 16bit bounds");
1146 uint64_t
hash = coord.level + (uint64_t(coord.x) << 16) + (uint64_t(coord.y) << 32);
1149 assert(coord.z <= 0xFFFF &&
"Coord.z value out of the 16bit bounds");
1150 hash += uint64_t(coord.z) << 48;
1162 if (tileHasModelRequests(componentState, tileset, tileId) ||
1163 tileHasSubTilesetRequests(componentState, tileId)) {
1167 assert(componentState->states[tileset].tileCache.contains(tileId) &&
"Tile not in cache");
1169 const std::vector<LoadedModel>& models = componentState->states[tileset].tileCache[tileId];
1170 size_t numReadyModels = 0;
1171 for (
const auto& lm : models) {
1173 numReadyModels += 1;
1177 if (modelIsReady(lm.modelHandle)) {
1178 numReadyModels += 1;
1181 componentState->numberOfModelsBeingPrepared += 1;
1185 return models.size() == numReadyModels;
1194 for (
const auto& [URL, pair] : componentState->states.at(tileset).modelRequests) {
1195 const TileCandidate& tileCandidate = pair.first;
1196 if (tileCandidate.tileId == tileId) {
1201 if (componentState->states.at(tileset).pendingModelRequests.contains(tileId)) {
1209OGC3DTilesSystem::tileHasSubTilesetRequests(
const OGC3DTilesData* componentState, uint64_t tileId)
const
1211 for (
const auto& [URL, pair] : componentState->subTilesetRequests) {
1212 if (pair.first == tileId) {
1226 size_t numReadyChildren = 0;
1227 for (
auto const& [childTileId, child] : node->children) {
1228 if (child.tile.URLs.empty()) {
1230 numReadyChildren += 1;
1235 numReadyChildren += 1;
1243 return node->children.size() == numReadyChildren;
1253 if (target.contains(node->tile.tileId)) {
1254 assert(
false &&
"Node has already been visited. Internal error.");
1258 if (!node->tile.URLs.empty()) {
1259 target[node->tile.tileId] = node->tile;
1262 for (
auto const& [childTileId, child] : node->children) {
1274 if (node->replaceRefine) {
1275 if (!componentState->waitForTileSiblings) {
1276 if (node->children.empty()) {
1277 setTileVisibility(componentState, tileset, node->tile.tileId,
true);
1280 setTileVisibility(componentState, tileset, node->tile.tileId,
false);
1284 if (componentState->states[tileset].tileCache.contains(node->tile.tileId)) {
1285 bool showNode =
true;
1286 if (!node->children.empty() &&
1293 setTileVisibility(componentState, tileset, node->tile.tileId, showNode);
1298 for (
auto const& [childTileId, child] : node->children) {
1313 const glm::mat4& globalTransform)
const
1317 node.replaceRefine = tileset->root.refine == OGC3DTilesTileset::RefineType::REPLACE;
1319 componentState->statistics.traversedDepth = std::max(componentState->statistics.traversedDepth, coord.level);
1322 OGC3DTilesUtils::getBoundingVolumeFromCoord(coord, tileset->root.boundingVolume);
1323 Cogs::Geometry::DBoundingBox tileBBox =
1324 OGC3DTilesUtils::boundingVolumeConverter(tileBoundingVolume, tileset->root.transform);
1325 Cogs::Geometry::DBoundingBox transformedTileBBox(tileBBox);
1327 OGC3DTilesUtils::transformBBox(transformedTileBBox, globalTransform);
1329 const bool isInside = isInsideFrustum(transformedTileBBox);
1331 if (!isInside && !componentState->loadAllSiblings) {
1341 const double distance = OGC3DTilesUtils::distanceFromBBox(getCurrentCameraPosition(), transformedTileBBox);
1346 const double geometricError = tileset->geometricError / OGC3DTilesUtils::fastPow2(coord.level);
1347 const double factor = componentState->detailFactor * geometricError;
1348 const bool hasContent = OGC3DTilesSubtree::hasContent(subtree, coord);
1350 if ((distance < factor || !hasContent) && isInside) {
1352 std::vector<OGC3DTiles::Coord> children =
1353 getTileCoordChildren(componentState, tileset, coord);
1356 if (componentState->dontRenderLeaves) {
1360 if (getTileCoordChildren(componentState, tileset, child).empty()) {
1369 assert(coord != childCoord &&
"Internal error");
1370 assert(childCoord.level != 0 &&
"Coord with level=0 cannot be a child");
1375 node.children[child.tile.tileId] = child;
1388 assert(tileset->root.contents.size() > 0 &&
"No content!");
1389 std::string path = tileset->root.contents[0].URI;
1390 path = path.replace(path.find(
"{level}"), 7, std::to_string(coord.level));
1391 path = path.replace(path.find(
"{x}"), 3, std::to_string(coord.x));
1392 path = path.replace(path.find(
"{y}"), 3, std::to_string(coord.y));
1394 path = path.replace(path.find(
"{z}"), 3, std::to_string(coord.z));
1397 node.tile.URLs.emplace_back(OGC3DTilesSystem::compileURL(componentState->tileBaseURL, tileset->baseURL, path));
1398 node.tile.transform = tileset->root.transform;
1401 node.tile.tileId = coord.toHash();
1402 node.tile.depth = coord.level;
1403 node.tile.bbox = tileBBox;
1418 const std::string& tileIdStr,
1419 const glm::mat4& currentTransform,
1420 const glm::mat4& globalTransform,
1421 std::unordered_set<const OGC3DTilesTileset::Tileset*> & visitedTilesets)
const
1425 node.replaceRefine = tile->refine == OGC3DTilesTileset::RefineType::REPLACE;
1426 componentState->statistics.traversedDepth = std::max(componentState->statistics.traversedDepth, (
int)tileIdStr.size());
1428 glm::mat4 transform = currentTransform;
1429 Cogs::Geometry::DBoundingBox bbox = OGC3DTilesUtils::boundingVolumeConverter(tile->boundingVolume, transform);
1430 Cogs::Geometry::DBoundingBox transformedBBox(bbox);
1431 OGC3DTilesUtils::transformBBox(transformedBBox, globalTransform);
1433 const bool isInside = isInsideFrustum(transformedBBox);
1435 if (!isInside && !componentState->loadAllSiblings) {
1439 const double distance = OGC3DTilesUtils::distanceFromBBox(getCurrentCameraPosition(), transformedBBox);
1440 const double factor = (node.tile.depth + 1) * std::pow(componentState->detailFactor, 2.0) * tile->geometricError;
1441 const bool hasContent = !tile->contents.empty();
1443 if ((distance < factor || !hasContent) && isInside) {
1445 if (componentState->dontRenderLeaves) {
1448 for (
size_t i=0; i < tile->children.size(); ++i) {
1450 if (childTile->children.empty()) {
1458 for (
size_t i = 0; i < tile->children.size(); ++i) {
1460 assert(!childTile->useImplicitTiling &&
"Internal error");
1463 Node childNode =
buildExplicitTree(componentState, tileset, childTile, tileIdStr + std::to_string(i), transform, globalTransform, visitedTilesets);
1465 if (childNode.valid) {
1466 assert(node.children.find(childNode.tile.tileId) == node.children.end() &&
"Child node has already been added");
1467 node.children[childNode.tile.tileId] = childNode;
1477 node.tile.depth = (uint32_t)tileIdStr.length();
1478 node.tile.bbox = bbox;
1479 node.tile.transform = transform;
1480 node.tile.URLs.reserve(tile->contents.size());
1484 const std::string url = OGC3DTilesSystem::compileURL(componentState->tileBaseURL, tileset->baseURL, c.URI);
1486 if (c.URI.find(
".json") != std::string::npos || c.URI.find(
".JSON") != std::string::npos) {
1487 const std::string tilesetURL = addOptionalURLParameters(componentState, url);
1488 if (componentState->states[tileset].subTilesets.contains(tilesetURL)) {
1489 visitedTilesets.insert(componentState->states[tileset].subTilesets[tilesetURL]);
1490 componentState->states[tileset].candidateForDelayedTraversal =
true;
1491 componentState->statistics.numberOfVisitedSubTilesets += 1;
1495 requestSubTileset(componentState, tilesetURL, node.tile.tileId, tileset);
1499 node.tile.URLs.emplace_back(url);
1510 if (componentState->subTilesetRequests.size() > MAX_NUM_ACTIVE_SUBTILESET_REQUESTS) {
1514 if (componentState->loadedSubTilesets.contains(URL) ||
1515 componentState->subTilesetRequests.contains(URL)) {
1519 componentState->subTilesetRequests[URL] = std::make_pair(tileId, parentTileset);
1535 if (componentState->colorTileAccordingToTreeDepth) {
1537 modelComponent->
materialInstance = debugTileTreeDepthColorMaterials[tile.depth % debugTileTreeDepthColorMaterials.size()];
1539 else if (componentState->overrideMaterial) {
1544 modelComponent->
model = handle;
1547 transformComponent->
transform = tile.transform;
1556 assert(componentState->states.contains(tileset) &&
"Internal error");
1557 if (!componentState->states[tileset].bboxIndicatorCache.contains(tileId)) {
1558 std::string title =
"BBox ";
1559 title += tileset->root.useImplicitTiling ? OGC3DTiles::Coord::fromHash(tileId).toString() : std::to_string(tileId);
1561 title += std::to_string(uint64_t(tileset));
1565 componentState->states[tileset].bboxIndicatorCache[tileId] = bboxEntity;
1567 auto createBBoxTask = [ctx = this->
context, bboxEntity, box]() {
1570 MaterialInstanceHandle material = ctx->materialInstanceManager->createMaterialInstance(ctx->materialManager->getDefaultMaterial());
1571 material->setPermutation(
"Line");
1572 material->setBoolProperty(DefaultMaterial::EnableLighting,
false);
1573 const float r = (rand() % 255) / 255.0f;
1574 material->setVec4Property(DefaultMaterial::DiffuseColor, BBOX_BASE_COLOR_FORMAT);
1575 material->options.depthBiasEnabled =
true;
1576 material->options.depthBias.constant = -1.f;
1577 material->options.depthBias.slope = -1.f;
1578 material->options.depthBias.clamp = 100.f;
1581 std::array<PositionVertex, 8> vertices = {
1592 std::array<unsigned int, 16> lineStripIndices = {
1601 meshRenderComponent->setMaterial(material);
1603 MeshHandle meshHandle = ctx->meshManager->create();
1606 Cogs::Geometry::BoundingBox fbox;
1611 Mesh* mesh = ctx->meshManager->get(meshHandle);
1612 assert(mesh &&
"Internal error");
1628 auto it = componentState->states[tileset].bboxIndicatorCache.find(tileId);
1629 if (it != componentState->states[tileset].bboxIndicatorCache.end()) {
1633 componentState->states[tileset].bboxIndicatorCache.erase(tileId);
1636 if (tileset->root.useImplicitTiling) {
1637 LOG_ERROR(logger,
"Instructed to remove BBox '%s', but it does not exist.", OGC3DTiles::Coord::fromHash(tileId).toString().c_str());
1640 LOG_ERROR(logger,
"Instructed to remove BBox '%llu', but it does not exist.", tileId);
1651 h.data = std::make_unique<OGC3DTilesData>();
1654 data->uniqueComponentId = createUniqueComponentId();
1656 if (this->
pool.size() >= 1 && this->bounds ==
nullptr) {
1668 LOG_DEBUG(logger,
"Destroying OGC3DTilesComponent (baseURL=%s)", comp->
baseURL.c_str());
1673 if (componentState->assetsFetchId != Cogs::Core::DataFetcherManager::NoFetchId) {
1674 Cogs::Core::DataFetcherManager::cancelAsyncFetch(this->
context, componentState->assetsFetchId);
1677 for (
auto & [guid, fetchId] : componentState->tilesetFetchIds) {
1678 Cogs::Core::DataFetcherManager::cancelAsyncFetch(this->
context, fetchId);
1681 for (
auto tileset : componentState->tilesets) {
1682 for (
auto& [guid, fetchId] : componentState->states[tileset].subtreeFetchIds) {
1683 Cogs::Core::DataFetcherManager::cancelAsyncFetch(this->
context, fetchId);
1686 for (
const auto& [tileId, loadedModels] : componentState->states[tileset].tileCache) {
1696 LOG_ERROR(logger,
"destroyComponent(): Could not find entity '%s' (%s)", lm.URL.c_str(), lm.uniqueName.c_str());
1702 for (
auto& [
id, subtreeMap] : componentState->states[tileset].subtreeCache) {
1703 for (
auto& [key, val] : subtreeMap) {
1710 for (
auto& b : componentState->states[tileset].bboxIndicatorCache) {
1717 componentState->tilesets.clear();
1718 componentState->states.clear();
1720 if (this->bounds !=
nullptr) {
1728OGC3DTilesSystem::getCurrentCameraPosition()
const
1730 return this->
context->cameraSystem->getMainCameraData().inverseViewMatrix * glm::dvec4(0, 0, 0, 1.0);
1734OGC3DTilesSystem::isInsideFrustum(
const Cogs::Geometry::DBoundingBox& box)
const
1737 const Cogs::Geometry::Frustum frustum = this->
context->cameraSystem->getMainCameraData().frustum;
1738 Cogs::Geometry::BoundingBox fbox;
1741 const int result = Cogs::Geometry::contains(frustum, fbox);
1742 return result == 2 || result == 1;
1748 const CameraData& cameraData = this->
context->cameraSystem->getMainCameraData();
1749 int result = OGC3DTilesUtils::bboxInsideViewFrustum(box, cameraData.rawViewProjection);
1755OGC3DTilesSystem::modelIsEmpty(
const ModelHandle handle)
1757 const size_t numParts = handle->parts.size();
1758 for (
size_t i = 0; i < numParts; ++i) {
1759 const ModelPart& part = handle->parts[i];
1760 if (part.vertexCount !=
static_cast<uint32_t
>(-1)) {
1767std::vector<OGC3DTiles::Coord>
1770 assert(tileset->root.useImplicitTiling &&
"Only IMPLICIT trees uses subtrees");
1771 std::vector<OGC3DTiles::Coord> children;
1775 LOG_ERROR(logger,
"getTileCoordChildren(): No subtree found. Internal error");
1780 if (OGC3DTilesSubtree::isLeafNodeInSubtree(subtree, parent) ||
1781 ((parent.level + 1) >= tileset->root.implicitTiling.availableLevels)) {
1782 std::vector<OGC3DTiles::Coord> subtreeCoords = OGC3DTilesSubtree::getLeafNodesSubtreeCoords(subtree, parent);
1783 children.reserve(subtreeCoords.size());
1785 for (
auto const& subtreeCoord : subtreeCoords) {
1787 if (subtreeCoord.level > tileset->root.implicitTiling.availableLevels) {
1788 LOG_WARNING(logger,
"Requested subtree-level too deep: %d vs %d", subtreeCoord.level, tileset->root.implicitTiling.availableLevels);
1793 if (subSubtree && (subtree->tileAvailability.availableCount > 0 || subtree->tileAvailability.allIsAvailable)) {
1794 children.emplace_back(subtreeCoord);
1799 if (subtree->tileAvailability.availableCount == 0 && !subtree->tileAvailability.allIsAvailable) {
1802 else if (subtree->tileAvailability.allIsAvailable) {
1804 children = OGC3DTilesSubtree::getAllChildren(subtree, parent);
1808 children = OGC3DTilesSubtree::getChildren(subtree, parent);
1825 assert(tileset->root.useImplicitTiling &&
"Only IMPLICIT trees uses subtrees");
1827 const int levelsPerSubtree = tileset->root.implicitTiling.subtreeLevels;
1828 const OGC3DTiles::Coord & subtreeCoord = OGC3DTilesSubtree::getSubtreesRootCoord(levelsPerSubtree, coord);
1830 if (subtreeCoord.level > tileset->root.implicitTiling.availableLevels) {
1831 LOG_ERROR(logger,
"Subtree coord-depth > tree-depth (%d vs %d)", subtreeCoord.level, tileset->root.implicitTiling.availableLevels);
1838 if (!componentState->states[tileset].subtreeCache.contains(subtreeCoord.level) ||
1839 !componentState->states[tileset].subtreeCache[subtreeCoord.level].contains(key)) {
1844 return componentState->states[tileset].subtreeCache[subtreeCoord.level][key];
1852 std::vector<std::string> urls;
1853 urls.reserve(componentState->subTilesetRequests.size());
1854 for (
const auto& [URL, tile] : componentState->subTilesetRequests) {
1855 const uint64_t tilesetGUID =
Cogs::hash(URL);
1856 if (!componentState->tilesetFetchIds.contains(tilesetGUID)) {
1857 urls.emplace_back(URL);
1861 for (
size_t i = 0; i < urls.size(); ++i) {
1862 const std::string url = addOptionalURLParameters(componentState, urls[i]);
1867 const uint64_t tilesetGUID =
Cogs::hash(url);
1868 componentState->tilesetFetchIds[tilesetGUID] = Cogs::Core::OGC3DTilesTileset::fetch(
context, url,
1869 [thisp=
this, component, url, parentTileset, tilesetGUID, uniqueComponentId=componentState->uniqueComponentId](
OGC3DTilesTileset::Tileset* tileset) {
1870 if (OGC3DTilesSystem::componentIsStale(thisp->context, uniqueComponentId)) {
1878 componentState->tilesets.push_back(tileset);
1880 componentState->states[parentTileset].subTilesets[url] = tileset;
1884 size_t offset = componentState->baseURL.size() + 1;
1885 tileset->baseURL = url.substr(offset, url.find_last_of(
"/") - offset);
1887 if (tileset->root.useImplicitTiling) {
1888 tileset->root.implicitTiling.subTreeURLScheme = tileset->baseURL +
"/" + tileset->root.implicitTiling.subTreeURLScheme;
1892 tileset->root.transform = tileset->root.transform * parentTileset->root.transform;
1895 if (tileset->root.useImplicitTiling) {
1896 LOG_DEBUG(logger,
" Sub-tileset tree-structure is of type '%s'", tileset->root.implicitTiling.isOctTree ?
"OCT" :
"QUAD");
1898 thisp->requestSubtree(componentState, tileset, rootCoord);
1901 componentState->loadedSubTilesets.insert(url);
1904 LOG_ERROR(logger,
"Could not parse incoming Sub-tileset from '%s'", url.c_str());
1907 componentState->subTilesetRequests.erase(url);
1908 componentState->tilesetFetchIds.erase(tilesetGUID);
1917 assert(tileset->root.useImplicitTiling &&
"Only IMPLICIT trees uses subtrees");
1920 std::vector<std::string> urls;
1921 urls.reserve(componentState->states[tileset].subtreeRequests.size());
1922 for (
const auto& [subtreeURL, coord] : componentState->states[tileset].subtreeRequests) {
1923 const uint64_t subtreeGUID =
Cogs::hash(subtreeURL);
1924 if (!componentState->states[tileset].subtreeFetchIds.contains(subtreeGUID)) {
1925 urls.emplace_back(subtreeURL);
1929 for (
size_t i=0; i<urls.size(); ++i) {
1930 const std::string subtreeURL = urls[i];
1931 const OGC3DTiles::Coord subtreeCoord = componentState->states[tileset].subtreeRequests[subtreeURL];
1933 const uint64_t subtreeGUID =
Cogs::hash(subtreeURL);
1935 componentState->states[tileset].subtreeFetchIds[subtreeGUID] = OGC3DTilesSubtree::fetchSubtree(this->
context, subtreeURL, componentState->uniqueComponentId,
1936 [thisp=
this, component, subtreeCoord, subtreeURL, tileset, uniqueComponentId = componentState->uniqueComponentId, subtreeGUID](
OGC3DTilesSubtree::Subtree* subtree) {
1937 if (OGC3DTilesSystem::componentIsStale(thisp->context, uniqueComponentId)) {
1944 assert(componentState->states.contains(tileset) &&
"The tileset is not a part of the component state!");
1947 if (subtree !=
nullptr) {
1948 subtree->globalCoord = subtreeCoord;
1949 subtree->levelsPerSubtree = tileset->root.implicitTiling.subtreeLevels;
1952 if (state->subtreeCache[subtree->globalCoord.level].contains(key)) {
1953 LOG_WARNING(logger,
"Subtree '%s' (%s) is already in the cache.", subtreeCoord.toString().c_str(), subtreeURL.c_str());
1957 state->subtreeCache[subtree->globalCoord.level][key] = subtree;
1961 LOG_ERROR(logger,
"Failed to load subtree '%s'", subtreeCoord.toString().c_str());
1964 state->subtreeRequests.erase(subtreeURL);
1965 state->subtreeFetchIds.erase(subtreeGUID);
1971 thisp->context->engine->setDirty();
1984 if (componentState->states[tileset].subtreeRequests.size() > MAX_NUM_ACTIVE_SUBTREES_REQUESTS) {
1988 const std::string subtreeURL = OGC3DTilesSystem::buildSubtreeURL(componentState, tileset, coord);
1989 const std::string fullURL = this->addOptionalURLParameters(componentState, subtreeURL);
1992 if (!componentState->states[tileset].subtreeCache[coord.level].contains(key) &&
1993 !componentState->states[tileset].subtreeRequests.contains(fullURL)) {
1994 componentState->states[tileset].subtreeRequests[fullURL] = coord;
void setChanged()
Sets the component to the ComponentFlags::Changed state with carry.
ComponentType * getComponent() const
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
virtual ComponentHandle createComponent()
Create a new component instance.
Context * context
Pointer to the Context instance the system lives in.
virtual void initialize(Context *context)
Initialize the system.
virtual void destroyComponent(ComponentHandle)
Destroy the component held by the given handle.
void update()
Updates the system state to that of the current frame.
Component system with parallel data per component stored in a pool similar to how the components them...
size_t size()
Returns the number of active components.
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 Bounds > bounds
Bounds service instance.
std::unique_ptr< class QualityService > qualityService
Quality service instance.
class EntityStore * store
Entity store.
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
std::unique_ptr< class Variables > variables
Variables service instance.
std::unique_ptr< class Time > time
Time service instance.
std::unique_ptr< class Engine > engine
Engine instance.
EntityPtr getEntity(const StringView &name, bool logIfNotFound=true) const
Retrieve a reference to the shared entity pointer to the Entity with the given name.
void addChild(ComponentModel::Entity *parent, const EntityPtr &entity)
Add a child to the given parent.
void destroyEntity(const EntityId id)
Destroy the entity with the given id.
EntityPtr createEntity(const StringView &name, const StringView &type, bool storeOwnership=true)
Create a new Entity.
void removeChild(ComponentModel::Entity *parent, const ComponentModel::Entity *entity)
Remove the parent-child relationship between parent and entity.
EntityPtr findEntity(const StringView &name, const ComponentModel::Entity *root=nullptr, EntityFind findOptions=EntityFind::Default) const
Finds an entity with the given name.
Contains a handle to a Mesh resource to use when rendering using the MeshRenderComponent.
MeshHandle meshHandle
Handle to a Mesh resource to use when rendering.
Node buildImplicitTree(OGC3DTilesData *componentState, const OGC3DTilesTileset::Tileset *tileset, const OGC3DTiles::Coord &coord, const glm::mat4 &globalTransform) const
bool requestModel(OGC3DTilesData *componentState, const OGC3DTilesTileset::Tileset *tileset, const TileCandidate &tile, const std::string URL) const
static uint64_t makeCacheKey(const OGC3DTiles::Coord &coord)
bool allChildrenAreReady(OGC3DTilesData *componentState, const OGC3DTilesTileset::Tileset *tileset, const Node *node) const
void collectTileCandidates(OGC3DTilesData *componentState, const Node *node, std::unordered_map< uint64_t, TileCandidate > &target) const
static bool componentIsStale(Context *context, uint32_t id)
void loadMissingModels(OGC3DTilesData *componentState, const OGC3DTilesTileset::Tileset *tileset, const std::unordered_map< uint64_t, TileCandidate > &toBeLoaded) const
void requestSubtree(OGC3DTilesData *componentState, const OGC3DTilesTileset::Tileset *tileset, const OGC3DTiles::Coord &coord) const
Node buildExplicitTree(OGC3DTilesData *componentState, const OGC3DTilesTileset::Tileset *tileset, const OGC3DTilesTileset::Tile *tile, const std::string &tileIdStr, const glm::mat4 ¤tTransform, const glm::mat4 &globalTransform, std::unordered_set< const OGC3DTilesTileset::Tileset * > &visitedTilesets) const
OGC3DTilesSubtree::Subtree * getSubtreeForTileCoord(OGC3DTilesData *componentState, const OGC3DTilesTileset::Tileset *tileset, const OGC3DTiles::Coord &coord) const
bool tileHasReadyContent(OGC3DTilesData *componentState, const OGC3DTilesTileset::Tileset *tileset, uint64_t tileId) const
void cleanupPendingSubtreeRequests(OGC3DTilesData *componentState, const OGC3DTilesTileset::Tileset *tileset) const
bool extractAndStoreOptionalSessionKeys(OGC3DTilesData *componentState, const std::string &url) const
size_t pruneTileCache(OGC3DTilesData *componentState, const OGC3DTilesTileset::Tileset *tileset, float gracePeriodBeforeTileRemoval) const
ComponentHandle createComponent() override
void calculateTileVisibilities(OGC3DTilesData *componentState, const OGC3DTilesTileset::Tileset *tileset, const Node *node) const
void addModelToScene(const std::string &uniqueName, TileCandidate tile, ModelHandle handle, const OGC3DTilesData *componentState) const
void initialize(Context *context) override
Initialize the system.
void destroyComponent(ComponentHandle component) override
Contains information on how the entity behaves in the scene.
bool visible
If the entity this component is a member of should be visible.
Renders a mesh with flexible submesh usage.
static constexpr TaskQueueId GlobalQueue
Global task queue.
Log implementation class.
Base allocator implementation.
Provides a weakly referenced view over the contents of a string.
std::string to_string() const
String conversion method.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
std::shared_ptr< ComponentModel::Entity > EntityPtr
Smart pointer for Entity access.
@ TilesetInitializing
The tileset is being initialized.
uint16_t VariableKey
Used to lookup material properties.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
uint32_t ComponentIndex
Type used to track component indexes in pools.
Handle to a Component instance.
COGSFOUNDATION_API class Component * resolve() const
Resolve the handle, returning a pointer to the held Component instance.
Contains data describing a Camera instance and its derived data structured such as matrix data and vi...
void setVec4Property(const VariableKey key, glm::vec4 value)
Set the vec4 property with the given key to value.
Material * material
Material resource this MaterialInstance is created from.
Meshes contain streams of vertex data in addition to index data and options defining geometry used fo...
void setBounds(Geometry::BoundingBox box)
Set custom bounds for the mesh.
void addSubMesh(std::span< uint32_t > collection, PrimitiveType primitiveType)
Add a sub-mesh to the Mesh.
void setVertexData(Element *elements, size_t count)
Set vertex data.
Contains a model reference to instance as children to the entity the ModelComponent belongs to.
bool inheritMaterial
If the model should inherit the material from the base entity.
MaterialInstanceHandle materialInstance
Explicit material handle.
ModelHandle model
Handle to a model resource that will be instanced onto the entity this ModelComponent belongs to.
std::string additionalURLParameters