diff --git a/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.cpp b/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.cpp index e88800eb90..cebd00d15d 100644 --- a/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.cpp +++ b/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.cpp @@ -19,6 +19,7 @@ #include "flowViewport/API/interfacesImp/fvpDataProducerSceneIndexInterfaceImp.h" #include "flowViewport/API/interfacesImp/fvpInformationInterfaceImp.h" #include "flowViewport/sceneIndex/fvpRenderIndexProxy.h" +#include "flowViewport/selection/fvpSelection.h" #include "flowViewport/API/perViewportSceneIndicesData/fvpFilteringSceneIndicesChainManager.h" //Hydra headers @@ -151,6 +152,91 @@ ViewportInformationAndSceneIndicesPerViewportData* ViewportInformationAndSceneIn return nullptr; } +SelectionPtr ViewportInformationAndSceneIndicesPerViewportDataManager::GetOrCreateIsolateSelection(const std::string& viewportId) +{ + auto found = _isolateSelection.find(viewportId); + if (found != _isolateSelection.end()) { + return found->second; + } + auto selection = std::make_shared(); + _isolateSelection[viewportId] = selection; + return selection; +} + +SelectionPtr ViewportInformationAndSceneIndicesPerViewportDataManager::GetIsolateSelection(const std::string& viewportId) const +{ + auto found = _isolateSelection.find(viewportId); + return (found != _isolateSelection.end()) ? found->second : nullptr; +} + +void ViewportInformationAndSceneIndicesPerViewportDataManager::AddIsolateSelection( + const std::string& viewportId, + const PrimSelections& primSelections +) +{ + if (!TF_VERIFY(_isolateSelectSceneIndex, "No isolate select scene index set.")) { + return; + } + _CheckAndSetViewport(viewportId); + _isolateSelectSceneIndex->AddIsolateSelection(primSelections); +} + +void ViewportInformationAndSceneIndicesPerViewportDataManager::RemoveIsolateSelection( + const std::string& viewportId, + const PrimSelections& primSelections +) +{ + if (!TF_VERIFY(_isolateSelectSceneIndex, "No isolate select scene index set.")) { + return; + } + _CheckAndSetViewport(viewportId); + _isolateSelectSceneIndex->RemoveIsolateSelection(primSelections); +} + +void ViewportInformationAndSceneIndicesPerViewportDataManager::ReplaceIsolateSelection( + const std::string& viewportId, + const SelectionConstPtr& selection +) +{ + if (!TF_VERIFY(_isolateSelectSceneIndex, "No isolate select scene index set.")) { + return; + } + _CheckAndSetViewport(viewportId); + _isolateSelectSceneIndex->ReplaceIsolateSelection(selection); +} + +void ViewportInformationAndSceneIndicesPerViewportDataManager::ClearIsolateSelection(const std::string& viewportId) +{ + if (!TF_VERIFY(_isolateSelectSceneIndex, "No isolate select scene index set.")) { + return; + } + _CheckAndSetViewport(viewportId); + _isolateSelectSceneIndex->ClearIsolateSelection(); +} + +void ViewportInformationAndSceneIndicesPerViewportDataManager::SetIsolateSelectSceneIndex( + const IsolateSelectSceneIndexRefPtr& sceneIndex +) +{ + _isolateSelectSceneIndex = sceneIndex; +} + +IsolateSelectSceneIndexRefPtr +ViewportInformationAndSceneIndicesPerViewportDataManager::GetIsolateSelectSceneIndex() const +{ + return _isolateSelectSceneIndex; +} + +void +ViewportInformationAndSceneIndicesPerViewportDataManager::_CheckAndSetViewport( + const std::string& viewportId +) +{ + if (_isolateSelectSceneIndex->GetViewportId() != viewportId) { + _isolateSelectSceneIndex->SetViewport(viewportId, _isolateSelection.at(viewportId)); + } +} + const std::set& ViewportInformationAndSceneIndicesPerViewportDataManager::GetDataProducerSceneIndicesDataFromViewportId(const std::string& viewportId)const { diff --git a/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.h b/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.h index c282968702..645b929109 100644 --- a/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.h +++ b/lib/flowViewport/API/perViewportSceneIndicesData/fvpViewportInformationAndSceneIndicesPerViewportDataManager.h @@ -20,6 +20,9 @@ //Local headers #include "fvpViewportInformationAndSceneIndicesPerViewportData.h" #include "flowViewport/sceneIndex/fvpRenderIndexProxyFwd.h" +#include "flowViewport/sceneIndex/fvpPathInterface.h" +#include "flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.h" +#include "flowViewport/selection/fvpSelectionFwd.h" //Hydra headers #include @@ -31,11 +34,16 @@ namespace FVP_NS_DEF { * * To get an instance of this class, please use * ViewportInformationAndSceneIndicesPerViewportDataManager& manager = ViewportInformationAndSceneIndicesPerViewportDataManager:Get(); +* +* The PerViewportDataManager also manages the per-viewport isolate selection, +* as well as providing access to the single isolate select scene index. */ class FVP_API ViewportInformationAndSceneIndicesPerViewportDataManager { public: + using ViewportIds = std::vector; + /// Manager accessor static ViewportInformationAndSceneIndicesPerViewportDataManager& Get(); @@ -53,18 +61,58 @@ class FVP_API ViewportInformationAndSceneIndicesPerViewportDataManager const ViewportInformationAndSceneIndicesPerViewportData* GetViewportInfoAndDataFromViewportId(const std::string& viewportId)const; ViewportInformationAndSceneIndicesPerViewportData* GetViewportInfoAndDataFromViewportId(const std::string& viewportId); + SelectionPtr GetOrCreateIsolateSelection(const std::string& viewportId); + SelectionPtr GetIsolateSelection(const std::string& viewportId) const; + + void AddIsolateSelection( + const std::string& viewportId, + const PrimSelections& primSelections + ); + void RemoveIsolateSelection( + const std::string& viewportId, + const PrimSelections& primSelections + ); + void ReplaceIsolateSelection( + const std::string& viewportId, + const SelectionConstPtr& selection + ); + void ClearIsolateSelection(const std::string& viewportId); + + // Get and set the isolate select scene index. This scene index provides + // isolate select services for all viewports. + void SetIsolateSelectSceneIndex( + const IsolateSelectSceneIndexRefPtr& sceneIndex + ); + IsolateSelectSceneIndexRefPtr GetIsolateSelectSceneIndex() const; + const std::set& GetDataProducerSceneIndicesDataFromViewportId(const std::string& viewportId)const; bool ModelPanelIsAlreadyRegistered(const std::string& modelPanel)const; void RemoveAllViewportsInformation(); private: + + // Singleton, no public creation or copy. + ViewportInformationAndSceneIndicesPerViewportDataManager() = default; + ViewportInformationAndSceneIndicesPerViewportDataManager( + const ViewportInformationAndSceneIndicesPerViewportDataManager& + ) = delete; + + void _CheckAndSetViewport(const std::string& viewportId); + ///Hydra viewport information ViewportInformationAndSceneIndicesPerViewportDataVector _viewportsInformationAndSceneIndicesPerViewportData; - ViewportInformationAndSceneIndicesPerViewportDataManager() = default; + // Isolate selection, keyed by viewportId. + std::map _isolateSelection; + + // Isolate select scene index. + IsolateSelectSceneIndexRefPtr _isolateSelectSceneIndex; }; +// Convenience shorthand declaration. +using PerViewportDataManager = ViewportInformationAndSceneIndicesPerViewportDataManager; + } //End of namespace FVP_NS_DEF -#endif // FLOW_VIEWPORT_API_PERVIEWPORTSCENEINDICESDATA_VIEWPORT_INFORMATION_AND_SCENE_INDICES_DATA_PER_VIEWPORT_DATA_MANAGER \ No newline at end of file +#endif // FLOW_VIEWPORT_API_PERVIEWPORTSCENEINDICESDATA_VIEWPORT_INFORMATION_AND_SCENE_INDICES_DATA_PER_VIEWPORT_DATA_MANAGER diff --git a/lib/flowViewport/debugCodes.cpp b/lib/flowViewport/debugCodes.cpp index 4df57df760..688747379d 100644 --- a/lib/flowViewport/debugCodes.cpp +++ b/lib/flowViewport/debugCodes.cpp @@ -44,6 +44,10 @@ TF_REGISTRY_FUNCTION(TfDebug) TF_DEBUG_ENVIRONMENT_SYMBOL( PXR_NS::FVP_WIREFRAME_SELECTION_HIGHLIGHT_SCENE_INDEX, "Print information about the Flow Viewport wireframe selection highlight scene index."); + + TF_DEBUG_ENVIRONMENT_SYMBOL( + PXR_NS::FVP_ISOLATE_SELECT_SCENE_INDEX, + "Print information about the Flow Viewport isolate select scene index."); } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/flowViewport/debugCodes.h b/lib/flowViewport/debugCodes.h index 29e5d6b04b..48fd50c3de 100644 --- a/lib/flowViewport/debugCodes.h +++ b/lib/flowViewport/debugCodes.h @@ -31,6 +31,7 @@ TF_DEBUG_CODES( , FVP_APP_SELECTION_CHANGE , FVP_MERGING_SCENE_INDEX , FVP_WIREFRAME_SELECTION_HIGHLIGHT_SCENE_INDEX + , FVP_ISOLATE_SELECT_SCENE_INDEX ); // clang-format on diff --git a/lib/flowViewport/sceneIndex/CMakeLists.txt b/lib/flowViewport/sceneIndex/CMakeLists.txt index 7ddb346eb9..0c379394f7 100644 --- a/lib/flowViewport/sceneIndex/CMakeLists.txt +++ b/lib/flowViewport/sceneIndex/CMakeLists.txt @@ -3,6 +3,7 @@ # ----------------------------------------------------------------------------- target_sources(${TARGET_NAME} PRIVATE + fvpIsolateSelectSceneIndex.cpp fvpMergingSceneIndex.cpp fvpPathInterface.cpp fvpPathInterfaceSceneIndex.cpp @@ -19,6 +20,7 @@ target_sources(${TARGET_NAME} ) set(HEADERS + fvpIsolateSelectSceneIndex.h fvpMergingSceneIndex.h fvpPathInterface.h fvpPathInterfaceSceneIndex.h diff --git a/lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.cpp b/lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.cpp new file mode 100644 index 0000000000..b09693936c --- /dev/null +++ b/lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.cpp @@ -0,0 +1,280 @@ +// +// Copyright 2024 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 +#include +#include +#include + +#include "flowViewport/debugCodes.h" + +#include +#include + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace { + +const HdContainerDataSourceHandle visOff = + HdVisibilitySchema::BuildRetained( + HdRetainedTypedSampledDataSource::New(false)); +} + +namespace FVP_NS_DEF { + +IsolateSelectSceneIndexRefPtr +IsolateSelectSceneIndex::New( + const std::string& viewportId, + const SelectionPtr& isolateSelection, + const HdSceneIndexBaseRefPtr& inputSceneIndex +) +{ + TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) + .Msg("IsolateSelectSceneIndex::New() called.\n"); + + auto isSi = PXR_NS::TfCreateRefPtr(new IsolateSelectSceneIndex( + viewportId, isolateSelection, inputSceneIndex)); + + isSi->SetDisplayName(kDisplayName); + return isSi; +} + +IsolateSelectSceneIndex::IsolateSelectSceneIndex( + const std::string& viewportId, + const SelectionPtr& isolateSelection, + const HdSceneIndexBaseRefPtr& inputSceneIndex +) + : ParentClass(inputSceneIndex) + , InputSceneIndexUtils(inputSceneIndex) + , _viewportId(viewportId) + , _isolateSelection(isolateSelection) +{} + +std::string IsolateSelectSceneIndex::GetViewportId() const +{ + return _viewportId; +} + +HdSceneIndexPrim IsolateSelectSceneIndex::GetPrim(const SdfPath& primPath) const +{ + TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) + .Msg("IsolateSelectSceneIndex::GetPrim(%s) called for viewport %s.\n", primPath.GetText(), _viewportId.c_str()); + + auto inputPrim = GetInputSceneIndex()->GetPrim(primPath); + + // If isolate selection is empty, everything is included. + if (_isolateSelection->IsEmpty()) { + return inputPrim; + } + + const bool included = + _isolateSelection->HasAncestorOrDescendantInclusive(primPath); + + TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) + .Msg(" prim path %s is %s isolate select set", primPath.GetText(), (included ? "INCLUDED in" : "EXCLUDED from")); + + if (!included) { + inputPrim.dataSource = HdContainerDataSourceEditor(inputPrim.dataSource) + .Set(HdVisibilitySchema::GetDefaultLocator(), visOff) + .Finish(); + } + + return inputPrim; +} + +SdfPathVector IsolateSelectSceneIndex::GetChildPrimPaths(const SdfPath& primPath) const { + // Prims are hidden, not removed. + return GetInputSceneIndex()->GetChildPrimPaths(primPath); +} + +void IsolateSelectSceneIndex::_PrimsAdded( + const HdSceneIndexBase& , + const HdSceneIndexObserver::AddedPrimEntries& entries) +{ + // Prims outside the isolate select set will be hidden in GetPrim(). + _SendPrimsAdded(entries); +} + +void IsolateSelectSceneIndex::_PrimsRemoved( + const HdSceneIndexBase& , + const HdSceneIndexObserver::RemovedPrimEntries& entries) +{ + // We rely on the application to remove from the isolate select set those + // prims that have been removed. + _SendPrimsRemoved(entries); +} + +void IsolateSelectSceneIndex::_PrimsDirtied( + const HdSceneIndexBase& , + const HdSceneIndexObserver::DirtiedPrimEntries& entries) +{ + _SendPrimsDirtied(entries); +} + +void IsolateSelectSceneIndex::AddIsolateSelection(const PrimSelections& primSelections) +{ + TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) + .Msg("IsolateSelectSceneIndex::AddIsolateSelection() called for viewport %s.\n", _viewportId.c_str()); + + HdSceneIndexObserver::DirtiedPrimEntries dirtiedEntries; + for (const auto& primSelection : primSelections) { + TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) + .Msg(" Adding %s to the isolate select set.\n", primSelection.primPath.GetText()); + _isolateSelection->Add(primSelection); + _DirtyVisibility(primSelection.primPath, &dirtiedEntries); + } + + _SendPrimsDirtied(dirtiedEntries); +} + +void IsolateSelectSceneIndex::RemoveIsolateSelection(const PrimSelections& primSelections) +{ + TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) + .Msg("IsolateSelectSceneIndex::RemoveIsolateSelection() called for viewport %s.\n", _viewportId.c_str()); + + HdSceneIndexObserver::DirtiedPrimEntries dirtiedEntries; + for (const auto& primSelection : primSelections) { + TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) + .Msg(" Removing %s from the isolate select set.\n", primSelection.primPath.GetText()); + _isolateSelection->Remove(primSelection); + _DirtyVisibility(primSelection.primPath, &dirtiedEntries); + } + + _SendPrimsDirtied(dirtiedEntries); +} + +void IsolateSelectSceneIndex::ClearIsolateSelection() +{ + TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) + .Msg("IsolateSelectSceneIndex::ClearIsolateSelection() called for viewport %s.\n", _viewportId.c_str()); + + auto isolateSelectPaths = _isolateSelection->GetFullySelectedPaths(); + + HdSceneIndexObserver::DirtiedPrimEntries dirtiedEntries; + for (const auto& primPath : isolateSelectPaths) { + TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) + .Msg(" Removing %s from the isolate select set.\n", primPath.GetText()); + _DirtyVisibility(primPath, &dirtiedEntries); + } + + _isolateSelection->Clear(); + + _SendPrimsDirtied(dirtiedEntries); +} + +void IsolateSelectSceneIndex::ReplaceIsolateSelection(const SelectionConstPtr& isolateSelection) +{ + TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) + .Msg("IsolateSelectSceneIndex::ReplaceIsolateSelection() called for viewport %s.\n", _viewportId.c_str()); + + _ReplaceIsolateSelection(isolateSelection); + + _isolateSelection->Replace(*isolateSelection); +} + +void IsolateSelectSceneIndex::_ReplaceIsolateSelection(const SelectionConstPtr& isolateSelection) +{ + // Keep paths in a set to minimize dirtying. First clear old paths. + auto clearedPaths = _isolateSelection->GetFullySelectedPaths(); + std::set dirtyPaths(clearedPaths.begin(), clearedPaths.end()); + + // Then add new paths. + const auto& newPaths = isolateSelection->GetFullySelectedPaths(); + for (const auto& primPath : newPaths) { + dirtyPaths.insert(primPath); + } + + // Finally, dirty all cleared and added prim paths. + HdSceneIndexObserver::DirtiedPrimEntries dirtiedEntries; + for (const auto& primPath : dirtyPaths) { + _DirtyVisibility(primPath, &dirtiedEntries); + } + + _SendPrimsDirtied(dirtiedEntries); +} + +void IsolateSelectSceneIndex::SetViewport( + const std::string& viewportId, + const SelectionPtr& isolateSelection +) +{ + TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) + .Msg("IsolateSelectSceneIndex::SetViewport() called for new viewport %s.\n", viewportId.c_str()); + 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); + + if ((isolateSelection == _isolateSelection) || + (viewportId == _viewportId)) { + TF_WARN("IsolateSelectSceneIndex::SetViewport() called with identical information, no operation performed."); + return; + } + + _ReplaceIsolateSelection(isolateSelection); + + _isolateSelection = isolateSelection; + _viewportId = viewportId; +} + +SelectionPtr IsolateSelectSceneIndex::GetIsolateSelection() const +{ + return _isolateSelection; +} + +void IsolateSelectSceneIndex::_DirtyVisibility( + const SdfPath& primPath, + HdSceneIndexObserver::DirtiedPrimEntries* dirtiedEntries +) const +{ + // Dirty visibility by going up the prim path. GetAncestorsRange() + // includes the path itself, as desired. + for (const auto& p : primPath.GetAncestorsRange()) { + TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) + .Msg(" %s: examining %s for isolate select dirtying.\n", _viewportId.c_str(), p.GetText()); + if (p.GetPathElementCount() == 0) { + break; + } + auto parent = p.GetParentPath(); + auto siblings = GetChildPrimPaths(parent); + for (const auto& s : siblings) { + if (s == p) { + continue; + } + TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) + .Msg(" %s: dirtying sibling %s for isolate select.\n", _viewportId.c_str(), s.GetText()); + _DirtyVisibilityRecursive(s, dirtiedEntries); + } + } +} + +void IsolateSelectSceneIndex::_DirtyVisibilityRecursive( + const SdfPath& primPath, + HdSceneIndexObserver::DirtiedPrimEntries* dirtiedEntries +) const +{ + TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX) + .Msg(" %s: marking %s visibility locator dirty.\n", _viewportId.c_str(), primPath.GetText()); + + dirtiedEntries->emplace_back( + primPath, HdVisibilitySchema::GetDefaultLocator()); + + for (const auto& childPath : GetChildPrimPaths(primPath)) { + _DirtyVisibilityRecursive(childPath, dirtiedEntries); + } +} + +} //end of namespace FVP_NS_DEF diff --git a/lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.h b/lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.h new file mode 100644 index 0000000000..f7a69d3c0a --- /dev/null +++ b/lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.h @@ -0,0 +1,181 @@ +// +// Copyright 2024 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. +// +#ifndef FLOW_VIEWPORT_SCENE_INDEX_ISOLATE_SELECT_SCENE_INDEX_H +#define FLOW_VIEWPORT_SCENE_INDEX_ISOLATE_SELECT_SCENE_INDEX_H + +//Local headers +#include "flowViewport/api.h" + +#include "flowViewport/selection/fvpSelectionFwd.h" +#include "flowViewport/sceneIndex/fvpSceneIndexUtils.h" +#include "flowViewport/sceneIndex/fvpPathInterface.h" // For PrimSelections + +//Hydra headers +#include + +namespace FVP_NS_DEF { + +// Pixar declarePtrs.h TF_DECLARE_REF_PTRS macro unusable, places resulting +// type in PXR_NS. +class IsolateSelectSceneIndex; +typedef PXR_NS::TfRefPtr IsolateSelectSceneIndexRefPtr; +typedef PXR_NS::TfRefPtr IsolateSelectSceneIndexConstRefPtr; + +/// \class IsolateSelectSceneIndex +/// +/// A filtering scene index that changes the visibility of prims that are not +/// in its set to false. +/// +/// The input isolate select data to the isolate select scene index is a set of +/// scene index prim selections tracked as an Fvp::Selection. External code is +/// responsible for converting the application isolate selection into prim +/// selections. +/// +/// Isolate select does not remove prims from the scene, it hides them. This +/// matches the Maya algorithm. A prim's previous visibility is restored +/// simply by taking out the isolate select scene index, thereby allowing +/// the original visibility to be sent to the renderer unchanged. +/// +/// 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(). +/// +/// IsolateSelectSceneIndex::GetPrim() passes through prims that have an +/// ancestor or descendant (including themselves) in the isolate selection. +/// Other prims are hidden by setting visibility off. +/// +/// When the isolate selection is changed, prim visibility in the scene is +/// dirtied in the following way: starting at the changed prim path, +/// - Dirty all sibling visibilities +/// - Move up to the prim's parent +/// - Recurse and dirty all sibling visibilities. +/// - End recursion at the scene root. +/// +/// Dirtying any prim's visibility recurses to its children, to dirty the +/// visibility for the entire subtree. +/// +/// For example, consider the following hierarchy: +/// +/// a +/// |_b +/// |_c +/// |_d +/// |_e +/// |_f +/// |_g +/// |_h +/// |_i +/// |_j +/// |_k +/// +/// Given an initially empty isolate selection, adding f to the isolate +/// selection will: +/// +/// - Dirty g's visibility, and recursively that of all its descendants. +/// - Recursing up to e, dirty b and k's visibility, and all their descendants. +/// - Recursing up to a (the root), the algorithm ends. +/// +class IsolateSelectSceneIndex : + public PXR_NS::HdSingleInputFilteringSceneIndexBase + , public Fvp::InputSceneIndexUtils +{ +public: + using ParentClass = PXR_NS::HdSingleInputFilteringSceneIndexBase; + using PXR_NS::HdSingleInputFilteringSceneIndexBase::_GetInputSceneIndex; + + static constexpr char kDisplayName[] = "Flow Viewport Isolate Select Scene Index"; + + FVP_API + static IsolateSelectSceneIndexRefPtr New( + const std::string& viewportId, + const SelectionPtr& isolateSelection, + const PXR_NS::HdSceneIndexBaseRefPtr& inputSceneIndex + ); + + // From HdSceneIndexBase + PXR_NS::HdSceneIndexPrim GetPrim(const PXR_NS::SdfPath& primPath) const override; + + PXR_NS::SdfPathVector GetChildPrimPaths(const PXR_NS::SdfPath& primPath) const override; + + ~IsolateSelectSceneIndex() override = default; + + FVP_API + void AddIsolateSelection(const PrimSelections& primSelections); + FVP_API + void RemoveIsolateSelection(const PrimSelections& primSelections); + FVP_API + void ReplaceIsolateSelection(const SelectionConstPtr& selection); + FVP_API + void ClearIsolateSelection(); + + // Set viewport information (viewport ID and isolate selection) for this + // scene index. This occurs when switching the single scene index between + // viewports. If the same viewport ID or isolate selection is given as an + // argument, a warning will be issued. Otherwise, the previous and the new + // isolate selections will be dirtied. + FVP_API + void SetViewport( + const std::string& viewportId, + const SelectionPtr& isolateSelection + ); + + // Get viewport information for this scene index. + FVP_API + std::string GetViewportId() const; + FVP_API + SelectionPtr GetIsolateSelection() const; + +protected: + void _PrimsAdded( + const PXR_NS::HdSceneIndexBase& sender, + const PXR_NS::HdSceneIndexObserver::AddedPrimEntries& entries) override; + + void _PrimsRemoved( + const PXR_NS::HdSceneIndexBase& sender, + const PXR_NS::HdSceneIndexObserver::RemovedPrimEntries& entries) override; + + void _PrimsDirtied( + const PXR_NS::HdSceneIndexBase& sender, + const PXR_NS::HdSceneIndexObserver::DirtiedPrimEntries& entries) override; + +private: + + IsolateSelectSceneIndex( + const std::string& viewportId, + const SelectionPtr& isolateSelection, + const PXR_NS::HdSceneIndexBaseRefPtr& inputSceneIndex + ); + + void _DirtyVisibility( + const PXR_NS::SdfPath& primPath, + PXR_NS::HdSceneIndexObserver::DirtiedPrimEntries* dirtiedEntries + ) const; + + void _DirtyVisibilityRecursive( + const PXR_NS::SdfPath& primPath, + PXR_NS::HdSceneIndexObserver::DirtiedPrimEntries* dirtiedEntries + ) const; + + void _ReplaceIsolateSelection(const SelectionConstPtr& selection); + + std::string _viewportId; + + SelectionPtr _isolateSelection{}; +}; + +}//end of namespace FVP_NS_DEF + +#endif //FLOW_VIEWPORT_SCENE_INDEX_ISOLATE_SELECT_SCENE_INDEX_H diff --git a/lib/flowViewport/selection/fvpPathMapperRegistry.cpp b/lib/flowViewport/selection/fvpPathMapperRegistry.cpp index 0ecf953c3a..3f69296766 100644 --- a/lib/flowViewport/selection/fvpPathMapperRegistry.cpp +++ b/lib/flowViewport/selection/fvpPathMapperRegistry.cpp @@ -25,6 +25,7 @@ namespace { Ufe::Trie mappers; +Fvp::PathMapperConstPtr fallbackMapper{}; } @@ -56,12 +57,28 @@ bool PathMapperRegistry::Unregister(const Ufe::Path& prefix) return mappers.remove(prefix) != nullptr; } +void PathMapperRegistry::SetFallbackMapper( + const PathMapperConstPtr& pathMapper +) +{ + fallbackMapper = pathMapper; +} + +PathMapperConstPtr PathMapperRegistry::GetFallbackMapper() const +{ + return fallbackMapper; +} + PathMapperConstPtr PathMapperRegistry::GetMapper(const Ufe::Path& path) const { - if (mappers.empty() || path.empty()) { + if (path.empty()) { return nullptr; } + if (mappers.empty()) { + return fallbackMapper; + } + // We are looking for the closest ancestor of the argument. Internal trie // nodes have no data, and exist only as parents for trie nodes with data. // In our case the trie node data is the path mapper, so we walk down the @@ -72,7 +89,7 @@ PathMapperConstPtr PathMapperRegistry::GetMapper(const Ufe::Path& path) const // If we've reached a trie leaf node before the end of our path, there // is no trie node with data as ancestor of the path. if (!child) { - return nullptr; + return fallbackMapper; } trieNode = child; @@ -83,7 +100,7 @@ PathMapperConstPtr PathMapperRegistry::GetMapper(const Ufe::Path& path) const } // We reached the end of the parent path without returning true, therefore // there are no ancestors. - return nullptr; + return fallbackMapper; } } diff --git a/lib/flowViewport/selection/fvpPathMapperRegistry.h b/lib/flowViewport/selection/fvpPathMapperRegistry.h index 171b3d4776..54e032e0b5 100644 --- a/lib/flowViewport/selection/fvpPathMapperRegistry.h +++ b/lib/flowViewport/selection/fvpPathMapperRegistry.h @@ -39,8 +39,13 @@ namespace FVP_NS_DEF { /// - All entries are unique. /// - No entry is a prefix (ancestor) of another entry. /// -/// Therefore, a fallback path mapping must be implemented outside the -/// application path to scene index path mapper. +/// A fallback path mapper can be provided to implement a path mapping chain of +/// responsibility, for an application's native data model paths. This is +/// useful as the path mapper uses plugin prim path prefixes to convert between +/// a data model path to one (or more) scene index prim path(s). The +/// application data model has no plugin data model Hydra scene index prim path +/// prefix, so the application data model should be made the fallback, if no +/// other path mapper prefix matches. class PathMapperRegistry { public: @@ -62,6 +67,14 @@ class PathMapperRegistry { FVP_API bool Unregister(const Ufe::Path& prefix); + //! Set a fallback path mapper. If set, it will be returned by + //! GetMapper() if no mapper is registered for a given argument path. + //! A null pointer argument removes the fallback path mapper. + FVP_API + void SetFallbackMapper(const PathMapperConstPtr& pathMapper); + FVP_API + PathMapperConstPtr GetFallbackMapper() const; + //! Get a path mapper for the argument application path. This //! mapper has a prefix that is an ancestor of the argument path. If no //! path mapper is found, returns a null pointer. diff --git a/lib/flowViewport/selection/fvpSelection.cpp b/lib/flowViewport/selection/fvpSelection.cpp index 4cb21834a5..42a564e8fe 100644 --- a/lib/flowViewport/selection/fvpSelection.cpp +++ b/lib/flowViewport/selection/fvpSelection.cpp @@ -99,6 +99,11 @@ void Selection::Replace(const PrimSelections& primSelections) } } +void Selection::Replace(const Selection& rhs) +{ + _pathToSelections = rhs._pathToSelections; +} + void Selection::RemoveHierarchy(const PXR_NS::SdfPath& primPath) { auto it = _pathToSelections.lower_bound(primPath); @@ -131,6 +136,74 @@ bool Selection::HasFullySelectedAncestorInclusive(const SdfPath& primPath, const return false; } +bool Selection::HasDescendantInclusive(const PXR_NS::SdfPath& primPath) const +{ + // No entries? No descendant + if (_pathToSelections.empty()) { + return false; + } + + // At least one entry. Skip all entries before argument. The iterator + // points to an entry with matching or greater key. + auto it = _pathToSelections.lower_bound(primPath); + + // Reached the end? Last entry is strictly smaller than, so no descendants. + if (it == _pathToSelections.end()) { + return false; + } + + // Not at the end. Query is exactly in the map, or is prefix to what is in + // the map (i.e. map contents is a descendant)? Return true. + return (it->first == primPath || it->first.HasPrefix(primPath)); +} + +bool +Selection::HasAncestorOrDescendantInclusive(const PXR_NS::SdfPath& primPath) const +{ + // Use std::map::lower_bound to accelerate prim path lookup. The map is + // lexically ordered on SdfPath, with shorter paths less than longer + // paths. Makes determining ancestors and descendants somewhat tricky, but + // efficient. A prefix tree would be an easier data structure to implement + // this functionality. + + // No entries? No ancestors or descendants. + if (_pathToSelections.empty()) { + return false; + } + + // At least one entry. Skip all entries before argument. The iterator + // points to an entry with matching or greater key. + auto it = _pathToSelections.lower_bound(primPath); + + // Reached the end? Last entry is strictly smaller than, so no descendants + // in map. Check if it's an ancestor. + if (it == _pathToSelections.end()) { + auto rit = _pathToSelections.rbegin(); + return primPath.HasPrefix(rit->first); + } + + // Not at the end. Map entry has matching or greater key, so check + // match, or if the map entry with greater key is a descendant (i.e. query + // is an ancestor). + if (it->first == primPath || // Query is in map + it->first.HasPrefix(primPath)) { // Query descendant in map + return true; + } + + // Map entry is strictly greater and not a descendant. For the map entry + // to be an ancestor of the query, it would have to be less than the query. + // Thus, if we're at the beginning, the map entry is unrelated to the query. + if (it == _pathToSelections.begin()) { + return false; + } + + // Map entry still strictly greater and not a descendant. Is the previous + // map entry (lower key) a prefix (ancestor) to the query (i.e. query is a + // descendant)? + it = std::prev(it); + return primPath.HasPrefix(it->first); // Ancestor in map +} + SdfPathVector Selection::FindFullySelectedAncestorsInclusive(const SdfPath& primPath, const SdfPath& topmostAncestor/* = SdfPath::AbsoluteRootPath()*/) const { // FLOW_VIEWPORT_TODO Prefix tree would be much higher performance diff --git a/lib/flowViewport/selection/fvpSelection.h b/lib/flowViewport/selection/fvpSelection.h index a6b3087444..a9362321fb 100644 --- a/lib/flowViewport/selection/fvpSelection.h +++ b/lib/flowViewport/selection/fvpSelection.h @@ -51,11 +51,14 @@ class Selection FVP_API bool Remove(const PrimSelection& primSelection); - // Replace the selection with the contents of the argument primPath vector. + // Replace the selection with the contents of the argument vector. // Any empty primPath in the argument will be skipped. FVP_API void Replace(const PrimSelections& primSelections); + FVP_API + void Replace(const Selection& selection); + // Remove all entries from the selection. FVP_API void Clear(); @@ -76,6 +79,16 @@ class Selection FVP_API bool HasFullySelectedAncestorInclusive(const PXR_NS::SdfPath& primPath, const PXR_NS::SdfPath& topmostAncestor = PXR_NS::SdfPath::AbsoluteRootPath()) const; + // Returns true if the argument itself is selected, or a descendant of the + // argument. + FVP_API + bool HasDescendantInclusive(const PXR_NS::SdfPath& primPath) const; + + // Returns true if the argument itself is selected, or an ancestor or + // descendant of the argument is selected. + FVP_API + bool HasAncestorOrDescendantInclusive(const PXR_NS::SdfPath& primPath) const; + // Returns the paths to all fully selected ancestors of the prim up to the specified // topmost ancestor. If the prim is itself selected, its path will also be returned. // By default, the topmost ancestor is set to the absolute root path, so that all diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp index ceca3f514a..952c3420b8 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp @@ -17,6 +17,8 @@ #include "mayaHydraSceneIndex.h" #include +#include +#include #include #include @@ -435,6 +437,21 @@ namespace { sActiveFacesName(L"PolyActiveFaces"); // When we have a render item which is a selection of // faces, it always has this name in maya. +class MayaPathMapper : public Fvp::PathMapper +{ +public: + MayaPathMapper(const MayaHydraSceneIndex& piSi) : _piSi(piSi) {} + + Fvp::PrimSelections + UfePathToPrimSelections(const Ufe::Path& appPath) const override { + return _piSi.UfePathToPrimSelectionsLit(appPath); + } + +private: + // Non-owning reference to prevent ownership cycle. + const MayaHydraSceneIndex& _piSi; +}; + } MayaHydraSceneIndex::MayaHydraSceneIndex( @@ -447,6 +464,7 @@ MayaHydraSceneIndex::MayaHydraSceneIndex( , _rprimPath(initData.delegateID.AppendPath(SdfPath(std::string("rprims")))) , _sprimPath(initData.delegateID.AppendPath(SdfPath(std::string("sprims")))) , _materialPath(initData.delegateID.AppendPath(SdfPath(std::string("materials")))) + , _mayaPathMapper(std::make_shared(*this)) { static std::once_flag once; std::call_once(once, []() { @@ -473,10 +491,18 @@ MayaHydraSceneIndex::MayaHydraSceneIndex( AddPrims({ { _mayaFacesSelectionMaterialPath, HdPrimTypeTokens->material, mayaHydraFacesSelectionMaterialDataSource } }); + + // Register a fallback path mapper in the path mapper registry. Non-Maya + // data models will have a Maya path segment prefix in their UFE path. + // Maya data will not, and will be picked up by the fallback mapper. + Fvp::PathMapperRegistry::Instance().SetFallbackMapper(_mayaPathMapper); } MayaHydraSceneIndex::~MayaHydraSceneIndex() { + // Unregister the fallback path mapper. + Fvp::PathMapperRegistry::Instance().SetFallbackMapper(nullptr); + //If you get a crash in a callback with a nullptr for _sceneIndex, // it may be due to the fact that the _sceneIndex pointer has been nulled as its ref count reached 0 but the destructor is still being called. //You should call RemoveCallbacksAndDeleteAdapters(); before the destructor is called. @@ -759,6 +785,29 @@ Fvp::PrimSelections MayaHydraSceneIndex::UfePathToPrimSelections(const Ufe::Path // the UFE path to a string, then does a Dag path lookup with the string. constexpr bool isSprim = false; // Can't handle sprims as of 15-Aug-2023. SdfPath primPath = GetPrimPath(UfeExtensions::ufeToDagPath(appPath), isSprim); + TF_DEBUG(MAYAHYDRALIB_SCENE_INDEX) + .Msg(" mapped to scene index path %s.\n", primPath.GetText()); + return Fvp::PrimSelections({Fvp::PrimSelection{primPath}}); +} + +Fvp::PrimSelections MayaHydraSceneIndex::UfePathToPrimSelectionsLit( + const Ufe::Path& appPath +) const +{ + TF_DEBUG(MAYAHYDRALIB_SCENE_INDEX) + .Msg("MayaHydraSceneIndex::UfePathToPrimSelectionsLit(const Ufe::Path& %s) called.\n", Ufe::PathString::string(appPath).c_str()); + + // Same as UfePathToPrimSelections(), except returns the "Lighted" + // hierarchy. Should not be required. Having the path mapper call + // UfePathToPrimSelections() would allow factoring out into a single path + // mapper for Usd and Maya (see registration.cpp). + if (appPath.runTimeId() != UfeExtensions::getMayaRunTimeId()) { + return {}; + } + + SdfPath primPath = GetLightedPrimsRootPath().AppendPath(toSdfPath(UfeExtensions::ufeToDagPath(appPath)).MakeRelativePath(SdfPath::AbsoluteRootPath())); + TF_DEBUG(MAYAHYDRALIB_SCENE_INDEX) + .Msg(" mapped to scene index path %s.\n", primPath.GetText()); return Fvp::PrimSelections({Fvp::PrimSelection{primPath}}); } diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h index 7fcb54815d..74e2c4a2ce 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h @@ -35,6 +35,7 @@ #include #include "flowViewport/sceneIndex/fvpPathInterface.h" +#include #include #include @@ -204,6 +205,7 @@ class MAYAHYDRALIB_API MayaHydraSceneIndex : public HdRetainedSceneIndex, public bool GetPlaybackRunning() const; Fvp::PrimSelections UfePathToPrimSelections(const Ufe::Path& appPath) const override; + Fvp::PrimSelections UfePathToPrimSelectionsLit(const Ufe::Path& appPath) const; //Sdfpath of the maya default material SdfPath GetDefaultMaterialPath() const{return _mayaDefaultMaterialPath;} @@ -354,6 +356,8 @@ class MAYAHYDRALIB_API MayaHydraSceneIndex : public HdRetainedSceneIndex, public SdfPath _rprimPath; SdfPath _sprimPath; SdfPath _materialPath; + + const Fvp::PathMapperConstPtr _mayaPathMapper{}; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp b/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp index 8c1adf879c..55e77217bd 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -206,7 +208,18 @@ class PathInterfaceSceneIndex : public Fvp::PathInterfaceSceneIndexBase (op.subOpType == ObjectPathChange::ObjectRename))) { const auto& siPath = _pi.GetSceneIndexAppPath(); if (siPath.startsWith(op.path)) { - _pi.SetSceneIndexAppPath(siPath.reparent(op.path, op.item->path())); + const auto oldPath = siPath; + auto newPath = oldPath.reparent(op.path, op.item->path()); + _pi.SetSceneIndexAppPath(newPath); + + // Update our entry in the path mapper registry. + auto mapper = Fvp::PathMapperRegistry::Instance().GetMapper( + oldPath); + TF_AXIOM(mapper); + TF_AXIOM(Fvp::PathMapperRegistry::Instance().Unregister( + oldPath)); + TF_AXIOM(Fvp::PathMapperRegistry::Instance().Register( + newPath, mapper)); } } } @@ -214,6 +227,21 @@ class PathInterfaceSceneIndex : public Fvp::PathInterfaceSceneIndexBase PathInterfaceSceneIndex& _pi; }; + class UsdPathMapper : public Fvp::PathMapper + { + public: + UsdPathMapper(const PathInterfaceSceneIndex& piSi) : _piSi(piSi) {} + + Fvp::PrimSelections + UfePathToPrimSelections(const Ufe::Path& appPath) const override { + return _piSi.UfePathToPrimSelections(appPath); + } + + private: + // Non-owning reference to prevent ownership cycle. + const PathInterfaceSceneIndex& _piSi; + }; + PathInterfaceSceneIndex( const HdSceneIndexBaseRefPtr& inputSceneIndex, const SdfPath& sceneIndexPathPrefix, @@ -223,6 +251,7 @@ class PathInterfaceSceneIndex : public Fvp::PathInterfaceSceneIndexBase , _sceneIndexPathPrefix(sceneIndexPathPrefix) , _appSceneObserver(std::make_shared(*this)) , _sceneIndexAppPath(sceneIndexAppPath) + , _usdPathMapper(std::make_shared(*this)) { // The gateway node (proxy shape) is a Maya node, so the scene index // path must be a single segment. @@ -231,9 +260,17 @@ class PathInterfaceSceneIndex : public Fvp::PathInterfaceSceneIndexBase // Observe the scene to be informed of path changes to the gateway node // (proxy shape) that corresponds to our scene index data producer. Scene::instance().addObserver(_appSceneObserver); + + // Register a mapper in the path mapper registry. + TF_AXIOM(Fvp::PathMapperRegistry::Instance().Register( + _sceneIndexAppPath, _usdPathMapper)); } ~PathInterfaceSceneIndex() { + // Unregister our path mapper. + TF_AXIOM(Fvp::PathMapperRegistry::Instance().Unregister( + _sceneIndexAppPath)); + // Ufe::Subject has automatic cleanup of stale observers, but this can // be problematic on application exit if the library of the observer is // cleaned up before that of the subject, so simply stop observing. @@ -243,6 +280,7 @@ class PathInterfaceSceneIndex : public Fvp::PathInterfaceSceneIndexBase const SdfPath _sceneIndexPathPrefix; const Observer::Ptr _appSceneObserver; Ufe::Path _sceneIndexAppPath; + const Fvp::PathMapperConstPtr _usdPathMapper; }; constexpr char kMayaUsdProxyShapeNode[] = { "mayaUsdProxyShape" }; diff --git a/lib/mayaHydra/mayaPlugin/pluginDebugCodes.cpp b/lib/mayaHydra/mayaPlugin/pluginDebugCodes.cpp index 71a2f98670..8473c11be6 100644 --- a/lib/mayaHydra/mayaPlugin/pluginDebugCodes.cpp +++ b/lib/mayaHydra/mayaPlugin/pluginDebugCodes.cpp @@ -40,6 +40,10 @@ TF_REGISTRY_FUNCTION(TfDebug) TF_DEBUG_ENVIRONMENT_SYMBOL( MAYAHYDRALIB_RENDEROVERRIDE_SELECTION, "Print information about selection for the Maya VP2 RenderOverride."); + + TF_DEBUG_ENVIRONMENT_SYMBOL( + MAYAHYDRALIB_RENDEROVERRIDE_SCENE_INDEX_CHAIN_MGMT, + "Print information about scene index tree management for the Maya VP2 RenderOverride."); } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaHydra/mayaPlugin/pluginDebugCodes.h b/lib/mayaHydra/mayaPlugin/pluginDebugCodes.h index 403ae5062e..d5f92cce76 100644 --- a/lib/mayaHydra/mayaPlugin/pluginDebugCodes.h +++ b/lib/mayaHydra/mayaPlugin/pluginDebugCodes.h @@ -28,7 +28,8 @@ TF_DEBUG_CODES( MAYAHYDRALIB_RENDEROVERRIDE_DEFAULT_LIGHTING, MAYAHYDRALIB_RENDEROVERRIDE_RENDER, MAYAHYDRALIB_RENDEROVERRIDE_RESOURCES, - MAYAHYDRALIB_RENDEROVERRIDE_SELECTION + MAYAHYDRALIB_RENDEROVERRIDE_SELECTION, + MAYAHYDRALIB_RENDEROVERRIDE_SCENE_INDEX_CHAIN_MGMT ); // clang-format on diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.cpp b/lib/mayaHydra/mayaPlugin/renderOverride.cpp index c6cacc7892..03aa5f398b 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.cpp +++ b/lib/mayaHydra/mayaPlugin/renderOverride.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -170,6 +171,16 @@ void replaceSelectionTask(PXR_NS::HdTaskSharedPtrVector* tasks) *found = HdTaskSharedPtr(new Fvp::SelectionTask); } +std::string getRenderingDestination( + const MHWRender::MFrameContext* frameContext +) +{ + TF_AXIOM(frameContext); + MString viewportId; + frameContext->renderingDestination(viewportId); + return std::string(viewportId.asChar()); +} + } PXR_NAMESPACE_OPEN_SCOPE @@ -649,11 +660,17 @@ MStatus MtohRenderOverride::Render( //This code with strings comparison will go away when doing multi viewports MString panelName; + std::string panelNameStr; auto framecontext = getFrameContext(); if (framecontext){ framecontext->renderingDestination(panelName); + panelNameStr = std::string(panelName.asChar()); + + TF_DEBUG(MAYAHYDRALIB_RENDEROVERRIDE_SCENE_INDEX_CHAIN_MGMT) + .Msg("Rendering destination is %s\n", panelName.asChar()); + auto& manager = Fvp::ViewportInformationAndSceneIndicesPerViewportDataManager::Get(); - if (false == manager.ModelPanelIsAlreadyRegistered(panelName.asChar())){ + if (false == manager.ModelPanelIsAlreadyRegistered(panelNameStr)){ //Get information from viewport std::string cameraName; @@ -666,7 +683,7 @@ MStatus MtohRenderOverride::Render( } //Create a HydraViewportInformation - const Fvp::InformationInterface::ViewportInformation hydraViewportInformation(std::string(panelName.asChar()), cameraName); + const Fvp::InformationInterface::ViewportInformation hydraViewportInformation(panelNameStr, cameraName); const bool dataProducerSceneIndicesAdded = manager.AddViewportInformation(hydraViewportInformation, _renderIndexProxy, _lastFilteringSceneIndexBeforeCustomFiltering); //Update the selection since we have added data producer scene indices through manager.AddViewportInformation to the merging scene index if (dataProducerSceneIndicesAdded && _selectionSceneIndex){ @@ -691,13 +708,16 @@ MStatus MtohRenderOverride::Render( _mayaHydraSceneIndex->SetParams(delegateParams); _mayaHydraSceneIndex->PreFrame(drawContext); + auto& manager = Fvp::ViewportInformationAndSceneIndicesPerViewportDataManager::Get(); if (_NeedToRecreateTheSceneIndicesChain(currentDisplayStyle)){ _blockPrimRemovalPropagationSceneIndex->setPrimRemovalBlocked(true);//Prevent prim removal propagation to keep the current selection. //We need to recreate the filtering scene index chain after the merging scene index as there was a change such as in the BBox display style which has been turned on or off. _lastFilteringSceneIndexBeforeCustomFiltering = nullptr;//Release + + TF_DEBUG(MAYAHYDRALIB_RENDEROVERRIDE_SCENE_INDEX_CHAIN_MGMT) + .Msg("Re-creating scene index chain to render %s\n", panelNameStr.c_str()); _CreateSceneIndicesChainAfterMergingSceneIndex(drawContext); - auto& manager = Fvp::ViewportInformationAndSceneIndicesPerViewportDataManager::Get(); - manager.RemoveViewportInformation(std::string(panelName.asChar())); + manager.RemoveViewportInformation(panelNameStr); //Get information from viewport std::string cameraName; M3dView view; @@ -707,10 +727,32 @@ MStatus MtohRenderOverride::Render( MFnCamera viewCamera(dpath); cameraName = viewCamera.name().asChar(); } - const Fvp::InformationInterface::ViewportInformation hydraViewportInformation(std::string(panelName.asChar()), cameraName); + const Fvp::InformationInterface::ViewportInformation hydraViewportInformation(panelNameStr, cameraName); manager.AddViewportInformation(hydraViewportInformation, _renderIndexProxy, _lastFilteringSceneIndexBeforeCustomFiltering); _blockPrimRemovalPropagationSceneIndex->setPrimRemovalBlocked(false);//Allow prim removal propagation again. } + else { + TF_DEBUG(MAYAHYDRALIB_RENDEROVERRIDE_SCENE_INDEX_CHAIN_MGMT) + .Msg("Re-using existing scene index chain to render %s\n", panelNameStr.c_str()); + + // 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 + // provides prims to render all viewports. + auto isSi = manager.GetIsolateSelectSceneIndex(); + auto isolateSelection = manager.GetOrCreateIsolateSelection(panelNameStr); + if (isSi->GetIsolateSelection() != isolateSelection) { + TF_DEBUG(MAYAHYDRALIB_RENDEROVERRIDE_SCENE_INDEX_CHAIN_MGMT) + .Msg("Switching scene index to isolate selection %p\n", &*isolateSelection); + // Isolate select scene index is being switched to a different + // viewport, set its isolate selection. + isSi->SetViewport(panelNameStr, isolateSelection); + } + else { + TF_DEBUG(MAYAHYDRALIB_RENDEROVERRIDE_SCENE_INDEX_CHAIN_MGMT) + .Msg("Re-using isolate selection %p\n", &*isolateSelection); + } + } } if (_displayStyleSceneIndex) { @@ -1119,9 +1161,21 @@ 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 + auto viewportId = getRenderingDestination(getFrameContext()); + + // Add isolate select scene index. + auto& perVpDataMgr = Fvp::PerViewportDataManager::Get(); + auto selection = perVpDataMgr.GetOrCreateIsolateSelection(viewportId); + auto isSi = Fvp::IsolateSelectSceneIndex::New( + viewportId, selection, _inputSceneIndexOfFilteringSceneIndicesChain); + // At time of writing we have a single selection scene index serving + // all viewports. + perVpDataMgr.SetIsolateSelectSceneIndex(isSi); + _lastFilteringSceneIndexBeforeCustomFiltering = isSi; + // Add display style scene index _lastFilteringSceneIndexBeforeCustomFiltering = _displayStyleSceneIndex = - Fvp::DisplayStyleOverrideSceneIndex::New(_inputSceneIndexOfFilteringSceneIndicesChain); + Fvp::DisplayStyleOverrideSceneIndex::New(_lastFilteringSceneIndexBeforeCustomFiltering); _displayStyleSceneIndex->addExcludedSceneRoot(MAYA_NATIVE_ROOT); // Maya native prims don't use global refinement // Add texture disabling Scene Index diff --git a/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt b/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt index db3a5e7b7d..7a0f7b65af 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt +++ b/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt @@ -53,6 +53,7 @@ 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 diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/CMakeLists.txt b/test/lib/mayaUsd/render/mayaToHydra/cpp/CMakeLists.txt index 430cfbc9a6..3d1c9f236f 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/CMakeLists.txt +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/CMakeLists.txt @@ -20,6 +20,7 @@ target_sources(${TARGET_NAME} testColorPreferences.cpp testCppFramework.cpp testHydraPrim.cpp + testIsolateSelect.cpp testMayaSceneFlattening.cpp testMayaUsdUfeItems.cpp testMergingSceneIndex.cpp diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testIsolateSelect.cpp b/test/lib/mayaUsd/render/mayaToHydra/cpp/testIsolateSelect.cpp new file mode 100644 index 0000000000..383bf221fb --- /dev/null +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testIsolateSelect.cpp @@ -0,0 +1,132 @@ +// Copyright 2024 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 "testUtils.h" + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +PXR_NAMESPACE_USING_DIRECTIVE + +using namespace MayaHydra; + +TEST(TestHydraPrim, isVisible) +{ + const auto& sceneIndices = GetTerminalSceneIndices(); + auto siRoot = sceneIndices.front(); + + auto [argc, argv] = getTestingArgs(); + ASSERT_EQ(argc, 1); + const Ufe::Path appPath(Ufe::PathString::path(argv[0])); + + auto primSelections = ufePathToPrimSelections(appPath); + + // If an application path maps to multiple prim selections, all prim + // selections must be visible, else we fail. + unsigned int primVis = 0; + for (const auto& primSelection : primSelections) { + if (visibility(siRoot, primSelection.primPath)) { + ++primVis; + } + } + + ASSERT_EQ(primVis, primSelections.size()); +} + +TEST(TestHydraPrim, notVisible) +{ + const auto& sceneIndices = GetTerminalSceneIndices(); + auto siRoot = sceneIndices.front(); + + auto [argc, argv] = getTestingArgs(); + ASSERT_EQ(argc, 1); + const Ufe::Path appPath(Ufe::PathString::path(argv[0])); + + auto primSelections = ufePathToPrimSelections(appPath); + + // If an application path maps to multiple prim selections, all prim + // selections must be invisible, else we fail. + int primVis = 0; + for (const auto& primSelection : primSelections) { + if (visibility(siRoot, primSelection.primPath)) { + ++primVis; + } + } + + ASSERT_EQ(primVis, 0); +} + +TEST(TestIsolateSelection, add) +{ + auto [argc, argv] = getTestingArgs(); + ASSERT_EQ(argc, 2); + 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); +} + +TEST(TestIsolateSelection, remove) +{ + auto [argc, argv] = getTestingArgs(); + ASSERT_EQ(argc, 2); + 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); +} + +TEST(TestIsolateSelection, clear) +{ + auto [argc, argv] = getTestingArgs(); + ASSERT_EQ(argc, 1); + const std::string viewportId(argv[0]); + + auto& perVpDataMgr = Fvp::PerViewportDataManager::Get(); + perVpDataMgr.ClearIsolateSelection(viewportId); +} + +TEST(TestIsolateSelection, replace) +{ + auto [argc, argv] = getTestingArgs(); + ASSERT_GE(argc, 2); + const std::string viewportId(argv[0]); + + auto isolateSelect = std::make_shared(); + + for (int i=1; i < argc; ++i) { + const Ufe::Path appPath(Ufe::PathString::path(argv[i])); + auto primSelections = ufePathToPrimSelections(appPath); + for (const auto& primSelection : primSelections) { + isolateSelect->Add(primSelection); + } + } + + auto& perVpDataMgr = Fvp::PerViewportDataManager::Get(); + perVpDataMgr.ReplaceIsolateSelection(viewportId, isolateSelect); +} diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testIsolateSelect.py b/test/lib/mayaUsd/render/mayaToHydra/cpp/testIsolateSelect.py new file mode 100644 index 0000000000..873eb7a170 --- /dev/null +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testIsolateSelect.py @@ -0,0 +1,334 @@ +# Copyright 2024 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. +# + +import fixturesUtils +import mtohUtils +from testUtils import PluginLoaded +import mayaUsd +import mayaUsd_createStageWithNewLayer +import maya.cmds as cmds +from pxr import UsdGeom + +class TestIsolateSelect(mtohUtils.MayaHydraBaseTestCase): + # MayaHydraBaseTestCase.setUpClass requirement. + _file = __file__ + + # Base class setUp() defines HdStorm as the renderer. + + def setupScene(self): + proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + stage = mayaUsd.lib.GetPrim(proxyShapePathStr).GetStage() + + 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") + + cmds.polyTorus() + cmds.polySphere() + cmds.polyCube() + cmds.polyCone() + cmds.group('pSphere1', 'pCube1') + cmds.group('pCone1') + + cmds.refresh() + + return ['|pTorus1', + '|pTorus1|pTorusShape1', + '|stage1|stageShape1,/parent1', + '|stage1|stageShape1,/parent1/cylinder1', + '|stage1|stageShape1,/parent2', + '|stage1|stageShape1,/parent2/cone1', + '|stage1|stageShape1,/parent2/sphere1', + '|group1', + '|group1|pSphere1', + '|group1|pSphere1|pSphereShape1', + '|group1|pCube1', + '|group1|pCube1|pCubeShape1', + '|group2', + '|group2|pCone1', + '|group2|pCone1|pConeShape1'] + + def setupMultiStageScene(self): + scene = self.setupScene() + + proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + stage = mayaUsd.lib.GetPrim(proxyShapePathStr).GetStage() + + 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") + + scene.extend([ + '|stage2|stageShape2,/parent1', + '|stage2|stageShape2,/parent1/cylinder1', + '|stage2|stageShape2,/parent2', + '|stage2|stageShape2,/parent2/cone1', + '|stage2|stageShape2,/parent2/sphere1']) + return scene + + def assertVisible(self, visible): + for v in visible: + self.trace("Testing %s for visibility\n" % v) + cmds.mayaHydraCppTest(v, f="TestHydraPrim.isVisible") + + def assertNotVisible(self, notVisible): + for nv in notVisible: + self.trace("Testing %s for invisibility\n" % nv) + cmds.mayaHydraCppTest(nv, f="TestHydraPrim.notVisible") + + def assertVisibility(self, visible, notVisible): + self.assertVisible(visible) + self.assertNotVisible(notVisible) + + def test_isolateSelectSingleViewport(self): + scene = self.setupScene() + with PluginLoaded('mayaHydraCppTests'): + # The default viewport is in the following panel. + vpPanel = 'modelPanel4' + + #============================================================ + # Add + #============================================================ + + # 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") + + visible = ['|pTorus1', '|pTorus1|pTorusShape1'] + notVisible = scene.copy() + for v in visible: + notVisible.remove(v) + + self.assertVisibility(visible, notVisible) + + # Add a USD object to the isolate selection. + cmds.mayaHydraCppTest(vpPanel, '|stage1|stageShape1,/parent2', f="TestIsolateSelection.add") + + for p in ['|stage1|stageShape1,/parent2', + '|stage1|stageShape1,/parent2/cone1', + '|stage1|stageShape1,/parent2/sphere1']: + visible.append(p) + notVisible.remove(p) + + self.assertVisibility(visible, notVisible) + + #============================================================ + # Remove + #============================================================ + + # Remove the Maya object from the isolate selection. Only the USD + # isolate selected objects are visible. + cmds.mayaHydraCppTest(vpPanel, "|pTorus1", f="TestIsolateSelection.remove") + + visible.clear() + notVisible = scene.copy() + + for p in ['|stage1|stageShape1,/parent2', + '|stage1|stageShape1,/parent2/cone1', + '|stage1|stageShape1,/parent2/sphere1']: + visible.append(p) + notVisible.remove(p) + + self.assertVisibility(visible, notVisible) + + # Remove the USD isolate selected object. Everything is now visible. + cmds.mayaHydraCppTest(vpPanel, '|stage1|stageShape1,/parent2', f="TestIsolateSelection.remove") + + visible = scene.copy() + notVisible.clear() + + self.assertVisibility(visible, notVisible) + + #============================================================ + # Clear + #============================================================ + + # Add an object back to the isolate selection. + cmds.mayaHydraCppTest(vpPanel, '|stage1|stageShape1,/parent1/cylinder1', f="TestIsolateSelection.add") + + notVisible = scene.copy() + visible.clear() + + for p in ['|stage1|stageShape1,/parent1', + '|stage1|stageShape1,/parent1/cylinder1']: + visible.append(p) + notVisible.remove(p) + + self.assertVisibility(visible, notVisible) + + # Clear the isolate selection. + cmds.mayaHydraCppTest(vpPanel, f="TestIsolateSelection.clear") + + visible = scene.copy() + notVisible.clear() + + self.assertVisibility(visible, notVisible) + + #============================================================ + # Replace + #============================================================ + + # Add an object back to the isolate selection. + cmds.mayaHydraCppTest(vpPanel, '|group2|pCone1', f="TestIsolateSelection.add") + + notVisible = scene.copy() + visible.clear() + + for p in ['|group2', '|group2|pCone1', '|group2|pCone1|pConeShape1']: + visible.append(p) + notVisible.remove(p) + + self.assertVisibility(visible, notVisible) + + # Replace this isolate selection with a different one. + cmds.mayaHydraCppTest(vpPanel, '|group1|pCube1', '|stage1|stageShape1,/parent2/cone1', f="TestIsolateSelection.replace") + + visible.clear() + notVisible = scene.copy() + + for p in ['|group1', '|group1|pCube1', '|group1|pCube1|pCubeShape1', + '|stage1|stageShape1,/parent2', + '|stage1|stageShape1,/parent2/cone1']: + visible.append(p) + notVisible.remove(p) + + self.assertVisibility(visible, notVisible) + + # Clear the isolate selection to avoid affecting other tests. + cmds.mayaHydraCppTest(vpPanel, f="TestIsolateSelection.clear") + + # 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. + + 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") + + 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) + + self.assertVisibility(visible, notVisible) + + # As a final step clear the isolate selection to avoid affecting + # other tests. + cmds.mayaHydraCppTest(vpPanel, f="TestIsolateSelection.clear") + +if __name__ == '__main__': + fixturesUtils.runTests(globals()) diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathMapperRegistry.cpp b/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathMapperRegistry.cpp index b9c5959003..babda835a7 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathMapperRegistry.cpp +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathMapperRegistry.cpp @@ -48,6 +48,10 @@ TEST(TestPathMapperRegistry, testRegistry) // Exercise the path mapper registry. auto& r = Fvp::PathMapperRegistry::Instance(); + // For the duration of this test set a null fallback mapper. + auto fbm = r.GetFallbackMapper(); + r.SetFallbackMapper(nullptr); + auto dummy = TestPathMapper::create(); // Can't register for an empty path. @@ -106,4 +110,6 @@ TEST(TestPathMapperRegistry, testRegistry) for (const auto& h : registered) { ASSERT_TRUE(r.Unregister(h)); } + r.SetFallbackMapper(fbm); + ASSERT_EQ(r.GetFallbackMapper(), fbm); } diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.cpp b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.cpp index 10ce9b43f0..c0294c9f41 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.cpp +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.cpp @@ -20,6 +20,10 @@ #include #include +#include +#include +#include + #include #include #include @@ -34,6 +38,9 @@ #include #include +#include +#include + #include #include @@ -513,4 +520,31 @@ 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); + auto handle = HdVisibilitySchema::GetFromParent(prim.dataSource).GetVisibility(); + // If there is no handle the prim is visible. + return (handle ? handle->GetTypedValue(0.0f) : true); +} + } // namespace MAYAHYDRA_NS_DEF diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.h b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.h index e3af995d55..22fe3c2cb4 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.h +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.h @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -29,6 +30,8 @@ #include #include +#include + #include #include @@ -37,6 +40,10 @@ #include #include +UFE_NS_DEF { +class Path; +} + PXR_NAMESPACE_OPEN_SCOPE constexpr double DEFAULT_TOLERANCE = std::numeric_limits::epsilon(); @@ -483,6 +490,23 @@ 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. + */ +bool visibility(const PXR_NS::HdSceneIndexBasePtr& sceneIndex, const PXR_NS::SdfPath& primPath); + } // namespace MAYAHYDRA_NS_DEF #endif // MAYAHYDRA_TEST_UTILS_H