diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ee5f04c3..fd4c39250 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -233,7 +233,9 @@ if(MSVC) _UNICODE # tell Windows to use the unicode version of string functions, as opposed to ASCII (https://docs.microsoft.com/en-us/windows/win32/learnwin32/working-with-strings) WIN32_LEAN_AND_MEAN # ignore some unnecessary Windows APIs NOMINMAX # don't let Windows create macros for MIN and MAX - _SILENCE_CXX17_RESULT_OF_DEPRECATION_WARNING) + _SILENCE_CXX17_RESULT_OF_DEPRECATION_WARNING + _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR # See https://developercommunity.visualstudio.com/t/Visual-Studio-17100-Update-leads-to-Pr/10669759?sort=newest + ) endif() if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") @@ -364,6 +366,7 @@ set(CESIUM_OMNI_CXX_DEFINES GLM_FORCE_XYZW_ONLY # Disable .rgba and .stpq to make it easier to view values from debugger GLM_FORCE_EXPLICIT_CTOR # Disallow implicit conversions between dvec3 <-> dvec4, dvec3 <-> fvec3, etc GLM_FORCE_SIZE_T_LENGTH # Make vec.length() and vec[idx] use size_t instead of int + GLM_ENABLE_EXPERIMENTAL ) # Boost is a dependency of USD. It is dynamically linked so we need to set this flag. diff --git a/cmake/conan-0.17.0.cmake b/cmake/conan-0.17.0.cmake index d16f705af..96315021b 100644 --- a/cmake/conan-0.17.0.cmake +++ b/cmake/conan-0.17.0.cmake @@ -55,7 +55,9 @@ function(_get_msvc_ide_version result) set(${result} 15 PARENT_SCOPE) elseif(NOT MSVC_VERSION VERSION_LESS 1920 AND MSVC_VERSION VERSION_LESS 1930) set(${result} 16 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1930 AND MSVC_VERSION VERSION_LESS 1940) + # Omniverse modification - 1940 was changed to 1945 + # This version of the conan-cmake script does not properly account for newer versions of MSVC + elseif(NOT MSVC_VERSION VERSION_LESS 1930 AND MSVC_VERSION VERSION_LESS 1945) set(${result} 17 PARENT_SCOPE) else() message(FATAL_ERROR "Conan: Unknown MSVC compiler version [${MSVC_VERSION}]") diff --git a/extern/cesium-native b/extern/cesium-native index 8fbaee154..1be4d4bf4 160000 --- a/extern/cesium-native +++ b/extern/cesium-native @@ -1 +1 @@ -Subproject commit 8fbaee15432da5a646e23161c9c9224278e5d014 +Subproject commit 1be4d4bf4379ecbd4ce7c2dde3c7fd36ce8d7499 diff --git a/src/core/include/cesium/omniverse/CesiumIonSession.h b/src/core/include/cesium/omniverse/CesiumIonSession.h index 8686df7fb..c6ce14bb8 100644 --- a/src/core/include/cesium/omniverse/CesiumIonSession.h +++ b/src/core/include/cesium/omniverse/CesiumIonSession.h @@ -1,7 +1,10 @@ #pragma once +#include "cesium/omniverse/OmniIonServer.h" + #include #include +#include #include #include @@ -37,6 +40,9 @@ class CesiumIonSession { CesiumAsync::AsyncSystem& getAsyncSystem() { return this->_asyncSystem; } + [[nodiscard]] const std::optional& getApplicationData() const { + return this->_appData; + } [[nodiscard]] bool isConnected() const { return this->_connection.has_value(); @@ -107,10 +113,19 @@ class CesiumIonSession { [[nodiscard]] CesiumAsync::Future> findToken(const std::string& token) const; + /** + * If the {@link _appData} field has no value, this method will request the + * ion server's /appData endpoint to obtain its data. + * @returns A future that resolves to true if _appData is present or false if + * it couldn't be fetched. + */ + CesiumAsync::Future ensureAppDataLoaded(); + private: CesiumAsync::AsyncSystem _asyncSystem; std::shared_ptr _pAssetAccessor; + std::optional _appData; std::optional _connection; std::optional _profile; std::optional _assets; diff --git a/src/core/include/cesium/omniverse/CesiumIonSessionManager.h b/src/core/include/cesium/omniverse/CesiumIonSessionManager.h new file mode 100644 index 000000000..456c80c84 --- /dev/null +++ b/src/core/include/cesium/omniverse/CesiumIonSessionManager.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +namespace cesium::omniverse { + +class CesiumIonSession; +class Context; + +class CesiumIonSessionManager { + public: + CesiumIonSessionManager(Context* pContext); + ~CesiumIonSessionManager() = default; + CesiumIonSessionManager(const CesiumIonSessionManager&) = delete; + CesiumIonSessionManager& operator=(const CesiumIonSessionManager&) = delete; + CesiumIonSessionManager(CesiumIonSessionManager&&) noexcept = delete; + CesiumIonSessionManager& operator=(CesiumIonSessionManager&&) noexcept = delete; + + std::shared_ptr + getOrCreateSession(const std::string& ionServerUrl, const std::string& ionServerApiUrl, int64_t applicationId); + + private: + std::unordered_map> _sessions; + Context* _pContext; +}; + +} // namespace cesium::omniverse diff --git a/src/core/include/cesium/omniverse/Context.h b/src/core/include/cesium/omniverse/Context.h index 1f2c5a993..6287f2390 100644 --- a/src/core/include/cesium/omniverse/Context.h +++ b/src/core/include/cesium/omniverse/Context.h @@ -26,6 +26,7 @@ namespace cesium::omniverse { class AssetRegistry; class CesiumIonServerManager; +class CesiumIonSessionManager; class FabricResourceManager; class Logger; class TaskProcessor; @@ -55,6 +56,8 @@ class Context { [[nodiscard]] AssetRegistry& getAssetRegistry(); [[nodiscard]] const FabricResourceManager& getFabricResourceManager() const; [[nodiscard]] FabricResourceManager& getFabricResourceManager(); + [[nodiscard]] const CesiumIonSessionManager& getCesiumIonSessionManager() const; + [[nodiscard]] CesiumIonSessionManager& getCesiumIonSessionManager(); [[nodiscard]] const CesiumIonServerManager& getCesiumIonServerManager() const; [[nodiscard]] CesiumIonServerManager& getCesiumIonServerManager(); @@ -88,6 +91,7 @@ class Context { std::shared_ptr _pCreditSystem; std::unique_ptr _pAssetRegistry; std::unique_ptr _pFabricResourceManager; + std::unique_ptr _pCesiumIonSessionManager; std::unique_ptr _pCesiumIonServerManager; std::unique_ptr _pUsdNotificationHandler; diff --git a/src/core/include/cesium/omniverse/MetadataUtil.h b/src/core/include/cesium/omniverse/MetadataUtil.h index fefce6cc5..bf064f8c2 100644 --- a/src/core/include/cesium/omniverse/MetadataUtil.h +++ b/src/core/include/cesium/omniverse/MetadataUtil.h @@ -227,13 +227,14 @@ void forEachPropertyTableProperty( for (uint64_t i = 0; i < pMeshFeatures->featureIds.size(); ++i) { const auto featureIdSetIndex = i; - const auto& featureId = pMeshFeatures->featureIds[featureIdSetIndex]; - if (featureId.propertyTable.has_value()) { - const auto pPropertyTable = model.getSafe( - &pStructuralMetadataModel->propertyTables, static_cast(featureId.propertyTable.value())); + const CesiumGltf::FeatureId& featureId = pMeshFeatures->featureIds[featureIdSetIndex]; + + if (featureId.propertyTable != -1) { + const auto pPropertyTable = + model.getSafe(&pStructuralMetadataModel->propertyTables, static_cast(featureId.propertyTable)); if (!pPropertyTable) { context.getLogger()->warn( - fmt::format("Property table index {} is out of range.", featureId.propertyTable.value())); + fmt::format("Property table index {} is out of range.", featureId.propertyTable)); continue; } diff --git a/src/core/include/cesium/omniverse/OmniIonServer.h b/src/core/include/cesium/omniverse/OmniIonServer.h index 674581bcd..6e8955e2b 100644 --- a/src/core/include/cesium/omniverse/OmniIonServer.h +++ b/src/core/include/cesium/omniverse/OmniIonServer.h @@ -33,6 +33,5 @@ class OmniIonServer { private: Context* _pContext; pxr::SdfPath _path; - std::shared_ptr _session; }; } // namespace cesium::omniverse diff --git a/src/core/src/CesiumIonServerManager.cpp b/src/core/src/CesiumIonServerManager.cpp index 1ee155990..516cbd88a 100644 --- a/src/core/src/CesiumIonServerManager.cpp +++ b/src/core/src/CesiumIonServerManager.cpp @@ -53,8 +53,8 @@ void CesiumIonServerManager::updateTokenTroubleshootingDetails( details.showDetails = true; - const auto pConnection = - std::make_shared(pSession->getAsyncSystem(), pSession->getAssetAccessor(), token); + const auto pConnection = std::make_shared( + pSession->getAsyncSystem(), pSession->getAssetAccessor(), token, pSession->getApplicationData().value()); pConnection->me() .thenInMainThread( diff --git a/src/core/src/CesiumIonSession.cpp b/src/core/src/CesiumIonSession.cpp index 0bd0e4cd3..a4a7fce0d 100644 --- a/src/core/src/CesiumIonSession.cpp +++ b/src/core/src/CesiumIonSession.cpp @@ -5,6 +5,7 @@ #include "cesium/omniverse/Broadcast.h" #include "cesium/omniverse/SettingsWrapper.h" +#include #include #include @@ -46,19 +47,51 @@ void CesiumIonSession::connect() { this->_isConnecting = true; - Connection::authorize( - this->_asyncSystem, - this->_pAssetAccessor, - "Cesium for Omniverse", - _ionApplicationId, - "/cesium-for-omniverse/oauth2/callback", - {"assets:list", "assets:read", "profile:read", "tokens:read", "tokens:write", "geocode"}, - [this](const std::string& url) { - // NOTE: We open the browser in the Python code. Check in the sign in widget's on_update_frame function. - this->_authorizeUrl = url; - }, - _ionApiUrl, - CesiumUtility::Uri::resolve(_ionServerUrl, "oauth")) + CesiumAsync::Future> futureApiUrl = + !_ionApiUrl.empty() + ? this->_asyncSystem.createResolvedFuture>(_ionApiUrl) + : CesiumIonClient::Connection::getApiUrl(this->_asyncSystem, this->_pAssetAccessor, _ionServerUrl); + + std::move(futureApiUrl) + .thenInMainThread([this](std::optional&& ionApiUrl) { + CesiumAsync::Promise promise = this->_asyncSystem.createPromise(); + + if (_ionApiUrl.empty()) { + _ionApiUrl = ionApiUrl.value(); + } + + // Make request to /appData to learn the server's authentication mode + return this->ensureAppDataLoaded(); + }) + .thenInMainThread([this](bool loadedAppData) { + if (!loadedAppData || !this->_appData.has_value()) { + CesiumAsync::Promise promise = + this->_asyncSystem.createPromise(); + + promise.reject(std::runtime_error("Failed to load _appData, can't create connection")); + return promise.getFuture(); + } + + if (this->_appData->needsOauthAuthentication()) { + return CesiumIonClient::Connection::authorize( + this->_asyncSystem, + this->_pAssetAccessor, + "Cesium for Omniverse", + _ionApplicationId, + "/cesium-for-omniverse/oauth2/callback", + {"assets:list", "assets:read", "profile:read", "tokens:read", "tokens:write", "geocode"}, + [this](const std::string& url) { + // NOTE: We open the browser in the Python code. Check in the sign in widget's on_update_frame function. + this->_authorizeUrl = url; + }, + this->_appData.value(), + _ionApiUrl, + CesiumUtility::Uri::resolve(_ionServerUrl, "oauth")); + } + + return this->_asyncSystem.createResolvedFuture(CesiumIonClient::Connection( + this->_asyncSystem, this->_pAssetAccessor, "", this->_appData.value(), _ionServerUrl)); + }) .thenInMainThread([this](CesiumIonClient::Connection&& connection) { this->_isConnecting = false; this->_connection = std::move(connection); @@ -105,17 +138,38 @@ void CesiumIonSession::resume() { this->_isResuming = true; - this->_connection = Connection(this->_asyncSystem, this->_pAssetAccessor, accessToken); - // Verify that the connection actually works. - this->_connection.value() - .me() - .thenInMainThread([this](Response&& response) { - if (!response.value.has_value()) { - this->_connection.reset(); + this->ensureAppDataLoaded() + .thenInMainThread([this, accessToken](bool loadedAppData) { + CesiumAsync::Promise promise = this->_asyncSystem.createPromise(); + + if (!loadedAppData || !this->_appData.has_value()) { + promise.reject(std::runtime_error("Failed to obtain _appData, can't resume connection")); + return promise.getFuture(); } - this->_isResuming = false; - Broadcast::connectionUpdated(); + + if (this->_appData->needsOauthAuthentication() && accessToken.empty()) { + // No user access token was stored, so there's no existing session to resume. + promise.resolve(); + this->_isResuming = false; + return promise.getFuture(); + } + + std::shared_ptr pConnection = std::make_shared( + this->_asyncSystem, this->_pAssetAccessor, accessToken, this->_appData.value(), _ionApiUrl); + + return pConnection->me().thenInMainThread( + [this, pConnection](CesiumIonClient::Response&& response) { + if (!response.value.has_value()) { + this->_connection.reset(); + } + this->_isResuming = false; + Broadcast::connectionUpdated(); + // logResponseErrors(response); + if (response.value.has_value()) { + this->_connection = std::move(*pConnection); + } + }); }) .catchInMainThread([this]([[maybe_unused]] std::exception&& e) { this->_isResuming = false; @@ -280,3 +334,22 @@ Future> CesiumIonSession::findToken(const std::string& token) co return this->_connection->token(*maybeTokenID); } + +CesiumAsync::Future CesiumIonSession::ensureAppDataLoaded() { + + return CesiumIonClient::Connection::appData(this->_asyncSystem, this->_pAssetAccessor, this->_ionApiUrl) + .thenInMainThread([this](CesiumIonClient::Response&& applicationData) { + CesiumAsync::Promise promise = this->_asyncSystem.createPromise(); + + this->_appData = applicationData.value; + if (!applicationData.value.has_value()) { + promise.resolve(false); + } else { + promise.resolve(true); + } + + return promise.getFuture(); + }) + .catchInMainThread( + [this]([[maybe_unused]] std::exception&& e) { return this->_asyncSystem.createResolvedFuture(false); }); +} \ No newline at end of file diff --git a/src/core/src/CesiumIonSessionManager.cpp b/src/core/src/CesiumIonSessionManager.cpp new file mode 100644 index 000000000..caea1fc6c --- /dev/null +++ b/src/core/src/CesiumIonSessionManager.cpp @@ -0,0 +1,29 @@ +#include "cesium/omniverse/CesiumIonSessionManager.h" + +#include "cesium/omniverse/CesiumIonSession.h" +#include "cesium/omniverse/Context.h" + +namespace cesium::omniverse { + +CesiumIonSessionManager::CesiumIonSessionManager(Context* pContext) + : _pContext(pContext) {} + +std::shared_ptr CesiumIonSessionManager::getOrCreateSession( + const std::string& ionServerUrl, + const std::string& ionServerApiUrl, + int64_t applicationId) { + const auto key = ionServerUrl + ionServerApiUrl + std::to_string(applicationId); + const auto foundIter = _sessions.find(key); + if (foundIter != _sessions.end()) { + return foundIter->second; + } + + auto pSession = std::make_shared( + _pContext->getAsyncSystem(), _pContext->getAssetAccessor(), ionServerUrl, ionServerApiUrl, applicationId); + + _sessions.emplace(key, pSession); + + return pSession; +} + +} // namespace cesium::omniverse diff --git a/src/core/src/Context.cpp b/src/core/src/Context.cpp index 5d33385fc..4093251c9 100644 --- a/src/core/src/Context.cpp +++ b/src/core/src/Context.cpp @@ -2,6 +2,7 @@ #include "cesium/omniverse/AssetRegistry.h" #include "cesium/omniverse/CesiumIonServerManager.h" +#include "cesium/omniverse/CesiumIonSessionManager.h" #include "cesium/omniverse/FabricResourceManager.h" #include "cesium/omniverse/FabricStatistics.h" #include "cesium/omniverse/FabricUtil.h" @@ -78,6 +79,7 @@ Context::Context(const std::filesystem::path& cesiumExtensionLocation) , _pCreditSystem(std::make_shared()) , _pAssetRegistry(std::make_unique(this)) , _pFabricResourceManager(std::make_unique(this)) + , _pCesiumIonSessionManager(std::make_unique(this)) , _pCesiumIonServerManager(std::make_unique(this)) , _pUsdNotificationHandler(std::make_unique(this)) , _contextId(static_cast(getSecondsSinceEpoch())) { @@ -152,6 +154,14 @@ FabricResourceManager& Context::getFabricResourceManager() { return *_pFabricResourceManager.get(); } +const CesiumIonSessionManager& Context::getCesiumIonSessionManager() const { + return *_pCesiumIonSessionManager.get(); +} + +CesiumIonSessionManager& Context::getCesiumIonSessionManager() { + return *_pCesiumIonSessionManager.get(); +} + const CesiumIonServerManager& Context::getCesiumIonServerManager() const { return *_pCesiumIonServerManager.get(); } diff --git a/src/core/src/OmniIonServer.cpp b/src/core/src/OmniIonServer.cpp index 10861b3fc..7755e9bfb 100644 --- a/src/core/src/OmniIonServer.cpp +++ b/src/core/src/OmniIonServer.cpp @@ -1,6 +1,7 @@ #include "cesium/omniverse/OmniIonServer.h" #include "cesium/omniverse/CesiumIonSession.h" +#include "cesium/omniverse/CesiumIonSessionManager.h" #include "cesium/omniverse/Context.h" #include "cesium/omniverse/UsdUtil.h" @@ -10,14 +11,8 @@ namespace cesium::omniverse { OmniIonServer::OmniIonServer(Context* pContext, const pxr::SdfPath& path) : _pContext(pContext) - , _path(path) - , _session(std::make_shared( - pContext->getAsyncSystem(), - pContext->getAssetAccessor(), - getIonServerUrl(), - getIonServerApiUrl(), - getIonServerApplicationId())) { - _session->resume(); + , _path(path) { + getSession()->resume(); } const pxr::SdfPath& OmniIonServer::getPath() const { @@ -25,7 +20,8 @@ const pxr::SdfPath& OmniIonServer::getPath() const { } std::shared_ptr OmniIonServer::getSession() const { - return _session; + return _pContext->getCesiumIonSessionManager().getOrCreateSession( + getIonServerUrl(), getIonServerApiUrl(), getIonServerApplicationId()); } std::string OmniIonServer::getIonServerUrl() const {