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"
24#include "Components/Appearance/MaterialComponent.h"
26#include "Systems/Core/CameraSystem.h"
27#include "Systems/Core/TransformSystem.h"
29#include "Resources/MeshManager.h"
30#include "Resources/ModelManager.h"
31#include "Resources/VertexFormats.h"
32#include "Resources/MaterialManager.h"
33#include "Resources/MaterialInstance.h"
34#include "Resources/DefaultMaterial.h"
35#include "Resources/Texture.h"
37#include "Renderer/RenderResource.h"
39#include "Foundation/Logging/Logger.h"
40#include "Foundation/HashFunctions.h"
42#include <glm/gtc/matrix_transform.hpp>
43#include <glm/gtx/quaternion.hpp>
53constexpr int MAX_NUM_ACTIVE_MODEL_REQUESTS = 64;
54constexpr int MAX_NUM_ACTIVE_SUBTREES_REQUESTS = 8;
55constexpr int MAX_NUM_ACTIVE_SUBTILESET_REQUESTS = 8;
56constexpr int MAX_NUM_MODEL_REQUESTS_PER_FRAME = 24;
57constexpr float DEFAULT_GRACE_PERIOD_BEFORE_TREE_TRAVERSAL = 0.0;
58constexpr float DEFAULT_GRACE_PERIOD_BEFORE_TILE_REMOVAL = 2.0;
61#define BBOX_BASE_COLOR_FORMAT glm::vec4(r, 1.0, r, 0.5)
64#define DEBUG_ADD_URL_TO_MODEL_NAMES false
66#define DEBUG_CHECK_IF_INCOMING_MODEL_EXISTS false
72 const Cogs::StringView colorTilesAccordingToTreeDepthName =
"OGC3DTiles.debug.colorTilesAccordingToTreeDepth";
74 const Cogs::StringView maxNumActiveModelRequestsName =
"OGC3DTiles.requests.maxConcurrent";
76 const Cogs::StringView maxNumModelRequestsPerFrameName =
"OGC3DTiles.requests.maxPerFrame";
87 const Cogs::StringView gracePeriodBeforeTreeTraversalName =
"OGC3DTiles.timing.gracePeriodBeforeTreeTraversal";
89 const Cogs::StringView gracePeriodBeforeTileRemovalName =
"OGC3DTiles.timing.gracePeriodBeforeTileRemoval";
93 uint32_t createUniqueComponentId() {
94 static uint32_t idCounter = 1;
95 if (idCounter == 0) idCounter++;
105OGC3DTilesSystem::~OGC3DTilesSystem() =
default;
113 context->
variables->set(maxNumActiveModelRequestsName, MAX_NUM_ACTIVE_MODEL_REQUESTS);
117 context->
variables->set(maxNumModelRequestsPerFrameName, MAX_NUM_MODEL_REQUESTS_PER_FRAME);
121 context->
variables->set(gracePeriodBeforeTreeTraversalName, DEFAULT_GRACE_PERIOD_BEFORE_TREE_TRAVERSAL);
125 context->
variables->set(gracePeriodBeforeTileRemovalName, DEFAULT_GRACE_PERIOD_BEFORE_TILE_REMOVAL);
139 context->materialManager->processLoading();
141 for (
int i = 1; i < 8; ++i) {
142 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);
144 materialInstance->setPermutation(
"FlatShaded");
145 if (
VariableKey key = materialInstance->
material->getVec4Key(
"diffuseColor"); key != NoProperty) {
149 debugTileTreeDepthColorMaterials.push_back(materialInstance);
163 for (
auto& comp : system->
pool) {
166 if (componentState->uniqueComponentId ==
id) {
175OGC3DTilesSystem::compileURL(
const std::string& baseURL,
const std::string& tilesetPath,
const std::string& modelPath)
177 return baseURL + (baseURL.empty() ?
"" :
"/") +
178 tilesetPath + (tilesetPath.empty() ?
"" :
"/") +
183OGC3DTilesSystem::printDebugStats(
const OGC3DTilesData* componentState)
185 LOG_DEBUG(logger,
"== OGC3DTiles debug stats ['%s'] ==", componentState->baseURL.c_str());
187 int totalNumTiles = 0;
188 int totalNumRequests = 0;
189 for (
auto const& [tileset, state] : componentState->states) {
190 totalNumTiles += (int)state.tileCache.size();
191 totalNumRequests += (int)state.modelRequests.size();
194 LOG_DEBUG(logger,
" Total num tiles in cache(s): %d", totalNumTiles);
195 LOG_DEBUG(logger,
" Num models in request-queue: %d", totalNumRequests);
196 LOG_DEBUG(logger,
" Max traversal depth: %d, Num canceled model requests: %d",
197 componentState->statistics.traversedDepth, componentState->statistics.numberOfCancelledRequests);
203 for (
auto tileset : componentState->tilesets) {
204 for (
auto const& [tileId, models] : componentState->states.at(tileset).tileCache) {
205 for (
const LoadedModel& model : models) {
206 applyMaterialToModel(model, materialHandle);
227 CpuInstrumentationScope(SCOPE_OGC3DTILES,
"OGC3DTilesSystem::update");
235 const CameraData& cameraData = this->context->cameraSystem->getMainCameraData();
239 bool cameraMoved = cameraData.rawViewProjection != this->lastCameraViewProjection;
245 if (componentState->overrideMaterial != component.overrideMaterial) {
247 applyMaterialToAllModels(componentState, component.overrideMaterial);
248 componentState->overrideMaterial = component.overrideMaterial;
251 if (componentState->detailFactor < 0.0) {
252 componentState->detailFactor = 0.0;
255 if (component.baseURL != componentState->baseURL && componentState->tilesets.empty()) {
256 componentState->parentEntity = component.getContainer();
257 initializeTileset(&component, componentState);
261 if (componentState->showTileBBoxes && !component.showTileBBoxes) {
263 for (
auto tileset : componentState->tilesets) {
264 for (
auto const& [tileId, entity] : componentState->states[tileset].bboxIndicatorCache) {
265 this->context->
store->
removeChild(componentState->parentEntity, entity.get());
268 componentState->states[tileset].bboxIndicatorCache.clear();
272 componentState->showTileBBoxes = component.showTileBBoxes;
273 componentState->neverDiscardRootTiles = component.neverDiscardRootTiles;
274 componentState->detailFactor = component.detailFactor;
275 componentState->waitForTileSiblings = component.waitForTileSiblings;
276 componentState->enableCaching = component.enableCaching;
277 componentState->colorTileAccordingToTreeDepth =
context->
variables->get(colorTilesAccordingToTreeDepthName,
false);
278 componentState->dontRenderLeaves = component.dontRenderLeaves;
279 componentState->loadAllSiblings = component.loadAllSiblings;
282 const double now = this->context->
time->getAnimationTime();
284 if (!componentState->tilesets.empty()) {
285 float gracePeriodBeforeTreeTraversal =
context->
variables->get(gracePeriodBeforeTreeTraversalName, DEFAULT_GRACE_PERIOD_BEFORE_TREE_TRAVERSAL);
287 bool needsTraversal =
false;
288 if (gracePeriodBeforeTreeTraversal > 0) {
289 const double delta = now - this->timestampOfLastCameraMovement;
290 needsTraversal = delta > gracePeriodBeforeTreeTraversal;
291 if (needsTraversal) {
296 needsTraversal = cameraMoved;
299 if (needsTraversal || hasPendingRequests(componentState) || componentState->requiresTraversal) {
300 float gracePeriodBeforeTileRemoval =
context->
variables->get(gracePeriodBeforeTileRemovalName, DEFAULT_GRACE_PERIOD_BEFORE_TILE_REMOVAL);
302 componentState->statistics.traversedDepth = 0;
303 componentState->statistics.numberOfCancelledRequests = 0;
304 componentState->statistics.numberOfPendingRequests = 0;
305 componentState->statistics.numberOfVisitedSubTilesets = 0;
306 componentState->numberOfModelsBeingPrepared = 0;
307 componentState->requiresTraversal =
false;
310 traverseTileset(&component, componentState, componentState->tilesets[0], gracePeriodBeforeTileRemoval);
314 for (
auto const& tileset : componentState->tilesets) {
315 double timestamp = componentState->states[tileset].timestampOfLastTraversal;
316 if (tileset != componentState->tilesets[0] &&
317 componentState->states[tileset].candidateForDelayedTraversal &&
318 (now - timestamp) >= gracePeriodBeforeTileRemoval) {
319 traverseTileset(&component, componentState, tileset, gracePeriodBeforeTileRemoval);
320 componentState->states[tileset].candidateForDelayedTraversal =
false;
325 this->processSubTilesetRequests(&component, componentState);
329 LOG_DEBUG(logger,
" Max traversal depth: %d, Num requests: %d, Num cancelled: %d, Num pending: %d",
330 componentState->statistics.traversedDepth,
331 componentState->modelRequests.size(),
332 componentState->statistics.numberOfCancelledRequests,
333 componentState->statistics.numberOfPendingRequests);
337 this->timestampOfLastCameraMovement = now;
338 this->lastCameraViewProjection = cameraData.rawViewProjection;
343 if (this->hasPendingRequests(componentState)) {
344 this->context->
engine->setDirty();
355 std::vector<std::string> toBeCancelled;
356 toBeCancelled.reserve(node.tile.URLs.size());
359 for (
auto const& url : node.tile.URLs) {
360 if (!state->modelRequests.contains(url)) {
362 for (
auto const& model : state->tileCache[node.tile.tileId]) {
363 if (model.URL == url) {
370 this->
context->modelManager->cancelModelLoad(state->modelRequests[url].second);
371 componentState->statistics.numberOfCancelledRequests += 1;
372 toBeCancelled.emplace_back(url);
377 for (
auto const& url : toBeCancelled) {
378 state->modelRequests.erase(url);
381 for (
auto const& [tileId, child] : node.children) {
382 cancelStaleModelRequests(componentState, tileset, child);
395 const std::unordered_map<std::string, OGC3DTiles::Coord> subtreeRequestsCopy = state->subtreeRequests;
397 for (
const auto& [subtreeURL, coord] : subtreeRequestsCopy) {
399 if (state->subtreeCache.contains(coord.level) && state->subtreeCache[coord.level].contains(key) &&
400 state->subtreeCache[coord.level][key]->pendingRequests == 0) {
401 const uint64_t subtreeGUID =
Cogs::hash(subtreeURL);
402 state->subtreeRequests.erase(subtreeURL);
403 state->subtreeFetchIds.erase(subtreeGUID);
409OGC3DTilesSystem::hasPendingRequests(
const OGC3DTilesData* componentState)
const
411 const bool subTilesetsState = !componentState->subTilesetRequests.empty();
412 const bool modelsPreparedState = componentState->numberOfModelsBeingPrepared > 0;
413 const bool tilesetFetches = !componentState->tilesetFetchIds.empty();
415 if (subTilesetsState || modelsPreparedState || tilesetFetches) {
419 for (
auto tileset : componentState->tilesets) {
420 const bool modelRequestsState = !componentState->states.at(tileset).pendingModelRequests.empty() || !componentState->states.at(tileset).modelRequests.empty();
421 const bool subtreeRequestsState = !componentState->states.at(tileset).subtreeRequests.empty();
423 if (modelRequestsState || subtreeRequestsState) {
434 std::unordered_set<const OGC3DTilesTileset::Tileset*> visitedTilesets;
437 rootNode.tile.depth = 0;
439 componentState->states[tileset].pendingModelRequests.clear();
442 const glm::mat4 globalTransform = this->
context->transformSystem->getLocalToWorld(componentTransform);
447 if (tileset->root.useImplicitTiling) {
449 OGC3DTiles::Coord rootCoord{ 0, 0, 0, tileset->root.implicitTiling.isOctTree ? 0 : -1 };
452 rootNode =
buildImplicitTree(componentState, tileset, rootCoord, globalTransform);
456 rootNode =
buildExplicitTree(componentState, tileset, &tileset->root,
"", tileset->root.transform, globalTransform, visitedTilesets);
459 if (rootNode.valid) {
460 std::unordered_map<uint64_t, OGC3DTilesSystem::TileCandidate> tilesInView;
464 LOG_DEBUG(logger,
"Tiles in view: %d\n", (
int)tilesInView.size());
465 OGC3DTilesSystem::printDebugStats(componentState);
477 cancelStaleModelRequests(componentState, tileset, rootNode);
479 const size_t numReleased =
pruneTileCache(componentState, tileset, gracePeriodBeforeTileRemoval);
481 if (numReleased != 0) {
482 LOG_DEBUG(logger,
"Released %zu model(s) (frame #%d)", numReleased,
context->
time->getFrame());
488 processModelRequests(componentState, tileset,
context->
time->getFrame());
490 componentState->states[tileset].timestampOfLastTraversal = this->
context->
time->getAnimationTime();
491 componentState->states[tileset].candidateForDelayedTraversal =
true;
493 if (tileset->root.useImplicitTiling) {
494 processSubtreeRequests(component, componentState, tileset);
498 for (
auto subTileset : visitedTilesets) {
499 assert(subTileset != componentState->tilesets[0] &&
"Don't traverse the main tileset twice!");
500 traverseTileset(component, componentState, subTileset, gracePeriodBeforeTileRemoval);
510 const std::string_view sessionToken =
"session=";
511 const std::string_view keyToken =
"key=";
513 size_t sessionTokenPos = url.find(sessionToken);
514 size_t sessionTokenEndPos = url.find(
"&");
515 if (sessionTokenPos != std::string::npos) {
516 size_t len = sessionTokenEndPos == std::string::npos ? std::string::npos : (sessionTokenEndPos - (sessionTokenPos + sessionToken.size()));
517 const std::string session = url.substr(sessionTokenPos + sessionToken.size(), len);
519 if (!componentState->optionalSessionKey.empty() && componentState->optionalSessionKey != session) {
520 LOG_WARNING(logger,
"Multiple session keys ('session=') for one domain is not supported.");
522 componentState->optionalSessionKey =
"session="+session;
523 LOG_DEBUG(logger,
"Found session key '%s' in URL", session.c_str());
527 LOG_DEBUG(logger,
"No optional session key in URL '%s'", url.c_str());
532 size_t keyTokenPos = url.find(keyToken);
533 size_t keyTokenEndPos = url.find(
"&");
534 if (keyTokenPos != std::string::npos) {
535 size_t len = keyTokenEndPos == std::string::npos ? std::string::npos : (keyTokenEndPos - (keyTokenPos + keyToken.size()));
536 const std::string key = url.substr(keyTokenPos + keyToken.size(), len);
538 if (!componentState->optionalAccessKey.empty() && componentState->optionalAccessKey != key) {
539 LOG_WARNING(logger,
"Multiple access-keys ('key=') for one domain is not supported.");
541 componentState->optionalAccessKey =
"key="+key;
542 LOG_DEBUG(logger,
"Found access key '%s' in URL", key.c_str());
546 LOG_DEBUG(logger,
"No optional access key in URL '%s'", url.c_str());
553OGC3DTilesSystem::addOptionalURLParameters(
const OGC3DTilesData* componentState,
const std::string& url)
const
555 std::string result = url;
556 bool hasParams = url.find(
"?") != std::string::npos;
559 if (result.find(componentState->optionalSessionKey) == std::string::npos) {
560 result += hasParams ?
"&" :
"?";
561 result += componentState->optionalSessionKey;
566 if (result.find(componentState->optionalAccessKey) == std::string::npos) {
567 result += hasParams ?
"&" :
"?";
568 result += componentState->optionalAccessKey;
572 if (!componentState->additionalURLParameters.empty()) {
573 result += hasParams ?
"&" :
"?";
574 result += componentState->additionalURLParameters;
583 LOG_INFO(logger,
"Initializing a OGC3DTilesComponent with baseURL='%s'.", component->
baseURL.c_str());
584 componentState->baseURL = component->
baseURL;
590 componentState->assetsFetchId = Cogs::Core::DataFetcherManager::NoFetchId;
592 LOG_DEBUG(logger,
"Fetching assets:\n URL='%s'\n accessToken='%s'\n AssetID=%d", component->
baseURL.c_str(), component->
accessToken.c_str(), component->
assetId);
593 componentState->assetsFetchId = fetchAndInitializeAsset(component, componentState->uniqueComponentId);
596 std::string tilesetURL = component->
baseURL +
"/tileset.json";
597 tilesetURL = addOptionalURLParameters(componentState, tilesetURL);
598 const uint64_t tilesetGUID =
Cogs::hash(tilesetURL);
600 if (componentState->tilesetFetchIds.contains(tilesetGUID)) {
601 LOG_ERROR(logger,
"Tileset '%s' is already being fetched [%lu]", tilesetURL.c_str(), componentState->tilesetFetchIds[tilesetGUID]);
604 tilesetURL = this->addOptionalURLParameters(componentState, tilesetURL);
605 componentState->tileBaseURL = component->
baseURL;
606 componentState->tilesetFetchIds[tilesetGUID] = fetchAndInitializeTileset(tilesetGUID, tilesetURL, component, componentState->uniqueComponentId);
610Cogs::Core::DataFetcherManager::FetchId
611OGC3DTilesSystem::fetchAndInitializeAsset(
OGC3DTilesComponent * component, uint32_t uniqueComponentId)
615 if (OGC3DTilesSystem::componentIsStale(thisp->context, uniqueComponentId)) {
623 std::string tilesetURL = component->
baseURL +
"/tileset.json";
624 componentState->tileBaseURL = component->
baseURL;
626 if (tilesAsset !=
nullptr) {
627 LOG_DEBUG(logger,
"Got asset data URL: '%s'", tilesAsset->url.c_str());
628 tilesetURL = tilesAsset->url;
630 thisp->extractAndStoreOptionalSessionKeys(componentState, tilesetURL);
633 size_t pos = tilesetURL.find_first_of(
'/', std::string(
"https://").
size()+1);
634 if (pos == std::string::npos) {
635 pos = tilesetURL.size();
637 componentState->tileBaseURL = tilesetURL.substr(0, pos);
640 if (!tilesAsset->bearerToken.empty()) {
642 LOG_DEBUG(logger,
"Appending bearer-token auth to tile requests URLs ('%s')", tilesAsset->bearerToken.c_str());
643 componentState->optionalAccessKey =
"access_token=" + tilesAsset->bearerToken;
646 LOG_DEBUG(logger,
"Tiles URL base is: '%s'", componentState->tileBaseURL.c_str());
650 LOG_ERROR(logger,
"Could not fetch asset data from '%s'\n", component->
baseURL.c_str());
654 tilesetURL = thisp->addOptionalURLParameters(componentState, tilesetURL);
659 const uint64_t tilesetGUID =
Cogs::hash(tilesetURL);
660 if (componentState->tilesetFetchIds.contains(tilesetGUID)) {
661 LOG_ERROR(logger,
"Tileset '%s' is already being fetched [%lu]", tilesetURL.c_str(), componentState->tilesetFetchIds[tilesetGUID]);
665 componentState->tilesetFetchIds[tilesetGUID] = thisp->fetchAndInitializeTileset(tilesetGUID, tilesetURL, component, uniqueComponentId);
666 componentState->assetsFetchId = Cogs::Core::DataFetcherManager::NoFetchId;
671OGC3DTilesSystem::fetchAndInitializeTileset(
const uint64_t tilesetGUID,
const std::string & tilesetURL,
OGC3DTilesComponent * component, uint32_t uniqueComponentId)
673 return Cogs::Core::OGC3DTilesTileset::fetch(this->
context, tilesetURL,
682 if (mainTileset !=
nullptr) {
684 componentState->originalRootTransform = mainTileset->root.transform;
685 mainTileset->root.transform = glm::mat4(1.0);
686 LOG_INFO(logger,
"Loaded tileset is of type '%s' [%p]", mainTileset->root.useImplicitTiling ?
"IMPLICIT" :
"EXPLICIT", mainTileset);
688 Cogs::Geometry::DBoundingBox bbox =
689 OGC3DTilesUtils::boundingVolumeConverter(mainTileset->root.boundingVolume, mainTileset->root.transform);
690 glm::vec3 center = (bbox.max - bbox.min) * 0.5 + bbox.min;
692 LOG_INFO(logger,
"Tileset center: <%.2f, %.2f, %.2f> [%p]", center.x, center.y, center.z, mainTileset);
693 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);
694 LOG_INFO(logger,
"Tileset bounding volume: %s", mainTileset->root.boundingVolume.toString().c_str());
700 Cogs::Geometry::DBoundingBox globalBBox =
701 OGC3DTilesUtils::boundingVolumeConverter(mainTileset->root.boundingVolume, mainTileset->root.transform);
702 thisp->addBBoxIndicatorToScene(componentState, mainTileset, 0, globalBBox);
707 if (mainTileset->root.useImplicitTiling) {
708 LOG_INFO(logger,
" Tree-structure is of type '%s' [%p]", mainTileset->root.implicitTiling.isOctTree ?
"OCT" :
"QUAD", mainTileset);
710 thisp->requestSubtree(componentState, mainTileset, rootCoord);
713 componentState->tilesets.push_back(mainTileset);
716 thisp->context->engine->invokeComponentNotifyCallback(*component,
718 reinterpret_cast<const double *
>(glm::value_ptr(componentState->originalRootTransform)),
722 LOG_ERROR(logger,
"Failed to initialize OGC3DTilesComponent (baseURL='%s')", component->
baseURL.c_str());
725 componentState->tilesetFetchIds.erase(tilesetGUID);
727 LOG_DEBUG(logger,
"Tileset fetched, initialized and ready");
728 thisp->context->engine->setDirty();
729 componentState->requiresTraversal =
true;
746 const float qualityDelta = componentState->enableCaching ?
749 if (qualityDelta >= 0.0) {
754 gracePeriodBeforeTileRemoval = std::max(2.0f/60.0f, gracePeriodBeforeTileRemoval);
756 std::set<std::pair<double, uint64_t>> toBeRemoved;
757 const double now = this->
context->
time->getAnimationTime();
760 bool tileHidden =
false;
761 for (
const auto& [tileId, loadedModels] : componentState->states[tileset].tileCache) {
762 if (componentState->neverDiscardRootTiles &&
763 tileset == componentState->tilesets[0] &&
770 if (lm.timestampLastUsed + gracePeriodBeforeTileRemoval < now) {
771 toBeRemoved.emplace(std::make_pair(lm.timestampLastUsed, tileId));
778 if (lm.timestampLastUsed < now) {
782 sceneComponent->
visible =
false;
794 if (toBeRemoved.empty()) {
798 std::list<std::pair<double, uint64_t>> sortedByTimestamp(toBeRemoved.begin(), toBeRemoved.end());
799 sortedByTimestamp.sort([](
const std::pair<double, uint64_t>& a,
const std::pair<double, uint64_t>& b) {
return a.first > b.first; });
802 size_t numRemoved = 0;
804 const size_t numToBeRemoved = sortedByTimestamp.size() * size_t(-qualityDelta);
806 for (
const auto& [timestamp, tileId] : sortedByTimestamp) {
807 removeTileFromScene(componentState, tileset, tileId);
810 if (numRemoved >= numToBeRemoved) {
819OGC3DTilesSystem::setVisibilityForAllTiles(
const OGC3DTilesData* componentState,
bool onoff)
const
822 for (
auto tileset : componentState->tilesets) {
823 for (
const auto& [tileId, loadedModels] : componentState->states.at(tileset).tileCache) {
824 setTileVisibility(componentState, tileset, tileId, onoff);
832 if (!componentState->states.at(tileset).tileCache.contains(tileId)) {
837 const std::vector<LoadedModel>& loadedModels = componentState->states.at(tileset).tileCache.at(tileId);
838 for (
const LoadedModel& lm : loadedModels) {
839 if (!modelIsReady(lm.modelHandle)) {
850 if (sceneComponent->
visible != onoff) {
853 sceneComponent->
visible = onoff;
856 LOG_WARNING(logger,
"Could not find '%s' for visibility toggling (tileset %p)", lm.URL.c_str(), tileset);
862OGC3DTilesSystem::modelIsReady(
const ModelHandle& modelhandle)
const
864 if (!modelhandle->isResident()) {
869 for (
const auto& mesh : modelhandle->meshes) {
870 if (!mesh->isActive()) {
874 if (mesh->hasAttachedResource()) {
875 const auto r = mesh->getAttachedResource();
876 if (!r->isActive()) {
883 for (
const auto& material : modelhandle->materials) {
884 if (!material->isResident()) {
888 for (
const auto& tv : material->textureVariables) {
889 if (!tv.texture.handle->isResident()) {
901 const std::vector<LoadedModel>& loadedModels = componentState->states[tileset].tileCache[tileId];
902 for (
const LoadedModel& lm : loadedModels) {
915 LOG_WARNING(logger,
"removeTileFromCache(): Entity '%s' (%s) not found.", lm.URL.c_str(), lm.uniqueName.c_str());
919 if (componentState->showTileBBoxes) {
920 removeBBoxIndicatorFromScene(componentState, tileset, tileId);
923 componentState->states[tileset].tileCache.erase(tileId);
929 std::vector<const std::string*> finished;
930 const double now = this->
context->
time->getAnimationTime();
932 for (
const auto& [URL, pair] : componentState->states[tileset].modelRequests) {
933 TileCandidate tileCandidate = pair.first;
935 if (this->modelIsReady(modelHandle)) {
936 finished.emplace_back(&URL);
938#if DEBUG_CHECK_IF_INCOMING_MODEL_EXISTS
939 const std::vector<LoadedModel>& loadedModels = componentState->tileCache[tileCandidate.tileId];
940 bool alreadyLoaded = std::any_of(loadedModels.begin(), loadedModels.end(), [&url=URL](
const LoadedModel & item) { return item.URL == url; });
942 LOG_ERROR(logger,
"Incoming model '%s' is already loaded", URL.c_str());
943 assert(alreadyLoaded &&
"Incoming model is already loaded for this tile!");
948#if DEBUG_ADD_URL_TO_MODEL_NAMES
950 const std::string uniqueName = (tileset->root.useImplicitTiling ? OGC3DTiles::Coord::fromHash(tileCandidate.tileId).toString() : std::to_string(tileCandidate.tileId)) +
" - " + URL;
953 std::string uniqueName = tileset->root.useImplicitTiling ? OGC3DTiles::Coord::fromHash(tileCandidate.tileId).toString() : std::to_string(tileCandidate.tileId);
955 uniqueName += std::to_string(uint64_t(tileset));
965 LoadedModel loadedModel;
966 loadedModel.modelHandle = modelHandle;
967 loadedModel.timestampLastUsed = now;
968 loadedModel.URL = URL;
969 loadedModel.uniqueName = uniqueName;
970 loadedModel.frameNumberAtArrival = frameNumber;
972 if (!OGC3DTilesSystem::modelIsEmpty(modelHandle)) {
973 addModelToScene(loadedModel.uniqueName, tileCandidate, modelHandle, componentState);
974 loadedModel.isValid =
true;
975 componentState->requiresTraversal =
true;
978 componentState->states[tileset].tileCache[tileCandidate.tileId].emplace_back(loadedModel);
980 else if (modelHandle->hasFailedLoad()) {
981 LOG_ERROR(logger,
"processModelRequests(): Could not load '%s'", URL.c_str());
982 finished.emplace_back(&URL);
984 else if (modelHandle->hasFailedActivation()) {
986 LOG_ERROR(logger,
"processModelRequests(): Model failed to activate!");
988 finished.emplace_back(&URL);
992 for (
const std::string* URL : finished) {
993 componentState->states[tileset].modelRequests.erase(*URL);
1008 if (URL.find(
".json") != std::string::npos) {
1009 LOG_ERROR(logger,
"Internal error: A sub-tileset was requested as model; '%s'.", URL.c_str());
1010 assert(
false &&
"Internal error");
1015 if (!componentState->states[tileset].modelRequests.contains(URL)) {
1016 const std::vector<LoadedModel>& loadedModels = componentState->states[tileset].tileCache[tile.tileId];
1017 bool alreadyLoaded = std::ranges::any_of(loadedModels.cbegin(), loadedModels.cend(), [&URL](
LoadedModel const& item) { return item.URL == URL; });
1018 if (!alreadyLoaded) {
1020 componentState->states[tileset].modelRequests[URL] = std::pair(tile, handle);
1042 std::vector<const TileCandidate*> sortedTilesInView;
1043 sortedTilesInView.reserve(tilesInView.size());
1044 for (
auto it = tilesInView.cbegin(); it != tilesInView.cend(); ++it) {
1045 sortedTilesInView.emplace_back(&it->second);
1050 const glm::vec3 cameraPos = getCurrentCameraPosition();
1051 std::ranges::sort(sortedTilesInView.begin(), sortedTilesInView.end(),
1053 const double distA = OGC3DTilesUtils::distanceFromBBox(cameraPos, a->bbox);
1054 const double distB = OGC3DTilesUtils::distanceFromBBox(cameraPos, b->bbox);
1055 return distA < distB;
1059 std::ranges::sort(sortedTilesInView.begin(), sortedTilesInView.end(),
1061 return a->depth < b->depth;
1065 const double now = this->
context->
time->getAnimationTime();
1066 int numScheduled = 0;
1068 int maxNumRequestsPerFrame =
context->
variables->get(maxNumModelRequestsPerFrameName, MAX_NUM_MODEL_REQUESTS_PER_FRAME);
1069 if (maxNumRequestsPerFrame <= 0) {
1070 LOG_WARNING_ONCE(logger,
"Variable '%s' is <= 0. Resetting to %d.",
1071 maxNumModelRequestsPerFrameName.
to_string().c_str(), MAX_NUM_MODEL_REQUESTS_PER_FRAME);
1072 maxNumRequestsPerFrame = MAX_NUM_MODEL_REQUESTS_PER_FRAME;
1073 context->
variables->set(maxNumModelRequestsPerFrameName, MAX_NUM_MODEL_REQUESTS_PER_FRAME);
1076 int maxNumActiveRequests =
context->
variables->get(maxNumActiveModelRequestsName, MAX_NUM_ACTIVE_MODEL_REQUESTS);
1077 if (maxNumActiveRequests <= 0) {
1078 LOG_WARNING_ONCE(logger,
"Variable '%s' is <= 0. Resetting to %d.",
1079 maxNumActiveModelRequestsName.
to_string().c_str(), MAX_NUM_ACTIVE_MODEL_REQUESTS);
1080 maxNumActiveRequests = MAX_NUM_ACTIVE_MODEL_REQUESTS;
1081 context->
variables->set(maxNumActiveModelRequestsName, MAX_NUM_ACTIVE_MODEL_REQUESTS);
1085 state->pendingModelRequests.reserve(sortedTilesInView.size());
1087 for (
const auto* tilecandidate : sortedTilesInView) {
1088 for (std::string URL : tilecandidate->URLs) {
1090 if (state->modelRequests.size() >=
size_t(maxNumActiveRequests)) {
1092 state->pendingModelRequests.emplace(tilecandidate->tileId);
1095 if (numScheduled < maxNumRequestsPerFrame) {
1096 if (
requestModel(componentState, tileset, *tilecandidate, addOptionalURLParameters(componentState, URL))) {
1098 if (componentState->showTileBBoxes) {
1099 addBBoxIndicatorToScene(componentState, tileset, tilecandidate->tileId, tilecandidate->bbox);
1105 state->pendingModelRequests.emplace(tilecandidate->tileId);
1111 if (state->tileCache.contains(tilecandidate->tileId)) {
1112 std::vector<LoadedModel>& loadedModels = state->tileCache[tilecandidate->tileId];
1114 m.timestampLastUsed = now;
1119 componentState->statistics.numberOfPendingRequests = (int) componentState->states[tileset].pendingModelRequests.size();
1126 std::string path = std::string(tileset->root.implicitTiling.subTreeURLScheme);
1127 path = path.replace(path.find(
"{level}"), 7, std::to_string(coord.level));
1128 path = path.replace(path.find(
"{x}"), 3, std::to_string(coord.x));
1129 path = path.replace(path.find(
"{y}"), 3, std::to_string(coord.y));
1131 path = path.replace(path.find(
"{z}"), 3, std::to_string(coord.z));
1134 return componentState->tileBaseURL +
"/" + path;
1146 assert(coord.level <= 0xFFFF && coord.x <= 0xFFFF && coord.y <= 0xFFFF &&
"Coord values out of the 16bit bounds");
1147 uint64_t
hash = coord.level + (uint64_t(coord.x) << 16) + (uint64_t(coord.y) << 32);
1150 assert(coord.z <= 0xFFFF &&
"Coord.z value out of the 16bit bounds");
1151 hash += uint64_t(coord.z) << 48;
1163 if (tileHasModelRequests(componentState, tileset, tileId) ||
1164 tileHasSubTilesetRequests(componentState, tileId)) {
1168 assert(componentState->states[tileset].tileCache.contains(tileId) &&
"Tile not in cache");
1170 const std::vector<LoadedModel>& models = componentState->states[tileset].tileCache[tileId];
1171 size_t numReadyModels = 0;
1172 for (
const auto& lm : models) {
1174 numReadyModels += 1;
1178 if (modelIsReady(lm.modelHandle)) {
1179 numReadyModels += 1;
1182 componentState->numberOfModelsBeingPrepared += 1;
1186 return models.size() == numReadyModels;
1195 for (
const auto& [URL, pair] : componentState->states.at(tileset).modelRequests) {
1196 const TileCandidate& tileCandidate = pair.first;
1197 if (tileCandidate.tileId == tileId) {
1202 if (componentState->states.at(tileset).pendingModelRequests.contains(tileId)) {
1210OGC3DTilesSystem::tileHasSubTilesetRequests(
const OGC3DTilesData* componentState, uint64_t tileId)
const
1212 for (
const auto& [URL, pair] : componentState->subTilesetRequests) {
1213 if (pair.first == tileId) {
1227 size_t numReadyChildren = 0;
1228 for (
auto const& [childTileId, child] : node->children) {
1229 if (child.tile.URLs.empty()) {
1231 numReadyChildren += 1;
1236 numReadyChildren += 1;
1244 return node->children.size() == numReadyChildren;
1254 if (target.contains(node->tile.tileId)) {
1255 assert(
false &&
"Node has already been visited. Internal error.");
1259 if (!node->tile.URLs.empty()) {
1260 target[node->tile.tileId] = node->tile;
1263 for (
auto const& [childTileId, child] : node->children) {
1275 if (node->replaceRefine) {
1276 if (!componentState->waitForTileSiblings) {
1277 if (node->children.empty()) {
1278 setTileVisibility(componentState, tileset, node->tile.tileId,
true);
1281 setTileVisibility(componentState, tileset, node->tile.tileId,
false);
1285 if (componentState->states[tileset].tileCache.contains(node->tile.tileId)) {
1286 bool showNode =
true;
1287 if (!node->children.empty() &&
1294 setTileVisibility(componentState, tileset, node->tile.tileId, showNode);
1299 for (
auto const& [childTileId, child] : node->children) {
1314 const glm::mat4& globalTransform)
const
1318 node.replaceRefine = tileset->root.refine == OGC3DTilesTileset::RefineType::REPLACE;
1320 componentState->statistics.traversedDepth = std::max(componentState->statistics.traversedDepth, coord.level);
1323 OGC3DTilesUtils::getBoundingVolumeFromCoord(coord, tileset->root.boundingVolume);
1324 Cogs::Geometry::DBoundingBox tileBBox =
1325 OGC3DTilesUtils::boundingVolumeConverter(tileBoundingVolume, tileset->root.transform);
1326 Cogs::Geometry::DBoundingBox transformedTileBBox(tileBBox);
1328 OGC3DTilesUtils::transformBBox(transformedTileBBox, globalTransform);
1330 const bool isInside = isInsideFrustum(transformedTileBBox);
1332 if (!isInside && !componentState->loadAllSiblings) {
1342 const double distance = OGC3DTilesUtils::distanceFromBBox(getCurrentCameraPosition(), transformedTileBBox);
1347 const double geometricError = tileset->geometricError / OGC3DTilesUtils::fastPow2(coord.level);
1348 const double factor = componentState->detailFactor * geometricError;
1349 const bool hasContent = OGC3DTilesSubtree::hasContent(subtree, coord);
1351 if ((distance < factor || !hasContent) && isInside) {
1353 std::vector<OGC3DTiles::Coord> children =
1354 getTileCoordChildren(componentState, tileset, coord);
1357 if (componentState->dontRenderLeaves) {
1361 if (getTileCoordChildren(componentState, tileset, child).empty()) {
1370 assert(coord != childCoord &&
"Internal error");
1371 assert(childCoord.level != 0 &&
"Coord with level=0 cannot be a child");
1376 node.children[child.tile.tileId] = child;
1389 assert(tileset->root.contents.size() > 0 &&
"No content!");
1390 std::string path = tileset->root.contents[0].URI;
1391 path = path.replace(path.find(
"{level}"), 7, std::to_string(coord.level));
1392 path = path.replace(path.find(
"{x}"), 3, std::to_string(coord.x));
1393 path = path.replace(path.find(
"{y}"), 3, std::to_string(coord.y));
1395 path = path.replace(path.find(
"{z}"), 3, std::to_string(coord.z));
1398 node.tile.URLs.emplace_back(OGC3DTilesSystem::compileURL(componentState->tileBaseURL, tileset->baseURL, path));
1399 node.tile.transform = tileset->root.transform;
1402 node.tile.tileId = coord.toHash();
1403 node.tile.depth = coord.level;
1404 node.tile.bbox = tileBBox;
1419 const std::string& tileIdStr,
1420 const glm::mat4& currentTransform,
1421 const glm::mat4& globalTransform,
1422 std::unordered_set<const OGC3DTilesTileset::Tileset*> & visitedTilesets)
const
1426 node.replaceRefine = tile->refine == OGC3DTilesTileset::RefineType::REPLACE;
1427 componentState->statistics.traversedDepth = std::max(componentState->statistics.traversedDepth, (
int)tileIdStr.size());
1429 glm::mat4 transform = currentTransform;
1430 Cogs::Geometry::DBoundingBox bbox = OGC3DTilesUtils::boundingVolumeConverter(tile->boundingVolume, transform);
1431 Cogs::Geometry::DBoundingBox transformedBBox(bbox);
1432 OGC3DTilesUtils::transformBBox(transformedBBox, globalTransform);
1434 const bool isInside = isInsideFrustum(transformedBBox);
1436 if (!isInside && !componentState->loadAllSiblings) {
1440 const double distance = OGC3DTilesUtils::distanceFromBBox(getCurrentCameraPosition(), transformedBBox);
1441 const double factor = (node.tile.depth + 1) * std::pow(componentState->detailFactor, 2.0) * tile->geometricError;
1442 const bool hasContent = !tile->contents.empty();
1444 if ((distance < factor || !hasContent) && isInside) {
1446 if (componentState->dontRenderLeaves) {
1449 for (
size_t i=0; i < tile->children.size(); ++i) {
1451 if (childTile->children.empty()) {
1459 for (
size_t i = 0; i < tile->children.size(); ++i) {
1461 assert(!childTile->useImplicitTiling &&
"Internal error");
1464 Node childNode =
buildExplicitTree(componentState, tileset, childTile, tileIdStr + std::to_string(i), transform, globalTransform, visitedTilesets);
1466 if (childNode.valid) {
1467 assert(node.children.find(childNode.tile.tileId) == node.children.end() &&
"Child node has already been added");
1468 node.children[childNode.tile.tileId] = childNode;
1478 node.tile.depth = (uint32_t)tileIdStr.length();
1479 node.tile.bbox = bbox;
1480 node.tile.transform = transform;
1481 node.tile.URLs.reserve(tile->contents.size());
1485 const std::string url = OGC3DTilesSystem::compileURL(componentState->tileBaseURL, tileset->baseURL, c.URI);
1487 if (c.URI.find(
".json") != std::string::npos || c.URI.find(
".JSON") != std::string::npos) {
1488 const std::string tilesetURL = addOptionalURLParameters(componentState, url);
1489 if (componentState->states[tileset].subTilesets.contains(tilesetURL)) {
1490 visitedTilesets.insert(componentState->states[tileset].subTilesets[tilesetURL]);
1491 componentState->states[tileset].candidateForDelayedTraversal =
true;
1492 componentState->statistics.numberOfVisitedSubTilesets += 1;
1496 requestSubTileset(componentState, tilesetURL, node.tile.tileId, tileset);
1500 node.tile.URLs.emplace_back(url);
1511 if (componentState->subTilesetRequests.size() > MAX_NUM_ACTIVE_SUBTILESET_REQUESTS) {
1515 if (componentState->loadedSubTilesets.contains(URL) ||
1516 componentState->subTilesetRequests.contains(URL)) {
1520 componentState->subTilesetRequests[URL] = std::make_pair(tileId, parentTileset);
1536 if (componentState->colorTileAccordingToTreeDepth) {
1538 modelComponent->
materialInstance = debugTileTreeDepthColorMaterials[tile.depth % debugTileTreeDepthColorMaterials.size()];
1540 else if (componentState->overrideMaterial) {
1545 modelComponent->
model = handle;
1556 transformComponent->transform.
matrix = tile.transform;
1565 assert(componentState->states.contains(tileset) &&
"Internal error");
1566 if (!componentState->states[tileset].bboxIndicatorCache.contains(tileId)) {
1567 std::string title =
"BBox ";
1568 title += tileset->root.useImplicitTiling ? OGC3DTiles::Coord::fromHash(tileId).toString() : std::to_string(tileId);
1570 title += std::to_string(uint64_t(tileset));
1574 componentState->states[tileset].bboxIndicatorCache[tileId] = bboxEntity;
1576 auto createBBoxTask = [ctx = this->
context, bboxEntity, box]() {
1579 MaterialInstanceHandle material = ctx->materialInstanceManager->createMaterialInstance(ctx->materialManager->getDefaultMaterial());
1580 material->setPermutation(
"Line");
1581 material->setBoolProperty(DefaultMaterial::EnableLighting,
false);
1582 const float r = (rand() % 255) / 255.0f;
1583 material->setVec4Property(DefaultMaterial::DiffuseColor, BBOX_BASE_COLOR_FORMAT);
1584 material->options.depthBiasEnabled =
true;
1585 material->options.depthBias.constant = -1.f;
1586 material->options.depthBias.slope = -1.f;
1587 material->options.depthBias.clamp = 100.f;
1590 std::array<PositionVertex, 8> vertices = {
1601 std::array<unsigned int, 16> lineStripIndices = {
1610 meshRenderComponent->setMaterial(material);
1613 MeshHandle meshHandle = ctx->meshManager->create();
1616 Cogs::Geometry::BoundingBox fbox;
1621 Mesh* mesh = ctx->meshManager->get(meshHandle);
1622 assert(mesh &&
"Internal error");
1638 auto it = componentState->states[tileset].bboxIndicatorCache.find(tileId);
1639 if (it != componentState->states[tileset].bboxIndicatorCache.end()) {
1643 componentState->states[tileset].bboxIndicatorCache.erase(tileId);
1646 if (tileset->root.useImplicitTiling) {
1647 LOG_ERROR(logger,
"Instructed to remove BBox '%s', but it does not exist.", OGC3DTiles::Coord::fromHash(tileId).toString().c_str());
1650 LOG_ERROR(logger,
"Instructed to remove BBox '%llu', but it does not exist.", tileId);
1661 h.data = std::make_unique<OGC3DTilesData>();
1664 data->uniqueComponentId = createUniqueComponentId();
1666 if (this->
pool.size() >= 1 && this->bounds ==
nullptr) {
1678 LOG_DEBUG(logger,
"Destroying OGC3DTilesComponent (baseURL=%s)", comp->
baseURL.c_str());
1683 if (componentState->assetsFetchId != Cogs::Core::DataFetcherManager::NoFetchId) {
1684 Cogs::Core::DataFetcherManager::cancelAsyncFetch(this->
context, componentState->assetsFetchId);
1687 for (
auto & [guid, fetchId] : componentState->tilesetFetchIds) {
1688 Cogs::Core::DataFetcherManager::cancelAsyncFetch(this->
context, fetchId);
1691 for (
auto tileset : componentState->tilesets) {
1692 for (
auto& [guid, fetchId] : componentState->states[tileset].subtreeFetchIds) {
1693 Cogs::Core::DataFetcherManager::cancelAsyncFetch(this->
context, fetchId);
1696 for (
const auto& [tileId, loadedModels] : componentState->states[tileset].tileCache) {
1706 LOG_ERROR(logger,
"destroyComponent(): Could not find entity '%s' (%s)", lm.URL.c_str(), lm.uniqueName.c_str());
1712 for (
auto& [
id, subtreeMap] : componentState->states[tileset].subtreeCache) {
1713 for (
auto& [key, val] : subtreeMap) {
1720 for (
auto& b : componentState->states[tileset].bboxIndicatorCache) {
1727 componentState->tilesets.clear();
1728 componentState->states.clear();
1730 if (this->bounds !=
nullptr) {
1738OGC3DTilesSystem::getCurrentCameraPosition()
const
1740 return this->
context->cameraSystem->getMainCameraData().inverseViewMatrix * glm::dvec4(0, 0, 0, 1.0);
1744OGC3DTilesSystem::isInsideFrustum(
const Cogs::Geometry::DBoundingBox& box)
const
1747 const Cogs::Geometry::Frustum frustum = this->
context->cameraSystem->getMainCameraData().frustum;
1748 Cogs::Geometry::BoundingBox fbox;
1751 const int result = Cogs::Geometry::contains(frustum, fbox);
1752 return result == 2 || result == 1;
1758 const CameraData& cameraData = this->
context->cameraSystem->getMainCameraData();
1759 int result = OGC3DTilesUtils::bboxInsideViewFrustum(box, cameraData.rawViewProjection);
1765OGC3DTilesSystem::modelIsEmpty(
const ModelHandle handle)
1767 const size_t numParts = handle->parts.size();
1768 for (
size_t i = 0; i < numParts; ++i) {
1769 const ModelPart& part = handle->parts[i];
1770 if (part.vertexCount !=
static_cast<uint32_t
>(-1)) {
1777std::vector<OGC3DTiles::Coord>
1780 assert(tileset->root.useImplicitTiling &&
"Only IMPLICIT trees uses subtrees");
1781 std::vector<OGC3DTiles::Coord> children;
1785 LOG_ERROR(logger,
"getTileCoordChildren(): No subtree found. Internal error");
1790 if (OGC3DTilesSubtree::isLeafNodeInSubtree(subtree, parent) ||
1791 ((parent.level + 1) >= tileset->root.implicitTiling.availableLevels)) {
1792 std::vector<OGC3DTiles::Coord> subtreeCoords = OGC3DTilesSubtree::getLeafNodesSubtreeCoords(subtree, parent);
1793 children.reserve(subtreeCoords.size());
1795 for (
auto const& subtreeCoord : subtreeCoords) {
1797 if (subtreeCoord.level > tileset->root.implicitTiling.availableLevels) {
1798 LOG_WARNING(logger,
"Requested subtree-level too deep: %d vs %d", subtreeCoord.level, tileset->root.implicitTiling.availableLevels);
1803 if (subSubtree && (subtree->tileAvailability.availableCount > 0 || subtree->tileAvailability.allIsAvailable)) {
1804 children.emplace_back(subtreeCoord);
1809 if (subtree->tileAvailability.availableCount == 0 && !subtree->tileAvailability.allIsAvailable) {
1812 else if (subtree->tileAvailability.allIsAvailable) {
1814 children = OGC3DTilesSubtree::getAllChildren(subtree, parent);
1818 children = OGC3DTilesSubtree::getChildren(subtree, parent);
1835 assert(tileset->root.useImplicitTiling &&
"Only IMPLICIT trees uses subtrees");
1837 const int levelsPerSubtree = tileset->root.implicitTiling.subtreeLevels;
1838 const OGC3DTiles::Coord & subtreeCoord = OGC3DTilesSubtree::getSubtreesRootCoord(levelsPerSubtree, coord);
1840 if (subtreeCoord.level > tileset->root.implicitTiling.availableLevels) {
1841 LOG_ERROR(logger,
"Subtree coord-depth > tree-depth (%d vs %d)", subtreeCoord.level, tileset->root.implicitTiling.availableLevels);
1848 if (!componentState->states[tileset].subtreeCache.contains(subtreeCoord.level) ||
1849 !componentState->states[tileset].subtreeCache[subtreeCoord.level].contains(key)) {
1854 return componentState->states[tileset].subtreeCache[subtreeCoord.level][key];
1862 std::vector<std::string> urls;
1863 urls.reserve(componentState->subTilesetRequests.size());
1864 for (
const auto& [URL, tile] : componentState->subTilesetRequests) {
1865 const uint64_t tilesetGUID =
Cogs::hash(URL);
1866 if (!componentState->tilesetFetchIds.contains(tilesetGUID)) {
1867 urls.emplace_back(URL);
1871 for (
size_t i = 0; i < urls.size(); ++i) {
1872 const std::string url = addOptionalURLParameters(componentState, urls[i]);
1877 const uint64_t tilesetGUID =
Cogs::hash(url);
1878 componentState->tilesetFetchIds[tilesetGUID] = Cogs::Core::OGC3DTilesTileset::fetch(
context, url,
1879 [thisp=
this, component, url, parentTileset, tilesetGUID, uniqueComponentId=componentState->uniqueComponentId](
OGC3DTilesTileset::Tileset* tileset) {
1880 if (OGC3DTilesSystem::componentIsStale(thisp->context, uniqueComponentId)) {
1888 componentState->tilesets.push_back(tileset);
1890 componentState->states[parentTileset].subTilesets[url] = tileset;
1894 size_t offset = componentState->baseURL.size() + 1;
1895 tileset->baseURL = url.substr(offset, url.find_last_of(
"/") - offset);
1897 if (tileset->root.useImplicitTiling) {
1898 tileset->root.implicitTiling.subTreeURLScheme = tileset->baseURL +
"/" + tileset->root.implicitTiling.subTreeURLScheme;
1902 tileset->root.transform = tileset->root.transform * parentTileset->root.transform;
1905 if (tileset->root.useImplicitTiling) {
1906 LOG_DEBUG(logger,
" Sub-tileset tree-structure is of type '%s'", tileset->root.implicitTiling.isOctTree ?
"OCT" :
"QUAD");
1908 thisp->requestSubtree(componentState, tileset, rootCoord);
1911 componentState->loadedSubTilesets.insert(url);
1914 LOG_ERROR(logger,
"Could not parse incoming Sub-tileset from '%s'", url.c_str());
1917 componentState->subTilesetRequests.erase(url);
1918 componentState->tilesetFetchIds.erase(tilesetGUID);
1927 assert(tileset->root.useImplicitTiling &&
"Only IMPLICIT trees uses subtrees");
1930 std::vector<std::string> urls;
1931 urls.reserve(componentState->states[tileset].subtreeRequests.size());
1932 for (
const auto& [subtreeURL, coord] : componentState->states[tileset].subtreeRequests) {
1933 const uint64_t subtreeGUID =
Cogs::hash(subtreeURL);
1934 if (!componentState->states[tileset].subtreeFetchIds.contains(subtreeGUID)) {
1935 urls.emplace_back(subtreeURL);
1939 for (
size_t i=0; i<urls.size(); ++i) {
1940 const std::string subtreeURL = urls[i];
1941 const OGC3DTiles::Coord subtreeCoord = componentState->states[tileset].subtreeRequests[subtreeURL];
1943 const uint64_t subtreeGUID =
Cogs::hash(subtreeURL);
1945 componentState->states[tileset].subtreeFetchIds[subtreeGUID] = OGC3DTilesSubtree::fetchSubtree(this->
context, subtreeURL, componentState->uniqueComponentId,
1946 [thisp=
this, component, subtreeCoord, subtreeURL, tileset, uniqueComponentId = componentState->uniqueComponentId, subtreeGUID](
OGC3DTilesSubtree::Subtree* subtree) {
1947 if (OGC3DTilesSystem::componentIsStale(thisp->context, uniqueComponentId)) {
1954 assert(componentState->states.contains(tileset) &&
"The tileset is not a part of the component state!");
1957 if (subtree !=
nullptr) {
1958 subtree->globalCoord = subtreeCoord;
1959 subtree->levelsPerSubtree = tileset->root.implicitTiling.subtreeLevels;
1962 if (state->subtreeCache[subtree->globalCoord.level].contains(key)) {
1963 LOG_WARNING(logger,
"Subtree '%s' (%s) is already in the cache.", subtreeCoord.toString().c_str(), subtreeURL.c_str());
1967 state->subtreeCache[subtree->globalCoord.level][key] = subtree;
1971 LOG_ERROR(logger,
"Failed to load subtree '%s'", subtreeCoord.toString().c_str());
1974 state->subtreeRequests.erase(subtreeURL);
1975 state->subtreeFetchIds.erase(subtreeGUID);
1981 thisp->context->engine->setDirty();
1994 if (componentState->states[tileset].subtreeRequests.size() > MAX_NUM_ACTIVE_SUBTREES_REQUESTS) {
1998 const std::string subtreeURL = OGC3DTilesSystem::buildSubtreeURL(componentState, tileset, coord);
1999 const std::string fullURL = this->addOptionalURLParameters(componentState, subtreeURL);
2002 if (!componentState->states[tileset].subtreeCache[coord.level].contains(key) &&
2003 !componentState->states[tileset].subtreeRequests.contains(fullURL)) {
2004 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
Base component for all rendering content.
constexpr void setRenderFlag(RenderFlags flag)
Sets the given flag.
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.
@ CastShadows
Casts shadows.
@ 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...
Exposes material properties for legacy entities and code.
bool shadowReceiver
If false, object never gets shadowed.
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