-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HYDRA-1098 : Viewport filters for Hydra data (#189)
- Loading branch information
1 parent
822a8ea
commit 18e304b
Showing
19 changed files
with
618 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <pxr/base/tf/staticTokens.h> | ||
#include <pxr/imaging/hd/filteringSceneIndex.h> | ||
#include <pxr/imaging/hd/sceneIndexPrimView.h> | ||
#include <pxr/imaging/hd/tokens.h> | ||
|
||
PXR_NAMESPACE_OPEN_SCOPE | ||
|
||
TF_DEFINE_PUBLIC_TOKENS(FvpPruningTokens, FVP_PRUNING_TOKENS); | ||
|
||
PXR_NAMESPACE_CLOSE_SCOPE | ||
|
||
PXR_NAMESPACE_USING_DIRECTIVE | ||
|
||
namespace { | ||
|
||
template<typename Container> | ||
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<bool(const HdSceneIndexBaseRefPtr&, const SdfPath&, const HdSceneIndexPrim&)>; | ||
static std::map<TfToken, FilterHandler> 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<TfToken> PruningSceneIndex::GetActiveFilters() | ||
{ | ||
std::set<TfToken> 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 |
Oops, something went wrong.