diff --git a/lib/flowViewport/sceneIndex/CMakeLists.txt b/lib/flowViewport/sceneIndex/CMakeLists.txt index 56d3c14588..364e0463d0 100644 --- a/lib/flowViewport/sceneIndex/CMakeLists.txt +++ b/lib/flowViewport/sceneIndex/CMakeLists.txt @@ -13,6 +13,7 @@ target_sources(${TARGET_NAME} fvpWireframeSelectionHighlightSceneIndex.cpp fvpDisplayStyleOverrideSceneIndex.cpp fvpPruneTexturesSceneIndex.cpp + fvpPruningSceneIndex.cpp fvpBBoxSceneIndex.cpp fvpReprSelectorSceneIndex.cpp fvpBlockPrimRemovalPropagationSceneIndex.cpp @@ -32,6 +33,7 @@ set(HEADERS fvpWireframeSelectionHighlightSceneIndex.h fvpDisplayStyleOverrideSceneIndex.h fvpPruneTexturesSceneIndex.h + fvpPruningSceneIndex.h fvpBBoxSceneIndex.h fvpReprSelectorSceneIndex.h fvpBlockPrimRemovalPropagationSceneIndex.h diff --git a/lib/flowViewport/sceneIndex/fvpPruningSceneIndex.cpp b/lib/flowViewport/sceneIndex/fvpPruningSceneIndex.cpp new file mode 100644 index 0000000000..783e8fdf31 --- /dev/null +++ b/lib/flowViewport/sceneIndex/fvpPruningSceneIndex.cpp @@ -0,0 +1,349 @@ +// 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 "fvpPruningSceneIndex.h" + +#include +#include +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + +TF_DEFINE_PUBLIC_TOKENS(FvpPruningTokens, FVP_PRUNING_TOKENS); + +PXR_NAMESPACE_CLOSE_SCOPE + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace { + +template +bool _HasAncestorInclusiveInContainer(const SdfPath& path, const Container& pathsContainer) { + SdfPath currPath = path; + while (!currPath.IsEmpty() && !currPath.IsAbsoluteRootPath()) { + if (pathsContainer.find(currPath) != pathsContainer.end()) { + return true; + } else { + currPath = currPath.GetParentPath(); + } + } + return false; +} + +bool _MeshesFilterHandler(const HdSceneIndexBaseRefPtr& sceneIndex, const SdfPath& primPath, const HdSceneIndexPrim& prim) +{ + // Currently we just flat out remove any prim with a mesh type. If we were to add extra checks to make sure this is not + // a mesh prim that serves another purpose, we would add them here. + return prim.primType == HdPrimTypeTokens->mesh; +} + +bool _CapsulesFilterHandler(const HdSceneIndexBaseRefPtr& sceneIndex, const SdfPath& primPath, const HdSceneIndexPrim& prim) +{ + return prim.primType == HdPrimTypeTokens->capsule; +} + +bool _ConesFilterHandler(const HdSceneIndexBaseRefPtr& sceneIndex, const SdfPath& primPath, const HdSceneIndexPrim& prim) +{ + return prim.primType == HdPrimTypeTokens->cone; +} + +bool _CubesFilterHandler(const HdSceneIndexBaseRefPtr& sceneIndex, const SdfPath& primPath, const HdSceneIndexPrim& prim) +{ + return prim.primType == HdPrimTypeTokens->cube; +} + +bool _CylindersFilterHandler(const HdSceneIndexBaseRefPtr& sceneIndex, const SdfPath& primPath, const HdSceneIndexPrim& prim) +{ + return prim.primType == HdPrimTypeTokens->cylinder; +} + +bool _SpheresFilterHandler(const HdSceneIndexBaseRefPtr& sceneIndex, const SdfPath& primPath, const HdSceneIndexPrim& prim) +{ + return prim.primType == HdPrimTypeTokens->sphere; +} + +bool _NurbsCurvesFilterHandler(const HdSceneIndexBaseRefPtr& sceneIndex, const SdfPath& primPath, const HdSceneIndexPrim& prim) +{ + return prim.primType == HdPrimTypeTokens->nurbsCurves; +} + +bool _NurbsPatchesFilterHandler(const HdSceneIndexBaseRefPtr& sceneIndex, const SdfPath& primPath, const HdSceneIndexPrim& prim) +{ + return prim.primType == HdPrimTypeTokens->nurbsPatch; +} + +} // namespace + +namespace FVP_NS_DEF { + +PruningSceneIndexRefPtr PruningSceneIndex::New(const HdSceneIndexBaseRefPtr &inputSceneIndex) +{ + return TfCreateRefPtr(new PruningSceneIndex(inputSceneIndex)); +} + +PruningSceneIndex::PruningSceneIndex(HdSceneIndexBaseRefPtr const &inputSceneIndex) : + HdSingleInputFilteringSceneIndexBase(inputSceneIndex), + InputSceneIndexUtils(inputSceneIndex) +{ +} + +void PruningSceneIndex::AddExcludedSceneRoot(const PXR_NS::SdfPath& sceneRoot) +{ + _excludedSceneRoots.emplace(sceneRoot); +} + +bool PruningSceneIndex::_IsExcluded(const PXR_NS::SdfPath& primPath) const +{ + return _HasAncestorInclusiveInContainer(primPath, _excludedSceneRoots); +} + +bool PruningSceneIndex::_PrunePrim(const SdfPath& primPath, const HdSceneIndexPrim& prim, const TfToken& pruningToken) const +{ + if (_IsExcluded(primPath)) { + return false; + } + using FilterHandler = std::function; + static std::map filterHandlers = { + { FvpPruningTokens->meshes, _MeshesFilterHandler }, + { FvpPruningTokens->capsules, _CapsulesFilterHandler }, + { FvpPruningTokens->cones, _ConesFilterHandler }, + { FvpPruningTokens->cubes, _CubesFilterHandler }, + { FvpPruningTokens->cylinders, _CylindersFilterHandler }, + { FvpPruningTokens->spheres, _SpheresFilterHandler }, + { FvpPruningTokens->nurbsCurves, _NurbsCurvesFilterHandler }, + { FvpPruningTokens->nurbsPatches, _NurbsPatchesFilterHandler } + }; + return filterHandlers[pruningToken](GetInputSceneIndex(), primPath, prim); +} + +bool PruningSceneIndex::_IsAncestorPrunedInclusive(const SdfPath& primPath) const +{ + return _HasAncestorInclusiveInContainer(primPath, _filtersByPrunedPath); +} + +HdSceneIndexPrim PruningSceneIndex::GetPrim(const SdfPath& primPath) const +{ + if (_filtersByPrunedPath.find(primPath) != _filtersByPrunedPath.end()) { + // Path is pruned out; return nothing. Note that we could also use + // _IsAncestorPrunedInclusive, but child paths of a pruned out prim + // should not be reachable in the first place due to GetChildPrimPaths + // pruning them out as well. + return {}; + } + return GetInputSceneIndex()->GetPrim(primPath); +} + +SdfPathVector PruningSceneIndex::GetChildPrimPaths(const SdfPath& primPath) const +{ + SdfPathVector baseChildPaths = GetInputSceneIndex()->GetChildPrimPaths(primPath); + SdfPathVector editedChildPaths; + for (const auto& baseChildPath : baseChildPaths) { + // Only keep child paths which are not pruned out. + if (_filtersByPrunedPath.find(baseChildPath) == _filtersByPrunedPath.end()) { + editedChildPaths.emplace_back(baseChildPath); + } + } + return editedChildPaths; +} + +void PruningSceneIndex::SetFilterStatus(const TfToken& pruningToken, bool enabled) +{ + if (enabled) { + _EnableFilter(pruningToken); + } else { + _DisableFilter(pruningToken); + } +} + +void PruningSceneIndex::_EnableFilter(const TfToken& pruningToken) +{ + if (_prunedPathsByFilter.find(pruningToken) != _prunedPathsByFilter.end()) { + // Filter already enabled, no change needed. + return; + } + + // Enable the filter + _prunedPathsByFilter[pruningToken] = SdfPathSet(); + + HdSceneIndexObserver::RemovedPrimEntries prunedPrims; + + for (const SdfPath& primPath : HdSceneIndexPrimView(GetInputSceneIndex())) { + if (_PrunePrim(primPath, GetInputSceneIndex()->GetPrim(primPath), pruningToken)) { + // Only send notification if it was not already pruned out, either directly or indirectly + if (!_IsAncestorPrunedInclusive(primPath)) { + prunedPrims.emplace_back(primPath); + } + + _InsertEntry(primPath, pruningToken); + } + } + + if (!prunedPrims.empty()) { + _SendPrimsRemoved(prunedPrims); + } +} + +void PruningSceneIndex::_DisableFilter(const TfToken& pruningToken) +{ + if (_prunedPathsByFilter.find(pruningToken) == _prunedPathsByFilter.end()) { + // Filter already disabled, no change needed. + return; + } + + HdSceneIndexObserver::AddedPrimEntries unprunedPrims; + + SdfPathSet prunedPaths = _prunedPathsByFilter[pruningToken]; + for (const auto& primPath : prunedPaths) { + _RemoveEntry(primPath, pruningToken); + + // Only send notification if it was pruned and no longer is + if (!_IsAncestorPrunedInclusive(primPath)) { + unprunedPrims.emplace_back(primPath, GetInputSceneIndex()->GetPrim(primPath).primType); + } + } + + // Disable the filter + _prunedPathsByFilter.erase(pruningToken); + + if (!unprunedPrims.empty()) { + _SendPrimsAdded(unprunedPrims); + } +} + +std::set PruningSceneIndex::GetActiveFilters() +{ + std::set pruningTokens; + for (const auto& filterEntry : _prunedPathsByFilter) { + pruningTokens.emplace(filterEntry.first); + } + return pruningTokens; +} + +void PruningSceneIndex::_InsertEntry(const PXR_NS::SdfPath& primPath, const PXR_NS::TfToken& pruningToken) +{ + _prunedPathsByFilter[pruningToken].emplace(primPath); + _filtersByPrunedPath[primPath].emplace(pruningToken); +} + +void PruningSceneIndex::_RemoveEntry(const PXR_NS::SdfPath& primPath, const PXR_NS::TfToken& pruningToken) +{ + if (_prunedPathsByFilter.find(pruningToken) != _prunedPathsByFilter.end()) { + _prunedPathsByFilter[pruningToken].erase(primPath); + } + + if (_filtersByPrunedPath.find(primPath) != _filtersByPrunedPath.end()) { + _filtersByPrunedPath[primPath].erase(pruningToken); + if (_filtersByPrunedPath[primPath].empty()) { + _filtersByPrunedPath.erase(primPath); + } + } +} + +void PruningSceneIndex::_PrimsAdded( + const PXR_NS::HdSceneIndexBase &sender, + const PXR_NS::HdSceneIndexObserver::AddedPrimEntries &entries) +{ + HdSceneIndexObserver::AddedPrimEntries editedEntries; + + for (const auto& addedEntry : entries) { + for (const auto& pruningToken : GetActiveFilters()) { + if (_PrunePrim(addedEntry.primPath, GetInputSceneIndex()->GetPrim(addedEntry.primPath), pruningToken)) { + _InsertEntry(addedEntry.primPath, pruningToken); + } + } + + // Only send notification if not pruned + if (!_IsAncestorPrunedInclusive(addedEntry.primPath)) { + editedEntries.emplace_back(addedEntry); + } + } + + if (!editedEntries.empty()) { + _SendPrimsAdded(editedEntries); + } +} + +void PruningSceneIndex::_PrimsRemoved( + const PXR_NS::HdSceneIndexBase &sender, + const PXR_NS::HdSceneIndexObserver::RemovedPrimEntries &entries) +{ + HdSceneIndexObserver::RemovedPrimEntries editedEntries; + + for (const auto& removedEntry : entries) { + if (!_IsAncestorPrunedInclusive(removedEntry.primPath)) { + // Prim was not pruned; forward the notification + editedEntries.emplace_back(removedEntry); + } else { + // Prim was pruned; we have either already sent a PrimsRemoved + // notification from EnableFilter() or have prevented the original + // PrimsAdded notification from being forwarded in the first place. + // No need to send a PrimsRemoved notification for a prim that + // doesn't exist, just remove the pruning entry. + for (const auto& pruningToken : GetActiveFilters()) { + _RemoveEntry(removedEntry.primPath, pruningToken); + } + } + } + + if (!editedEntries.empty()) { + _SendPrimsRemoved(editedEntries); + } +} + +void PruningSceneIndex::_PrimsDirtied( + const PXR_NS::HdSceneIndexBase &sender, + const PXR_NS::HdSceneIndexObserver::DirtiedPrimEntries &entries) +{ + HdSceneIndexObserver::RemovedPrimEntries removedEntries; + HdSceneIndexObserver::AddedPrimEntries addedEntries; + HdSceneIndexObserver::DirtiedPrimEntries editedEntries; + + for (const auto& dirtiedEntry : entries) { + bool wasInitiallyPruned = _IsAncestorPrunedInclusive(dirtiedEntry.primPath); + + HdSceneIndexPrim dirtiedPrim = GetInputSceneIndex()->GetPrim(dirtiedEntry.primPath); + + for (const auto& pruningToken : GetActiveFilters()) { + if (_PrunePrim(dirtiedEntry.primPath, dirtiedPrim, pruningToken)) { + _InsertEntry(dirtiedEntry.primPath, pruningToken); + } else { + _RemoveEntry(dirtiedEntry.primPath, pruningToken); + } + } + + bool isNowPruned = _IsAncestorPrunedInclusive(dirtiedEntry.primPath); + + if (!wasInitiallyPruned && isNowPruned) { + removedEntries.emplace_back(dirtiedEntry.primPath); + } else if (wasInitiallyPruned && !isNowPruned) { + addedEntries.emplace_back(dirtiedEntry.primPath, dirtiedPrim.primType); + } else { + editedEntries.emplace_back(dirtiedEntry); + } + } + + if (!removedEntries.empty()) { + _SendPrimsRemoved(removedEntries); + } + if (!addedEntries.empty()) { + _SendPrimsAdded(addedEntries); + } + if (!editedEntries.empty()) { + _SendPrimsDirtied(editedEntries); + } +} + +} // namespace FVP_NS_DEF diff --git a/lib/flowViewport/sceneIndex/fvpPruningSceneIndex.h b/lib/flowViewport/sceneIndex/fvpPruningSceneIndex.h new file mode 100644 index 0000000000..3549959cb2 --- /dev/null +++ b/lib/flowViewport/sceneIndex/fvpPruningSceneIndex.h @@ -0,0 +1,143 @@ +// 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 FVP_PRUNING_SCENE_INDEX_H +#define FVP_PRUNING_SCENE_INDEX_H + +#include "flowViewport/api.h" +#include "flowViewport/sceneIndex/fvpSceneIndexUtils.h" +#include "flowViewport/sceneIndex/fvpPathInterface.h" + +#include +#include +#include + +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + +// clang-format off +#define FVP_PRUNING_TOKENS \ + (meshes) \ + (capsules) \ + (cones) \ + (cubes) \ + (cylinders) \ + (spheres) \ + (nurbsCurves) \ + (nurbsPatches) \ +// clang-format on + +TF_DECLARE_PUBLIC_TOKENS(FvpPruningTokens, FVP_API, FVP_PRUNING_TOKENS); + +PXR_NAMESPACE_CLOSE_SCOPE + +namespace FVP_NS_DEF { + +class PruningSceneIndex; +typedef PXR_NS::TfRefPtr PruningSceneIndexRefPtr; +typedef PXR_NS::TfRefPtr PruningSceneIndexConstRefPtr; + +class PruningSceneIndex : + public PXR_NS::HdSingleInputFilteringSceneIndexBase + , public InputSceneIndexUtils + , public PathInterface // As a workaround until we move to exclusively using PathMappers +{ +public: + using PXR_NS::HdSingleInputFilteringSceneIndexBase::_GetInputSceneIndex; + + FVP_API + static PruningSceneIndexRefPtr New(const PXR_NS::HdSceneIndexBaseRefPtr &inputScene); + + FVP_API + ~PruningSceneIndex() override = default; + + FVP_API + PXR_NS::HdSceneIndexPrim GetPrim(const PXR_NS::SdfPath& primPath) const override; + + FVP_API + PXR_NS::SdfPathVector GetChildPrimPaths(const PXR_NS::SdfPath& primPath) const override; + + // Add a path for which it and all prims under it will not be considered for filtering. + FVP_API + void AddExcludedSceneRoot(const PXR_NS::SdfPath& sceneRoot); + + // Enable or disable a filter and send corresponding prim notifications + FVP_API + void SetFilterStatus(const PXR_NS::TfToken& pruningToken, bool enabled); + + // Returns the tokens corresponding to the currently enabled filters. + FVP_API + std::set GetActiveFilters(); + + // As a workaround until we move to exclusively using PathMappers + FVP_API + PrimSelections UfePathToPrimSelections(const Ufe::Path& appPath) const override { + PXR_NAMESPACE_USING_DIRECTIVE; + const PathInterface* pathInterface = dynamic_cast(&*GetInputSceneIndex()); + TF_AXIOM(pathInterface); + return pathInterface->UfePathToPrimSelections(appPath); + } + +protected: + FVP_API + PruningSceneIndex(PXR_NS::HdSceneIndexBaseRefPtr const &inputSceneIndex); + + FVP_API + void _PrimsAdded( + const PXR_NS::HdSceneIndexBase &sender, + const PXR_NS::HdSceneIndexObserver::AddedPrimEntries &entries) override; + + FVP_API + void _PrimsRemoved( + const PXR_NS::HdSceneIndexBase &sender, + const PXR_NS::HdSceneIndexObserver::RemovedPrimEntries &entries) override; + + FVP_API + void _PrimsDirtied( + const PXR_NS::HdSceneIndexBase &sender, + const PXR_NS::HdSceneIndexObserver::DirtiedPrimEntries &entries) override; + + FVP_API + void _EnableFilter(const PXR_NS::TfToken& pruningToken); + + FVP_API + void _DisableFilter(const PXR_NS::TfToken& pruningToken); + + FVP_API + void _InsertEntry(const PXR_NS::SdfPath& primPath, const PXR_NS::TfToken& pruningToken); + + FVP_API + void _RemoveEntry(const PXR_NS::SdfPath& primPath, const PXR_NS::TfToken& pruningToken); + + FVP_API + bool _IsExcluded(const PXR_NS::SdfPath& primPath) const; + + FVP_API + bool _PrunePrim(const PXR_NS::SdfPath& primPath, const PXR_NS::HdSceneIndexPrim& prim, const PXR_NS::TfToken& pruningToken) const; + + FVP_API + bool _IsAncestorPrunedInclusive(const PXR_NS::SdfPath& primPath) const; + + std::set _excludedSceneRoots; + + // Maps a pruning token to the set of prim paths that have been pruned out by this token + std::map _prunedPathsByFilter; + std::map> _filtersByPrunedPath; +}; + +} // namespace FVP_NS_DEF + +#endif // FVP_PRUNING_SCENE_INDEX_H diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.cpp b/lib/mayaHydra/mayaPlugin/renderOverride.cpp index f04ba1b09a..63efe13615 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.cpp +++ b/lib/mayaHydra/mayaPlugin/renderOverride.cpp @@ -102,6 +102,7 @@ #include #include #include +#include #include #include #include @@ -113,6 +114,7 @@ #include #include #include +#include #include #include @@ -802,6 +804,31 @@ MStatus MtohRenderOverride::Render( _displayStyleSceneIndex->SetRefineLevel({true, delegateParams.refineLevel}); } + // Update "Show" menu filters + { + auto objectExclusions = framecontext->objectTypeExclusions(); + + static const TfTokenVector polygonFilters = { + FvpPruningTokens->meshes, + FvpPruningTokens->capsules, + FvpPruningTokens->cones, + FvpPruningTokens->cubes, + FvpPruningTokens->cylinders, + FvpPruningTokens->spheres + }; + static const std::map mayaFiltersToFvpPruningTokens = { + { MHWRender::MFrameContext::kExcludeMeshes, polygonFilters }, + { MHWRender::MFrameContext::kExcludeNurbsCurves, {FvpPruningTokens->nurbsCurves} }, + { MHWRender::MFrameContext::kExcludeNurbsSurfaces, {FvpPruningTokens->nurbsPatches} } + }; + + for (auto [mayaFilter, fvpPruningTokens] : mayaFiltersToFvpPruningTokens) { + for (const auto& fvpPruningToken : fvpPruningTokens) { + _pruningSceneIndex->SetFilterStatus(fvpPruningToken, objectExclusions & mayaFilter); + } + } + } + // Toggle textures in the material network const unsigned int currentDisplayMode = drawContext.getDisplayStyle(); bool isTextured = currentDisplayMode & MHWRender::MFrameContext::kTextured; @@ -1088,8 +1115,10 @@ void MtohRenderOverride::_InitHydraResources(const MHWRender::MDrawContext& draw //Put BlockPrimRemovalPropagationSceneIndex first as it can block/unblock the prim removal propagation on the whole scene indices chain _blockPrimRemovalPropagationSceneIndex = Fvp::BlockPrimRemovalPropagationSceneIndex::New(_inputSceneIndexOfFilteringSceneIndicesChain); + _pruningSceneIndex = Fvp::PruningSceneIndex::New(_blockPrimRemovalPropagationSceneIndex); + _pruningSceneIndex->AddExcludedSceneRoot(MAYA_NATIVE_ROOT); // Maya filtering is handled by VP2/OGS. _selection = std::make_shared(); - _selectionSceneIndex = Fvp::SelectionSceneIndex::New(_blockPrimRemovalPropagationSceneIndex, _selection); + _selectionSceneIndex = Fvp::SelectionSceneIndex::New(_pruningSceneIndex, _selection); _selectionSceneIndex->SetDisplayName("Flow Viewport Selection Scene Index"); _inputSceneIndexOfFilteringSceneIndicesChain = _selectionSceneIndex; diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.h b/lib/mayaHydra/mayaPlugin/renderOverride.h index a04d4b6c04..41cf634c1d 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.h +++ b/lib/mayaHydra/mayaPlugin/renderOverride.h @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -280,6 +281,7 @@ class MtohRenderOverride : public MHWRender::MRenderOverride, Fvp::SelectionPtr _selection; Fvp::WireframeSelectionHighlightSceneIndexRefPtr _wireframeSelectionHighlightSceneIndex; Fvp::BlockPrimRemovalPropagationSceneIndexRefPtr _blockPrimRemovalPropagationSceneIndex; + Fvp::PruningSceneIndexRefPtr _pruningSceneIndex; Fvp::LightsManagementSceneIndexRefPtr _lightsManagementSceneIndex; // Naming this identifier _ufeSelection clashes with UFE's selection.h diff --git a/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/cameras_USD_excluded.png b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/cameras_USD_excluded.png new file mode 100644 index 0000000000..dbedef32fd Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/cameras_USD_excluded.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/cameras_USD_included.png b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/cameras_USD_included.png new file mode 100644 index 0000000000..a82b4d776b Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/cameras_USD_included.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/lights_USD_excluded.png b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/lights_USD_excluded.png new file mode 100644 index 0000000000..dbedef32fd Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/lights_USD_excluded.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/lights_USD_included.png b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/lights_USD_included.png new file mode 100644 index 0000000000..0455435c71 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/lights_USD_included.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/nurbsCurves_USD_excluded.png b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/nurbsCurves_USD_excluded.png new file mode 100644 index 0000000000..dbedef32fd Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/nurbsCurves_USD_excluded.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/nurbsCurves_USD_included.png b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/nurbsCurves_USD_included.png new file mode 100644 index 0000000000..ddd8f44330 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/nurbsCurves_USD_included.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/nurbsPatches_USD_excluded.png b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/nurbsPatches_USD_excluded.png new file mode 100644 index 0000000000..dbedef32fd Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/nurbsPatches_USD_excluded.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/nurbsPatches_USD_included.png b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/nurbsPatches_USD_included.png new file mode 100644 index 0000000000..6c42ebcf78 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/nurbsPatches_USD_included.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/polygons_DataProducer_excluded.png b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/polygons_DataProducer_excluded.png new file mode 100644 index 0000000000..dbedef32fd Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/polygons_DataProducer_excluded.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/polygons_DataProducer_included.png b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/polygons_DataProducer_included.png new file mode 100644 index 0000000000..094ca1f827 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/polygons_DataProducer_included.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/polygons_USD_excluded.png b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/polygons_USD_excluded.png new file mode 100644 index 0000000000..dbedef32fd Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/polygons_USD_excluded.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/polygons_USD_included.png b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/polygons_USD_included.png new file mode 100644 index 0000000000..9f33a50475 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/ViewportFiltersTest/polygons_USD_included.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/testViewportFilters.py b/test/lib/mayaUsd/render/mayaToHydra/testViewportFilters.py index a09780cfa8..9580dbb97f 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/testViewportFilters.py +++ b/test/lib/mayaUsd/render/mayaToHydra/testViewportFilters.py @@ -14,8 +14,15 @@ # import maya.cmds as cmds import fixturesUtils +import functools +import mayaUsd_createStageWithNewLayer +import mayaUsd.lib import mayaUtils import mtohUtils +import testUtils +import usdUtils + +from testUtils import PluginLoaded # Note : the order of the bit flags does not correspond to the order # of the options in the "Show" -> "Viewport" UI. @@ -103,6 +110,8 @@ def checkFilter(self, name, exclusionMask, cameraDistance=15): # Restore old mask cmds.modelEditor(activeViewport, edit=True, excludeObjectMask=oldMask) + # --- Maya data --- + # TODO : Construction planes (not working in Hydra as of 2024-05-03) def test_Dimensions(self): @@ -222,5 +231,94 @@ def test_Strokes(self): # TODO : Texture Placements (not working in Hydra as of 2024-05-03) + # --- USD data --- + + def test_UsdPolygons(self): + from pxr import UsdGeom + + stagePath = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + stage = mayaUsd.lib.GetPrim(stagePath).GetStage() + + capsuleName = "UsdCapsule" + capsuleXform = UsdGeom.Xform.Define(stage, "/" + capsuleName + "Xform") + capsuleXform.AddTranslateOp().Set(value=(6, 0, 0)) + UsdGeom.Capsule.Define(stage, str(capsuleXform.GetPath()) + "/" + capsuleName) + + coneName = "UsdCone" + coneXform = UsdGeom.Xform.Define(stage, "/" + coneName + "Xform") + coneXform.AddTranslateOp().Set(value=(2, 0, -2)) + UsdGeom.Cone.Define(stage, str(coneXform.GetPath()) + "/" + coneName) + + cubeName = "UsdCube" + cubeXform = UsdGeom.Xform.Define(stage, "/" + cubeName + "Xform") + cubeXform.AddTranslateOp().Set(value=(-3, 0, -3)) + UsdGeom.Cube.Define(stage, str(cubeXform.GetPath()) + "/" + cubeName) + + cylinderName = "UsdCylinder" + cylinderXform = UsdGeom.Xform.Define(stage, "/" + cylinderName + "Xform") + cylinderXform.AddTranslateOp().Set(value=(-2, 0, 2)) + UsdGeom.Cylinder.Define(stage, str(cylinderXform.GetPath()) + "/" + cylinderName) + + sphereName = "UsdSphere" + sphereXform = UsdGeom.Xform.Define(stage, "/" + sphereName + "Xform") + sphereXform.AddTranslateOp().Set(value=(0, 0, 6)) + UsdGeom.Sphere.Define(stage, str(sphereXform.GetPath()) + "/" + sphereName) + + torusName = cmds.polyTorus() + cmds.move(3, 0, 3) + mayaUsd.lib.PrimUpdaterManager.duplicate(cmds.ls(torusName[0], long=True)[0], stagePath) + cmds.delete(torusName) + + cmds.select(clear=True) + self.checkFilter("polygons_USD", kExcludeMeshes, 10) + + def test_UsdNurbsCurves(self): + def createUsdCurve(stagePath): + circleName = cmds.circle() + usdCircleName = mayaUsd.lib.PrimUpdaterManager.duplicate(cmds.ls(circleName[0], long=True)[0], stagePath) + cmds.delete(circleName) + cmds.select(usdCircleName) + stagePath = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + self.stackInstances(functools.partial(createUsdCurve, stagePath), 50, [0, 0, 0.005]) + self.checkFilter("nurbsCurves_USD", kExcludeNurbsCurves, 2) + + def test_UsdNurbsPatches(self): + stagePath = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + torusName = cmds.torus(sections=20, spans=10, heightRatio=0.5) + mayaUsd.lib.PrimUpdaterManager.duplicate(cmds.ls(torusName[0], long=True)[0], stagePath) + cmds.delete(torusName) + cmds.select(clear=True) + self.checkFilter("nurbsPatches_USD", kExcludeNurbsSurfaces, 3) + + def test_UsdLights(self): + def createUsdLight(stagePath): + lightName = cmds.directionalLight() + usdLightName = mayaUsd.lib.PrimUpdaterManager.duplicate(cmds.ls(lightName, long=True)[0], stagePath) + cmds.delete(lightName) + cmds.select(usdLightName) + stagePath = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + self.stackInstances(functools.partial(createUsdLight, stagePath), 50, [0.005, 0, 0]) + self.checkFilter("lights_USD", kExcludeLights, 2) + + def test_UsdCameras(self): + def createUsdCamera(stagePath): + cameraName = cmds.camera() + usdCameraName = mayaUsd.lib.PrimUpdaterManager.duplicate(cmds.ls(cameraName[0], long=True)[0], stagePath) + cmds.delete(cameraName) + cmds.select(usdCameraName) + stagePath = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + self.stackInstances(functools.partial(createUsdCamera, stagePath), 50, [0.005, 0, 0]) + self.checkFilter("cameras_USD", kExcludeCameras, 3) + + # --- 3rd party data producers --- + + def test_DataProducerPolygons(self): + with PluginLoaded('mayaHydraFlowViewportAPILocator'): + locator = cmds.createNode('MhFlowViewportAPILocator') + cmds.setAttr(locator + '.numCubesX', 2) + cmds.setAttr(locator + '.numCubesY', 2) + cmds.setAttr(locator + '.numCubesZ', 2) + self.checkFilter("polygons_DataProducer", kExcludeMeshes, 12) + if __name__ == '__main__': fixturesUtils.runTests(globals()) diff --git a/test/testUtils/fixturesUtils.py b/test/testUtils/fixturesUtils.py index ab0dd39195..b33ef11622 100644 --- a/test/testUtils/fixturesUtils.py +++ b/test/testUtils/fixturesUtils.py @@ -20,7 +20,7 @@ import unittest # Plugins that are bundled and loaded by default in a Maya installation -DEFAULT_PLUGINS = ['ArubaTessellator', 'modelingToolkit'] +DEFAULT_PLUGINS = ['ArubaTessellator', 'modelingToolkit', 'drawUfe'] def _setUpClass(modulePathName, pluginName, initializeStandalone): '''