diff --git a/CMakeLists.txt b/CMakeLists.txt index 6208f90ac1..f36033e90f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,7 +85,9 @@ endif() # modules and definitions #------------------------------------------------------------------------------ include(cmake/utils.cmake) -find_package(Maya 2018 REQUIRED) +if (BUILD_MAYAUSD_LIBRARY) + find_package(Maya 2018 REQUIRED) +endif() if(APPLE AND BUILD_UB2 AND NOT MAYA_MACOSX_BUILT_WITH_UB2) message(WARNING "Maya was NOT built with Universal Binary 2") @@ -117,9 +119,16 @@ message(STATUS " PYTHON_INCLUDE_DIRS = ${PYTHON_INCLUDE_DIRS}") message(STATUS " PYTHON_LIBRARIES = ${PYTHON_LIBRARIES}") message(STATUS " Python_EXECUTABLE = ${Python_EXECUTABLE}") -include(cmake/jinja.cmake) +if (BUILD_MAYAUSD_LIBRARY) + include(cmake/jinja.cmake) +endif() find_package(USD 0.21.11 REQUIRED) +if (BUILD_MAYAUSD_LIBRARY OR BUILD_AL_PLUGIN OR BUILD_TESTS) + if(NOT USD_GENSCHEMA) + message(FATAL_ERROR "Building MayaUsd requires usdGenSchema (from USD)") + endif() +endif() if (CMAKE_WANT_MATERIALX_BUILD) # Requires at least USD 21.08 for hdMtlx module and USD must have been built with MaterialX: if(NOT TARGET hdMtlx) @@ -129,11 +138,13 @@ if (CMAKE_WANT_MATERIALX_BUILD) endif() include(cmake/usd.cmake) -if(${MAYA_APP_VERSION} STRLESS "2019" AND CMAKE_WANT_UFE_BUILD) - set(CMAKE_WANT_UFE_BUILD OFF) - message(AUTHOR_WARNING "========================================================================================= \ - UFE is not available in Maya${MAYA_MAJOR_VERSION}. Maya2019 and later are supported only. \ - ========================================================================================= ") +if(DEFINED MAYA_APP_VERSION) + if (${MAYA_APP_VERSION} STRLESS "2019" AND CMAKE_WANT_UFE_BUILD) + set(CMAKE_WANT_UFE_BUILD OFF) + message(AUTHOR_WARNING "========================================================================================= \ + UFE is not available in Maya${MAYA_MAJOR_VERSION}. Maya2019 and later are supported only. \ + ========================================================================================= ") + endif() endif() if(CMAKE_WANT_UFE_BUILD) @@ -163,15 +174,16 @@ if(DEFINED QT_LOCATION) if(BUILD_WITH_QT_6) set(QT_VERSION "6.5") message(STATUS "Setting Qt version to Qt ${QT_VERSION}") - elseif(${MAYA_APP_VERSION} STRLESS_EQUAL "2019") - set(QT_VERSION "5.6") - message(STATUS "Setting Qt version to Qt ${QT_VERSION}") - elseif(${MAYA_APP_VERSION} STRLESS_EQUAL "2020") - set(QT_VERSION "5.12") - message(STATUS "Setting Qt version to Qt ${QT_VERSION}") - else() # 2022, 2023, 2024 - set(QT_VERSION "5.15") - message(STATUS "Setting Qt version to Qt ${QT_VERSION}") + else() + set(QT_VERSION "5.15") # default version Maya 2022/2023/2024 + if(DEFINED MAYA_APP_VERSION) + if(${MAYA_APP_VERSION} STRLESS_EQUAL "2019") + set(QT_VERSION "5.6") + elseif(${MAYA_APP_VERSION} STRLESS_EQUAL "2020") + set(QT_VERSION "5.12") + endif() + message(STATUS "Setting Qt version to Qt ${QT_VERSION}") + endif() endif() endif() set(CMAKE_PREFIX_PATH "${QT_LOCATION}") @@ -198,7 +210,9 @@ include(cmake/compiler_config.cmake) #------------------------------------------------------------------------------ # gulrak filesystem #------------------------------------------------------------------------------ -include(cmake/gulrak.cmake) +if (BUILD_MAYAUSD_LIBRARY OR BUILD_AL_PLUGIN) + include(cmake/gulrak.cmake) +endif() #------------------------------------------------------------------------------ # test @@ -215,9 +229,7 @@ endif() #------------------------------------------------------------------------------ # lib #------------------------------------------------------------------------------ -if (BUILD_MAYAUSD_LIBRARY) - add_subdirectory(lib) -endif() +add_subdirectory(lib) #------------------------------------------------------------------------------ # plugins diff --git a/cmake/modules/FindUSD.cmake b/cmake/modules/FindUSD.cmake index 33281f9caa..1030b1652b 100644 --- a/cmake/modules/FindUSD.cmake +++ b/cmake/modules/FindUSD.cmake @@ -19,6 +19,7 @@ find_path(USD_INCLUDE_DIR "USD Include directory" ) +# This component is optional. find_file(USD_GENSCHEMA NAMES usdGenSchema @@ -142,14 +143,6 @@ if (USD_LIBRARY_DIR AND EXISTS "${USD_LIBRARY_DIR}/${USD_LIB_PREFIX}usdMtlx${CMA endif() endif() -message(STATUS "USD include dir: ${USD_INCLUDE_DIR}") -message(STATUS "USD library dir: ${USD_LIBRARY_DIR}") -message(STATUS "USD version: ${USD_VERSION}") -message(STATUS "Autodesk USD version: ${ADSK_USD_VERSION}") -if(DEFINED USD_BOOST_VERSION) - message(STATUS "USD Boost::boost version: ${USD_BOOST_VERSION}") -endif() - include(FindPackageHandleStandardArgs) find_package_handle_standard_args(USD @@ -157,7 +150,6 @@ find_package_handle_standard_args(USD PXR_USD_LOCATION USD_INCLUDE_DIR USD_LIBRARY_DIR - USD_GENSCHEMA USD_CONFIG_FILE USD_VERSION ADSK_USD_VERSION @@ -165,3 +157,17 @@ find_package_handle_standard_args(USD VERSION_VAR USD_VERSION ) + +if (USD_FOUND) + # This will follow a message "-- Found USD: ..." + message(STATUS " USD include dir: ${USD_INCLUDE_DIR}") + message(STATUS " USD library dir: ${USD_LIBRARY_DIR}") + if (USD_GENSCHEMA) + message(STATUS " usdGenSchema: ${USD_GENSCHEMA}") + endif() + message(STATUS " USD version: ${USD_VERSION}") + message(STATUS " Autodesk USD version: ${ADSK_USD_VERSION}") + if(DEFINED USD_BOOST_VERSION) + message(STATUS " USD Boost::boost version: ${USD_BOOST_VERSION}") + endif() +endif() diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index dda39e058a..b0507bdc59 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -2,5 +2,7 @@ if(UFE_FOUND) add_subdirectory(usdUfe) endif() -add_subdirectory(mayaUsd) -add_subdirectory(usd) +if (BUILD_MAYAUSD_LIBRARY) + add_subdirectory(mayaUsd) + add_subdirectory(usd) +endif() diff --git a/lib/mayaUsd/fileio/primUpdaterManager.cpp b/lib/mayaUsd/fileio/primUpdaterManager.cpp index 36cea9a854..efef508d08 100644 --- a/lib/mayaUsd/fileio/primUpdaterManager.cpp +++ b/lib/mayaUsd/fileio/primUpdaterManager.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -37,6 +36,7 @@ #include #include +#include #include #include @@ -618,7 +618,7 @@ UsdMayaPrimUpdaterSharedPtr createUpdater( // path to form a proper UFE path. auto psPath = MayaUsd::ufe::stagePath(context.GetUsdStage()); Ufe::Path::Segments segments { psPath.getSegments()[0], - MayaUsd::ufe::usdPathToUfePathSegment(dstPath) }; + UsdUfe::usdPathToUfePathSegment(dstPath) }; Ufe::Path ufePath(std::move(segments)); // Get the Maya object corresponding to the SdfPath. As of 19-Oct-2021, @@ -1596,7 +1596,7 @@ void PrimUpdaterManager::onProxyContentChanged( != UsdMayaPrimUpdater::Supports::AutoPull) return false; - const Ufe::PathSegment pathSegment = MayaUsd::ufe::usdPathToUfePathSegment(prim.GetPath()); + const Ufe::PathSegment pathSegment = UsdUfe::usdPathToUfePathSegment(prim.GetPath()); const Ufe::Path path = proxyShapeUfePath + pathSegment; auto factory = std::get(registryItem); diff --git a/lib/mayaUsd/render/vp2RenderDelegate/proxyRenderDelegate.cpp b/lib/mayaUsd/render/vp2RenderDelegate/proxyRenderDelegate.cpp index b6ec254958..fcd5672d24 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/proxyRenderDelegate.cpp +++ b/lib/mayaUsd/render/vp2RenderDelegate/proxyRenderDelegate.cpp @@ -26,6 +26,8 @@ #include #include +#include + #include #include #include @@ -1487,8 +1489,7 @@ bool ProxyRenderDelegate::getInstancedSelectionPath( } } - const Ufe::PathSegment pathSegment - = MayaUsd::ufe::usdPathToUfePathSegment(usdPath, instanceIndex); + const Ufe::PathSegment pathSegment = UsdUfe::usdPathToUfePathSegment(usdPath, instanceIndex); const Ufe::SceneItem::Ptr& si = handler->createItem(_proxyShapeData->ProxyShape()->ufePath() + pathSegment); if (!si) { diff --git a/lib/mayaUsd/sceneIndex/proxyShapeSceneIndexPlugin.cpp b/lib/mayaUsd/sceneIndex/proxyShapeSceneIndexPlugin.cpp index f9b2eef8e0..f6bd4822aa 100644 --- a/lib/mayaUsd/sceneIndex/proxyShapeSceneIndexPlugin.cpp +++ b/lib/mayaUsd/sceneIndex/proxyShapeSceneIndexPlugin.cpp @@ -22,6 +22,8 @@ #include #endif +#include + #include #if defined(HD_API_VERSION) && HD_API_VERSION >= 51 #include @@ -211,8 +213,8 @@ Ufe::Path MayaUsdProxyShapeSceneIndex::InterpretRprimPath( MStatus status; MDagPath dagPath( MDagPath::getAPathTo(proxyShapeSceneIndex->_proxyShape->thisMObject(), &status)); - return Ufe::Path({ MayaUsd::ufe::dagPathToPathSegment(dagPath), - MayaUsd::ufe::usdPathToUfePathSegment(path) }); + return Ufe::Path( + { MayaUsd::ufe::dagPathToPathSegment(dagPath), UsdUfe::usdPathToUfePathSegment(path) }); } return Ufe::Path(); diff --git a/lib/mayaUsd/ufe/CMakeLists.txt b/lib/mayaUsd/ufe/CMakeLists.txt index 5a763aad32..5598594452 100644 --- a/lib/mayaUsd/ufe/CMakeLists.txt +++ b/lib/mayaUsd/ufe/CMakeLists.txt @@ -4,11 +4,11 @@ target_sources(${PROJECT_NAME} PRIVATE Global.cpp + MayaStagesSubject.cpp ProxyShapeHandler.cpp ProxyShapeHierarchy.cpp ProxyShapeHierarchyHandler.cpp SetVariantSelectionCommand.cpp - StagesSubject.cpp UsdPointInstanceOrientationModifier.cpp UsdPointInstancePositionModifier.cpp UsdPointInstanceScaleModifier.cpp @@ -41,8 +41,6 @@ if(CMAKE_UFE_V2_FEATURES_AVAILABLE) UsdAttributeHolder.cpp UsdAttributes.cpp UsdAttributesHandler.cpp - UsdCamera.cpp - UsdCameraHandler.cpp UsdContextOps.cpp UsdContextOpsHandler.cpp UsdSetXformOpUndoableCommandBase.cpp @@ -177,11 +175,11 @@ endif() set(HEADERS Global.h + MayaStagesSubject.h ProxyShapeHandler.h ProxyShapeHierarchy.h ProxyShapeHierarchyHandler.h SetVariantSelectionCommand.h - StagesSubject.h UsdPointInstanceModifierBase.h UsdPointInstanceOrientationModifier.h UsdPointInstancePositionModifier.h @@ -213,8 +211,6 @@ if(CMAKE_UFE_V2_FEATURES_AVAILABLE) UsdAttributeHolder.h UsdAttributes.h UsdAttributesHandler.h - UsdCamera.h - UsdCameraHandler.h UsdContextOps.h UsdContextOpsHandler.h UsdPointInstanceUndoableCommands.h diff --git a/lib/mayaUsd/ufe/Global.cpp b/lib/mayaUsd/ufe/Global.cpp index 6950ec9087..49dbaac8af 100644 --- a/lib/mayaUsd/ufe/Global.cpp +++ b/lib/mayaUsd/ufe/Global.cpp @@ -17,9 +17,9 @@ #include "private/UfeNotifGuard.h" +#include #include #include -#include #include #include #include @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -145,9 +144,6 @@ Ufe::PathMappingHandler::Ptr g_MayaPathMappingHandler; Ufe::UIInfoHandler::Ptr g_MayaUIInfoHandler; #endif -// Subject singleton for observation of all USD stages. -StagesSubject::Ptr g_StagesSubject; - //------------------------------------------------------------------------------ // Functions //------------------------------------------------------------------------------ @@ -162,6 +158,8 @@ MStatus initialize() // Set the Maya specific functions required for the UsdUfe plugin to work correctly. UsdUfe::DCCFunctions dccFunctions; + dccFunctions.stageAccessorFn = MayaUsd::ufe::getStage; + dccFunctions.stagePathAccessorFn = MayaUsd::ufe::stagePath; dccFunctions.ufePathToPrimFn = MayaUsd::ufe::ufePathToPrim; dccFunctions.timeAccessorFn = MayaUsd::ufe::getTime; dccFunctions.isAttributeLockedFn = MayaUsd::Editability::isAttributeLocked; @@ -203,7 +201,6 @@ MStatus initialize() usdUfeHandlers.object3dHandler = MayaUsdObject3dHandler::create(); handlers.contextOpsHandler = UsdContextOpsHandler::create(); handlers.uiInfoHandler = UsdUIInfoHandler::create(); - handlers.cameraHandler = UsdCameraHandler::create(); #ifdef UFE_V4_FEATURES_AVAILABLE @@ -269,7 +266,8 @@ MStatus initialize() // Initialize UsdUfe which will register all the default handlers // and the overrides we provide. - auto usdRtid = UsdUfe::initialize(dccFunctions, usdUfeHandlers); + auto ss = MayaStagesSubject::create(); + auto usdRtid = UsdUfe::initialize(dccFunctions, usdUfeHandlers, ss); // TEMP (UsdUfe) // Can only call Ufe::RunTimeMgr::register_() once for a given runtime name. @@ -287,8 +285,6 @@ MStatus initialize() runTimeMgr.setContextOpsHandler(usdRtid, handlers.contextOpsHandler); if (handlers.uiInfoHandler) runTimeMgr.setUIInfoHandler(usdRtid, handlers.uiInfoHandler); - if (handlers.cameraHandler) - runTimeMgr.setCameraHandler(usdRtid, handlers.cameraHandler); #ifdef UFE_V4_FEATURES_AVAILABLE if (handlers.lightHandler) runTimeMgr.setLightHandler(usdRtid, handlers.lightHandler); @@ -336,8 +332,6 @@ MStatus initialize() if (usdRtid == 0) return MS::kFailure; - g_StagesSubject = StagesSubject::create(); - // Register for UFE string to path service using path component separator '/' UFE_V2(Ufe::PathString::registerPathComponentSeparator(usdRtid, '/');) @@ -389,8 +383,6 @@ MStatus finalize(bool exiting) g_MayaUIInfoHandler.reset(); #endif - g_StagesSubject.Reset(); - MMessage::removeCallback(gExitingCbId); return MS::kSuccess; diff --git a/lib/mayaUsd/ufe/MayaStagesSubject.cpp b/lib/mayaUsd/ufe/MayaStagesSubject.cpp new file mode 100644 index 0000000000..60f0a77531 --- /dev/null +++ b/lib/mayaUsd/ufe/MayaStagesSubject.cpp @@ -0,0 +1,214 @@ +// +// Copyright 2023 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "MayaStagesSubject.h" + +#include +#include +#include + +#include + +#include +#include +#include + +namespace { + +// Prevent re-entrant stage set. +std::atomic_bool stageSetGuardCount { false }; + +} // namespace + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +//------------------------------------------------------------------------------ +// Global variables & macros +//------------------------------------------------------------------------------ +extern UsdStageMap g_StageMap; + +//------------------------------------------------------------------------------ +// MayaStagesSubject +//------------------------------------------------------------------------------ + +MayaStagesSubject::MayaStagesSubject() +{ + // Workaround to MAYA-65920: at startup, MSceneMessage.kAfterNew file + // callback is incorrectly called by Maya before the + // MSceneMessage.kBeforeNew file callback, which should be illegal. + // Detect this and ignore illegal calls to after new file callbacks. + // PPT, 19-Jan-16. + fBeforeNewCallback = false; + + MStatus res; + fCbIds.append( + MSceneMessage::addCallback(MSceneMessage::kBeforeNew, beforeNewCallback, this, &res)); + CHECK_MSTATUS(res); + fCbIds.append( + MSceneMessage::addCallback(MSceneMessage::kBeforeOpen, beforeOpenCallback, this, &res)); + CHECK_MSTATUS(res); + fCbIds.append( + MSceneMessage::addCallback(MSceneMessage::kAfterOpen, afterOpenCallback, this, &res)); + CHECK_MSTATUS(res); + fCbIds.append( + MSceneMessage::addCallback(MSceneMessage::kAfterNew, afterNewCallback, this, &res)); + CHECK_MSTATUS(res); + + TfWeakPtr me(this); + TfNotice::Register(me, &MayaStagesSubject::onStageSet); + TfNotice::Register(me, &MayaStagesSubject::onStageInvalidate); +} + +MayaStagesSubject::~MayaStagesSubject() +{ + MMessage::removeCallbacks(fCbIds); + fCbIds.clear(); +} + +/*static*/ +MayaStagesSubject::Ptr MayaStagesSubject::create() +{ + return TfCreateWeakPtr(new MayaStagesSubject); +} + +bool MayaStagesSubject::beforeNewCallback() const { return fBeforeNewCallback; } + +void MayaStagesSubject::beforeNewCallback(bool b) +{ + fBeforeNewCallback = b; + fInvalidStages.clear(); +} + +/*static*/ +void MayaStagesSubject::beforeNewCallback(void* clientData) +{ + MayaStagesSubject* ss = static_cast(clientData); + ss->beforeNewCallback(true); +} + +/*static*/ +void MayaStagesSubject::beforeOpenCallback(void* clientData) +{ + MayaStagesSubject::beforeNewCallback(clientData); +} + +/*static*/ +void MayaStagesSubject::afterNewCallback(void* clientData) +{ + MayaStagesSubject* ss = static_cast(clientData); + + // Workaround to MAYA-65920: detect and avoid illegal callback sequence. + if (!ss->beforeNewCallback()) + return; + + ss->beforeNewCallback(false); + MayaStagesSubject::afterOpenCallback(clientData); +} + +/*static*/ +void MayaStagesSubject::afterOpenCallback(void* clientData) +{ + MayaStagesSubject* ss = static_cast(clientData); + ss->afterOpen(); +} + +void MayaStagesSubject::afterOpen() +{ + // Observe stage changes, for all stages. Return listener object can + // optionally be used to call Revoke() to remove observation, but must + // keep reference to it, otherwise its reference count is immediately + // decremented, falls to zero, and no observation occurs. + + // Ideally, we would observe the data model only if there are observers, + // to minimize cost of observation. However, since observation is + // frequent, we won't implement this for now. PPT, 22-Dec-2017. + std::for_each( + std::begin(fStageListeners), + std::end(fStageListeners), + [](StageListenerMap::value_type element) { + for (auto& noticeKey : element.second) { + TfNotice::Revoke(noticeKey); + } + }); + fStageListeners.clear(); + + // Set up our stage to proxy shape UFE path (and reverse) + // mapping. We do this with the following steps: + // - get all proxyShape nodes in the scene. + // - get their Dag paths. + // - convert the Dag paths to UFE paths. + // - get their stage. + g_StageMap.setDirty(); +} + +void MayaStagesSubject::onStageSet(const MayaUsdProxyStageSetNotice& notice) +{ + auto noticeStage = notice.GetStage(); + // Check if stage received from notice is valid. We could have cases where a ProxyShape has an + // invalid stage. + if (noticeStage) { + // Track the edit target layer's state + UsdUndoManager::instance().trackLayerStates(noticeStage->GetEditTarget().GetLayer()); + } + + // Handle re-entrant MayaUsdProxyShapeBase::compute; allow update only on first compute call. + if (MayaUsdProxyShapeBase::in_compute > 1) + return; + + // Handle re-entrant onStageSet + bool expectedState = false; + if (stageSetGuardCount.compare_exchange_strong(expectedState, true)) { + // We should have no listeners and stage map is dirty. + TF_VERIFY(g_StageMap.isDirty()); + TF_VERIFY(fStageListeners.empty()); + + MayaStagesSubject::Ptr me(this); + for (auto stage : ProxyShapeHandler::getAllStages()) { + NoticeKeys noticeKeys; + noticeKeys[0] = TfNotice::Register(me, &MayaStagesSubject::stageChanged, stage); + noticeKeys[1] + = TfNotice::Register(me, &MayaStagesSubject::stageEditTargetChanged, stage); + fStageListeners[stage] = noticeKeys; + } + + // Now we can send the notifications about stage change. + for (auto& path : fInvalidStages) { + Ufe::SceneItem::Ptr sceneItem = Ufe::Hierarchy::createItem(path); + if (sceneItem) { + sendSubtreeInvalidate(sceneItem); + } + } + + fInvalidStages.clear(); + + stageSetGuardCount = false; + } +} + +void MayaStagesSubject::onStageInvalidate(const MayaUsdProxyStageInvalidateNotice& notice) +{ + afterOpen(); + + auto p = notice.GetProxyShape().ufePath(); + if (!p.empty()) { + // We can't send notification to clients from dirty propagation. + // Delay it till the new stage is actually set during compute. + fInvalidStages.insert(p); + } +} + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/StagesSubject.h b/lib/mayaUsd/ufe/MayaStagesSubject.h similarity index 52% rename from lib/mayaUsd/ufe/StagesSubject.h rename to lib/mayaUsd/ufe/MayaStagesSubject.h index 364d0647d5..c37280581a 100644 --- a/lib/mayaUsd/ufe/StagesSubject.h +++ b/lib/mayaUsd/ufe/MayaStagesSubject.h @@ -1,5 +1,5 @@ // -// Copyright 2019 Autodesk +// Copyright 2023 Autodesk // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,15 +18,12 @@ #include #include +#include + #include -#include -#include -#include -#include #include #include -#include // For UFE_V2_FEATURES_AVAILABLE #include @@ -35,29 +32,28 @@ namespace ufe { //! \brief Subject class to observe Maya scene. /*! - This class observes Maya file open, to register a USD observer on each - stage the Maya scene contains. This USD observer translates USD - notifications into UFE notifications. + This class observes Maya file new/open, to register a USD observer on each + stage the Maya scene contains. */ -class MAYAUSD_CORE_PUBLIC StagesSubject : public PXR_NS::TfWeakBase +class MAYAUSD_CORE_PUBLIC MayaStagesSubject : public UsdUfe::StagesSubject { public: - typedef PXR_NS::TfWeakPtr Ptr; + typedef PXR_NS::TfWeakPtr Ptr; //! Constructor - StagesSubject(); + MayaStagesSubject(); //! Destructor - ~StagesSubject(); + ~MayaStagesSubject(); - //! Create the StagesSubject. - static StagesSubject::Ptr create(); + //! Create the MayaStagesSubject. + static MayaStagesSubject::Ptr create(); // Delete the copy/move constructors assignment operators. - StagesSubject(const StagesSubject&) = delete; - StagesSubject& operator=(const StagesSubject&) = delete; - StagesSubject(StagesSubject&&) = delete; - StagesSubject& operator=(StagesSubject&&) = delete; + MayaStagesSubject(const MayaStagesSubject&) = delete; + MayaStagesSubject& operator=(const MayaStagesSubject&) = delete; + MayaStagesSubject(MayaStagesSubject&&) = delete; + MayaStagesSubject& operator=(MayaStagesSubject&&) = delete; bool beforeNewCallback() const; void beforeNewCallback(bool b); @@ -71,18 +67,6 @@ class MAYAUSD_CORE_PUBLIC StagesSubject : public PXR_NS::TfWeakBase static void afterNewCallback(void* clientData); static void afterOpenCallback(void* clientData); - //! Call the stageChanged() methods on stage observers. - void stageChanged( - PXR_NS::UsdNotice::ObjectsChanged const& notice, - PXR_NS::UsdStageWeakPtr const& sender); - -#ifdef UFE_V2_FEATURES_AVAILABLE - //! Call the stageEditTargetChanged() methods on stage observers. - void stageEditTargetChanged( - PXR_NS::UsdNotice::StageEditTargetChanged const& notice, - PXR_NS::UsdStageWeakPtr const& sender); -#endif - private: // Notice listener method for proxy stage set void onStageSet(const PXR_NS::MayaUsdProxyStageSetNotice& notice); @@ -91,11 +75,7 @@ class MAYAUSD_CORE_PUBLIC StagesSubject : public PXR_NS::TfWeakBase void onStageInvalidate(const PXR_NS::MayaUsdProxyStageInvalidateNotice& notice); // Array of Notice::Key for registered listener -#ifdef UFE_V2_FEATURES_AVAILABLE using NoticeKeys = std::array; -#else - using NoticeKeys = std::array; -#endif // Map of per-stage listeners, indexed by stage. typedef PXR_NS::TfHashMap StageListenerMap; @@ -114,32 +94,7 @@ class MAYAUSD_CORE_PUBLIC StagesSubject : public PXR_NS::TfWeakBase MCallbackIdArray fCbIds; -}; // StagesSubject - -#ifdef UFE_V2_FEATURES_AVAILABLE -//! \brief Guard to delay attribute changed notifications. -/*! - Instantiating an object of this class allows the attribute changed - notifications to be delayed until the guard expires. - - The guard collapses down notifications for a given UFE path, which is - desirable to avoid duplicate notifications. However, it is an error to - have notifications for more than one attribute within a single guard. - */ -class MAYAUSD_CORE_PUBLIC AttributeChangedNotificationGuard -{ -public: - AttributeChangedNotificationGuard(); - ~AttributeChangedNotificationGuard(); - - //@{ - //! Cannot be copied or assigned. - AttributeChangedNotificationGuard(const AttributeChangedNotificationGuard&) = delete; - const AttributeChangedNotificationGuard& operator&(const AttributeChangedNotificationGuard&) - = delete; - //@} -}; -#endif +}; // MayaStagesSubject } // namespace ufe } // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/ProxyShapeCameraHandler.cpp b/lib/mayaUsd/ufe/ProxyShapeCameraHandler.cpp index 2e6454c58d..cbb5f6b5c5 100644 --- a/lib/mayaUsd/ufe/ProxyShapeCameraHandler.cpp +++ b/lib/mayaUsd/ufe/ProxyShapeCameraHandler.cpp @@ -15,12 +15,11 @@ // #include "ProxyShapeCameraHandler.h" -#include "Global.h" -#include "UsdCameraHandler.h" #include "pxr/usd/usdGeom/camera.h" #include +#include #include #include diff --git a/lib/mayaUsd/ufe/UsdAttribute.cpp b/lib/mayaUsd/ufe/UsdAttribute.cpp index 6b432a4cbf..bc9a783896 100644 --- a/lib/mayaUsd/ufe/UsdAttribute.cpp +++ b/lib/mayaUsd/ufe/UsdAttribute.cpp @@ -18,9 +18,9 @@ #include "Utils.h" #include "private/UfeNotifGuard.h" -#include #include +#include #include #include @@ -87,9 +87,9 @@ template bool setUsdAttr(MayaUsd::ufe::UsdAttribute& attr, const T& // our own in the StagesSubject, which we invoke here, so that only a // single UFE attribute changed notification is generated. - UsdUfe::InSetAttribute inSetAttr; - MayaUsd::ufe::AttributeChangedNotificationGuard guard; - const std::string errMsg = attr.isEditAllowedMsg(); + UsdUfe::InSetAttribute inSetAttr; + UsdUfe::AttributeChangedNotificationGuard guard; + const std::string errMsg = attr.isEditAllowedMsg(); if (!errMsg.empty()) { throw std::runtime_error(errMsg); } diff --git a/lib/mayaUsd/ufe/UsdConnections.cpp b/lib/mayaUsd/ufe/UsdConnections.cpp index 5351ab484b..1e3be04de2 100644 --- a/lib/mayaUsd/ufe/UsdConnections.cpp +++ b/lib/mayaUsd/ufe/UsdConnections.cpp @@ -14,10 +14,10 @@ // limitations under the License. // -#include -#include +#include "UsdConnections.h" #include +#include #include #include @@ -79,7 +79,7 @@ std::vector UsdConnections::allConnections() const // Find the Maya Ufe::Path of the connected shader node. const PXR_NS::UsdPrim connectedPrim = sourceInfo.source.GetPrim(); const Ufe::Path connectedPrimPath - = stagePath + usdPathToUfePathSegment(connectedPrim.GetPrimPath()); + = stagePath + UsdUfe::usdPathToUfePathSegment(connectedPrim.GetPrimPath()); // Find the name of the connected source attribute name. PXR_NS::TfToken tkSourceName = PXR_NS::UsdShadeUtils::GetFullName( diff --git a/lib/mayaUsd/ufe/UsdMaterial.cpp b/lib/mayaUsd/ufe/UsdMaterial.cpp index 99826cbca5..60691576cd 100644 --- a/lib/mayaUsd/ufe/UsdMaterial.cpp +++ b/lib/mayaUsd/ufe/UsdMaterial.cpp @@ -15,7 +15,7 @@ // #include "UsdMaterial.h" -#include +#include #include @@ -75,7 +75,7 @@ std::vector UsdMaterial::getMaterials() const // 3. Find the associated Ufe::SceneItem for each material attached to our object. for (auto& materialPrim : materialPrims) { const PXR_NS::SdfPath& materialSdfPath = materialPrim.GetPath(); - const Ufe::Path materialUfePath = usdPathToUfePathSegment(materialSdfPath); + const Ufe::Path materialUfePath = UsdUfe::usdPathToUfePathSegment(materialSdfPath); // Construct a UFE path consisting of two segments: // 1. The path to the USD stage diff --git a/lib/mayaUsd/ufe/Utils.cpp b/lib/mayaUsd/ufe/Utils.cpp index c524b8b526..f77919295e 100644 --- a/lib/mayaUsd/ufe/Utils.cpp +++ b/lib/mayaUsd/ufe/Utils.cpp @@ -105,31 +105,6 @@ Ufe::Path stagePath(UsdStageWeakPtr stage) { return g_StageMap.path(stage); } TfHashSet getAllStages() { return g_StageMap.allStages(); } -Ufe::PathSegment usdPathToUfePathSegment(const SdfPath& usdPath, int instanceIndex) -{ - const Ufe::Rtid usdRuntimeId = getUsdRunTimeId(); - static const char separator = SdfPathTokens->childDelimiter.GetText()[0u]; - - if (usdPath.IsEmpty()) { - // Return an empty segment. - return Ufe::PathSegment(Ufe::PathSegment::Components(), usdRuntimeId, separator); - } - - std::string pathString = usdPath.GetString(); - - if (instanceIndex >= 0) { - // Note here that we're taking advantage of the fact that identifiers - // in SdfPaths must be C/Python identifiers; that is, they must *not* - // begin with a digit. This means that when we see a path component at - // the end of a USD path segment that does begin with a digit, we can - // be sure that it represents an instance index and not a prim or other - // USD entity. - pathString += TfStringPrintf("%c%d", separator, instanceIndex); - } - - return Ufe::PathSegment(pathString, usdRuntimeId, separator); -} - UsdPrim ufePathToPrim(const Ufe::Path& path) { // When called we do not make any assumption on whether or not the @@ -927,7 +902,7 @@ void ReplicateExtrasToUSD::finalize( usdPrimPath = usdPrimPath.ReplacePrefix(*oldPrefix, *newPrefix); } - auto primPath = MayaUsd::ufe::usdPathToUfePathSegment(usdPrimPath); + auto primPath = UsdUfe::usdPathToUfePathSegment(usdPrimPath); Ufe::Path::Segments segments { stagePath.getSegments()[0], primPath }; Ufe::Path ufePath(std::move(segments)); diff --git a/lib/mayaUsd/ufe/Utils.h b/lib/mayaUsd/ufe/Utils.h index eeaeaf6250..2ee31f61f7 100644 --- a/lib/mayaUsd/ufe/Utils.h +++ b/lib/mayaUsd/ufe/Utils.h @@ -32,7 +32,6 @@ #include #include #include -#include #include #include @@ -74,14 +73,6 @@ Ufe::Path stagePath(PXR_NS::UsdStageWeakPtr stage); MAYAUSD_CORE_PUBLIC PXR_NS::TfHashSet getAllStages(); -//! Get the UFE path segment corresponding to the argument USD path. -//! If an instanceIndex is provided, the path segment for a point instance with -//! that USD path and index is returned. -MAYAUSD_CORE_PUBLIC -Ufe::PathSegment usdPathToUfePathSegment( - const PXR_NS::SdfPath& usdPath, - int instanceIndex = PXR_NS::UsdImagingDelegate::ALL_INSTANCES); - //! Return the USD prim corresponding to the argument UFE path. MAYAUSD_CORE_PUBLIC PXR_NS::UsdPrim ufePathToPrim(const Ufe::Path& path); diff --git a/lib/mayaUsd/ufe/wrapUtils.cpp b/lib/mayaUsd/ufe/wrapUtils.cpp index 78a4345d35..fa585054e9 100644 --- a/lib/mayaUsd/ufe/wrapUtils.cpp +++ b/lib/mayaUsd/ufe/wrapUtils.cpp @@ -79,27 +79,6 @@ std::string getNodeTypeFromRawItem(uint64_t rawItem) } #endif -PXR_NS::UsdStageWeakPtr _getStage(const std::string& ufePathString) -{ -#ifdef UFE_V2_FEATURES_AVAILABLE - return ufe::getStage(Ufe::PathString::path(ufePathString)); -#else - // This function works on a single-segment path, i.e. the Maya Dag path - // segment to the proxy shape. We know the Maya run-time ID is 1, - // separator is '|'. - // The helper function proxyShapeHandle() assumes Maya path starts - // with "|world" and will pop it off. So make sure our string has it. - // Note: std::string starts_with only added for C++20. So use diff trick. - std::string proxyPath; - if (ufePathString.rfind("|world", 0) == std::string::npos) { - proxyPath = "|world" + ufePathString; - } else { - proxyPath = ufePathString; - } - return ufe::getStage(Ufe::Path(Ufe::PathSegment(proxyPath, 1, '|'))); -#endif -} - std::vector _getAllStages() { auto allStages = ufe::getAllStages(); @@ -111,25 +90,6 @@ std::vector _getAllStages() return output; } -std::string _stagePath(PXR_NS::UsdStageWeakPtr stage) -{ -#ifdef UFE_V2_FEATURES_AVAILABLE - // Even though the Proxy shape node's UFE path is a single segment, we always - // need to return as a Ufe::PathString (to remove |world). - return Ufe::PathString::string(ufe::stagePath(stage)); -#else - // Remove the leading '|world' component. - return ufe::stagePath(stage).popHead().string(); -#endif -} - -std::string _usdPathToUfePathSegment( - const PXR_NS::SdfPath& usdPath, - int instanceIndex = PXR_NS::UsdImagingDelegate::ALL_INSTANCES) -{ - return ufe::usdPathToUfePathSegment(usdPath, instanceIndex).string(); -} - #ifndef UFE_V2_FEATURES_AVAILABLE // Helper function for UFE versions before version 2 for converting a path // string to a UFE path. @@ -212,11 +172,6 @@ void wrapUtils() // here, and are forced to use strings. Use the tentative string // representation of Ufe::Path as comma-separated segments. We know that // the USD path separator is '/'. PPT, 8-Dec-2019. - def("getStage", _getStage); def("getAllStages", _getAllStages, return_value_policy()); - def("stagePath", _stagePath); - def("usdPathToUfePathSegment", - _usdPathToUfePathSegment, - (arg("usdPath"), arg("instanceIndex") = PXR_NS::UsdImagingDelegate::ALL_INSTANCES)); def("getProxyShapePurposes", _getProxyShapePurposes); } diff --git a/lib/mayaUsd/utils/util.h b/lib/mayaUsd/utils/util.h index 47ca380d4c..d0bcae5070 100644 --- a/lib/mayaUsd/utils/util.h +++ b/lib/mayaUsd/utils/util.h @@ -132,12 +132,6 @@ inline double ConvertMMToInches(const double mm) { return mm / MillimetersPerInc /// in millimeters. inline double ConvertInchesToMM(const double inches) { return inches * MillimetersPerInch; } -/// Converts the given value \p d from units \p from to the equivalent value in units \p -inline double ConvertUnit(double d, double from, double to) -{ - return from == to ? d : d * from / to; -} - const double MillimetersPerCentimeter = 10.0; /// Converts the given value \p mm in millimeters to the equivalent value diff --git a/lib/usdUfe/python/wrapUtils.cpp b/lib/usdUfe/python/wrapUtils.cpp index 9958a3d7c7..108a3c8c91 100644 --- a/lib/usdUfe/python/wrapUtils.cpp +++ b/lib/usdUfe/python/wrapUtils.cpp @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -//#include + #include #include @@ -35,6 +35,25 @@ using namespace boost::python; +PXR_NS::UsdStageWeakPtr _getStage(const std::string& ufePathString) +{ + return UsdUfe::getStage(Ufe::PathString::path(ufePathString)); +} + +std::string _stagePath(PXR_NS::UsdStageWeakPtr stage) +{ + // Even though the Proxy shape node's UFE path is a single segment, we always + // need to return as a Ufe::PathString (to remove |world). + return Ufe::PathString::string(UsdUfe::stagePath(stage)); +} + +std::string _usdPathToUfePathSegment( + const PXR_NS::SdfPath& usdPath, + int instanceIndex = PXR_NS::UsdImagingDelegate::ALL_INSTANCES) +{ + return UsdUfe::usdPathToUfePathSegment(usdPath, instanceIndex).string(); +} + std::string _uniqueChildName(const PXR_NS::UsdPrim& parent, const std::string& name) { return UsdUfe::uniqueChildName(parent, name); @@ -80,6 +99,11 @@ void wrapUtils() // here, and are forced to use strings. Use the tentative string // representation of Ufe::Path as comma-separated segments. We know that // the USD path separator is '/'. PPT, 8-Dec-2019. + def("getStage", _getStage); + def("stagePath", _stagePath); + def("usdPathToUfePathSegment", + _usdPathToUfePathSegment, + (arg("usdPath"), arg("instanceIndex") = PXR_NS::UsdImagingDelegate::ALL_INSTANCES)); def("uniqueChildName", _uniqueChildName); def("stripInstanceIndexFromUfePath", _stripInstanceIndexFromUfePath, (arg("ufePathString"))); def("ufePathToPrim", _ufePathToPrim); diff --git a/lib/usdUfe/ufe/CMakeLists.txt b/lib/usdUfe/ufe/CMakeLists.txt index 06f016e819..2d96de497c 100644 --- a/lib/usdUfe/ufe/CMakeLists.txt +++ b/lib/usdUfe/ufe/CMakeLists.txt @@ -4,6 +4,7 @@ target_sources(${PROJECT_NAME} PRIVATE Global.cpp + StagesSubject.cpp UsdHierarchy.cpp UsdHierarchyHandler.cpp UsdRootChildHierarchy.cpp @@ -14,6 +15,8 @@ target_sources(${PROJECT_NAME} if(CMAKE_UFE_V2_FEATURES_AVAILABLE) target_sources(${PROJECT_NAME} PRIVATE + UsdCamera.cpp + UsdCameraHandler.cpp UsdObject3d.cpp UsdObject3dHandler.cpp UsdUndoableCommand.cpp @@ -36,6 +39,7 @@ endif() set(HEADERS Global.h + StagesSubject.h UsdHierarchy.h UsdHierarchyHandler.h UsdRootChildHierarchy.h @@ -46,6 +50,8 @@ set(HEADERS if(CMAKE_UFE_V2_FEATURES_AVAILABLE) list(APPEND HEADERS + UsdCamera.h + UsdCameraHandler.h UsdObject3d.h UsdObject3dHandler.h UsdUndoableCommand.h diff --git a/lib/usdUfe/ufe/Global.cpp b/lib/usdUfe/ufe/Global.cpp index a389c215dc..7716e1b704 100644 --- a/lib/usdUfe/ufe/Global.cpp +++ b/lib/usdUfe/ufe/Global.cpp @@ -15,6 +15,7 @@ // #include "Global.h" +#include #include #include @@ -43,11 +44,17 @@ static const std::string kUSDRunTimeName("USD"); // with illegal 0 value. Ufe::Rtid g_USDRtid = 0; +// Subject singleton for observation of all USD stages. +StagesSubject::Ptr g_StagesSubject; + //------------------------------------------------------------------------------ // Functions //------------------------------------------------------------------------------ -Ufe::Rtid initialize(const DCCFunctions& dccFunctions, const Handlers& handlers) +Ufe::Rtid initialize( + const DCCFunctions& dccFunctions, + const Handlers& handlers, + StagesSubject::Ptr ss /*= nullptr*/) { PXR_NAMESPACE_USING_DIRECTIVE @@ -56,6 +63,8 @@ Ufe::Rtid initialize(const DCCFunctions& dccFunctions, const Handlers& handlers) return g_USDRtid; // Set the DCC specific functions required for the UsdUfe plugin to work. + UsdUfe::setStageAccessorFn(dccFunctions.stageAccessorFn); + UsdUfe::setStagePathAccessorFn(dccFunctions.stagePathAccessorFn); UsdUfe::setUfePathToPrimFn(dccFunctions.ufePathToPrimFn); UsdUfe::setTimeAccessorFn(dccFunctions.timeAccessorFn); @@ -63,6 +72,9 @@ Ufe::Rtid initialize(const DCCFunctions& dccFunctions, const Handlers& handlers) if (dccFunctions.isAttributeLockedFn) UsdUfe::setIsAttributeLockedFn(dccFunctions.isAttributeLockedFn); + // Store the input stages subject if provided, otherwise create the default one. + g_StagesSubject = ss ? ss : StagesSubject::create(); + // Copy all the input handlers into the Ufe handler struct and // create any default ones which are null. Ufe::RunTimeMgr::Handlers rtHandlers; @@ -70,6 +82,8 @@ Ufe::Rtid initialize(const DCCFunctions& dccFunctions, const Handlers& handlers) = handlers.hierarchyHandler ? handlers.hierarchyHandler : UsdHierarchyHandler::create(); rtHandlers.object3dHandler = handlers.object3dHandler ? handlers.object3dHandler : UsdObject3dHandler::create(); + rtHandlers.cameraHandler + = handlers.cameraHandler ? handlers.cameraHandler : UsdCameraHandler::create(); g_USDRtid = Ufe::RunTimeMgr::instance().register_(kUSDRunTimeName, rtHandlers); TF_VERIFY(g_USDRtid != 0); @@ -85,6 +99,8 @@ bool finalize(bool exiting) auto& runTimeMgr = Ufe::RunTimeMgr::instance(); runTimeMgr.unregister(g_USDRtid); + g_StagesSubject.Reset(); + return true; } diff --git a/lib/usdUfe/ufe/Global.h b/lib/usdUfe/ufe/Global.h index 06b338df2c..104f19c1de 100644 --- a/lib/usdUfe/ufe/Global.h +++ b/lib/usdUfe/ufe/Global.h @@ -16,8 +16,10 @@ #pragma once #include +#include #include +#include #include #include #include @@ -34,8 +36,10 @@ namespace USDUFE_NS_DEF { struct USDUFE_PUBLIC DCCFunctions { // Mandatory: function whichs must be supplied. - UfePathToPrimFn ufePathToPrimFn = nullptr; - TimeAccessorFn timeAccessorFn = nullptr; + StageAccessorFn stageAccessorFn = nullptr; + StagePathAccessorFn stagePathAccessorFn = nullptr; + UfePathToPrimFn ufePathToPrimFn = nullptr; + TimeAccessorFn timeAccessorFn = nullptr; // Optional: default values will be used if no function is supplied. IsAttributeLockedFn isAttributeLockedFn = nullptr; @@ -60,7 +64,7 @@ struct USDUFE_PUBLIC Handlers Ufe::Object3dHandler::Ptr object3dHandler; // Ufe::ContextOpsHandler::Ptr contextOpsHandler; // Ufe::UIInfoHandler::Ptr uiInfoHandler; - // Ufe::CameraHandler::Ptr cameraHandler; + Ufe::CameraHandler::Ptr cameraHandler; #ifdef UFE_V3_FEATURES_AVAILABLE // Ufe::PathMappingHandler::Ptr pathMappingHandler; @@ -78,9 +82,16 @@ struct USDUFE_PUBLIC Handlers //! Only intended to be called by the plugin initialization, to //! initialize the handlers and stage model. +//! \param [in] dccFunctions Struct containing DCC specific functions for plugin to function. +//! \param [in] handlers Struct containing Ufe runtime handlers used to initialize the plugin. +//! \param [in] ss Optional USD subject class, it not provided basic one will be created. +//! //! \return Ufe runtime ID for USD or 0 in case of error. USDUFE_PUBLIC -Ufe::Rtid initialize(const DCCFunctions&, const Handlers&); +Ufe::Rtid initialize( + const DCCFunctions& dccFunctions, + const Handlers& handlers, + StagesSubject::Ptr ss = nullptr); //! Only intended to be called by the plugin finalization, to //! finalize the handlers stage model. diff --git a/lib/mayaUsd/ufe/StagesSubject.cpp b/lib/usdUfe/ufe/StagesSubject.cpp similarity index 74% rename from lib/mayaUsd/ufe/StagesSubject.cpp rename to lib/usdUfe/ufe/StagesSubject.cpp index f57daab0b2..e0d76c3d3e 100644 --- a/lib/mayaUsd/ufe/StagesSubject.cpp +++ b/lib/usdUfe/ufe/StagesSubject.cpp @@ -17,61 +17,37 @@ #include "private/UfeNotifGuard.h" -#include -#include -#include - +#include #include -#ifdef UFE_V2_FEATURES_AVAILABLE -#include -#endif -#include -#include -#ifdef UFE_V2_FEATURES_AVAILABLE +#include +#include #include -#endif -#include -#include #include #include #include #include -#include -#include +#include #include +#include +#include #include #include #include #include -#include -#include #include #include -#ifdef UFE_V2_FEATURES_AVAILABLE -#include -#include -#include - -#include -#endif - -PXR_NAMESPACE_USING_DIRECTIVE - namespace { -// Prevent re-entrant stage set. -std::atomic_bool stageSetGuardCount { false }; - bool isTransformChange(const TfToken& nameToken) { return nameToken == UsdGeomTokens->xformOpOrder || UsdGeomXformOp::IsXformOp(nameToken); } -// Prevent exception from the notifications from escaping and breaking USD/Maya. +// Prevent exception from the notifications from escaping and breaking USD/DCC. // USD does not wrap its notification in try/catch, so we need to do it ourselves. template void notifyWithoutExceptions(const NOTIFICATION& notif) @@ -83,7 +59,6 @@ void notifyWithoutExceptions(const NOTIFICATION& notif) } } -#ifdef UFE_V2_FEATURES_AVAILABLE // The attribute change notification guard is not meant to be nested, but // use a counter nonetheless to provide consistent behavior in such cases. std::atomic_int attributeChangedNotificationGuardCount { 0 }; @@ -169,7 +144,7 @@ void sendAttributeChanged( notifyWithoutExceptions( Ufe::AttributeValueChanged(ufePath, changedToken.GetString())); - if (MayaUsd::ufe::UsdCamera::isCameraToken(changedToken)) { + if (UsdUfe::UsdCamera::isCameraToken(changedToken)) { notifyWithoutExceptions(ufePath); } } break; @@ -178,7 +153,7 @@ void sendAttributeChanged( notifyWithoutExceptions( Ufe::AttributeValueChanged(ufePath, changedToken.GetString())); - if (MayaUsd::ufe::UsdCamera::isCameraToken(changedToken)) { + if (UsdUfe::UsdCamera::isCameraToken(changedToken)) { notifyWithoutExceptions(ufePath); } } else { @@ -202,7 +177,7 @@ void sendAttributeChanged( notifyWithoutExceptions( Ufe::AttributeValueChanged(ufePath, changedToken.GetString())); - if (MayaUsd::ufe::UsdCamera::isCameraToken(changedToken)) { + if (UsdUfe::UsdCamera::isCameraToken(changedToken)) { notifyWithoutExceptions(ufePath); } #endif @@ -380,198 +355,21 @@ void processAttributeChanges( #endif }; -#endif - -void sendObjectAdd(const Ufe::SceneItem::Ptr& sceneItem) -{ - try { -#ifdef UFE_V2_FEATURES_AVAILABLE - Ufe::Scene::instance().notify(Ufe::ObjectAdd(sceneItem)); -#else - auto notification = Ufe::ObjectAdd(sceneItem); - Ufe::Scene::notifyObjectAdd(notification); -#endif - } catch (const std::exception& ex) { - TF_WARN("Caught error during notification: %s", ex.what()); - } -} - -void sendObjectPostDelete(const Ufe::SceneItem::Ptr& sceneItem) -{ - try { -#ifdef UFE_V2_FEATURES_AVAILABLE - Ufe::Scene::instance().notify(Ufe::ObjectPostDelete(sceneItem)); -#else - auto notification = Ufe::ObjectPostDelete(sceneItem); - Ufe::Scene::notifyObjectDelete(notification); -#endif - } catch (const std::exception& ex) { - TF_WARN("Caught error during notification: %s", ex.what()); - } -} - -void sendObjectDestroyed(const Ufe::Path& ufePath) -{ - try { -#ifdef UFE_V2_FEATURES_AVAILABLE - Ufe::Scene::instance().notify(Ufe::ObjectDestroyed(ufePath)); -#else - // Unfortunately in Ufe v1 there was no object destroyed notif - // and the only delete notifs we have both take a scene item - // which we cannot create for the input Ufe path since that - // path is no longer valid (it has already been destroyed). - // So the only choice we have is to subtree invalidate the - // parent which will remove the destroyed item (keeping all - // the valid children). - auto sceneItem = Ufe::Hierarchy::createItem(ufePath.pop()); - if (TF_VERIFY(sceneItem)) { - sendObjectPostDelete(sceneItem); - sendObjectAdd(sceneItem); - } -#endif - } catch (const std::exception& ex) { - TF_WARN("Caught error during notification: %s", ex.what()); - } -} - -void sendSubtreeInvalidate(const Ufe::SceneItem::Ptr& sceneItem) -{ - try { -#ifdef UFE_V2_FEATURES_AVAILABLE - Ufe::Scene::instance().notify(Ufe::SubtreeInvalidate(sceneItem)); -#else - // In Ufe v1 there was no subtree invalidate notif. So we mimic it by sending - // delete/add notifs. - sendObjectPostDelete(sceneItem); - sendObjectAdd(sceneItem); -#endif - } catch (const std::exception& ex) { - TF_WARN("Caught error during notification: %s", ex.what()); - } -} - } // namespace -namespace MAYAUSD_NS_DEF { -namespace ufe { - -//------------------------------------------------------------------------------ -// Global variables & macros -//------------------------------------------------------------------------------ -extern UsdStageMap g_StageMap; +namespace USDUFE_NS_DEF { //------------------------------------------------------------------------------ // StagesSubject //------------------------------------------------------------------------------ -StagesSubject::StagesSubject() -{ - // Workaround to MAYA-65920: at startup, MSceneMessage.kAfterNew file - // callback is incorrectly called by Maya before the - // MSceneMessage.kBeforeNew file callback, which should be illegal. - // Detect this and ignore illegal calls to after new file callbacks. - // PPT, 19-Jan-16. - fBeforeNewCallback = false; - - MStatus res; - fCbIds.append( - MSceneMessage::addCallback(MSceneMessage::kBeforeNew, beforeNewCallback, this, &res)); - CHECK_MSTATUS(res); - fCbIds.append( - MSceneMessage::addCallback(MSceneMessage::kBeforeOpen, beforeOpenCallback, this, &res)); - CHECK_MSTATUS(res); - fCbIds.append( - MSceneMessage::addCallback(MSceneMessage::kAfterOpen, afterOpenCallback, this, &res)); - CHECK_MSTATUS(res); - fCbIds.append( - MSceneMessage::addCallback(MSceneMessage::kAfterNew, afterNewCallback, this, &res)); - CHECK_MSTATUS(res); - - TfWeakPtr me(this); - TfNotice::Register(me, &StagesSubject::onStageSet); - TfNotice::Register(me, &StagesSubject::onStageInvalidate); -} +StagesSubject::StagesSubject() { } -StagesSubject::~StagesSubject() -{ - MMessage::removeCallbacks(fCbIds); - fCbIds.clear(); -} +StagesSubject::~StagesSubject() { } /*static*/ StagesSubject::Ptr StagesSubject::create() { return TfCreateWeakPtr(new StagesSubject); } -bool StagesSubject::beforeNewCallback() const { return fBeforeNewCallback; } - -void StagesSubject::beforeNewCallback(bool b) -{ - fBeforeNewCallback = b; - fInvalidStages.clear(); -} - -/*static*/ -void StagesSubject::beforeNewCallback(void* clientData) -{ - StagesSubject* ss = static_cast(clientData); - ss->beforeNewCallback(true); -} - -/*static*/ -void StagesSubject::beforeOpenCallback(void* clientData) -{ - // StagesSubject* ss = static_cast(clientData); - StagesSubject::beforeNewCallback(clientData); -} - -/*static*/ -void StagesSubject::afterNewCallback(void* clientData) -{ - StagesSubject* ss = static_cast(clientData); - - // Workaround to MAYA-65920: detect and avoid illegal callback sequence. - if (!ss->beforeNewCallback()) - return; - - ss->beforeNewCallback(false); - StagesSubject::afterOpenCallback(clientData); -} - -/*static*/ -void StagesSubject::afterOpenCallback(void* clientData) -{ - StagesSubject* ss = static_cast(clientData); - ss->afterOpen(); -} - -void StagesSubject::afterOpen() -{ - // Observe stage changes, for all stages. Return listener object can - // optionally be used to call Revoke() to remove observation, but must - // keep reference to it, otherwise its reference count is immediately - // decremented, falls to zero, and no observation occurs. - - // Ideally, we would observe the data model only if there are observers, - // to minimize cost of observation. However, since observation is - // frequent, we won't implement this for now. PPT, 22-Dec-2017. - std::for_each( - std::begin(fStageListeners), - std::end(fStageListeners), - [](StageListenerMap::value_type element) { - for (auto& noticeKey : element.second) { - TfNotice::Revoke(noticeKey); - } - }); - fStageListeners.clear(); - - // Set up our stage to proxy shape UFE path (and reverse) - // mapping. We do this with the following steps: - // - get all proxyShape nodes in the scene. - // - get their Dag paths. - // - convert the Dag paths to UFE paths. - // - get their stage. - g_StageMap.setDirty(); -} - void StagesSubject::stageChanged( UsdNotice::ObjectsChanged const& notice, UsdStageWeakPtr const& sender) @@ -586,7 +384,7 @@ void StagesSubject::stageChanged( const auto& changedPath = *it; if (changedPath.IsPrimPropertyPath()) { // Special case to detect when an xformop is added or removed from a prim. - // We need to send some notifs so Maya can update (such as on undo + // We need to send some notifs so DCC can update (such as on undo // to move the transform manipulator back to original position). const TfToken nameToken = changedPath.GetNameToken(); auto usdPrimPathStr = changedPath.GetPrimPath().GetString(); @@ -610,7 +408,7 @@ void StagesSubject::stageChanged( // Assume proxy shapes (and thus stages) cannot be instanced. We can // therefore map the stage to a single UFE path. Lifting this // restriction would mean sending one add or delete notification for - // each Maya Dag path instancing the proxy shape / stage. + // each DCC path instancing the proxy shape / stage. Ufe::Path ufePath; UsdPrim prim; if (changedPath == SdfPath::AbsoluteRootPath()) { @@ -618,14 +416,15 @@ void StagesSubject::stageChanged( prim = stage->GetPseudoRoot(); } else { const std::string& usdPrimPathStr = changedPath.GetPrimPath().GetString(); - ufePath = stagePath(sender) + Ufe::PathSegment(usdPrimPathStr, getUsdRunTimeId(), '/'); + ufePath = stagePath(sender) + + Ufe::PathSegment(usdPrimPathStr, UsdUfe::getUsdRunTimeId(), '/'); prim = stage->GetPrimAtPath(changedPath); } if (prim.IsValid() && !InPathChange::inPathChange()) { auto sceneItem = Ufe::Hierarchy::createItem(ufePath); - // AL LayerCommands.addSubLayer test will cause Maya to crash + // AL LayerCommands.addSubLayer test will cause crash // if we don't filter invalid sceneItems. This patch is provided // to prevent crashes, but more investigation will have to be // done to understand why ufePath in case of sub layer @@ -696,9 +495,9 @@ void StagesSubject::stageChanged( ++it) { const auto& changedPath = *it; auto usdPrimPathStr = changedPath.GetPrimPath().GetString(); - auto ufePath = stagePath(sender) + Ufe::PathSegment(usdPrimPathStr, getUsdRunTimeId(), '/'); + auto ufePath + = stagePath(sender) + Ufe::PathSegment(usdPrimPathStr, UsdUfe::getUsdRunTimeId(), '/'); -#ifdef UFE_V2_FEATURES_AVAILABLE bool sendValueChangedFallback = true; // isPrimPropertyPath() does not consider relational attributes @@ -715,7 +514,6 @@ void StagesSubject::stageChanged( notifyWithoutExceptions(vis); sendValueChangedFallback = false; } -#endif if (!UsdUfe::InTransform3dChange::inTransform3dChange()) { // Is the change a Transform3d change? @@ -763,7 +561,6 @@ void StagesSubject::stageChanged( } } -#ifdef UFE_V2_FEATURES_AVAILABLE if (sendValueChangedFallback) { // check to see if there is an entry which Ufe should notify about. @@ -779,20 +576,16 @@ void StagesSubject::stageChanged( break; } } -#endif } -#ifdef UFE_V2_FEATURES_AVAILABLE // Special case when we are notified, but no paths given. if (notice.GetResyncedPaths().empty() && notice.GetChangedInfoOnlyPaths().empty()) { auto ufePath = stagePath(sender); Ufe::AttributeValueChanged vc(ufePath, "/"); notifyWithoutExceptions(vc); } -#endif } -#ifdef UFE_V2_FEATURES_AVAILABLE void StagesSubject::stageEditTargetChanged( UsdNotice::StageEditTargetChanged const& notice, UsdStageWeakPtr const& sender) @@ -800,69 +593,42 @@ void StagesSubject::stageEditTargetChanged( // Track the edit target layer's state UsdUndoManager::instance().trackLayerStates(notice.GetStage()->GetEditTarget().GetLayer()); } -#endif -void StagesSubject::onStageSet(const MayaUsdProxyStageSetNotice& notice) +void StagesSubject::sendObjectAdd(const Ufe::SceneItem::Ptr& sceneItem) const { -#ifdef UFE_V2_FEATURES_AVAILABLE - auto noticeStage = notice.GetStage(); - // Check if stage received from notice is valid. We could have cases where a ProxyShape has an - // invalid stage. - if (noticeStage) { - // Track the edit target layer's state - UsdUndoManager::instance().trackLayerStates(noticeStage->GetEditTarget().GetLayer()); + try { + Ufe::Scene::instance().notify(Ufe::ObjectAdd(sceneItem)); + } catch (const std::exception& ex) { + TF_WARN("Caught error during notification: %s", ex.what()); } -#endif - - // Handle re-entrant MayaUsdProxyShapeBase::compute; allow update only on first compute call. - if (MayaUsdProxyShapeBase::in_compute > 1) - return; - - // Handle re-entrant onStageSet - bool expectedState = false; - if (stageSetGuardCount.compare_exchange_strong(expectedState, true)) { - // We should have no listeners and stage map is dirty. - TF_VERIFY(g_StageMap.isDirty()); - TF_VERIFY(fStageListeners.empty()); - - StagesSubject::Ptr me(this); - for (auto stage : ProxyShapeHandler::getAllStages()) { - - NoticeKeys noticeKeys; - - noticeKeys[0] = TfNotice::Register(me, &StagesSubject::stageChanged, stage); - UFE_V2(noticeKeys[1] - = TfNotice::Register(me, &StagesSubject::stageEditTargetChanged, stage);) - fStageListeners[stage] = noticeKeys; - } - - // Now we can send the notifications about stage change. - for (auto& path : fInvalidStages) { - Ufe::SceneItem::Ptr sceneItem = Ufe::Hierarchy::createItem(path); - if (sceneItem) { - sendSubtreeInvalidate(sceneItem); - } - } - - fInvalidStages.clear(); +} - stageSetGuardCount = false; +void StagesSubject::sendObjectPostDelete(const Ufe::SceneItem::Ptr& sceneItem) const +{ + try { + Ufe::Scene::instance().notify(Ufe::ObjectPostDelete(sceneItem)); + } catch (const std::exception& ex) { + TF_WARN("Caught error during notification: %s", ex.what()); } } -void StagesSubject::onStageInvalidate(const MayaUsdProxyStageInvalidateNotice& notice) +void StagesSubject::sendObjectDestroyed(const Ufe::Path& ufePath) const { - afterOpen(); - - auto p = notice.GetProxyShape().ufePath(); - if (!p.empty()) { - // We can't send notification to clients from dirty propagation. - // Delay it till the new stage is actually set during compute. - fInvalidStages.insert(p); + try { + Ufe::Scene::instance().notify(Ufe::ObjectDestroyed(ufePath)); + } catch (const std::exception& ex) { + TF_WARN("Caught error during notification: %s", ex.what()); } } -#ifdef UFE_V2_FEATURES_AVAILABLE +void StagesSubject::sendSubtreeInvalidate(const Ufe::SceneItem::Ptr& sceneItem) const +{ + try { + Ufe::Scene::instance().notify(Ufe::SubtreeInvalidate(sceneItem)); + } catch (const std::exception& ex) { + TF_WARN("Caught error during notification: %s", ex.what()); + } +} AttributeChangedNotificationGuard::AttributeChangedNotificationGuard() { if (inAttributeChangedNotificationGuard()) { @@ -907,7 +673,5 @@ AttributeChangedNotificationGuard::~AttributeChangedNotificationGuard() pendingAttributeChangedNotifications.clear(); } -#endif -} // namespace ufe -} // namespace MAYAUSD_NS_DEF +} // namespace USDUFE_NS_DEF diff --git a/lib/usdUfe/ufe/StagesSubject.h b/lib/usdUfe/ufe/StagesSubject.h new file mode 100644 index 0000000000..ea4123f544 --- /dev/null +++ b/lib/usdUfe/ufe/StagesSubject.h @@ -0,0 +1,110 @@ +// +// Copyright 2019 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#pragma once + +#include + +#include +#include +#include +#include + +#include +#include + +namespace USDUFE_NS_DEF { + +//! \brief Subject class to observe USD stage changes. +/*! + This USD observer translates USD notifications into UFE notifications. + + A client should derive their own version from this class and then + when a gateway node is created connect that stage to USD notifications. + Example: + key1 = TfNotice::Register(me, &StagesSubject::stageChanged, stage); + key2 = TfNotice::Register(me, &StagesSubject::stageEditTargetChanged, stage); + + When a new scene is created/opened in the DCC you should remove the + TfNotice registration by calling: + TfNotice::Revoke(key1); + TfNotice::Revoke(key2); + + See the MayaUsd implmentation for more details: + https://github.com/Autodesk/maya-usd/blob/dev/lib/mayaUsd/ufe/MayaStagesSubject.cpp + */ +class USDUFE_PUBLIC StagesSubject : public PXR_NS::TfWeakBase +{ +public: + typedef PXR_NS::TfWeakPtr Ptr; + + //! Constructor + StagesSubject(); + + //! Destructor + ~StagesSubject(); + + //! Create the StagesSubject. + static StagesSubject::Ptr create(); + + // Delete the copy/move constructors assignment operators. + StagesSubject(const StagesSubject&) = delete; + StagesSubject& operator=(const StagesSubject&) = delete; + StagesSubject(StagesSubject&&) = delete; + StagesSubject& operator=(StagesSubject&&) = delete; + + // Ufe notification helpers - send notification trapping any exception. + void sendObjectAdd(const Ufe::SceneItem::Ptr& sceneItem) const; + void sendObjectPostDelete(const Ufe::SceneItem::Ptr& sceneItem) const; + void sendObjectDestroyed(const Ufe::Path& ufePath) const; + void sendSubtreeInvalidate(const Ufe::SceneItem::Ptr& sceneItem) const; + +protected: + //! Call the stageChanged() methods on stage observers. + virtual void stageChanged( + PXR_NS::UsdNotice::ObjectsChanged const& notice, + PXR_NS::UsdStageWeakPtr const& sender); + + //! Call the stageEditTargetChanged() methods on stage observers. + virtual void stageEditTargetChanged( + PXR_NS::UsdNotice::StageEditTargetChanged const& notice, + PXR_NS::UsdStageWeakPtr const& sender); + +}; // StagesSubject + +//! \brief Guard to delay attribute changed notifications. +/*! + Instantiating an object of this class allows the attribute changed + notifications to be delayed until the guard expires. + + The guard collapses down notifications for a given UFE path, which is + desirable to avoid duplicate notifications. However, it is an error to + have notifications for more than one attribute within a single guard. + */ +class USDUFE_PUBLIC AttributeChangedNotificationGuard +{ +public: + AttributeChangedNotificationGuard(); + ~AttributeChangedNotificationGuard(); + + //@{ + //! Cannot be copied or assigned. + AttributeChangedNotificationGuard(const AttributeChangedNotificationGuard&) = delete; + const AttributeChangedNotificationGuard& operator&(const AttributeChangedNotificationGuard&) + = delete; + //@} +}; + +} // namespace USDUFE_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdCamera.cpp b/lib/usdUfe/ufe/UsdCamera.cpp similarity index 93% rename from lib/mayaUsd/ufe/UsdCamera.cpp rename to lib/usdUfe/ufe/UsdCamera.cpp index 0d58834715..ba76105546 100644 --- a/lib/mayaUsd/ufe/UsdCamera.cpp +++ b/lib/usdUfe/ufe/UsdCamera.cpp @@ -15,19 +15,16 @@ // #include "UsdCamera.h" -#include "pxr/base/gf/frustum.h" -#include "pxr/usd/usdGeom/camera.h" -#include "pxr/usd/usdGeom/metrics.h" - -#include -#include - #include +#include +#include +#include #include +#include +#include -namespace MAYAUSD_NS_DEF { -namespace ufe { +namespace USDUFE_NS_DEF { UsdCamera::UsdCamera() : Camera() @@ -81,7 +78,7 @@ float convertToStageUnits(float value, double valueUnits, const PXR_NS::UsdPrim& stageUnits = UsdGeomGetStageMetersPerUnit(stage); } - return UsdMayaUtil::ConvertUnit(value, valueUnits, stageUnits); + return UsdUfe::convertUnit(value, valueUnits, stageUnits); } float convertToTenthOfStageUnits(float value, double valueUnits, const PXR_NS::UsdPrim& prim) @@ -133,7 +130,7 @@ float UsdCamera::horizontalAperture() const stageUnits = UsdGeomGetStageMetersPerUnit(stage); } - return UsdMayaUtil::ConvertUnit(horizontalAperture, stageUnits, UsdGeomLinearUnits::inches); + return convertUnit(horizontalAperture, stageUnits, UsdGeomLinearUnits::inches); } Ufe::VerticalApertureUndoableCommand::Ptr UsdCamera::verticalApertureCmd(float value) @@ -171,7 +168,7 @@ float UsdCamera::verticalAperture() const stageUnits = UsdGeomGetStageMetersPerUnit(stage); } - return UsdMayaUtil::ConvertUnit(verticalAperture, stageUnits, UsdGeomLinearUnits::inches); + return convertUnit(verticalAperture, stageUnits, UsdGeomLinearUnits::inches); } Ufe::HorizontalApertureOffsetUndoableCommand::Ptr @@ -211,8 +208,7 @@ float UsdCamera::horizontalApertureOffset() const stageUnits = UsdGeomGetStageMetersPerUnit(stage); } - return UsdMayaUtil::ConvertUnit( - horizontalApertureOffset, stageUnits, UsdGeomLinearUnits::inches); + return convertUnit(horizontalApertureOffset, stageUnits, UsdGeomLinearUnits::inches); } Ufe::VerticalApertureOffsetUndoableCommand::Ptr UsdCamera::verticalApertureOffsetCmd(float value) @@ -251,7 +247,7 @@ float UsdCamera::verticalApertureOffset() const stageUnits = UsdGeomGetStageMetersPerUnit(stage); } - return UsdMayaUtil::ConvertUnit(verticalApertureOffset, stageUnits, UsdGeomLinearUnits::inches); + return convertUnit(verticalApertureOffset, stageUnits, UsdGeomLinearUnits::inches); } Ufe::FStopUndoableCommand::Ptr UsdCamera::fStopCmd(float value) @@ -294,12 +290,7 @@ float UsdCamera::fStop() const stageUnits = UsdGeomGetStageMetersPerUnit(stage); } -#if MAYA_API_VERSION >= 20220100 - return UsdMayaUtil::ConvertUnit(fStop, stageUnits, UsdGeomLinearUnits::millimeters); -#else - float retVal = UsdMayaUtil::ConvertUnit(fStop, stageUnits, UsdGeomLinearUnits::millimeters); - return retVal < FLT_EPSILON ? FLT_EPSILON : retVal; -#endif + return convertUnit(fStop, stageUnits, UsdGeomLinearUnits::millimeters); } Ufe::FocalLengthUndoableCommand::Ptr UsdCamera::focalLengthCmd(float value) @@ -337,7 +328,7 @@ float UsdCamera::focalLength() const stageUnits = UsdGeomGetStageMetersPerUnit(stage); } - return UsdMayaUtil::ConvertUnit(focalLength, stageUnits, UsdGeomLinearUnits::millimeters); + return convertUnit(focalLength, stageUnits, UsdGeomLinearUnits::millimeters); } Ufe::FocusDistanceUndoableCommand::Ptr UsdCamera::focusDistanceCmd(float value) @@ -375,7 +366,7 @@ float UsdCamera::focusDistance() const stageUnits = UsdGeomGetStageMetersPerUnit(stage); } - return UsdMayaUtil::ConvertUnit(focusDistance, stageUnits, UsdGeomLinearUnits::centimeters); + return convertUnit(focusDistance, stageUnits, UsdGeomLinearUnits::centimeters); } Ufe::NearClipPlaneUndoableCommand::Ptr UsdCamera::nearClipPlaneCmd(float value) @@ -478,5 +469,4 @@ Ufe::Camera::Projection UsdCamera::projection() const return Ufe::Camera::Perspective; } -} // namespace ufe -} // namespace MAYAUSD_NS_DEF +} // namespace USDUFE_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdCamera.h b/lib/usdUfe/ufe/UsdCamera.h similarity index 94% rename from lib/mayaUsd/ufe/UsdCamera.h rename to lib/usdUfe/ufe/UsdCamera.h index 6c42fc25d8..70721564c5 100644 --- a/lib/mayaUsd/ufe/UsdCamera.h +++ b/lib/usdUfe/ufe/UsdCamera.h @@ -15,8 +15,7 @@ // #pragma once -#include - +#include #include #include @@ -25,11 +24,10 @@ #include #include -namespace MAYAUSD_NS_DEF { -namespace ufe { +namespace USDUFE_NS_DEF { //! \brief Interface to transform objects in 3D. -class MAYAUSD_CORE_PUBLIC UsdCamera : public Ufe::Camera +class USDUFE_PUBLIC UsdCamera : public Ufe::Camera { public: typedef std::shared_ptr Ptr; @@ -95,5 +93,4 @@ class MAYAUSD_CORE_PUBLIC UsdCamera : public Ufe::Camera }; // UsdCamera -} // namespace ufe -} // namespace MAYAUSD_NS_DEF +} // namespace USDUFE_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdCameraHandler.cpp b/lib/usdUfe/ufe/UsdCameraHandler.cpp similarity index 94% rename from lib/mayaUsd/ufe/UsdCameraHandler.cpp rename to lib/usdUfe/ufe/UsdCameraHandler.cpp index 5fc23af29d..8e848d4e72 100644 --- a/lib/mayaUsd/ufe/UsdCameraHandler.cpp +++ b/lib/usdUfe/ufe/UsdCameraHandler.cpp @@ -15,13 +15,12 @@ // #include "UsdCameraHandler.h" -#include "Global.h" -#include "UsdCamera.h" #include "pxr/usd/usdGeom/camera.h" -#include - +#include +#include #include +#include #include #include @@ -30,8 +29,7 @@ PXR_NAMESPACE_USING_DIRECTIVE -namespace MAYAUSD_NS_DEF { -namespace ufe { +namespace USDUFE_NS_DEF { UsdCameraHandler::UsdCameraHandler() : Ufe::CameraHandler() @@ -89,5 +87,4 @@ Ufe::Selection UsdCameraHandler::find( } #endif -} // namespace ufe -} // namespace MAYAUSD_NS_DEF +} // namespace USDUFE_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdCameraHandler.h b/lib/usdUfe/ufe/UsdCameraHandler.h similarity index 89% rename from lib/mayaUsd/ufe/UsdCameraHandler.h rename to lib/usdUfe/ufe/UsdCameraHandler.h index 4f5838bbed..61654d29c9 100644 --- a/lib/mayaUsd/ufe/UsdCameraHandler.h +++ b/lib/usdUfe/ufe/UsdCameraHandler.h @@ -15,17 +15,16 @@ // #pragma once -#include +#include #include #include -namespace MAYAUSD_NS_DEF { -namespace ufe { +namespace USDUFE_NS_DEF { //! \brief Interface to create a UsdCameraHandler interface object. -class MAYAUSD_CORE_PUBLIC UsdCameraHandler : public Ufe::CameraHandler +class USDUFE_PUBLIC UsdCameraHandler : public Ufe::CameraHandler { public: typedef std::shared_ptr Ptr; @@ -54,5 +53,4 @@ class MAYAUSD_CORE_PUBLIC UsdCameraHandler : public Ufe::CameraHandler }; // UsdCameraHandler -} // namespace ufe -} // namespace MAYAUSD_NS_DEF +} // namespace USDUFE_NS_DEF diff --git a/lib/usdUfe/ufe/Utils.cpp b/lib/usdUfe/ufe/Utils.cpp index c5139c8aa3..fda3ece14e 100644 --- a/lib/usdUfe/ufe/Utils.cpp +++ b/lib/usdUfe/ufe/Utils.cpp @@ -15,6 +15,7 @@ // #include "Utils.h" +#include #include #include @@ -25,6 +26,8 @@ #include #include +#include + #include #include @@ -79,6 +82,8 @@ uint32_t findLayerIndex(const UsdPrim& prim, const SdfLayerHandle& layer) return position; } +UsdUfe::StageAccessorFn gStageAccessorFn = nullptr; +UsdUfe::StagePathAccessorFn gStagePathAccessorFn = nullptr; UsdUfe::UfePathToPrimFn gUfePathToPrimFn = nullptr; UsdUfe::TimeAccessorFn gTimeAccessorFn = nullptr; UsdUfe::IsAttributeLockedFn gIsAttributeLockedFn = nullptr; @@ -87,6 +92,67 @@ UsdUfe::IsAttributeLockedFn gIsAttributeLockedFn = nullptr; namespace USDUFE_NS_DEF { +//------------------------------------------------------------------------------ +// Utility Functions +//------------------------------------------------------------------------------ + +void setStageAccessorFn(StageAccessorFn fn) +{ + if (nullptr == fn) { + throw std::invalid_argument("Path to prim function cannot be empty."); + } + gStageAccessorFn = fn; +}; + +PXR_NS::UsdStageWeakPtr getStage(const Ufe::Path& path) +{ +#if !defined(NDEBUG) + assert(gStageAccessorFn != nullptr); +#endif + return gStageAccessorFn(path); +} + +void setStagePathAccessorFn(StagePathAccessorFn fn) +{ + if (nullptr == fn) { + throw std::invalid_argument("Path to prim function cannot be empty."); + } + gStagePathAccessorFn = fn; +} + +Ufe::Path stagePath(PXR_NS::UsdStageWeakPtr stage) +{ +#if !defined(NDEBUG) + assert(gStagePathAccessorFn != nullptr); +#endif + return gStagePathAccessorFn(stage); +} + +Ufe::PathSegment usdPathToUfePathSegment(const SdfPath& usdPath, int instanceIndex) +{ + const Ufe::Rtid usdRuntimeId = getUsdRunTimeId(); + static const char separator = SdfPathTokens->childDelimiter.GetText()[0u]; + + if (usdPath.IsEmpty()) { + // Return an empty segment. + return Ufe::PathSegment(Ufe::PathSegment::Components(), usdRuntimeId, separator); + } + + std::string pathString = usdPath.GetString(); + + if (instanceIndex >= 0) { + // Note here that we're taking advantage of the fact that identifiers + // in SdfPaths must be C/Python identifiers; that is, they must *not* + // begin with a digit. This means that when we see a path component at + // the end of a USD path segment that does begin with a digit, we can + // be sure that it represents an instance index and not a prim or other + // USD entity. + pathString += TfStringPrintf("%c%d", separator, instanceIndex); + } + + return Ufe::PathSegment(pathString, usdRuntimeId, separator); +} + Ufe::Path stripInstanceIndexFromUfePath(const Ufe::Path& path) { if (path.empty()) { @@ -116,10 +182,9 @@ void setUfePathToPrimFn(UfePathToPrimFn fn) UsdPrim ufePathToPrim(const Ufe::Path& path) { - if (nullptr == gUfePathToPrimFn) { - throw std::runtime_error("Path to prim function cannot be empty."); - } - +#if !defined(NDEBUG) + assert(gUfePathToPrimFn != nullptr); +#endif return gUfePathToPrimFn(path); } @@ -133,10 +198,9 @@ void setTimeAccessorFn(TimeAccessorFn fn) PXR_NS::UsdTimeCode getTime(const Ufe::Path& path) { - if (nullptr == gTimeAccessorFn) { - throw std::runtime_error("Time accessor function cannot be empty."); - } - +#if !defined(NDEBUG) + assert(gTimeAccessorFn != nullptr); +#endif return gTimeAccessorFn(path); } diff --git a/lib/usdUfe/ufe/Utils.h b/lib/usdUfe/ufe/Utils.h index f963753621..dc272205e4 100644 --- a/lib/usdUfe/ufe/Utils.h +++ b/lib/usdUfe/ufe/Utils.h @@ -18,6 +18,9 @@ #include #include +#include +#include + #include #include #include @@ -34,6 +37,8 @@ UFE_NS_DEF namespace USDUFE_NS_DEF { // DCC specific accessor functions. +typedef PXR_NS::UsdStageWeakPtr (*StageAccessorFn)(const Ufe::Path&); +typedef Ufe::Path (*StagePathAccessorFn)(PXR_NS::UsdStageWeakPtr); typedef PXR_NS::UsdPrim (*UfePathToPrimFn)(const Ufe::Path&); typedef PXR_NS::UsdTimeCode (*TimeAccessorFn)(const Ufe::Path&); typedef bool (*IsAttributeLockedFn)(const PXR_NS::UsdAttribute& attr, std::string* errMsg); @@ -42,6 +47,34 @@ typedef bool (*IsAttributeLockedFn)(const PXR_NS::UsdAttribute& attr, std::strin // Helper functions //------------------------------------------------------------------------------ +//! Set the DCC specific stage accessor function. +//! It cannot be empty. +//! \exception std::invalid_argument If fn is empty. +USDUFE_PUBLIC +void setStageAccessorFn(StageAccessorFn fn); + +//! Get USD stage corresponding to argument UFE path. +USDUFE_PUBLIC +PXR_NS::UsdStageWeakPtr getStage(const Ufe::Path& path); + +//! Set the DCC specific stage path accessor function. +//! It cannot be empty. +//! \exception std::invalid_argument If fn is empty. +USDUFE_PUBLIC +void setStagePathAccessorFn(StagePathAccessorFn fn); + +//! Return the ProxyShape node UFE path for the argument stage. +USDUFE_PUBLIC +Ufe::Path stagePath(PXR_NS::UsdStageWeakPtr stage); + +//! Get the UFE path segment corresponding to the argument USD path. +//! If an instanceIndex is provided, the path segment for a point instance with +//! that USD path and index is returned. +USDUFE_PUBLIC +Ufe::PathSegment usdPathToUfePathSegment( + const PXR_NS::SdfPath& usdPath, + int instanceIndex = PXR_NS::UsdImagingDelegate::ALL_INSTANCES); + //! Get the UFE path representing just the USD prim for the argument UFE path. //! Any instance index component at the tail of the given path is removed from //! the returned path. diff --git a/lib/usdUfe/utils/Utils.h b/lib/usdUfe/utils/Utils.h index b12f358c8a..bf83909ff6 100644 --- a/lib/usdUfe/utils/Utils.h +++ b/lib/usdUfe/utils/Utils.h @@ -23,6 +23,12 @@ /// General utilities for working with the UsdUfe library. namespace USDUFE_NS_DEF { +/// Converts the given value \p d from units \p from to the equivalent value in units \p +inline double convertUnit(double d, double from, double to) +{ + return from == to ? d : d * from / to; +} + USDUFE_PUBLIC std::string sanitizeName(const std::string& name);