diff --git a/cmake/modules/FindMaya.cmake b/cmake/modules/FindMaya.cmake index f29bd6d87..cbcd7ca7e 100644 --- a/cmake/modules/FindMaya.cmake +++ b/cmake/modules/FindMaya.cmake @@ -24,6 +24,7 @@ # MAYA_HAS_DISPLAY_LAYER_API Presence of MFnDisplayLayer # MAYA_HAS_NEW_DISPLAY_LAYER_MESSAGING_API Presence of MDisplayLayerMemberChangedFunction # MAYA_HAS_RENDER_ITEM_HIDE_ON_PLAYBACK_API Presence of MRenderItem has HideOnPlayback API +# MAYA_HAS_VIEW_SELECTED_OBJECT_API Presence of M3dView::viewSelectedObject # MAYA_LINUX_BUILT_WITH_CXX11_ABI Maya Linux was built with new cxx11 ABI. # MAYA_MACOSX_BUILT_WITH_UB2 Maya OSX was built with Universal Binary 2. @@ -403,6 +404,15 @@ if(MAYA_INCLUDE_DIRS AND EXISTS "${MAYA_INCLUDE_DIR}/maya/MHWGeometry.h") endif() endif() +set(MAYA_HAS_VIEW_SELECTED_OBJECT_API FALSE CACHE INTERNAL "hasViewSelectedObject") +if(MAYA_INCLUDE_DIRS AND EXISTS "${MAYA_INCLUDE_DIR}/maya/M3dView.h") + file(STRINGS ${MAYA_INCLUDE_DIR}/maya/M3dView.h MAYA_HAS_API REGEX "numViewSelectedObjects") + if(MAYA_HAS_API) + set(MAYA_HAS_VIEW_SELECTED_OBJECT_API TRUE CACHE INTERNAL "hasViewSelectedObject") + message(STATUS "M3dView has viewSelectedObject API") + endif() +endif() + set(MAYA_LINUX_BUILT_WITH_CXX11_ABI FALSE CACHE INTERNAL "MayaLinuxBuiltWithCxx11ABI") if(IS_LINUX AND MAYA_Foundation_LIBRARY) # Determine if Maya (on Linux) was built using the new CXX11 ABI. diff --git a/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.cpp b/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.cpp index cebd00d15..d006f6e91 100644 --- a/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.cpp +++ b/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.cpp @@ -158,9 +158,9 @@ SelectionPtr ViewportInformationAndSceneIndicesPerViewportDataManager::GetOrCrea if (found != _isolateSelection.end()) { return found->second; } - auto selection = std::make_shared(); - _isolateSelection[viewportId] = selection; - return selection; + // Initially isolate selection is disabled. + _isolateSelection[viewportId] = nullptr; + return nullptr; } SelectionPtr ViewportInformationAndSceneIndicesPerViewportDataManager::GetIsolateSelection(const std::string& viewportId) const @@ -169,6 +169,29 @@ SelectionPtr ViewportInformationAndSceneIndicesPerViewportDataManager::GetIsolat return (found != _isolateSelection.end()) ? found->second : nullptr; } +void ViewportInformationAndSceneIndicesPerViewportDataManager::DisableIsolateSelection( + const std::string& viewportId +) +{ + _isolateSelection[viewportId] = nullptr; +} + +SelectionPtr +ViewportInformationAndSceneIndicesPerViewportDataManager::_EnableIsolateSelection( + const std::string& viewportId +) +{ + auto& is = _isolateSelection.at(viewportId); + + // If the viewport didn't have an isolate selection because it was + // disabled, create it now. + if (!is) { + is = std::make_shared(); + _isolateSelection[viewportId] = is; + } + return is; +} + void ViewportInformationAndSceneIndicesPerViewportDataManager::AddIsolateSelection( const std::string& viewportId, const PrimSelections& primSelections @@ -177,7 +200,8 @@ void ViewportInformationAndSceneIndicesPerViewportDataManager::AddIsolateSelecti if (!TF_VERIFY(_isolateSelectSceneIndex, "No isolate select scene index set.")) { return; } - _CheckAndSetViewport(viewportId); + + _EnableIsolateSelectAndSetViewport(viewportId); _isolateSelectSceneIndex->AddIsolateSelection(primSelections); } @@ -189,20 +213,21 @@ void ViewportInformationAndSceneIndicesPerViewportDataManager::RemoveIsolateSele if (!TF_VERIFY(_isolateSelectSceneIndex, "No isolate select scene index set.")) { return; } - _CheckAndSetViewport(viewportId); + _EnableIsolateSelectAndSetViewport(viewportId); _isolateSelectSceneIndex->RemoveIsolateSelection(primSelections); } void ViewportInformationAndSceneIndicesPerViewportDataManager::ReplaceIsolateSelection( - const std::string& viewportId, - const SelectionConstPtr& selection + const std::string& viewportId, + const SelectionPtr& isolateSelection ) { if (!TF_VERIFY(_isolateSelectSceneIndex, "No isolate select scene index set.")) { return; } - _CheckAndSetViewport(viewportId); - _isolateSelectSceneIndex->ReplaceIsolateSelection(selection); + + _isolateSelection[viewportId] = isolateSelection; + _isolateSelectSceneIndex->SetViewport(viewportId, isolateSelection); } void ViewportInformationAndSceneIndicesPerViewportDataManager::ClearIsolateSelection(const std::string& viewportId) @@ -210,7 +235,7 @@ void ViewportInformationAndSceneIndicesPerViewportDataManager::ClearIsolateSelec if (!TF_VERIFY(_isolateSelectSceneIndex, "No isolate select scene index set.")) { return; } - _CheckAndSetViewport(viewportId); + _EnableIsolateSelectAndSetViewport(viewportId); _isolateSelectSceneIndex->ClearIsolateSelection(); } @@ -218,7 +243,10 @@ void ViewportInformationAndSceneIndicesPerViewportDataManager::SetIsolateSelectS const IsolateSelectSceneIndexRefPtr& sceneIndex ) { - _isolateSelectSceneIndex = sceneIndex; + _isolateSelectSceneIndex = sceneIndex; + // If we're resetting the isolate select scene index, we're starting anew, + // so clear out existing isolate selections. + _isolateSelection.clear(); } IsolateSelectSceneIndexRefPtr @@ -228,12 +256,22 @@ ViewportInformationAndSceneIndicesPerViewportDataManager::GetIsolateSelectSceneI } void -ViewportInformationAndSceneIndicesPerViewportDataManager::_CheckAndSetViewport( +ViewportInformationAndSceneIndicesPerViewportDataManager::_EnableIsolateSelectAndSetViewport( const std::string& viewportId ) { + const bool enabled{_isolateSelectSceneIndex->GetIsolateSelection()}; + auto isolateSelection = _EnableIsolateSelection(viewportId); + + // If the isolate select scene index is not set to the right viewport, + // do a viewport switch. if (_isolateSelectSceneIndex->GetViewportId() != viewportId) { - _isolateSelectSceneIndex->SetViewport(viewportId, _isolateSelection.at(viewportId)); + _isolateSelectSceneIndex->SetViewport(viewportId, isolateSelection); + } + else if (!enabled) { + // Same viewport, so no viewport switch, but must move from disabled to + // enabled for that viewport. + _isolateSelectSceneIndex->SetIsolateSelection(isolateSelection); } } @@ -288,6 +326,9 @@ void ViewportInformationAndSceneIndicesPerViewportDataManager::RemoveAllViewport #endif _viewportsInformationAndSceneIndicesPerViewportData.clear();//Delete all of them + + _isolateSelection.clear(); + _isolateSelectSceneIndex = nullptr; } } //End of namespace FVP_NS_DEF { diff --git a/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.h b/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.h index 645b92910..4c24c2be4 100644 --- a/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.h +++ b/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.h @@ -63,6 +63,7 @@ class FVP_API ViewportInformationAndSceneIndicesPerViewportDataManager SelectionPtr GetOrCreateIsolateSelection(const std::string& viewportId); SelectionPtr GetIsolateSelection(const std::string& viewportId) const; + void DisableIsolateSelection(const std::string& viewportId); void AddIsolateSelection( const std::string& viewportId, @@ -73,8 +74,8 @@ class FVP_API ViewportInformationAndSceneIndicesPerViewportDataManager const PrimSelections& primSelections ); void ReplaceIsolateSelection( - const std::string& viewportId, - const SelectionConstPtr& selection + const std::string& viewportId, + const SelectionPtr& selection ); void ClearIsolateSelection(const std::string& viewportId); @@ -98,12 +99,16 @@ class FVP_API ViewportInformationAndSceneIndicesPerViewportDataManager const ViewportInformationAndSceneIndicesPerViewportDataManager& ) = delete; - void _CheckAndSetViewport(const std::string& viewportId); + SelectionPtr _EnableIsolateSelection(const std::string& viewportId); + void _EnableIsolateSelectAndSetViewport(const std::string& viewportId); ///Hydra viewport information ViewportInformationAndSceneIndicesPerViewportDataVector _viewportsInformationAndSceneIndicesPerViewportData; - // Isolate selection, keyed by viewportId. + // Isolate selection, keyed by viewportId. A null selection pointer means + // isolate select for that viewport is disabled. Disabling isolate select + // on a viewport clears its isolate selection, so that at next isolate + // select enable for that viewport its isolate selection is empty. std::map _isolateSelection; // Isolate select scene index. @@ -111,7 +116,7 @@ class FVP_API ViewportInformationAndSceneIndicesPerViewportDataManager }; // Convenience shorthand declaration. -using PerViewportDataManager = ViewportInformationAndSceneIndicesPerViewportDataManager; +using ViewportDataMgr = ViewportInformationAndSceneIndicesPerViewportDataManager; } //End of namespace FVP_NS_DEF diff --git a/lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.cpp b/lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.cpp index b09693936..1513a8bc3 100644 --- a/lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.cpp +++ b/lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.cpp @@ -31,6 +31,12 @@ namespace { const HdContainerDataSourceHandle visOff = HdVisibilitySchema::BuildRetained( HdRetainedTypedSampledDataSource::New(false)); + +bool disabled(const Fvp::SelectionConstPtr& a, const Fvp::SelectionConstPtr& b) +{ + return !a && !b; +} + } namespace FVP_NS_DEF { @@ -75,11 +81,13 @@ HdSceneIndexPrim IsolateSelectSceneIndex::GetPrim(const SdfPath& primPath) const auto inputPrim = GetInputSceneIndex()->GetPrim(primPath); - // If isolate selection is empty, everything is included. - if (_isolateSelection->IsEmpty()) { + // If there is no isolate selection, everything is included. + if (!_isolateSelection) { return inputPrim; } + // If isolate selection is empty, then nothing is included (everything + // is excluded), as desired. const bool included = _isolateSelection->HasAncestorOrDescendantInclusive(primPath); @@ -129,6 +137,10 @@ void IsolateSelectSceneIndex::AddIsolateSelection(const PrimSelections& primSele TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) .Msg("IsolateSelectSceneIndex::AddIsolateSelection() called for viewport %s.\n", _viewportId.c_str()); + if (!TF_VERIFY(_isolateSelection, "AddIsolateSelection() called for viewport %s while isolate selection is disabled", _viewportId.c_str())) { + return; + } + HdSceneIndexObserver::DirtiedPrimEntries dirtiedEntries; for (const auto& primSelection : primSelections) { TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) @@ -145,6 +157,10 @@ void IsolateSelectSceneIndex::RemoveIsolateSelection(const PrimSelections& primS TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) .Msg("IsolateSelectSceneIndex::RemoveIsolateSelection() called for viewport %s.\n", _viewportId.c_str()); + if (!TF_VERIFY(_isolateSelection, "RemoveIsolateSelection() called for viewport %s while isolate selection is disabled", _viewportId.c_str())) { + return; + } + HdSceneIndexObserver::DirtiedPrimEntries dirtiedEntries; for (const auto& primSelection : primSelections) { TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) @@ -161,6 +177,10 @@ void IsolateSelectSceneIndex::ClearIsolateSelection() TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) .Msg("IsolateSelectSceneIndex::ClearIsolateSelection() called for viewport %s.\n", _viewportId.c_str()); + if (!TF_VERIFY(_isolateSelection, "ClearIsolateSelection() called for viewport %s while isolate selection is disabled", _viewportId.c_str())) { + return; + } + auto isolateSelectPaths = _isolateSelection->GetFullySelectedPaths(); HdSceneIndexObserver::DirtiedPrimEntries dirtiedEntries; @@ -175,30 +195,53 @@ void IsolateSelectSceneIndex::ClearIsolateSelection() _SendPrimsDirtied(dirtiedEntries); } -void IsolateSelectSceneIndex::ReplaceIsolateSelection(const SelectionConstPtr& isolateSelection) +void IsolateSelectSceneIndex::ReplaceIsolateSelection(const SelectionConstPtr& newIsolateSelection) { TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) .Msg("IsolateSelectSceneIndex::ReplaceIsolateSelection() called for viewport %s.\n", _viewportId.c_str()); - _ReplaceIsolateSelection(isolateSelection); + if (!TF_VERIFY(_isolateSelection, "ReplaceIsolateSelection() called for viewport %s while isolate selection is disabled", _viewportId.c_str())) { + return; + } + + if (!TF_VERIFY(newIsolateSelection, "ReplaceIsolateSelection() called for viewport %s with illegal null isolate selection pointer", _viewportId.c_str())) { + return; + } + + _ReplaceIsolateSelection(newIsolateSelection); - _isolateSelection->Replace(*isolateSelection); + _isolateSelection->Replace(*newIsolateSelection); } -void IsolateSelectSceneIndex::_ReplaceIsolateSelection(const SelectionConstPtr& isolateSelection) +void IsolateSelectSceneIndex::_ReplaceIsolateSelection(const SelectionConstPtr& newIsolateSelection) { - // Keep paths in a set to minimize dirtying. First clear old paths. - auto clearedPaths = _isolateSelection->GetFullySelectedPaths(); - std::set dirtyPaths(clearedPaths.begin(), clearedPaths.end()); + // Trivial case of going from disabled to disabled is an early out. + if (disabled(_isolateSelection, newIsolateSelection)) { + return; + } - // Then add new paths. - const auto& newPaths = isolateSelection->GetFullySelectedPaths(); - for (const auto& primPath : newPaths) { - dirtyPaths.insert(primPath); + // If the old and new isolate selection are equal, nothing to do. Only + // handling trivial case of empty isolate select (i.e. hide everything) for + // the moment. + if ((_isolateSelection && _isolateSelection->IsEmpty()) && + (newIsolateSelection && newIsolateSelection->IsEmpty())) { + return; } + + // Keep paths in a set to minimize dirtying. + std::set dirtyPaths; + + // First clear old paths. If we were disabled do nothing. If there were + // no selected paths the whole scene was invisible. + _InsertSelectedPaths(_isolateSelection, dirtyPaths); + + // Then add new paths. If we're disabled do nothing. If there are no + // selected paths the whole scene is invisible. + _InsertSelectedPaths(newIsolateSelection, dirtyPaths); - // Finally, dirty all cleared and added prim paths. HdSceneIndexObserver::DirtiedPrimEntries dirtiedEntries; + + // Dirty all cleared and added prim paths. for (const auto& primPath : dirtyPaths) { _DirtyVisibility(primPath, &dirtiedEntries); } @@ -206,9 +249,25 @@ void IsolateSelectSceneIndex::_ReplaceIsolateSelection(const SelectionConstPtr& _SendPrimsDirtied(dirtiedEntries); } +void IsolateSelectSceneIndex::_InsertSelectedPaths( + const SelectionConstPtr& selection, + std::set& dirtyPaths +) +{ + if (!selection) { + return; + } + const auto& paths = selection->IsEmpty() ? + GetChildPrimPaths(SdfPath::AbsoluteRootPath()) : + selection->GetFullySelectedPaths(); + for (const auto& primPath : paths) { + dirtyPaths.insert(primPath); + } +} + void IsolateSelectSceneIndex::SetViewport( const std::string& viewportId, - const SelectionPtr& isolateSelection + const SelectionPtr& newIsolateSelection ) { TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) @@ -216,20 +275,34 @@ void IsolateSelectSceneIndex::SetViewport( TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) .Msg(" Old viewport was %s.\n", _viewportId.c_str()); TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) - .Msg(" Old selection is %p, new selection is %p.\n", &*_isolateSelection, &*isolateSelection); + .Msg(" Old selection is %p, new selection is %p.\n", (_isolateSelection ? &*_isolateSelection : (void*) 0), (newIsolateSelection ? &*newIsolateSelection : (void*) 0)); - if ((isolateSelection == _isolateSelection) || + if ((newIsolateSelection == _isolateSelection) && (viewportId == _viewportId)) { TF_WARN("IsolateSelectSceneIndex::SetViewport() called with identical information, no operation performed."); return; } - _ReplaceIsolateSelection(isolateSelection); + // If the previous and new viewports both had isolate select disabled, + // no visibility to dirty. + if (disabled(_isolateSelection, newIsolateSelection)) { + _viewportId = viewportId; + return; + } + + _ReplaceIsolateSelection(newIsolateSelection); - _isolateSelection = isolateSelection; + _isolateSelection = newIsolateSelection; _viewportId = viewportId; } +void IsolateSelectSceneIndex::SetIsolateSelection( + const SelectionPtr& newIsolateSelection +) +{ + _isolateSelection = newIsolateSelection; +} + SelectionPtr IsolateSelectSceneIndex::GetIsolateSelection() const { return _isolateSelection; diff --git a/lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.h b/lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.h index f7a69d3c0..775ea20e4 100644 --- a/lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.h +++ b/lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.h @@ -51,7 +51,9 @@ typedef PXR_NS::TfRefPtr IsolateSelectSceneIndexC /// /// At time of writing a single isolate select scene index is used to service /// all viewports in the application, by switching the isolate selection on the -/// isolate scene index using IsolateSelectSceneIndex::SetViewport(). +/// isolate scene index using IsolateSelectSceneIndex::SetViewport(). If a +/// null pointer selection is passed to SetViewport(), the isolate select scene +/// index is disabled and behaves as a pass-through. /// /// IsolateSelectSceneIndex::GetPrim() passes through prims that have an /// ancestor or descendant (including themselves) in the isolate selection. @@ -132,7 +134,12 @@ class IsolateSelectSceneIndex : const SelectionPtr& isolateSelection ); - // Get viewport information for this scene index. + FVP_API + void SetIsolateSelection(const SelectionPtr& selection); + + // Get viewport information for this scene index, respectively the viewport + // ID and the isolate selection. A null isolate selection pointer means + // the isolate select scene index is disabled (pass-through). FVP_API std::string GetViewportId() const; FVP_API @@ -171,6 +178,11 @@ class IsolateSelectSceneIndex : void _ReplaceIsolateSelection(const SelectionConstPtr& selection); + void _InsertSelectedPaths( + const SelectionConstPtr& selection, + std::set& dirtyPaths + ); + std::string _viewportId; SelectionPtr _isolateSelection{}; diff --git a/lib/flowViewport/selection/fvpPathMapperRegistry.cpp b/lib/flowViewport/selection/fvpPathMapperRegistry.cpp index 3f6929676..629155956 100644 --- a/lib/flowViewport/selection/fvpPathMapperRegistry.cpp +++ b/lib/flowViewport/selection/fvpPathMapperRegistry.cpp @@ -20,6 +20,7 @@ #include #include +#include #include namespace { @@ -103,4 +104,25 @@ PathMapperConstPtr PathMapperRegistry::GetMapper(const Ufe::Path& path) const return fallbackMapper; } +Fvp::PrimSelections ufePathToPrimSelections(const Ufe::Path& appPath) +{ + PXR_NAMESPACE_USING_DIRECTIVE + + Fvp::PrimSelections primSelections; + + auto mapper = Fvp::PathMapperRegistry::Instance().GetMapper(appPath); + + if (!mapper) { + TF_WARN("No registered mapping for path %s, no prim path returned.", Ufe::PathString::string(appPath).c_str()); + } + else { + primSelections = mapper->UfePathToPrimSelections(appPath); + if (primSelections.empty()) { + TF_WARN("Mapping for path %s returned no prim path.", Ufe::PathString::string(appPath).c_str()); + } + } + + return primSelections; +} + } diff --git a/lib/flowViewport/selection/fvpPathMapperRegistry.h b/lib/flowViewport/selection/fvpPathMapperRegistry.h index 54e032e0b..d4a97a4ce 100644 --- a/lib/flowViewport/selection/fvpPathMapperRegistry.h +++ b/lib/flowViewport/selection/fvpPathMapperRegistry.h @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -91,6 +92,21 @@ class PathMapperRegistry { friend class PXR_NS::TfSingleton; }; +/** + * @brief Get the prim selections for a given application path. + * + * If an application path corresponds to a scene index prim, this function will + * return one or more prim selections for it. If no such scene index prim + * exists, the returned prim selections will be empty. It retrieves the + * appropriate path mapper from the path mapper registry and invokes it on the + * argument appPath. + * + * @param[in] appPath The application path for which prim selections should be returned. + * @return Zero or more prim selections. + */ +FVP_API +PrimSelections ufePathToPrimSelections(const Ufe::Path& appPath); + } #endif diff --git a/lib/flowViewport/selection/fvpSelection.cpp b/lib/flowViewport/selection/fvpSelection.cpp index 5377a47c2..ac04120e4 100644 --- a/lib/flowViewport/selection/fvpSelection.cpp +++ b/lib/flowViewport/selection/fvpSelection.cpp @@ -42,10 +42,18 @@ #include "pxr/imaging/hd/selectionsSchema.h" +#include // std::shared_ptr + PXR_NAMESPACE_USING_DIRECTIVE namespace FVP_NS_DEF { +/* static */ +SelectionPtr Selection::New() +{ + return std::make_shared(); +} + bool Selection::Add(const PrimSelection& primSelection) { diff --git a/lib/flowViewport/selection/fvpSelection.h b/lib/flowViewport/selection/fvpSelection.h index a9362321f..c949762a2 100644 --- a/lib/flowViewport/selection/fvpSelection.h +++ b/lib/flowViewport/selection/fvpSelection.h @@ -43,6 +43,10 @@ class Selection { public: + // Create a reference-counted Selection. + FVP_API + static SelectionPtr New(); + // Add primPath to selection and return true if the argument is not empty. FVP_API bool Add(const PrimSelection& primSelection); diff --git a/lib/mayaHydra/mayaPlugin/CMakeLists.txt b/lib/mayaHydra/mayaPlugin/CMakeLists.txt index 689dfeeae..e07ecbf00 100644 --- a/lib/mayaHydra/mayaPlugin/CMakeLists.txt +++ b/lib/mayaHydra/mayaPlugin/CMakeLists.txt @@ -41,6 +41,13 @@ target_compile_definitions(${TARGET_NAME} $<$:_WIN32> ) +if (MAYA_HAS_VIEW_SELECTED_OBJECT_API) + target_compile_definitions(${TARGET_NAME} + PRIVATE + MAYA_HAS_VIEW_SELECTED_OBJECT_API=1 + ) +endif() + # ----------------------------------------------------------------------------- # include directories # ----------------------------------------------------------------------------- diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.cpp b/lib/mayaHydra/mayaPlugin/renderOverride.cpp index ae0b36f82..ebc7334e8 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.cpp +++ b/lib/mayaHydra/mayaPlugin/renderOverride.cpp @@ -46,13 +46,16 @@ #include #include #include +#ifdef MAYA_HAS_VIEW_SELECTED_OBJECT_API #include +#endif #include #include #include #include #include #include +#include #include #include @@ -171,6 +174,7 @@ void replaceSelectionTask(PXR_NS::HdTaskSharedPtrVector* tasks) *found = HdTaskSharedPtr(new Fvp::SelectionTask); } +#ifdef MAYA_HAS_VIEW_SELECTED_OBJECT_API std::string getRenderingDestination( const MHWRender::MFrameContext* frameContext ) @@ -180,6 +184,7 @@ std::string getRenderingDestination( frameContext->renderingDestination(viewportId); return std::string(viewportId.asChar()); } +#endif inline Fvp::LightsManagementSceneIndex::LightingMode convertFromMayaLightingModeToFlowViewportLightMode(MFrameContext::LightingMode mayaLightingMode) { @@ -341,6 +346,12 @@ MtohRenderOverride::~MtohRenderOverride() if (_timerCallback) MMessage::removeCallback(_timerCallback); +#ifdef MAYA_HAS_VIEW_SELECTED_OBJECT_API + if (_viewSelectedChangedCb) { + MMessage::removeCallback(_viewSelectedChangedCb); + } +#endif + constexpr bool fullReset = true; ClearHydraResources(fullReset); @@ -764,6 +775,7 @@ MStatus MtohRenderOverride::Render( TF_DEBUG(MAYAHYDRALIB_RENDEROVERRIDE_SCENE_INDEX_CHAIN_MGMT) .Msg("Re-using existing scene index chain to render %s\n", panelNameStr.c_str()); +#ifdef MAYA_HAS_VIEW_SELECTED_OBJECT_API // Make sure the isolate selection scene index set to the proper // isolate selection. We currently have a single scene index tree, // thus a single isolate select scene index is common to and @@ -778,9 +790,11 @@ MStatus MtohRenderOverride::Render( isSi->SetViewport(panelNameStr, isolateSelection); } else { + // This case includes disabled (null pointer) isolate selection. TF_DEBUG(MAYAHYDRALIB_RENDEROVERRIDE_SCENE_INDEX_CHAIN_MGMT) - .Msg("Re-using isolate selection %p\n", &*isolateSelection); + .Msg("Re-using isolate selection %p\n", (isolateSelection ? &*isolateSelection : (void*) 0)); } +#endif } } @@ -1188,10 +1202,11 @@ void MtohRenderOverride::_CreateSceneIndicesChainAfterMergingSceneIndex(const MH { //This function is where happens the ordering of filtering scene indices that are after the merging scene index //We use as its input scene index : _inputSceneIndexOfFilteringSceneIndicesChain +#ifdef MAYA_HAS_VIEW_SELECTED_OBJECT_API auto viewportId = getRenderingDestination(getFrameContext()); // Add isolate select scene index. - auto& perVpDataMgr = Fvp::PerViewportDataManager::Get(); + auto& perVpDataMgr = Fvp::ViewportDataMgr::Get(); auto selection = perVpDataMgr.GetOrCreateIsolateSelection(viewportId); auto isSi = Fvp::IsolateSelectSceneIndex::New( viewportId, selection, _inputSceneIndexOfFilteringSceneIndicesChain); @@ -1199,6 +1214,9 @@ void MtohRenderOverride::_CreateSceneIndicesChainAfterMergingSceneIndex(const MH // all viewports. perVpDataMgr.SetIsolateSelectSceneIndex(isSi); _lastFilteringSceneIndexBeforeCustomFiltering = isSi; +#else + _lastFilteringSceneIndexBeforeCustomFiltering = _inputSceneIndexOfFilteringSceneIndicesChain; +#endif // Add display style scene index _lastFilteringSceneIndexBeforeCustomFiltering = _displayStyleSceneIndex = @@ -1376,6 +1394,16 @@ MStatus MtohRenderOverride::setup(const MString& destination) _renderPanelCallbacks.emplace_back(destination, newCallbacks); } +#ifdef MAYA_HAS_VIEW_SELECTED_OBJECT_API + if (!_viewSelectedChangedCb) { + _viewSelectedChangedCb = MUiMessage::add3dViewSelectedChangedCallback( + _ViewSelectedChangedCb, this, &status); + if (status != MStatus::kSuccess) { + return status; + } + } +#endif + auto* renderer = MHWRender::MRenderer::theRenderer(); if (renderer == nullptr) { return MStatus::kFailure; @@ -1725,6 +1753,56 @@ void MtohRenderOverride::_RenderOverrideChangedCallback( } } +#ifdef MAYA_HAS_VIEW_SELECTED_OBJECT_API +/* static */ +void MtohRenderOverride::_ViewSelectedChangedCb( + const MString& viewName, + bool viewSelectedObjectsChanged, + void* /* data */ +) +{ + M3dView view; + if (!TF_VERIFY(M3dView::getM3dViewFromModelPanel(viewName, view) == MS::kSuccess, + "No view found for view name %.", viewName.asChar())) { + return; + } + + // The M3dView returns the list of view selected objects as strings. + // If isolate select is turned off, we want to disable isolate selection. + // Otherwise, replace with what is in the M3dView. + auto& vpDataMgr = Fvp::ViewportDataMgr::Get(); + if (!view.viewSelected()) { + vpDataMgr.DisableIsolateSelection(viewName.asChar()); + return; + } + + // The M3dView returns the list of view selected objects as strings. + // Loop over the view selected objects and try to create UFE paths from + // them. If there is more than one element to the MStringArray, this + // indicates Maya components. Currently not supporting components, + // including the case where a single element holds components. + auto isolateSelection = std::make_shared(); + const auto nbObjects = view.numViewSelectedObjects(); + for (unsigned int i = 0; i < nbObjects; ++i) { + MStringArray objectStrings; + TF_VERIFY(view.viewSelectedObject(i, objectStrings) == MS::kSuccess); + if (objectStrings.length() > 1) { + std::ostringstream oss; + oss << objectStrings; + TF_WARN("Unimplemented isolate select on Maya components %s", oss.str().c_str()); + continue; + } + auto path = Ufe::PathString::path(objectStrings[0].asChar()); + auto primSelections = Fvp::ufePathToPrimSelections(path); + for (const auto& primSelection : primSelections) { + isolateSelection->Add(primSelection); + } + } + + vpDataMgr.ReplaceIsolateSelection(viewName.asChar(), isolateSelection); +} +#endif + // return true if we need to recreate the filtering scene indices chain because of a change, false otherwise. bool MtohRenderOverride::_NeedToRecreateTheSceneIndicesChain(unsigned int currentDisplayStyle) { diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.h b/lib/mayaHydra/mayaPlugin/renderOverride.h index cec0dcb4e..a04d4b6c0 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.h +++ b/lib/mayaHydra/mayaPlugin/renderOverride.h @@ -232,6 +232,12 @@ class MtohRenderOverride : public MHWRender::MRenderOverride, const MString& oldOverride, const MString& newOverride, void* data); +#ifdef MAYA_HAS_VIEW_SELECTED_OBJECT_API + static void _ViewSelectedChangedCb( + const MString& panelName, + bool viewSelectedObjectsChanged, + void* data); +#endif MtohRendererDescription _rendererDesc; @@ -242,6 +248,10 @@ class MtohRenderOverride : public MHWRender::MRenderOverride, PanelCallbacksList _renderPanelCallbacks; const MtohRenderGlobals& _globals; +#ifdef MAYA_HAS_VIEW_SELECTED_OBJECT_API + MCallbackId _viewSelectedChangedCb{0}; +#endif + std::mutex _lastRenderTimeMutex; std::chrono::system_clock::time_point _lastRenderTime; std::atomic _backupFrameBufferWorkaround = { false }; diff --git a/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt b/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt index 97a9ed99e..2558c9448 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt +++ b/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt @@ -57,7 +57,6 @@ set(INTERACTIVE_TEST_SCRIPT_FILES cpp/testColorPreferences.py cpp/testCppFramework.py cpp/testDataProducerExample.py - cpp/testIsolateSelect.py cpp/testMayaSceneFlattening.py cpp/testMayaUsdUfeItems.py cpp/testMergingSceneIndex.py @@ -84,6 +83,12 @@ set(INTERACTIVE_TEST_SCRIPT_FILES cpp/testGeomSubsetsWireframeHighlight.py ) +if (MAYA_HAS_VIEW_SELECTED_OBJECT_API) + list(APPEND INTERACTIVE_TEST_SCRIPT_FILES + cpp/testIsolateSelect.py + ) +endif() + #Add this test only if the MayaUsd_FOUND (so also MAYAUSDAPI_LIBRARY) has been found during compile time. if(MayaUsd_FOUND) list(APPEND INTERACTIVE_TEST_SCRIPT_FILES testMayaUsdAPIUsage.py) diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/IsolateSelectTest/singleViewportIsolateSelectCylinder1.png b/test/lib/mayaUsd/render/mayaToHydra/cpp/IsolateSelectTest/singleViewportIsolateSelectCylinder1.png new file mode 100644 index 000000000..1a1f1385f Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/cpp/IsolateSelectTest/singleViewportIsolateSelectCylinder1.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testIsolateSelect.cpp b/test/lib/mayaUsd/render/mayaToHydra/cpp/testIsolateSelect.cpp index 383bf221f..ceb0e5c1c 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testIsolateSelect.cpp +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testIsolateSelect.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -40,7 +41,7 @@ TEST(TestHydraPrim, isVisible) ASSERT_EQ(argc, 1); const Ufe::Path appPath(Ufe::PathString::path(argv[0])); - auto primSelections = ufePathToPrimSelections(appPath); + auto primSelections = Fvp::ufePathToPrimSelections(appPath); // If an application path maps to multiple prim selections, all prim // selections must be visible, else we fail. @@ -63,7 +64,7 @@ TEST(TestHydraPrim, notVisible) ASSERT_EQ(argc, 1); const Ufe::Path appPath(Ufe::PathString::path(argv[0])); - auto primSelections = ufePathToPrimSelections(appPath); + auto primSelections = Fvp::ufePathToPrimSelections(appPath); // If an application path maps to multiple prim selections, all prim // selections must be invisible, else we fail. @@ -84,9 +85,9 @@ TEST(TestIsolateSelection, add) const std::string viewportId(argv[0]); const Ufe::Path appPath(Ufe::PathString::path(argv[1])); - auto& perVpDataMgr = Fvp::PerViewportDataManager::Get(); - auto primSelections = ufePathToPrimSelections(appPath); - perVpDataMgr.AddIsolateSelection(viewportId, primSelections); + auto& vpDataMgr = Fvp::ViewportDataMgr::Get(); + auto primSelections = Fvp::ufePathToPrimSelections(appPath); + vpDataMgr.AddIsolateSelection(viewportId, primSelections); } TEST(TestIsolateSelection, remove) @@ -96,9 +97,9 @@ TEST(TestIsolateSelection, remove) const std::string viewportId(argv[0]); const Ufe::Path appPath(Ufe::PathString::path(argv[1])); - auto& perVpDataMgr = Fvp::PerViewportDataManager::Get(); - auto primSelections = ufePathToPrimSelections(appPath); - perVpDataMgr.RemoveIsolateSelection(viewportId, primSelections); + auto& vpDataMgr = Fvp::ViewportDataMgr::Get(); + auto primSelections = Fvp::ufePathToPrimSelections(appPath); + vpDataMgr.RemoveIsolateSelection(viewportId, primSelections); } TEST(TestIsolateSelection, clear) @@ -107,8 +108,8 @@ TEST(TestIsolateSelection, clear) ASSERT_EQ(argc, 1); const std::string viewportId(argv[0]); - auto& perVpDataMgr = Fvp::PerViewportDataManager::Get(); - perVpDataMgr.ClearIsolateSelection(viewportId); + auto& vpDataMgr = Fvp::ViewportDataMgr::Get(); + vpDataMgr.ClearIsolateSelection(viewportId); } TEST(TestIsolateSelection, replace) @@ -117,16 +118,42 @@ TEST(TestIsolateSelection, replace) ASSERT_GE(argc, 2); const std::string viewportId(argv[0]); - auto isolateSelect = std::make_shared(); + // Use allocator from Flow Viewport Toolkit library. Directly calling + // + // auto isolateSelect = std::make_shared(); + // + // here will cause a crash on test exit, as the last reference to the + // isolate selection is in the isolate select scene index, which can be + // destroyed and rebuilt on redraw. On Windows, if the DLL that performed + // the allocation of the shared_ptr has been unloaded from the process, the + // isolate selection destruction called by the shared_ptr destructor will + // crash. + // + // This is exactly our case, as our host mayaHydraCppTests DLL plugin is + // unloaded at program exit, but Maya performs additional redraws after the + // plugin unload, which cause the isolate selection shared_ptr to be + // destroyed, which causes a call to code in an unloaded DLL, which crashes. + auto isolateSelect = Fvp::Selection::New(); for (int i=1; i < argc; ++i) { const Ufe::Path appPath(Ufe::PathString::path(argv[i])); - auto primSelections = ufePathToPrimSelections(appPath); + auto primSelections = Fvp::ufePathToPrimSelections(appPath); for (const auto& primSelection : primSelections) { isolateSelect->Add(primSelection); } } - auto& perVpDataMgr = Fvp::PerViewportDataManager::Get(); - perVpDataMgr.ReplaceIsolateSelection(viewportId, isolateSelect); + auto& vpDataMgr = Fvp::ViewportDataMgr::Get(); + vpDataMgr.ReplaceIsolateSelection(viewportId, isolateSelect); } + +TEST(TestIsolateSelection, disable) +{ + auto [argc, argv] = getTestingArgs(); + ASSERT_EQ(argc, 1); + const std::string viewportId(argv[0]); + + auto& vpDataMgr = Fvp::ViewportDataMgr::Get(); + vpDataMgr.DisableIsolateSelection(viewportId); +} + diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testIsolateSelect.py b/test/lib/mayaUsd/render/mayaToHydra/cpp/testIsolateSelect.py index 873eb7a17..7492c0b69 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testIsolateSelect.py +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testIsolateSelect.py @@ -15,18 +15,57 @@ import fixturesUtils import mtohUtils -from testUtils import PluginLoaded import mayaUsd import mayaUsd_createStageWithNewLayer import maya.cmds as cmds +import maya.mel as mel from pxr import UsdGeom +def enableIsolateSelect(modelPanel): + # Surprisingly + # + # cmds.isolateSelect('modelPanel1', state=1) + # + # is insufficient to turn on isolate selection in a viewport, and we must + # use the MEL script used by the menu and Ctrl-1 hotkey. This is because + # the viewport uses the selectionConnection command to filter the selection + # it receives and create its isolate selection, and the the + # mainListConnection, lockMainConnection and unlockMainConnection flags of + # the editor command to suspend changes to its selection connection. See + # the documentation for more details. + cmds.setFocus(modelPanel) + mel.eval("enableIsolateSelect %s 1" % modelPanel) + +def disableIsolateSelect(modelPanel): + cmds.setFocus(modelPanel) + mel.eval("enableIsolateSelect %s 0" % modelPanel) + class TestIsolateSelect(mtohUtils.MayaHydraBaseTestCase): # MayaHydraBaseTestCase.setUpClass requirement. _file = __file__ # Base class setUp() defines HdStorm as the renderer. + _pluginsToLoad = ['mayaHydraCppTests', 'mayaHydraFlowViewportAPILocator'] + _pluginsToUnload = [] + + @classmethod + def setUpClass(cls): + super(TestIsolateSelect, cls).setUpClass() + for p in cls._pluginsToLoad: + if not cmds.pluginInfo(p, q=True, loaded=True): + cls._pluginsToUnload.append(p) + cmds.loadPlugin(p, quiet=True) + + @classmethod + def tearDownClass(cls): + super(TestIsolateSelect, cls).tearDownClass() + # Clean out the scene to allow all plugins to unload cleanly. + cmds.file(new=True, force=True) + for p in reversed(cls._pluginsToUnload): + if p != 'mayaHydraFlowViewportAPILocator': + cmds.unloadPlugin(p) + def setupScene(self): proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() stage = mayaUsd.lib.GetPrim(proxyShapePathStr).GetStage() @@ -34,16 +73,37 @@ def setupScene(self): stage.DefinePrim('/parent1', 'Xform') stage.DefinePrim('/parent2', 'Xform') - UsdGeom.Cylinder.Define(stage, "/parent1/cylinder1") - UsdGeom.Cone.Define(stage, "/parent2/cone1") - UsdGeom.Sphere.Define(stage, "/parent2/sphere1") + cylinder1 = UsdGeom.Cylinder.Define(stage, "/parent1/cylinder1") + cone1 = UsdGeom.Cone.Define(stage, "/parent2/cone1") + sphere1 = UsdGeom.Sphere.Define(stage, "/parent2/sphere1") + + # Move objects around so that snapshot comparison is significant. + + # Move USD objects. Can also use undoable Maya cmds.move(), but using + # the USD APIs is simpler. + cylinder1.AddTranslateOp().Set(value=(0, 2, 0)) + cone1.AddTranslateOp().Set(value=(2, 0, 0)) + sphere1.AddTranslateOp().Set(value=(-2, 0, 0)) cmds.polyTorus() cmds.polySphere() + cmds.move(2, 0, 0, r=True) cmds.polyCube() + cmds.move(-2, 0, 0, r=True) cmds.polyCone() cmds.group('pSphere1', 'pCube1') + cmds.move(0, 0, 2, r=True) cmds.group('pCone1') + cmds.move(0, 0, 2, r=True) + + # Add a Hydra-only data producer. + cmds.createNode("MhFlowViewportAPILocator") + cmds.setAttr("MhFlowViewportAPILocator1.numCubesX", 2) + cmds.setAttr("MhFlowViewportAPILocator1.numCubesY", 2) + cmds.setAttr("MhFlowViewportAPILocator1.cubeHalfSize", 0.25) + cmds.setAttr("MhFlowViewportAPILocator1.cubesDeltaTrans", 1, 1, 1) + + cmds.move(0, 0, 4, "transform1", r=True) cmds.refresh() @@ -100,235 +160,215 @@ def assertVisibility(self, visible, notVisible): def test_isolateSelectSingleViewport(self): scene = self.setupScene() - with PluginLoaded('mayaHydraCppTests'): - # The default viewport is in the following panel. - vpPanel = 'modelPanel4' - #============================================================ - # Add - #============================================================ + # The default viewport is in the following panel. + modelPanel = 'modelPanel4' - # Add a single object to the isolate selection. Only that object, - # its ancestors, and its descendants are visible. - cmds.mayaHydraCppTest(vpPanel, "|pTorus1", f="TestIsolateSelection.add") + cmds.select(clear=True) + enableIsolateSelect(modelPanel) - visible = ['|pTorus1', '|pTorus1|pTorusShape1'] - notVisible = scene.copy() - for v in visible: - notVisible.remove(v) + #============================================================ + # Add + #============================================================ - self.assertVisibility(visible, notVisible) + # Add a single object to the isolate selection. Only that object, + # its ancestors, and its descendants are visible. + cmds.mayaHydraCppTest(modelPanel, "|pTorus1", f="TestIsolateSelection.add") - # Add a USD object to the isolate selection. - cmds.mayaHydraCppTest(vpPanel, '|stage1|stageShape1,/parent2', f="TestIsolateSelection.add") + visible = ['|pTorus1', '|pTorus1|pTorusShape1'] + notVisible = scene.copy() + for v in visible: + notVisible.remove(v) - for p in ['|stage1|stageShape1,/parent2', - '|stage1|stageShape1,/parent2/cone1', - '|stage1|stageShape1,/parent2/sphere1']: - visible.append(p) - notVisible.remove(p) + self.assertVisibility(visible, notVisible) - self.assertVisibility(visible, notVisible) + # Add a USD object to the isolate selection. + cmds.mayaHydraCppTest(modelPanel, '|stage1|stageShape1,/parent2', f="TestIsolateSelection.add") - #============================================================ - # Remove - #============================================================ + for p in ['|stage1|stageShape1,/parent2', + '|stage1|stageShape1,/parent2/cone1', + '|stage1|stageShape1,/parent2/sphere1']: + visible.append(p) + notVisible.remove(p) - # Remove the Maya object from the isolate selection. Only the USD - # isolate selected objects are visible. - cmds.mayaHydraCppTest(vpPanel, "|pTorus1", f="TestIsolateSelection.remove") + self.assertVisibility(visible, notVisible) - visible.clear() - notVisible = scene.copy() + #============================================================ + # Remove + #============================================================ - for p in ['|stage1|stageShape1,/parent2', - '|stage1|stageShape1,/parent2/cone1', - '|stage1|stageShape1,/parent2/sphere1']: - visible.append(p) - notVisible.remove(p) + # Remove the Maya object from the isolate selection. Only the USD + # isolate selected objects are visible. + cmds.mayaHydraCppTest(modelPanel, "|pTorus1", f="TestIsolateSelection.remove") - self.assertVisibility(visible, notVisible) + visible.clear() + notVisible = scene.copy() - # Remove the USD isolate selected object. Everything is now visible. - cmds.mayaHydraCppTest(vpPanel, '|stage1|stageShape1,/parent2', f="TestIsolateSelection.remove") + for p in ['|stage1|stageShape1,/parent2', + '|stage1|stageShape1,/parent2/cone1', + '|stage1|stageShape1,/parent2/sphere1']: + visible.append(p) + notVisible.remove(p) - visible = scene.copy() - notVisible.clear() + self.assertVisibility(visible, notVisible) - self.assertVisibility(visible, notVisible) + # Remove the USD isolate selected object. The isolate selection + # is empty, isolate selection is enabled, so nothing is now visible. + cmds.mayaHydraCppTest(modelPanel, '|stage1|stageShape1,/parent2', f="TestIsolateSelection.remove") - #============================================================ - # Clear - #============================================================ + visible.clear() + notVisible = scene.copy() - # Add an object back to the isolate selection. - cmds.mayaHydraCppTest(vpPanel, '|stage1|stageShape1,/parent1/cylinder1', f="TestIsolateSelection.add") + self.assertVisibility(visible, notVisible) - notVisible = scene.copy() - visible.clear() + #============================================================ + # Clear + #============================================================ - for p in ['|stage1|stageShape1,/parent1', - '|stage1|stageShape1,/parent1/cylinder1']: - visible.append(p) - notVisible.remove(p) + # Add an object back to the isolate selection. + cmds.mayaHydraCppTest(modelPanel, '|stage1|stageShape1,/parent1/cylinder1', f="TestIsolateSelection.add") - self.assertVisibility(visible, notVisible) + notVisible = scene.copy() + visible.clear() - # Clear the isolate selection. - cmds.mayaHydraCppTest(vpPanel, f="TestIsolateSelection.clear") + for p in ['|stage1|stageShape1,/parent1', + '|stage1|stageShape1,/parent1/cylinder1']: + visible.append(p) + notVisible.remove(p) - visible = scene.copy() - notVisible.clear() + self.assertVisibility(visible, notVisible) - self.assertVisibility(visible, notVisible) + # Clear the isolate selection. + cmds.mayaHydraCppTest(modelPanel, f="TestIsolateSelection.clear") - #============================================================ - # Replace - #============================================================ + visible.clear() + notVisible = scene.copy() - # Add an object back to the isolate selection. - cmds.mayaHydraCppTest(vpPanel, '|group2|pCone1', f="TestIsolateSelection.add") + self.assertVisibility(visible, notVisible) - notVisible = scene.copy() - visible.clear() + #============================================================ + # Replace + #============================================================ - for p in ['|group2', '|group2|pCone1', '|group2|pCone1|pConeShape1']: - visible.append(p) - notVisible.remove(p) + # Add an object back to the isolate selection. + cmds.mayaHydraCppTest(modelPanel, '|group2|pCone1', f="TestIsolateSelection.add") - self.assertVisibility(visible, notVisible) + notVisible = scene.copy() + visible.clear() - # Replace this isolate selection with a different one. - cmds.mayaHydraCppTest(vpPanel, '|group1|pCube1', '|stage1|stageShape1,/parent2/cone1', f="TestIsolateSelection.replace") + for p in ['|group2', '|group2|pCone1', '|group2|pCone1|pConeShape1']: + visible.append(p) + notVisible.remove(p) - visible.clear() - notVisible = scene.copy() + self.assertVisibility(visible, notVisible) - for p in ['|group1', '|group1|pCube1', '|group1|pCube1|pCubeShape1', - '|stage1|stageShape1,/parent2', - '|stage1|stageShape1,/parent2/cone1']: - visible.append(p) - notVisible.remove(p) + # Replace this isolate selection with a different one. + cmds.mayaHydraCppTest(modelPanel, '|group1|pCube1', '|stage1|stageShape1,/parent2/cone1', f="TestIsolateSelection.replace") - self.assertVisibility(visible, notVisible) + visible.clear() + notVisible = scene.copy() - # Clear the isolate selection to avoid affecting other tests. - cmds.mayaHydraCppTest(vpPanel, f="TestIsolateSelection.clear") + for p in ['|group1', '|group1|pCube1', '|group1|pCube1|pCubeShape1', + '|stage1|stageShape1,/parent2', + '|stage1|stageShape1,/parent2/cone1']: + visible.append(p) + notVisible.remove(p) - # To test multi-viewport behavior we would have wanted to test scene index - # prim visibility for each viewport, and demonstrate per-viewport - # visibility. Unfortunately, tracing demonstrates that we can't obtain - # scene index prim visibility in one viewport before a draw is performed in - # another viewport. - # - # Since visibility is according to the last viewport drawn, and don't know - # of way to control order of viewport draw, this testing strategy fails. - # For example, consider drawing modelPanel4, then modelPanel1: - # - #====================================================================== - # Re-using existing scene index chain to render modelPanel4 - # found isolate selection 0000022DD5557B30 for viewport ID modelPanel4 - # Re-using isolate selection 0000022DD5557B30 - # Rendering destination is modelPanel1 - # Re-using existing scene index chain to render modelPanel1 - # found isolate selection 0000022E05EC5740 for viewport ID modelPanel1 - # Switching scene index to isolate selection 0000022E05EC5740 - # IsolateSelectSceneIndex::SetViewport() called for new viewport modelPanel1. - # Old viewport was modelPanel4. - # Old selection is 0000022DD5557B30, new selection is 0000022E05EC5740. - # modelPanel4: examining /MayaHydraViewportRenderer/rprims/Lighted/pSphere1 for isolate select dirtying. - # [...] - # [Dirtying to bring objects invisible in modelPanel4 into modelPanel1] - # [...] - # [Multiple GetPrim() calls for modelPanel1 which all succeed because the - # isolate selection for modelPanel1 is empty] - # IsolateSelectSceneIndex::GetPrim(/MayaHydraViewportRenderer/rprims/pCone1/pConeShape1/DormantPolyWire_58) called for viewport modelPanel1. - # [...] - # Rendering destination is modelPanel1 - # Re-using existing scene index chain to render modelPanel1 - # found isolate selection 0000022E05EC5740 for viewport ID modelPanel1 - # Re-using isolate selection 0000022E05EC5740 - # - # [For an unknown reason we switch back to rendering modelPanel4.] - # - # Rendering destination is modelPanel4 - # Re-using existing scene index chain to render modelPanel4 - # found isolate selection 0000022DD5557B30 for viewport ID modelPanel4 - # Switching scene index to isolate selection 0000022DD5557B30 - # IsolateSelectSceneIndex::SetViewport() called for new viewport modelPanel4. - # Old viewport was modelPanel1. - # Old selection is 0000022E05EC5740, new selection is 0000022DD5557B30. - #====================================================================== - # - # And at this point if we ask for visibility we'll get the modelPanel4 - # visibility, rather than the desired modelPanel1 visibility. - # - # We may have to resort to image comparison to test this. + self.assertVisibility(visible, notVisible) + # Disable the isolate selection to avoid affecting other tests. + disableIsolateSelect(modelPanel) + + # A robust multi-viewport test would have tested scene index prim + # visibility for each viewport, and demonstrated per-viewport visibility. + # Unfortunately, tracing demonstrates that we can't obtain scene index prim + # visibility in one viewport before a draw is performed in another + # viewport. Since visibility is according to the last viewport drawn, and + # don't know of way to control order of viewport draw, this testing + # strategy fails. + # + # Unfortunately performing image comparisons is also not possible, as at + # time of writing playblast doesn't respect isolate select when using + # MayaHydra in a multi-viewport setting. Therefore, the following test is + # weak and does not validate results. def test_isolateSelectMultiViewport(self): scene = self.setupScene() - with PluginLoaded('mayaHydraCppTests'): - # We start in single viewport mode. Set an isolate selection there. - cmds.mayaHydraCppTest('modelPanel4', '|group1', '|stage1|stageShape1,/parent1/cylinder1', f="TestIsolateSelection.replace") - - notVisible = scene.copy() - visible = ['|group1', - '|group1|pSphere1', - '|group1|pSphere1|pSphereShape1', - '|group1|pCube1', - '|group1|pCube1|pCubeShape1', - '|stage1|stageShape1,/parent1', - '|stage1|stageShape1,/parent1/cylinder1'] - for p in visible: - notVisible.remove(p) - - self.assertVisibility(visible, notVisible) - - # Switch to four-up viewport mode. Set the renderer in each new - # viewport to be Hydra Storm. Viewport 4 is already set. - # Everything should be initially visible in viewports 1-3. - cmds.FourViewLayout() - visible = scene.copy() - notVisible.clear() - for i in range(1, 4): - cmds.setFocus('modelPanel'+str(i)) - self.setHdStormRenderer() - # self.assertVisibility(visible, notVisible) - - # Here we would set different isolate selections in each viewport. - - # As a final step clear the isolate selections to avoid affecting - # other tests. - for i in range(1, 5): - modelPanel = 'modelPanel'+str(i) - cmds.setFocus(modelPanel) - cmds.mayaHydraCppTest(modelPanel, f="TestIsolateSelection.clear") + # We start in single viewport mode. Set an isolate selection there. + cmds.select('|group1', '|stage1|stageShape1,/parent1/cylinder1') + enableIsolateSelect("modelPanel4") + + notVisible = scene.copy() + visible = ['|group1', + '|group1|pSphere1', + '|group1|pSphere1|pSphereShape1', + '|group1|pCube1', + '|group1|pCube1|pCubeShape1', + '|stage1|stageShape1,/parent1', + '|stage1|stageShape1,/parent1/cylinder1'] + for p in visible: + notVisible.remove(p) + + cmds.refresh() + + self.assertVisibility(visible, notVisible) + + # Move the camera closer for a view where objects fill in more of the + # viewport. FrameSelectedWithoutChildren() is good, but a manually + # chosen camera position is better. + cmds.setAttr("persp.translate", 4.9, 3.5, 5.7) + cmds.select(clear=1) + + cmds.refresh() + + self.assertSnapshotClose("singleViewportIsolateSelectCylinder1.png", 0.1, 2) + + # Switch to four-up viewport mode. Set the renderer in each new + # viewport to be Hydra Storm. Viewport 4 is already set. + cmds.FourViewLayout() + visible = scene.copy() + notVisible.clear() + for i in range(1, 4): + cmds.setFocus('modelPanel'+str(i)) + self.setHdStormRenderer() + + cmds.select('|pTorus1') + + enableIsolateSelect("modelPanel1") + + cmds.refresh() + + # As a final step disable the isolate selections to avoid affecting + # other tests. + for i in range(1, 5): + disableIsolateSelect('modelPanel'+str(i)) def test_isolateSelectMultipleStages(self): scene = self.setupMultiStageScene() - with PluginLoaded('mayaHydraCppTests'): - vpPanel = 'modelPanel4' - cmds.mayaHydraCppTest( - vpPanel, '|group1|pCube1', '|stage1|stageShape1,/parent2/cone1', - '|stage2|stageShape2,/parent1/cylinder1', - f="TestIsolateSelection.replace") - - visible = ['|group1', '|group1|pCube1', '|group1|pCube1|pCubeShape1', - '|stage1|stageShape1,/parent2', - '|stage1|stageShape1,/parent2/cone1', - '|stage2|stageShape2,/parent1', - '|stage2|stageShape2,/parent1/cylinder1'] - notVisible = scene.copy() - for p in visible: - notVisible.remove(p) + modelPanel = 'modelPanel4' + + cmds.select('|group1|pCube1', '|stage1|stageShape1,/parent2/cone1', + '|stage2|stageShape2,/parent1/cylinder1') + enableIsolateSelect(modelPanel) + + visible = ['|group1', '|group1|pCube1', '|group1|pCube1|pCubeShape1', + '|stage1|stageShape1,/parent2', + '|stage1|stageShape1,/parent2/cone1', + '|stage2|stageShape2,/parent1', + '|stage2|stageShape2,/parent1/cylinder1'] + notVisible = scene.copy() + + for p in visible: + notVisible.remove(p) + + cmds.refresh() - self.assertVisibility(visible, notVisible) + self.assertVisibility(visible, notVisible) - # As a final step clear the isolate selection to avoid affecting - # other tests. - cmds.mayaHydraCppTest(vpPanel, f="TestIsolateSelection.clear") + # As a final step disable the isolate selection to avoid affecting + # other tests. + disableIsolateSelect(modelPanel) if __name__ == '__main__': fixturesUtils.runTests(globals()) diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.cpp b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.cpp index c0294c9f4..520d6bbc6 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.cpp +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.cpp @@ -520,25 +520,6 @@ void assertSelectionHighlightCorrectness( } } -Fvp::PrimSelections ufePathToPrimSelections(const Ufe::Path& appPath) -{ - Fvp::PrimSelections primSelections; - - auto mapper = Fvp::PathMapperRegistry::Instance().GetMapper(appPath); - - if (!mapper) { - TF_WARN("No registered mapping for path %s, no prim path returned.", Ufe::PathString::string(appPath).c_str()); - } - else { - primSelections = mapper->UfePathToPrimSelections(appPath); - if (primSelections.empty()) { - TF_WARN("Mapping for path %s returned no prim path.", Ufe::PathString::string(appPath).c_str()); - } - } - - return primSelections; -} - bool visibility(const HdSceneIndexBasePtr& sceneIndex, const SdfPath& primPath) { auto prim = sceneIndex->GetPrim(primPath); diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.h b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.h index 22fe3c2cb..186a53cfb 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.h +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.h @@ -490,18 +490,6 @@ void assertSelectionHighlightCorrectness( const std::string& selectionHighlightMirrorTag, const PXR_NS::TfToken& leafDisplayStyle); -/** - * @brief Get the prim selections for a given application path. - * - * If an application path corresponds to a scene index prim, this function will - * return one or more prim selections for it. If no such scene index prim - * exists, the return prim selections will be empty. - * - * @param[in] appPath The application path for which prim selections should be returned. - * @return Zero or more prim selections. - */ -Fvp::PrimSelections ufePathToPrimSelections(const Ufe::Path& appPath); - /** * @brief Return whether the prim is visible or not. */