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++;
98 std::vector<MaterialInstanceHandle> debugTileTreeDepthColorMaterials;
106OGC3DTilesSystem::~OGC3DTilesSystem() =
default;
114 context->
variables->set(maxNumActiveModelRequestsName, MAX_NUM_ACTIVE_MODEL_REQUESTS);
118 context->
variables->set(maxNumModelRequestsPerFrameName, MAX_NUM_MODEL_REQUESTS_PER_FRAME);
122 context->
variables->set(gracePeriodBeforeTreeTraversalName, DEFAULT_GRACE_PERIOD_BEFORE_TREE_TRAVERSAL);
126 context->
variables->set(gracePeriodBeforeTileRemovalName, DEFAULT_GRACE_PERIOD_BEFORE_TILE_REMOVAL);
140 context->materialManager->processLoading();
142 for (
int i = 1; i < 8; ++i) {
143 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);
145 materialInstance->setPermutation(
"FlatShaded");
146 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;
281 const double now = this->context->
time->getAnimationTime();
283 if (!componentState->tilesets.empty()) {
284 float gracePeriodBeforeTreeTraversal = DEFAULT_GRACE_PERIOD_BEFORE_TREE_TRAVERSAL;
286 gracePeriodBeforeTreeTraversal = var->getFloat();
289 bool needsTraversal =
false;
290 if (gracePeriodBeforeTreeTraversal > 0) {
291 const double delta = now - this->timestampOfLastCameraMovement;
292 needsTraversal = delta > gracePeriodBeforeTreeTraversal;
293 if (needsTraversal) {
298 needsTraversal = cameraMoved;
301 if (needsTraversal || hasPendingRequests(componentState) || componentState->requiresTraversal) {
302 float gracePeriodBeforeTileRemoval = DEFAULT_GRACE_PERIOD_BEFORE_TILE_REMOVAL;
304 gracePeriodBeforeTileRemoval = var->getFloat();
307 componentState->statistics.traversedDepth = 0;
308 componentState->statistics.numberOfCancelledRequests = 0;
309 componentState->statistics.numberOfPendingRequests = 0;
310 componentState->statistics.numberOfVisitedSubTilesets = 0;
311 componentState->numberOfModelsBeingPrepared = 0;
312 componentState->requiresTraversal =
false;
315 traverseTileset(&component, componentState, componentState->tilesets[0], gracePeriodBeforeTileRemoval);
319 for (
auto const& tileset : componentState->tilesets) {
320 double timestamp = componentState->states[tileset].timestampOfLastTraversal;
321 if (tileset != componentState->tilesets[0] &&
322 componentState->states[tileset].candidateForDelayedTraversal &&
323 (now - timestamp) >= gracePeriodBeforeTileRemoval) {
324 traverseTileset(&component, componentState, tileset, gracePeriodBeforeTileRemoval);
325 componentState->states[tileset].candidateForDelayedTraversal =
false;
330 this->processSubTilesetRequests(&component, componentState);
334 LOG_DEBUG(logger,
" Max traversal depth: %d, Num requests: %d, Num cancelled: %d, Num pending: %d",
335 componentState->statistics.traversedDepth,
336 componentState->modelRequests.size(),
337 componentState->statistics.numberOfCancelledRequests,
338 componentState->statistics.numberOfPendingRequests);
342 this->timestampOfLastCameraMovement = now;
343 this->lastCameraViewProjection = cameraData.rawViewProjection;
348 if (this->hasPendingRequests(componentState)) {
349 this->context->
engine->setDirty();
360 std::vector<std::string> toBeCancelled;
361 toBeCancelled.reserve(node.tile.URLs.size());
363 for (
auto const& url : node.tile.URLs) {
364 if (!componentState->states[tileset].modelRequests.contains(url)) {
366 for (
auto const& model : componentState->states[tileset].tileCache[node.tile.tileId]) {
367 if (model.URL == url) {
374 this->
context->modelManager->cancelModelLoad(componentState->states[tileset].modelRequests[url].second);
375 componentState->statistics.numberOfCancelledRequests += 1;
376 toBeCancelled.emplace_back(url);
381 for (
auto const& url : toBeCancelled) {
382 componentState->states[tileset].modelRequests.erase(url);
385 for (
auto const& [tileId, child] : node.children) {
386 cancelStaleModelRequests(componentState, tileset, child);
399 const std::unordered_map<std::string, OGC3DTiles::Coord> subtreeRequestsCopy = state->subtreeRequests;
401 for (
const auto& [subtreeURL, coord] : subtreeRequestsCopy) {
403 if (state->subtreeCache.contains(coord.level) && state->subtreeCache[coord.level].contains(key) &&
404 state->subtreeCache[coord.level][key]->pendingRequests == 0) {
405 const uint64_t subtreeGUID =
Cogs::hash(subtreeURL);
406 state->subtreeRequests.erase(subtreeURL);
407 state->subtreeFetchIds.erase(subtreeGUID);
413OGC3DTilesSystem::hasPendingRequests(
const OGC3DTilesData* componentState)
const
415 const bool subTilesetsState = !componentState->subTilesetRequests.empty();
416 const bool modelsPreparedState = componentState->numberOfModelsBeingPrepared > 0;
417 const bool tilesetFetches = !componentState->tilesetFetchIds.empty();
419 if (subTilesetsState || modelsPreparedState || tilesetFetches) {
423 for (
auto tileset : componentState->tilesets) {
424 const bool modelRequestsState = !componentState->states.at(tileset).pendingModelRequests.empty() || !componentState->states.at(tileset).modelRequests.empty();
425 const bool subtreeRequestsState = !componentState->states.at(tileset).subtreeRequests.empty();
427 if (modelRequestsState || subtreeRequestsState) {
438 std::unordered_set<const OGC3DTilesTileset::Tileset*> visitedTilesets;
441 rootNode.tile.depth = 0;
443 componentState->states[tileset].pendingModelRequests.clear();
446 const glm::mat4 globalTransform = this->
context->transformSystem->getLocalToWorld(componentTransform);
451 if (tileset->root.useImplicitTiling) {
453 OGC3DTiles::Coord rootCoord{ 0, 0, 0, tileset->root.implicitTiling.isOctTree ? 0 : -1 };
456 rootNode =
buildImplicitTree(componentState, tileset, rootCoord, globalTransform);
460 rootNode =
buildExplicitTree(componentState, tileset, &tileset->root,
"", tileset->root.transform, globalTransform, visitedTilesets);
463 if (rootNode.valid) {
464 std::unordered_map<uint64_t, OGC3DTilesSystem::TileCandidate> tilesInView;
468 LOG_DEBUG(logger,
"Tiles in view: %d\n", (
int)tilesInView.size());
469 OGC3DTilesSystem::printDebugStats(componentState);
481 cancelStaleModelRequests(componentState, tileset, rootNode);
483 const size_t numReleased =
pruneTileCache(componentState, tileset, gracePeriodBeforeTileRemoval);
485 if (numReleased != 0) {
486 LOG_DEBUG(logger,
"Released %zu model(s) (frame #%d)", numReleased,
context->
time->getFrame());
492 processModelRequests(componentState, tileset,
context->
time->getFrame());
494 componentState->states[tileset].timestampOfLastTraversal = this->
context->
time->getAnimationTime();
495 componentState->states[tileset].candidateForDelayedTraversal =
true;
497 if (tileset->root.useImplicitTiling) {
498 processSubtreeRequests(component, componentState, tileset);
502 for (
auto subTileset : visitedTilesets) {
503 assert(subTileset != componentState->tilesets[0] &&
"Don't traverse the main tileset twice!");
504 traverseTileset(component, componentState, subTileset, gracePeriodBeforeTileRemoval);
514 const std::string_view sessionToken =
"session=";
515 const std::string_view keyToken =
"key=";
517 size_t sessionTokenPos = url.find(sessionToken);
518 size_t sessionTokenEndPos = url.find(
"&");
519 if (sessionTokenPos != std::string::npos) {
520 size_t len = sessionTokenEndPos == std::string::npos ? std::string::npos : (sessionTokenEndPos - (sessionTokenPos + sessionToken.size()));
521 const std::string session = url.substr(sessionTokenPos + sessionToken.size(), len);
523 if (!componentState->optionalSessionKey.empty() && componentState->optionalSessionKey != session) {
524 LOG_WARNING(logger,
"Multiple session keys ('session=') for one domain is not supported.");
526 componentState->optionalSessionKey =
"session="+session;
527 LOG_DEBUG(logger,
"Found session key '%s' in URL", session.c_str());
531 LOG_DEBUG(logger,
"No optional session key in URL '%s'", url.c_str());
536 size_t keyTokenPos = url.find(keyToken);
537 size_t keyTokenEndPos = url.find(
"&");
538 if (keyTokenPos != std::string::npos) {
539 size_t len = keyTokenEndPos == std::string::npos ? std::string::npos : (keyTokenEndPos - (keyTokenPos + keyToken.size()));
540 const std::string key = url.substr(keyTokenPos + keyToken.size(), len);
542 if (!componentState->optionalAccessKey.empty() && componentState->optionalAccessKey != key) {
543 LOG_WARNING(logger,
"Multiple access-keys ('key=') for one domain is not supported.");
545 componentState->optionalAccessKey =
"key="+key;
546 LOG_DEBUG(logger,
"Found access key '%s' in URL", key.c_str());
550 LOG_DEBUG(logger,
"No optional access key in URL '%s'", url.c_str());
557OGC3DTilesSystem::addOptionalURLParameters(
const OGC3DTilesData* componentState,
const std::string& url)
const
559 std::string result = url;
560 bool hasParams = url.find(
"?") != std::string::npos;
563 if (result.find(componentState->optionalSessionKey) == std::string::npos) {
564 result += hasParams ?
"&" :
"?";
565 result += componentState->optionalSessionKey;
570 if (result.find(componentState->optionalAccessKey) == std::string::npos) {
571 result += hasParams ?
"&" :
"?";
572 result += componentState->optionalAccessKey;
576 if (!componentState->additionalURLParameters.empty()) {
577 result += hasParams ?
"&" :
"?";
578 result += componentState->additionalURLParameters;
587 LOG_INFO(logger,
"Initializing a OGC3DTilesComponent with baseURL='%s'.", component->
baseURL.c_str());
588 componentState->baseURL = component->
baseURL;
594 componentState->assetsFetchId = Cogs::Core::DataFetcherManager::NoFetchId;
596 LOG_DEBUG(logger,
"Fetching assets:\n URL='%s'\n accessToken='%s'\n AssetID=%d", component->
baseURL.c_str(), component->
accessToken.c_str(), component->
assetId);
597 componentState->assetsFetchId = fetchAndInitializeAsset(component, componentState->uniqueComponentId);
600 std::string tilesetURL = component->
baseURL +
"/tileset.json";
601 tilesetURL = addOptionalURLParameters(componentState, tilesetURL);
602 const uint64_t tilesetGUID =
Cogs::hash(tilesetURL);
604 if (componentState->tilesetFetchIds.contains(tilesetGUID)) {
605 LOG_ERROR(logger,
"Tileset '%s' is already being fetched [%lu]", tilesetURL.c_str(), componentState->tilesetFetchIds[tilesetGUID]);
608 tilesetURL = this->addOptionalURLParameters(componentState, tilesetURL);
609 componentState->tileBaseURL = component->
baseURL;
610 componentState->tilesetFetchIds[tilesetGUID] = fetchAndInitializeTileset(tilesetGUID, tilesetURL, component, componentState->uniqueComponentId);
614Cogs::Core::DataFetcherManager::FetchId
615OGC3DTilesSystem::fetchAndInitializeAsset(
OGC3DTilesComponent * component, uint32_t uniqueComponentId)
619 if (OGC3DTilesSystem::componentIsStale(thisp->context, uniqueComponentId)) {
627 std::string tilesetURL = component->
baseURL +
"/tileset.json";
628 componentState->tileBaseURL = component->
baseURL;
630 if (tilesAsset !=
nullptr) {
631 LOG_DEBUG(logger,
"Got asset data URL: '%s'", tilesAsset->url.c_str());
632 tilesetURL = tilesAsset->url;
634 thisp->extractAndStoreOptionalSessionKeys(componentState, tilesetURL);
637 size_t pos = tilesetURL.find_first_of(
'/', std::string(
"https://").
size()+1);
638 if (pos == std::string::npos) {
639 pos = tilesetURL.size();
641 componentState->tileBaseURL = tilesetURL.substr(0, pos);
644 if (!tilesAsset->bearerToken.empty()) {
646 LOG_DEBUG(logger,
"Appending bearer-token auth to tile requests URLs ('%s')", tilesAsset->bearerToken.c_str());
647 componentState->optionalAccessKey =
"access_token=" + tilesAsset->bearerToken;
650 LOG_DEBUG(logger,
"Tiles URL base is: '%s'", componentState->tileBaseURL.c_str());
654 LOG_ERROR(logger,
"Could not fetch asset data from '%s'\n", component->
baseURL.c_str());
658 tilesetURL = thisp->addOptionalURLParameters(componentState, tilesetURL);
663 const uint64_t tilesetGUID =
Cogs::hash(tilesetURL);
664 if (componentState->tilesetFetchIds.contains(tilesetGUID)) {
665 LOG_ERROR(logger,
"Tileset '%s' is already being fetched [%lu]", tilesetURL.c_str(), componentState->tilesetFetchIds[tilesetGUID]);
669 componentState->tilesetFetchIds[tilesetGUID] = thisp->fetchAndInitializeTileset(tilesetGUID, tilesetURL, component, uniqueComponentId);
670 componentState->assetsFetchId = Cogs::Core::DataFetcherManager::NoFetchId;
675OGC3DTilesSystem::fetchAndInitializeTileset(
const uint64_t tilesetGUID,
const std::string & tilesetURL,
OGC3DTilesComponent * component, uint32_t uniqueComponentId)
677 return Cogs::Core::OGC3DTilesTileset::fetch(this->
context, tilesetURL,
686 if (mainTileset !=
nullptr) {
688 componentState->originalRootTransform = mainTileset->root.transform;
689 mainTileset->root.transform = glm::mat4(1.0);
690 LOG_INFO(logger,
"Loaded tileset is of type '%s' [%p]", mainTileset->root.useImplicitTiling ?
"IMPLICIT" :
"EXPLICIT", mainTileset);
692 Cogs::Geometry::DBoundingBox bbox =
693 OGC3DTilesUtils::boundingVolumeConverter(mainTileset->root.boundingVolume, mainTileset->root.transform);
694 glm::vec3 center = (bbox.max - bbox.min) * 0.5 + bbox.min;
696 LOG_INFO(logger,
"Tileset center: <%.2f, %.2f, %.2f> [%p]", center.x, center.y, center.z, mainTileset);
697 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);
698 LOG_INFO(logger,
"Tileset bounding volume: %s", mainTileset->root.boundingVolume.toString().c_str());
704 Cogs::Geometry::DBoundingBox globalBBox =
705 OGC3DTilesUtils::boundingVolumeConverter(mainTileset->root.boundingVolume, mainTileset->root.transform);
706 thisp->addBBoxIndicatorToScene(componentState, mainTileset, 0, globalBBox);
711 if (mainTileset->root.useImplicitTiling) {
712 LOG_INFO(logger,
" Tree-structure is of type '%s' [%p]", mainTileset->root.implicitTiling.isOctTree ?
"OCT" :
"QUAD", mainTileset);
714 thisp->requestSubtree(componentState, mainTileset, rootCoord);
717 componentState->tilesets.push_back(mainTileset);
720 thisp->context->engine->invokeComponentNotifyCallback(*component,
722 reinterpret_cast<const double *
>(glm::value_ptr(componentState->originalRootTransform)),
726 LOG_ERROR(logger,
"Failed to initialize OGC3DTilesComponent (baseURL='%s')", component->
baseURL.c_str());
729 componentState->tilesetFetchIds.erase(tilesetGUID);
731 LOG_DEBUG(logger,
"Tileset fetched, initialized and ready");
732 thisp->context->engine->setDirty();
733 componentState->requiresTraversal =
true;
750 const float qualityDelta = componentState->enableCaching ?
753 if (qualityDelta >= 0.0) {
757 std::set<std::pair<double, uint64_t>> toBeRemoved;
758 const double now = this->
context->
time->getAnimationTime();
761 bool tileHidden =
false;
762 for (
const auto& [tileId, loadedModels] : componentState->states[tileset].tileCache) {
763 if (componentState->neverDiscardRootTiles &&
764 tileset == componentState->tilesets[0] &&
771 if (lm.timestampLastUsed + gracePeriodBeforeTileRemoval < now) {
772 toBeRemoved.emplace(std::make_pair(lm.timestampLastUsed, tileId));
779 if (lm.timestampLastUsed < now) {
783 sceneComponent->
visible =
false;
795 if (toBeRemoved.empty()) {
799 std::list<std::pair<double, uint64_t>> sortedByTimestamp(toBeRemoved.begin(), toBeRemoved.end());
800 sortedByTimestamp.sort([](
const std::pair<double, uint64_t>& a,
const std::pair<double, uint64_t>& b) {
return a.first > b.first; });
803 size_t numRemoved = 0;
805 const size_t numToBeRemoved = sortedByTimestamp.size() * size_t(-qualityDelta);
807 for (
const auto& [timestamp, tileId] : sortedByTimestamp) {
808 removeTileFromScene(componentState, tileset, tileId);
811 if (numRemoved >= numToBeRemoved) {
820OGC3DTilesSystem::setVisibilityForAllTiles(
const OGC3DTilesData* componentState,
bool onoff)
const
823 for (
auto tileset : componentState->tilesets) {
824 for (
const auto& [tileId, loadedModels] : componentState->states.at(tileset).tileCache) {
825 setTileVisibility(componentState, tileset, tileId, onoff);
833 if (!componentState->states.at(tileset).tileCache.contains(tileId)) {
838 const std::vector<LoadedModel>& loadedModels = componentState->states.at(tileset).tileCache.at(tileId);
839 for (
const LoadedModel& lm : loadedModels) {
840 if (!modelIsReady(lm.modelHandle)) {
851 if (sceneComponent->
visible != onoff) {
854 sceneComponent->
visible = onoff;
857 LOG_WARNING(logger,
"Could not find '%s' for visibility toggling (tileset %p)", lm.URL.c_str(), tileset);
863OGC3DTilesSystem::modelIsReady(
const ModelHandle& modelhandle)
const
865 if (!modelhandle->isResident()) {
871 if (!r->isActive()) {
877 for (
auto& mesh : modelhandle->meshes) {
878 if (!mesh->isInitialized() && mesh->getCount() == 0) {
882 if (!mesh->hasAttachedResource() || !mesh->isActive()) {
886 if (mesh->hasAttachedResource()) {
887 auto r = mesh->getAttachedResource();
888 if (!r->isActive()) {
895 for (
auto& material : modelhandle->materials) {
896 if (!material->isResident()) {
900 for (
auto tv : material->textureVariables) {
901 if (!tv.texture.handle->isResident()) {
913 const std::vector<LoadedModel>& loadedModels = componentState->states[tileset].tileCache[tileId];
914 for (
const LoadedModel& lm : loadedModels) {
927 LOG_WARNING(logger,
"removeTileFromCache(): Entity '%s' (%s) not found.", lm.URL.c_str(), lm.uniqueName.c_str());
931 if (componentState->showTileBBoxes) {
932 removeBBoxIndicatorFromScene(componentState, tileset, tileId);
935 componentState->states[tileset].tileCache.erase(tileId);
941 std::vector<const std::string*> finished;
942 const double now = this->
context->
time->getAnimationTime();
944 for (
const auto& [URL, pair] : componentState->states[tileset].modelRequests) {
945 TileCandidate tileCandidate = pair.first;
947 if (this->modelIsReady(modelHandle)) {
948 finished.emplace_back(&URL);
950#if DEBUG_CHECK_IF_INCOMING_MODEL_EXISTS
951 const std::vector<LoadedModel>& loadedModels = componentState->tileCache[tileCandidate.tileId];
952 bool alreadyLoaded = std::any_of(loadedModels.begin(), loadedModels.end(), [&url=URL](
const LoadedModel & item) { return item.URL == url; });
954 LOG_ERROR(logger,
"Incoming model '%s' is already loaded", URL.c_str());
955 assert(alreadyLoaded &&
"Incoming model is already loaded for this tile!");
960#if DEBUG_ADD_URL_TO_MODEL_NAMES
962 const std::string uniqueName = (tileset->root.useImplicitTiling ? OGC3DTiles::Coord::fromHash(tileCandidate.tileId).toString() : std::to_string(tileCandidate.tileId)) +
" - " + URL;
965 std::string uniqueName = tileset->root.useImplicitTiling ? OGC3DTiles::Coord::fromHash(tileCandidate.tileId).toString() : std::to_string(tileCandidate.tileId);
967 uniqueName += std::to_string(uint64_t(tileset));
977 LoadedModel loadedModel;
978 loadedModel.modelHandle = modelHandle;
979 loadedModel.timestampLastUsed = now;
980 loadedModel.URL = URL;
981 loadedModel.uniqueName = uniqueName;
982 loadedModel.frameNumberAtArrival = frameNumber;
984 if (!OGC3DTilesSystem::modelIsEmpty(modelHandle)) {
985 addModelToScene(loadedModel.uniqueName, tileCandidate, modelHandle, componentState);
986 loadedModel.isValid =
true;
987 componentState->requiresTraversal =
true;
990 componentState->states[tileset].tileCache[tileCandidate.tileId].emplace_back(loadedModel);
992 else if (modelHandle->hasFailedLoad()) {
993 LOG_ERROR(logger,
"processModelRequests(): Could not load '%s'", URL.c_str());
994 finished.emplace_back(&URL);
996 else if (modelHandle->hasFailedActivation()) {
998 LOG_ERROR(logger,
"processModelRequests(): Model failed to activate!");
1000 finished.emplace_back(&URL);
1004 for (
const std::string* URL : finished) {
1005 componentState->states[tileset].modelRequests.erase(*URL);
1020 if (URL.find(
".json") != std::string::npos) {
1021 LOG_ERROR(logger,
"Internal error: A sub-tileset was requested as model; '%s'.", URL.c_str());
1022 assert(
false &&
"Internal error");
1027 if (!componentState->states[tileset].modelRequests.contains(URL)) {
1028 const std::vector<LoadedModel>& loadedModels = componentState->states[tileset].tileCache[tile.tileId];
1029 bool alreadyLoaded = std::ranges::any_of(loadedModels.cbegin(), loadedModels.cend(), [&URL](
LoadedModel const& item) { return item.URL == URL; });
1030 if (!alreadyLoaded) {
1032 componentState->states[tileset].modelRequests[URL] = std::pair(tile, handle);
1054 std::vector<const TileCandidate*> sortedTilesInView;
1055 sortedTilesInView.reserve(tilesInView.size());
1056 for (
auto it = tilesInView.cbegin(); it != tilesInView.cend(); ++it) {
1057 sortedTilesInView.emplace_back(&it->second);
1062 const glm::vec3 cameraPos = getCurrentCameraPosition();
1063 std::ranges::sort(sortedTilesInView.begin(), sortedTilesInView.end(),
1065 const double distA = OGC3DTilesUtils::distanceFromBBox(cameraPos, a->bbox);
1066 const double distB = OGC3DTilesUtils::distanceFromBBox(cameraPos, b->bbox);
1067 return distA < distB;
1071 const double now = this->
context->
time->getAnimationTime();
1072 size_t maxNumRequestsPerFrame =
context->
variables->get(maxNumModelRequestsPerFrameName, MAX_NUM_MODEL_REQUESTS_PER_FRAME);
1073 size_t maxNumActiveRequests =
context->
variables->get(maxNumActiveModelRequestsName, MAX_NUM_ACTIVE_MODEL_REQUESTS);
1074 size_t numScheduled = 0;
1075 componentState->states[tileset].pendingModelRequests.reserve(sortedTilesInView.size());
1077 for (
const auto* tilecandidate : sortedTilesInView) {
1078 for (std::string URL : tilecandidate->URLs) {
1080 if (componentState->states[tileset].modelRequests.size() >= maxNumActiveRequests) {
1082 componentState->states[tileset].pendingModelRequests.emplace(tilecandidate->tileId);
1085 if (numScheduled < maxNumRequestsPerFrame) {
1086 if (
requestModel(componentState, tileset, *tilecandidate, addOptionalURLParameters(componentState, URL))) {
1088 if (componentState->showTileBBoxes) {
1089 addBBoxIndicatorToScene(componentState, tileset, tilecandidate->tileId, tilecandidate->bbox);
1095 componentState->states[tileset].pendingModelRequests.emplace(tilecandidate->tileId);
1101 if (componentState->states[tileset].tileCache.contains(tilecandidate->tileId)) {
1102 std::vector<LoadedModel>& loadedModels = componentState->states[tileset].tileCache[tilecandidate->tileId];
1104 m.timestampLastUsed = now;
1109 componentState->statistics.numberOfPendingRequests = (int) componentState->states[tileset].pendingModelRequests.size();
1116 std::string path = std::string(tileset->root.implicitTiling.subTreeURLScheme);
1117 path = path.replace(path.find(
"{level}"), 7, std::to_string(coord.level));
1118 path = path.replace(path.find(
"{x}"), 3, std::to_string(coord.x));
1119 path = path.replace(path.find(
"{y}"), 3, std::to_string(coord.y));
1121 path = path.replace(path.find(
"{z}"), 3, std::to_string(coord.z));
1124 return componentState->tileBaseURL +
"/" + path;
1136 assert(coord.level <= 0xFFFF && coord.x <= 0xFFFF && coord.y <= 0xFFFF &&
"Coord values out of the 16bit bounds");
1137 uint64_t
hash = coord.level + (uint64_t(coord.x) << 16) + (uint64_t(coord.y) << 32);
1140 assert(coord.z <= 0xFFFF &&
"Coord.z value out of the 16bit bounds");
1141 hash += uint64_t(coord.z) << 48;
1153 if (tileHasModelRequests(componentState, tileset, tileId) ||
1154 tileHasSubTilesetRequests(componentState, tileId)) {
1158 assert(componentState->states[tileset].tileCache.contains(tileId) &&
"Tile not in cache");
1160 const std::vector<LoadedModel>& models = componentState->states[tileset].tileCache[tileId];
1161 size_t numReadyModels = 0;
1162 for (
const auto& lm : models) {
1164 numReadyModels += 1;
1168 if (modelIsReady(lm.modelHandle)) {
1169 numReadyModels += 1;
1172 componentState->numberOfModelsBeingPrepared += 1;
1176 return models.size() == numReadyModels;
1185 for (
const auto& [URL, pair] : componentState->states.at(tileset).modelRequests) {
1186 const TileCandidate& tileCandidate = pair.first;
1187 if (tileCandidate.tileId == tileId) {
1192 if (componentState->states.at(tileset).pendingModelRequests.contains(tileId)) {
1200OGC3DTilesSystem::tileHasSubTilesetRequests(
const OGC3DTilesData* componentState, uint64_t tileId)
const
1202 for (
const auto& [URL, pair] : componentState->subTilesetRequests) {
1203 if (pair.first == tileId) {
1217 size_t numReadyChildren = 0;
1218 for (
auto const& [childTileId, child] : node->children) {
1219 if (child.tile.URLs.empty()) {
1221 numReadyChildren += 1;
1226 numReadyChildren += 1;
1234 return node->children.size() == numReadyChildren;
1244 if (target.contains(node->tile.tileId)) {
1245 assert(
false &&
"Node has already been visited. Internal error.");
1249 if (!node->tile.URLs.empty()) {
1250 target[node->tile.tileId] = node->tile;
1253 for (
auto const& [childTileId, child] : node->children) {
1265 if (node->replaceRefine) {
1266 if (!componentState->waitForTileSiblings) {
1267 if (node->children.empty()) {
1268 setTileVisibility(componentState, tileset, node->tile.tileId,
true);
1271 setTileVisibility(componentState, tileset, node->tile.tileId,
false);
1275 if (componentState->states[tileset].tileCache.contains(node->tile.tileId)) {
1276 bool showNode =
true;
1277 if (!node->children.empty() &&
1284 setTileVisibility(componentState, tileset, node->tile.tileId, showNode);
1289 for (
auto const& [childTileId, child] : node->children) {
1304 const glm::mat4& globalTransform)
const
1308 node.replaceRefine = tileset->root.refine == OGC3DTilesTileset::RefineType::REPLACE;
1310 componentState->statistics.traversedDepth = std::max(componentState->statistics.traversedDepth, coord.level);
1313 OGC3DTilesUtils::getBoundingVolumeFromCoord(coord, tileset->root.boundingVolume);
1314 Cogs::Geometry::DBoundingBox tileBBox =
1315 OGC3DTilesUtils::boundingVolumeConverter(tileBoundingVolume, tileset->root.transform);
1316 Cogs::Geometry::DBoundingBox transformedTileBBox(tileBBox);
1318 OGC3DTilesUtils::transformBBox(transformedTileBBox, globalTransform);
1320 if (!isInsideFrustum(transformedTileBBox)) {
1330 const double distance = OGC3DTilesUtils::distanceFromBBox(getCurrentCameraPosition(), transformedTileBBox);
1335 const double geometricError = tileset->geometricError / std::pow(2, coord.level);
1336 const double factor = std::pow(componentState->detailFactor, 1.2) * geometricError;
1337 const bool hasContent = OGC3DTilesSubtree::hasContent(subtree, coord);
1339 if (distance < factor || !hasContent) {
1341 std::vector<OGC3DTiles::Coord> children =
1342 getTileCoordChildren(componentState, tileset, coord);
1345 if (componentState->dontRenderLeaves) {
1349 if (getTileCoordChildren(componentState, tileset, child).empty()) {
1358 assert(coord != childCoord &&
"Internal error");
1359 assert(childCoord.level != 0 &&
"Coord with level=0 cannot be a child");
1364 node.children[child.tile.tileId] = child;
1377 assert(tileset->root.contents.size() > 0 &&
"No content!");
1378 std::string path = tileset->root.contents[0].URI;
1379 path = path.replace(path.find(
"{level}"), 7, std::to_string(coord.level));
1380 path = path.replace(path.find(
"{x}"), 3, std::to_string(coord.x));
1381 path = path.replace(path.find(
"{y}"), 3, std::to_string(coord.y));
1383 path = path.replace(path.find(
"{z}"), 3, std::to_string(coord.z));
1386 node.tile.URLs.emplace_back(OGC3DTilesSystem::compileURL(componentState->tileBaseURL, tileset->baseURL, path));
1387 node.tile.transform = tileset->root.transform;
1390 node.tile.tileId = coord.toHash();
1391 node.tile.depth = coord.level;
1392 node.tile.bbox = tileBBox;
1407 const std::string& tileIdStr,
1408 const glm::mat4& currentTransform,
1409 const glm::mat4& globalTransform,
1410 std::unordered_set<const OGC3DTilesTileset::Tileset*> & visitedTilesets)
const
1414 node.replaceRefine = tile->refine == OGC3DTilesTileset::RefineType::REPLACE;
1415 componentState->statistics.traversedDepth = std::max(componentState->statistics.traversedDepth, (
int)tileIdStr.size());
1417 glm::mat4 transform = currentTransform;
1418 Cogs::Geometry::DBoundingBox bbox = OGC3DTilesUtils::boundingVolumeConverter(tile->boundingVolume, transform);
1419 Cogs::Geometry::DBoundingBox transformedBBox(bbox);
1420 OGC3DTilesUtils::transformBBox(transformedBBox, globalTransform);
1422 if (!isInsideFrustum(transformedBBox)) {
1426 const double distance = OGC3DTilesUtils::distanceFromBBox(getCurrentCameraPosition(), transformedBBox);
1427 const double factor = (node.tile.depth + 1) * std::pow(componentState->detailFactor, 2.0) * tile->geometricError;
1428 const bool hasContent = !tile->contents.empty();
1430 if (distance < factor || !hasContent) {
1432 if (componentState->dontRenderLeaves) {
1435 for (
size_t i=0; i < tile->children.size(); ++i) {
1437 if (childTile->children.empty()) {
1445 for (
size_t i = 0; i < tile->children.size(); ++i) {
1447 assert(!childTile->useImplicitTiling &&
"Internal error");
1450 Node childNode =
buildExplicitTree(componentState, tileset, childTile, tileIdStr + std::to_string(i), transform, globalTransform, visitedTilesets);
1452 if (childNode.valid) {
1453 assert(node.children.find(childNode.tile.tileId) == node.children.end() &&
"Child node has already been added");
1454 node.children[childNode.tile.tileId] = childNode;
1464 node.tile.depth = (uint32_t)tileIdStr.length();
1465 node.tile.bbox = bbox;
1466 node.tile.transform = transform;
1467 node.tile.URLs.reserve(tile->contents.size());
1471 const std::string url = OGC3DTilesSystem::compileURL(componentState->tileBaseURL, tileset->baseURL, c.URI);
1473 if (c.URI.find(
".json") != std::string::npos || c.URI.find(
".JSON") != std::string::npos) {
1474 const std::string tilesetURL = addOptionalURLParameters(componentState, url);
1475 if (componentState->states[tileset].subTilesets.contains(tilesetURL)) {
1476 visitedTilesets.insert(componentState->states[tileset].subTilesets[tilesetURL]);
1477 componentState->states[tileset].candidateForDelayedTraversal =
true;
1478 componentState->statistics.numberOfVisitedSubTilesets += 1;
1482 requestSubTileset(componentState, tilesetURL, node.tile.tileId, tileset);
1486 node.tile.URLs.emplace_back(url);
1497 if (componentState->subTilesetRequests.size() > MAX_NUM_ACTIVE_SUBTILESET_REQUESTS) {
1501 if (componentState->loadedSubTilesets.contains(URL) ||
1502 componentState->subTilesetRequests.contains(URL)) {
1506 componentState->subTilesetRequests[URL] = std::make_pair(tileId, parentTileset);
1522 if (componentState->colorTileAccordingToTreeDepth) {
1524 modelComponent->
materialInstance = debugTileTreeDepthColorMaterials[tile.depth % debugTileTreeDepthColorMaterials.size()];
1526 else if (componentState->overrideMaterial) {
1531 modelComponent->
model = handle;
1534 transformComponent->
transform = tile.transform;
1543 assert(componentState->states.contains(tileset) &&
"Internal error");
1544 if (!componentState->states[tileset].bboxIndicatorCache.contains(tileId)) {
1545 std::string title =
"BBox ";
1546 title += tileset->root.useImplicitTiling ? OGC3DTiles::Coord::fromHash(tileId).toString() : std::to_string(tileId);
1548 title += std::to_string(uint64_t(tileset));
1552 componentState->states[tileset].bboxIndicatorCache[tileId] = bboxEntity;
1554 auto createBBoxTask = [ctx = this->
context, bboxEntity, box]() {
1557 MaterialInstanceHandle material = ctx->materialInstanceManager->createMaterialInstance(ctx->materialManager->getDefaultMaterial());
1558 material->setPermutation(
"Line");
1559 material->setBoolProperty(DefaultMaterial::EnableLighting,
false);
1560 const float r = (rand() % 255) / 255.0f;
1561 material->setVec4Property(DefaultMaterial::DiffuseColor, BBOX_BASE_COLOR_FORMAT);
1562 material->options.depthBiasEnabled =
true;
1563 material->options.depthBias.constant = -1.f;
1564 material->options.depthBias.slope = -1.f;
1565 material->options.depthBias.clamp = 100.f;
1568 std::array<PositionVertex, 8> vertices = {
1579 std::array<unsigned int, 16> lineStripIndices = {
1588 meshRenderComponent->setMaterial(material);
1590 MeshHandle meshHandle = ctx->meshManager->create();
1593 Cogs::Geometry::BoundingBox fbox;
1598 Mesh* mesh = ctx->meshManager->get(meshHandle);
1599 assert(mesh &&
"Internal error");
1615 auto it = componentState->states[tileset].bboxIndicatorCache.find(tileId);
1616 if (it != componentState->states[tileset].bboxIndicatorCache.end()) {
1620 componentState->states[tileset].bboxIndicatorCache.erase(tileId);
1623 if (tileset->root.useImplicitTiling) {
1624 LOG_ERROR(logger,
"Instructed to remove BBox '%s', but it does not exist.", OGC3DTiles::Coord::fromHash(tileId).toString().c_str());
1627 LOG_ERROR(logger,
"Instructed to remove BBox '%llu', but it does not exist.", tileId);
1638 h.data = std::make_unique<OGC3DTilesData>();
1641 data->uniqueComponentId = createUniqueComponentId();
1643 if (this->
pool.size() >= 1 && this->bounds ==
nullptr) {
1655 LOG_DEBUG(logger,
"Destroying OGC3DTilesComponent (baseURL=%s)", comp->
baseURL.c_str());
1660 if (componentState->assetsFetchId != Cogs::Core::DataFetcherManager::NoFetchId) {
1661 Cogs::Core::DataFetcherManager::cancelAsyncFetch(this->
context, componentState->assetsFetchId);
1664 for (
auto & [guid, fetchId] : componentState->tilesetFetchIds) {
1665 Cogs::Core::DataFetcherManager::cancelAsyncFetch(this->
context, fetchId);
1668 for (
auto tileset : componentState->tilesets) {
1669 for (
auto& [guid, fetchId] : componentState->states[tileset].subtreeFetchIds) {
1670 Cogs::Core::DataFetcherManager::cancelAsyncFetch(this->
context, fetchId);
1673 for (
const auto& [tileId, loadedModels] : componentState->states[tileset].tileCache) {
1683 LOG_ERROR(logger,
"destroyComponent(): Could not find entity '%s' (%s)", lm.URL.c_str(), lm.uniqueName.c_str());
1689 for (
auto& [
id, subtreeMap] : componentState->states[tileset].subtreeCache) {
1690 for (
auto& [key, val] : subtreeMap) {
1697 for (
auto& b : componentState->states[tileset].bboxIndicatorCache) {
1704 componentState->tilesets.clear();
1705 componentState->states.clear();
1707 if (this->bounds !=
nullptr) {
1715OGC3DTilesSystem::getCurrentCameraPosition()
const
1717 return this->
context->cameraSystem->getMainCameraData().inverseViewMatrix * glm::dvec4(0, 0, 0, 1.0);
1721OGC3DTilesSystem::isInsideFrustum(
const Cogs::Geometry::DBoundingBox& box)
const
1728 Cogs::Geometry::Frustum frustum = this->
context->cameraSystem->getMainCameraData().frustum;
1729 Cogs::Geometry::BoundingBox fbox;
1732 int result = Cogs::Geometry::contains(frustum, fbox);
1733 return result == 2 || result == 1;
1736 const CameraData& cameraData = this->
context->cameraSystem->getMainCameraData();
1737 int result = OGC3DTilesUtils::bboxInsideViewFrustum(box, cameraData.rawViewProjection);
1743OGC3DTilesSystem::modelIsEmpty(
const ModelHandle handle)
1745 const size_t numParts = handle->parts.size();
1746 for (
size_t i = 0; i < numParts; ++i) {
1747 const ModelPart& part = handle->parts[i];
1748 if (part.vertexCount !=
static_cast<uint32_t
>(-1)) {
1755std::vector<OGC3DTiles::Coord>
1758 assert(tileset->root.useImplicitTiling &&
"Only IMPLICIT trees uses subtrees");
1759 std::vector<OGC3DTiles::Coord> children;
1763 LOG_ERROR(logger,
"getTileCoordChildren(): No subtree found. Internal error");
1768 if (OGC3DTilesSubtree::isLeafNodeInSubtree(subtree, parent) ||
1769 ((parent.level + 1) >= tileset->root.implicitTiling.availableLevels)) {
1770 std::vector<OGC3DTiles::Coord> subtreeCoords = OGC3DTilesSubtree::getLeafNodesSubtreeCoords(subtree, parent);
1771 children.reserve(subtreeCoords.size());
1773 for (
auto const& subtreeCoord : subtreeCoords) {
1775 if (subtreeCoord.level > tileset->root.implicitTiling.availableLevels) {
1776 LOG_WARNING(logger,
"Requested subtree-level too deep: %d vs %d", subtreeCoord.level, tileset->root.implicitTiling.availableLevels);
1781 if (subSubtree && (subtree->tileAvailability.availableCount > 0 || subtree->tileAvailability.allIsAvailable)) {
1782 children.emplace_back(subtreeCoord);
1787 if (subtree->tileAvailability.availableCount == 0 && !subtree->tileAvailability.allIsAvailable) {
1790 else if (subtree->tileAvailability.allIsAvailable) {
1792 children = OGC3DTilesSubtree::getAllChildren(subtree, parent);
1796 children = OGC3DTilesSubtree::getChildren(subtree, parent);
1813 assert(tileset->root.useImplicitTiling &&
"Only IMPLICIT trees uses subtrees");
1815 const int levelsPerSubtree = tileset->root.implicitTiling.subtreeLevels;
1816 OGC3DTiles::Coord subtreeCoord = OGC3DTilesSubtree::getSubtreesRootCoord(levelsPerSubtree, coord);
1818 if (subtreeCoord.level > tileset->root.implicitTiling.availableLevels) {
1819 LOG_ERROR(logger,
"Subtree coord-depth > tree-depth (%d vs %d)", subtreeCoord.level, tileset->root.implicitTiling.availableLevels);
1826 if (!componentState->states[tileset].subtreeCache.contains(subtreeCoord.level) ||
1827 !componentState->states[tileset].subtreeCache[subtreeCoord.level].contains(key)) {
1832 return componentState->states[tileset].subtreeCache[subtreeCoord.level][key];
1840 std::vector<std::string> urls;
1841 urls.reserve(componentState->subTilesetRequests.size());
1842 for (
const auto& [URL, tile] : componentState->subTilesetRequests) {
1843 const uint64_t tilesetGUID =
Cogs::hash(URL);
1844 if (!componentState->tilesetFetchIds.contains(tilesetGUID)) {
1845 urls.emplace_back(URL);
1849 for (
size_t i = 0; i < urls.size(); ++i) {
1850 const std::string url = addOptionalURLParameters(componentState, urls[i]);
1855 const uint64_t tilesetGUID =
Cogs::hash(url);
1856 componentState->tilesetFetchIds[tilesetGUID] = Cogs::Core::OGC3DTilesTileset::fetch(
context, url,
1857 [thisp=
this, component, url, parentTileset, tilesetGUID, uniqueComponentId=componentState->uniqueComponentId](
OGC3DTilesTileset::Tileset* tileset) {
1858 if (OGC3DTilesSystem::componentIsStale(thisp->context, uniqueComponentId)) {
1866 componentState->tilesets.push_back(tileset);
1868 componentState->states[parentTileset].subTilesets[url] = tileset;
1872 size_t offset = componentState->baseURL.size() + 1;
1873 tileset->baseURL = url.substr(offset, url.find_last_of(
"/") - offset);
1875 if (tileset->root.useImplicitTiling) {
1876 tileset->root.implicitTiling.subTreeURLScheme = tileset->baseURL +
"/" + tileset->root.implicitTiling.subTreeURLScheme;
1880 tileset->root.transform = tileset->root.transform * parentTileset->root.transform;
1883 if (tileset->root.useImplicitTiling) {
1884 LOG_DEBUG(logger,
" Sub-tileset tree-structure is of type '%s'", tileset->root.implicitTiling.isOctTree ?
"OCT" :
"QUAD");
1886 thisp->requestSubtree(componentState, tileset, rootCoord);
1890 LOG_ERROR(logger,
"Could not parse incoming Sub-tileset from '%s'", url.c_str());
1891 assert(tileset &&
"Internal error. Failed loading and parsing incoming tileset");
1895 componentState->subTilesetRequests.erase(url);
1896 componentState->tilesetFetchIds.erase(tilesetGUID);
1897 componentState->loadedSubTilesets.insert(url);
1906 assert(tileset->root.useImplicitTiling &&
"Only IMPLICIT trees uses subtrees");
1909 std::vector<std::string> urls;
1910 urls.reserve(componentState->states[tileset].subtreeRequests.size());
1911 for (
const auto& [subtreeURL, coord] : componentState->states[tileset].subtreeRequests) {
1912 const uint64_t subtreeGUID =
Cogs::hash(subtreeURL);
1913 if (!componentState->states[tileset].subtreeFetchIds.contains(subtreeGUID)) {
1914 urls.emplace_back(subtreeURL);
1918 for (
size_t i=0; i<urls.size(); ++i) {
1919 const std::string subtreeURL = urls[i];
1920 const OGC3DTiles::Coord subtreeCoord = componentState->states[tileset].subtreeRequests[subtreeURL];
1922 const uint64_t subtreeGUID =
Cogs::hash(subtreeURL);
1923 componentState->states[tileset].subtreeFetchIds[subtreeGUID] = OGC3DTilesSubtree::fetchSubtree(this->
context, subtreeURL, componentState->uniqueComponentId,
1924 [thisp=
this, component, subtreeCoord, subtreeURL, tileset, uniqueComponentId = componentState->uniqueComponentId](
OGC3DTilesSubtree::Subtree* subtree) {
1925 if (OGC3DTilesSystem::componentIsStale(thisp->context, uniqueComponentId)) {
1932 assert(componentState->states.contains(tileset) &&
"The tileset is not a part of the component state!");
1934 if (subtree !=
nullptr) {
1935 subtree->globalCoord = subtreeCoord;
1936 subtree->levelsPerSubtree = tileset->root.implicitTiling.subtreeLevels;
1939 assert(!componentState->states[tileset].subtreeCache[subtree->globalCoord.level].contains(key) &&
"Subtree is already loaded!");
1940 componentState->states[tileset].subtreeCache[subtree->globalCoord.level][key] = subtree;
1943 LOG_ERROR(logger,
"Failed to load subtree '%s'", subtreeCoord.toString().c_str());
1949 thisp->context->engine->setDirty();
1962 if (componentState->states[tileset].subtreeRequests.size() > MAX_NUM_ACTIVE_SUBTREES_REQUESTS) {
1966 const std::string subtreeURL = OGC3DTilesSystem::buildSubtreeURL(componentState, tileset, coord);
1967 const std::string fullURL = this->addOptionalURLParameters(componentState, subtreeURL);
1970 if (!componentState->states[tileset].subtreeCache[coord.level].contains(key) &&
1971 !componentState->states[tileset].subtreeRequests.contains(fullURL)) {
1972 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.
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 setVertexData(Element *elements, size_t count)
Set vertex data.
void addSubMesh(std::span< uint32_t > collection, PrimitiveType::EPrimitiveType primitiveType)
Add a sub-mesh to the Mesh.
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
RenderResource * getAttachedResource() const
Get the attached resource.
bool hasAttachedResource() const
Check if the resource has an attachment.
Runtime control variable.