diff --git a/doc/selectionHighlightingArchitecture.md b/doc/selectionHighlightingArchitecture.md index 7603f2fa34..e22b7fa006 100644 --- a/doc/selectionHighlightingArchitecture.md +++ b/doc/selectionHighlightingArchitecture.md @@ -82,103 +82,21 @@ provide the following services: ## Sample Code ### Selection Change -The following selection change code shows the use of the *Path Interface*, -through the *SceneIndexPath()* method, called on the input scene index. The -path interface allows the selection scene index to translate selected -application paths to selected Hydra scene index paths. -``` -void -SelectionSceneIndex::AddSelection(const Ufe::Path& appPath) -{ - TF_DEBUG(FVP_SELECTION_SCENE_INDEX) - .Msg("SelectionSceneIndex::AddSelection(const Ufe::Path& %s) called.\n", Ufe::PathString::string(appPath).c_str()); - - HdSelectionSchema::Builder selectionBuilder; - selectionBuilder.SetFullySelected( - HdRetainedTypedSampledDataSource::New(true)); - - // Call our input scene index to convert the application path to a scene - // index path. - auto sceneIndexPath = _inputSceneIndexPathInterface->SceneIndexPath(appPath); - - TF_DEBUG(FVP_SELECTION_SCENE_INDEX) - .Msg(" Adding %s to the Hydra selection.\n", sceneIndexPath.GetText()); - - _selection->pathToState[sceneIndexPath].selectionSources.push_back( - selectionBuilder.Build()); - - _SendPrimsDirtied({{sceneIndexPath, locators}}); -} -``` +This +[selection change code](../lib/flowViewport/sceneIndex/fvpSelectionSceneIndex.cpp#L150-L167) +shows the use of the *Path Interface*, through the *SceneIndexPath()* method, +called on the input scene index. The path interface allows the selection scene +index to translate selected application paths to selected Hydra scene index +paths. ### Wireframe Selection Highlighting -The following wireframe selection highlighting code shows the use of the -*Selection Interface*, through the *HasFullySelectedAncestorInclusive()* -method, called on the input scene index. The selection interface allows a -selection highlighting filtering scene index to query selected prims. -``` -bool WireframeSelectionHighlightSceneIndex::HasFullySelectedAncestorInclusive(const SdfPath& primPath) const -{ - return _inputSceneIndexSelectionInterface->HasFullySelectedAncestorInclusive(primPath); -} - -HdSceneIndexPrim -WireframeSelectionHighlightSceneIndex::GetPrim(const SdfPath &primPath) const -{ - TF_DEBUG(FVP_WIREFRAME_SELECTION_HIGHLIGHT_SCENE_INDEX) - .Msg("WireframeSelectionHighlightSceneIndex::GetPrim(%s) called.\n", primPath.GetText()); - - auto prim = _GetInputSceneIndex()->GetPrim(primPath); - - // If this isn't one of our prims, we're not responsible for providing a - // selection highlight for it. - if (primPath.HasPrefix(_sceneRoot) && - prim.primType == HdPrimTypeTokens->mesh) { - prim.dataSource = HdOverlayContainerDataSource::New( - { prim.dataSource, HasFullySelectedAncestorInclusive(primPath) ? - sSelectedDisplayStyleDataSource : - sUnselectedDisplayStyleDataSource }); - } - return prim; - -} - -void -WireframeSelectionHighlightSceneIndex::_PrimsDirtied( - const HdSceneIndexBase &sender, - const HdSceneIndexObserver::DirtiedPrimEntries &entries) -{ - TF_DEBUG(FVP_WIREFRAME_SELECTION_HIGHLIGHT_SCENE_INDEX) - .Msg("WireframeSelectionHighlightSceneIndex::_PrimsDirtied() called.\n"); - - HdSceneIndexObserver::DirtiedPrimEntries highlightEntries; - for (const auto& entry : entries) { - // If the dirtied prim isn't one of ours, we're not responsible for - // providing a selection highlight for it. - if (entry.primPath.HasPrefix(_sceneRoot) && - entry.dirtyLocators.Contains( - HdSelectionsSchema::GetDefaultLocator())) { - TF_DEBUG(FVP_WIREFRAME_SELECTION_HIGHLIGHT_SCENE_INDEX) - .Msg(" %s selections locator dirty.\n", entry.primPath.GetText()); - // All mesh prims recursively under the selection dirty prim have a - // dirty wireframe selection highlight. - dirtySelectionHighlightRecursive(entry.primPath, &highlightEntries); - } - } - - if (!highlightEntries.empty()) { - // Append all incoming dirty entries. - highlightEntries.reserve(highlightEntries.size()+entries.size()); - highlightEntries.insert( - highlightEntries.end(), entries.begin(), entries.end()); - _SendPrimsDirtied(highlightEntries); - } - else { - _SendPrimsDirtied(entries); - } -} -``` +This +[wireframe selection highlighting code](../lib/flowViewport/sceneIndex/fvpWireframeSelectionHighlightSceneIndex.cpp#L76-L97) +shows the use of the *Selection*, through the +*HasFullySelectedAncestorInclusive()* method, called on the input selection. +The selection allows a selection highlighting filtering scene index to query +selected prims. ## Design Option Discussion @@ -237,11 +155,11 @@ graph BT; ph1[Plugin highlighting 1]-->hm; subgraph ph[Plugin highlighting] ph2[Plugin highlighting 2]-->ph1; - ph1-. Selection .->ph2; + roSn[/Read-only Selection/]-.->ph1; + roSn-.->ph2; end - sn[Selection scene index]-->ph2; - ph2-. Selection .->sn; - fvpm[Flow Viewport merge]-->sn; + snSi[Selection scene index]-->ph2; + fvpm[Flow Viewport merge]-->snSi; subgraph pd[Plugin data] p1[Plugin 1]; p2[Plugin 2]; @@ -249,7 +167,8 @@ graph BT; fvpm-. Path .->p1; p1-->fvpm; p2-->fvpm; - sn-. Path .->fvpm; + snSi-. Path .->fvpm; + sn[/Selection/]-.->snSi; ``` The plugin data and plugin highlighting subtrees are where plugins add their scene indices. The data scene index is required, and the highlighting scene @@ -258,10 +177,13 @@ index is optional. ### Object Modeling The object modeling is the following: +- **Selection**: builtin provided by the Flow Viewport library. + - Encapsulates the Hydra selection as scene index paths. + - Is shared by the selection scene index and all selection highlighting + scene indices. - **Selection scene index**: builtin provided by the Flow Viewport library. - - Owns and encapsulates the Hydra selection. + - Has a pointer to read and write the Hydra selection. - Translates the application selection to Hydra selection. - - Derives from and implements the selection interface. - **Flow Viewport merging scene index**: builtin provided by the Flow Viewport library. - Receives data from data provider plugin scene indices. @@ -269,30 +191,27 @@ The object modeling is the following: - **Plugin data scene index**: provided by plugin. - Injects plugin data into Hydra - **Plugin selection highlighting scene index**: provided by plugin. + - Has a pointer to a read-only view of the Hydra selection. - Processes dirty selection notifications to dirty the appropriate prim(s) in plugin data, including hierarchical selection highlighting - Adds required geometry or data sources to implement selection highlighting -### New Scene Index Mixin Interface Base Classes +### New Scene Index Mixin Interface Base Class -The Flow Viewport library has two new mixin interface classes: +The Flow Viewport library has a new mixin interface class: - **Path Interface**: so that the builtin selection scene index can query plugins to translate selected object application paths to selected Hydra prim paths. The plugin provides the concrete implementation of this interface. -- **Selection Interface**: so that the plugin selection highlighting scene - indices can query the selection scene index for selected object status. - Plugins provide a pass-through implementation, and the selection scene index - provides the implementation. ### Implementation Classes - **Wireframe selection highlighting scene index**: - Uses Hydra HdRepr to add wireframe representation to selected objects *and their descendants*. - - Requires selected ancestor query from selection interface. + - Requires selected ancestor query from selection. - Dirties descendants on selection dirty. - **Render index proxy**: - Provides encapsulated access to the builtin Flow Viewport merging scene @@ -307,7 +226,7 @@ classDiagram class HdSingleInputFilteringSceneIndexBase class HdMergingSceneIndex -class SelectionInterface{ +class Selection{ +IsFullySelected(SdfPath) bool +HasFullySelectedAncestorInclusive(SdfPath) bool } @@ -327,18 +246,19 @@ class RenderIndexProxy{ } HdSingleInputFilteringSceneIndexBase <|-- SelectionSceneIndex -SelectionInterface <|-- SelectionSceneIndex HdMergingSceneIndex <|-- MergingSceneIndex PathInterface <|-- MergingSceneIndex HdSingleInputFilteringSceneIndexBase <|-- WireframeSelectionHighlightSceneIndex -SelectionInterface <|-- WireframeSelectionHighlightSceneIndex RenderIndexProxy *-- MergingSceneIndex : Owns -WireframeSelectionHighlightSceneIndex ..> SelectionSceneIndex : Selected SelectionSceneIndex ..> MergingSceneIndex : Path + +SelectionSceneIndex o-- Selection : Read / Write + +WireframeSelectionHighlightSceneIndex o-- Selection : Read ``` ## Algorithmic Complexity diff --git a/lib/flowViewport/sceneIndex/CMakeLists.txt b/lib/flowViewport/sceneIndex/CMakeLists.txt index 04dd9b47c9..019e55d697 100644 --- a/lib/flowViewport/sceneIndex/CMakeLists.txt +++ b/lib/flowViewport/sceneIndex/CMakeLists.txt @@ -4,22 +4,18 @@ target_sources(${TARGET_NAME} PRIVATE fvpMergingSceneIndex.cpp - fvpPassThroughSelectionInterfaceSceneIndex.cpp fvpPathInterface.cpp fvpPathInterfaceSceneIndex.cpp fvpRenderIndexProxy.cpp - fvpSelectionInterface.cpp fvpSelectionSceneIndex.cpp fvpWireframeSelectionHighlightSceneIndex.cpp ) set(HEADERS fvpMergingSceneIndex.h - fvpPassThroughSelectionInterfaceSceneIndex.h fvpPathInterface.h fvpPathInterfaceSceneIndex.h fvpRenderIndexProxy.h - fvpSelectionInterface.h fvpSelectionSceneIndex.h fvpWireframeSelectionHighlightSceneIndex.h ) diff --git a/lib/flowViewport/sceneIndex/fvpPassThroughSelectionInterfaceSceneIndex.cpp b/lib/flowViewport/sceneIndex/fvpPassThroughSelectionInterfaceSceneIndex.cpp deleted file mode 100644 index 7cd82362aa..0000000000 --- a/lib/flowViewport/sceneIndex/fvpPassThroughSelectionInterfaceSceneIndex.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright 2023 Autodesk -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include "flowViewport/sceneIndex/fvpPassThroughSelectionInterfaceSceneIndex.h" - -#include "flowViewport/debugCodes.h" - -PXR_NAMESPACE_USING_DIRECTIVE - -namespace FVP_NS_DEF { - -PassThroughSelectionInterfaceSceneIndexBase:: -PassThroughSelectionInterfaceSceneIndexBase(HdSceneIndexBaseRefPtr const &inputSceneIndex) - : HdSingleInputFilteringSceneIndexBase(inputSceneIndex) - , _inputSceneIndexSelectionInterface(dynamic_cast(&*_GetInputSceneIndex())) -{ - TF_AXIOM(_inputSceneIndexSelectionInterface); -} - -bool PassThroughSelectionInterfaceSceneIndexBase::IsFullySelected(const SdfPath& primPath) const -{ - return _inputSceneIndexSelectionInterface->IsFullySelected(primPath); -} - -bool PassThroughSelectionInterfaceSceneIndexBase::HasFullySelectedAncestorInclusive(const SdfPath& primPath) const -{ - return _inputSceneIndexSelectionInterface->HasFullySelectedAncestorInclusive(primPath); -} - -} diff --git a/lib/flowViewport/sceneIndex/fvpPassThroughSelectionInterfaceSceneIndex.h b/lib/flowViewport/sceneIndex/fvpPassThroughSelectionInterfaceSceneIndex.h deleted file mode 100644 index 641c16e3f6..0000000000 --- a/lib/flowViewport/sceneIndex/fvpPassThroughSelectionInterfaceSceneIndex.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2023 Autodesk -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef FVP_PASS_THROUGH_SELECTION_INTERFACE_SCENE_INDEX_H -#define FVP_PASS_THROUGH_SELECTION_INTERFACE_SCENE_INDEX_H - -#include "flowViewport/api.h" -#include "flowViewport/sceneIndex/fvpSelectionInterface.h" - -#include - -namespace FVP_NS_DEF { - -/// \class PassThroughSelectionInterfaceSceneIndexBase -/// -/// Convenience base class that passes through the SelectionInterface queries -/// to its input filtering scene index. -/// -class PassThroughSelectionInterfaceSceneIndexBase - : public PXR_NS::HdSingleInputFilteringSceneIndexBase, - public SelectionInterface -{ -public: - - //! Selection interface overrides. - //@{ - FVP_API - bool IsFullySelected(const PXR_NS::SdfPath& primPath) const override; - - FVP_API - bool HasFullySelectedAncestorInclusive(const PXR_NS::SdfPath& primPath) const override; - //@} - -protected: - - FVP_API - PassThroughSelectionInterfaceSceneIndexBase( - const PXR_NS::HdSceneIndexBaseRefPtr &inputSceneIndex); - -private: - - const SelectionInterface* const _inputSceneIndexSelectionInterface; -}; - -} - -#endif diff --git a/lib/flowViewport/sceneIndex/fvpSelectionInterface.h b/lib/flowViewport/sceneIndex/fvpSelectionInterface.h deleted file mode 100644 index 3ab73a2c78..0000000000 --- a/lib/flowViewport/sceneIndex/fvpSelectionInterface.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2023 Autodesk -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef FVP_SELECTION_INTERFACE_H -#define FVP_SELECTION_INTERFACE_H - -#include "flowViewport/api.h" - -#include - -PXR_NAMESPACE_OPEN_SCOPE -class SdfPath; -PXR_NAMESPACE_CLOSE_SCOPE - -namespace FVP_NS_DEF { - -/// \class SelectionInterface -/// -/// A pure interface class to allow querying a scene index for selected status -/// without inspecting data sources in scene prims. To be used as a mix-in -/// class for scene indices. -/// -class SelectionInterface -{ -public: - - //! Returns whether the prim path is a fully selected prim. - //! If no such path exists, false is returned. - //! \return Whether the argument path is a fully selected prim. - FVP_API - virtual bool IsFullySelected(const PXR_NS::SdfPath& primPath) const = 0; - - //! Returns whether the prim path is a fully selected prim, or has an - //! ancestor that is a fully selected prim. If no such path exists, false - //! is returned. - //! \return Whether the argument path is a fully selected prim, or has a - //! fully selected ancestor. - FVP_API - virtual bool HasFullySelectedAncestorInclusive(const PXR_NS::SdfPath& primPath) const = 0; - -protected: - - FVP_API - SelectionInterface() = default; - - FVP_API - virtual ~SelectionInterface(); -}; - -} - -#endif diff --git a/lib/flowViewport/sceneIndex/fvpSelectionSceneIndex.cpp b/lib/flowViewport/sceneIndex/fvpSelectionSceneIndex.cpp index 3236f13b08..23da925531 100644 --- a/lib/flowViewport/sceneIndex/fvpSelectionSceneIndex.cpp +++ b/lib/flowViewport/sceneIndex/fvpSelectionSceneIndex.cpp @@ -38,6 +38,7 @@ #include "flowViewport/sceneIndex/fvpSelectionSceneIndex.h" #include "flowViewport/sceneIndex/fvpPathInterface.h" +#include "flowViewport/selection/fvpSelection.h" #include "flowViewport/debugCodes.h" @@ -56,53 +57,13 @@ const HdDataSourceLocatorSet selectionsSchemaDefaultLocator{HdSelectionsSchema:: namespace FVP_NS_DEF { -namespace SelectionSceneIndex_Impl -{ - -struct _PrimSelectionState -{ - // Container data sources conforming to HdSelectionSchema - std::vector selectionSources; - - HdDataSourceBaseHandle GetVectorDataSource() { - return HdSelectionsSchema::BuildRetained( - selectionSources.size(), - selectionSources.data()); - } -}; - -struct _Selection -{ - bool IsSelected(const SdfPath& primPath) const - { - return pathToState.find(primPath) != pathToState.end(); - } - - bool HasSelectedAncestorInclusive(const SdfPath& primPath) const - { - // FLOW_VIEWPORT_TODO Prefix tree would be much higher performance - // than iterating over the whole selection, especially for a large - // selection. PPT, 13-Sep-2023. - for(const auto& entry : pathToState) { - if (primPath.HasPrefix(entry.first)) { - return true; - } - } - return false; - } - - // Maps prim path to data sources to be returned by the vector data - // source at locator selections. - std::map pathToState; -}; - class _PrimSource : public HdContainerDataSource { public: HD_DECLARE_DATASOURCE(_PrimSource); _PrimSource(HdContainerDataSourceHandle const &inputSource, - _SelectionSharedPtr const selection, + const SelectionConstPtr& selection, const SdfPath &primPath) : _inputSource(inputSource) , _selection(selection) @@ -113,7 +74,7 @@ class _PrimSource : public HdContainerDataSource TfTokenVector GetNames() override { TfTokenVector names = _inputSource->GetNames(); - if (_selection->IsSelected(_primPath)) { + if (_selection->IsFullySelected(_primPath)) { names.push_back(HdSelectionsSchemaTokens->selections); } return names; @@ -122,9 +83,7 @@ class _PrimSource : public HdContainerDataSource HdDataSourceBaseHandle Get(const TfToken &name) override { if (name == HdSelectionsSchemaTokens->selections) { - auto it = _selection->pathToState.find(_primPath); - return (it != _selection->pathToState.end()) ? - it->second.GetVectorDataSource() : nullptr; + return _selection->GetVectorDataSource(_primPath); } return _inputSource->Get(name); @@ -132,26 +91,28 @@ class _PrimSource : public HdContainerDataSource private: HdContainerDataSourceHandle const _inputSource; - _SelectionSharedPtr const _selection; + const SelectionConstPtr _selection; const SdfPath _primPath; }; -} - -using namespace SelectionSceneIndex_Impl; - SelectionSceneIndexRefPtr -SelectionSceneIndex::New(HdSceneIndexBaseRefPtr const &inputSceneIndex) +SelectionSceneIndex::New( + const HdSceneIndexBaseRefPtr& inputSceneIndex, + const SelectionPtr& selection +) { TF_DEBUG(FVP_SELECTION_SCENE_INDEX) .Msg("SelectionSceneIndex::New() called.\n"); - return TfCreateRefPtr(new SelectionSceneIndex(inputSceneIndex)); + return TfCreateRefPtr(new SelectionSceneIndex(inputSceneIndex, selection)); } SelectionSceneIndex:: -SelectionSceneIndex(HdSceneIndexBaseRefPtr const &inputSceneIndex) +SelectionSceneIndex( + const HdSceneIndexBaseRefPtr& inputSceneIndex, + const SelectionPtr& selection +) : HdSingleInputFilteringSceneIndexBase(inputSceneIndex) - , _selection(std::make_shared<_Selection>()) + , _selection(selection) , _inputSceneIndexPathInterface(dynamic_cast(&*_GetInputSceneIndex())) { TF_DEBUG(FVP_SELECTION_SCENE_INDEX) @@ -192,25 +153,16 @@ SelectionSceneIndex::AddSelection(const Ufe::Path& appPath) TF_DEBUG(FVP_SELECTION_SCENE_INDEX) .Msg("SelectionSceneIndex::AddSelection(const Ufe::Path& %s) called.\n", Ufe::PathString::string(appPath).c_str()); - HdSelectionSchema::Builder selectionBuilder; - selectionBuilder.SetFullySelected( - HdRetainedTypedSampledDataSource::New(true)); - // Call our input scene index to convert the application path to a scene // index path. auto sceneIndexPath = SceneIndexPath(appPath); - if (sceneIndexPath.IsEmpty()) { - return; - } - TF_DEBUG(FVP_SELECTION_SCENE_INDEX) .Msg(" Adding %s to the Hydra selection.\n", sceneIndexPath.GetText()); - _selection->pathToState[sceneIndexPath].selectionSources.push_back( - selectionBuilder.Build()); - - _SendPrimsDirtied({{sceneIndexPath, selectionsSchemaDefaultLocator}}); + if (_selection->Add(sceneIndexPath)) { + _SendPrimsDirtied({{sceneIndexPath, selectionsSchemaDefaultLocator}}); + } } void SelectionSceneIndex::RemoveSelection(const Ufe::Path& appPath) @@ -219,18 +171,12 @@ void SelectionSceneIndex::RemoveSelection(const Ufe::Path& appPath) .Msg("SelectionSceneIndex::RemoveSelection(const Ufe::Path& %s) called.\n", Ufe::PathString::string(appPath).c_str()); // Call our input scene index to convert the application path to a scene - // index path. If there is no path, or the path is not selected, early out. + // index path. auto sceneIndexPath = SceneIndexPath(appPath); - if (sceneIndexPath.IsEmpty() || - (_selection->pathToState.erase(sceneIndexPath) != 1)) { - return; + if (_selection->Remove(sceneIndexPath)) { + _SendPrimsDirtied({{sceneIndexPath, selectionsSchemaDefaultLocator}}); } - - HdSceneIndexObserver::DirtiedPrimEntries entry; - entry.emplace_back(sceneIndexPath, selectionsSchemaDefaultLocator); - - _SendPrimsDirtied(entry); } void @@ -239,17 +185,18 @@ SelectionSceneIndex::ClearSelection() TF_DEBUG(FVP_SELECTION_SCENE_INDEX) .Msg("SelectionSceneIndex::ClearSelection() called.\n"); - if (_selection->pathToState.empty()) { + if (_selection->IsEmpty()) { return; } HdSceneIndexObserver::DirtiedPrimEntries entries; - entries.reserve(_selection->pathToState.size()); - for (const auto &pathAndSelections : _selection->pathToState) { - entries.emplace_back(pathAndSelections.first, selectionsSchemaDefaultLocator); + auto paths = _selection->GetFullySelectedPaths(); + entries.reserve(paths.size()); + for (const auto& path : paths) { + entries.emplace_back(path, selectionsSchemaDefaultLocator); } - _selection->pathToState.clear(); + _selection->Clear(); _SendPrimsDirtied(entries); } @@ -259,21 +206,20 @@ void SelectionSceneIndex::ReplaceSelection(const Ufe::Selection& selection) TF_DEBUG(FVP_SELECTION_SCENE_INDEX) .Msg("SelectionSceneIndex::ReplaceSelection() called.\n"); - HdSelectionSchema::Builder selectionBuilder; - selectionBuilder.SetFullySelected( - HdRetainedTypedSampledDataSource::New(true)); - // Process the selection replace by performing dirty notification of the // existing selection state. We could do this more efficiently by // accounting for overlapping previous and new selections. HdSceneIndexObserver::DirtiedPrimEntries entries; - entries.reserve(_selection->pathToState.size() + selection.size()); - for (const auto &pathAndSelections : _selection->pathToState) { - entries.emplace_back(pathAndSelections.first, selectionsSchemaDefaultLocator); + auto paths = _selection->GetFullySelectedPaths(); + entries.reserve(paths.size() + selection.size()); + for (const auto& path : paths) { + entries.emplace_back(path, selectionsSchemaDefaultLocator); } - _selection->pathToState.clear(); + _selection->Clear(); + SdfPathVector sceneIndexSn; + sceneIndexSn.reserve(selection.size()); for (const auto& snItem : selection) { // Call our input scene index to convert the application path to a scene // index path. @@ -283,26 +229,24 @@ void SelectionSceneIndex::ReplaceSelection(const Ufe::Selection& selection) continue; } + sceneIndexSn.emplace_back(sceneIndexPath); TF_DEBUG(FVP_SELECTION_SCENE_INDEX) .Msg(" Adding %s to the Hydra selection.\n", sceneIndexPath.GetText()); - - _selection->pathToState[sceneIndexPath].selectionSources.push_back( - selectionBuilder.Build()); - entries.emplace_back(sceneIndexPath, selectionsSchemaDefaultLocator); } + _selection->Replace(sceneIndexSn); _SendPrimsDirtied(entries); } bool SelectionSceneIndex::IsFullySelected(const SdfPath& primPath) const { - return _selection->IsSelected(primPath); + return _selection->IsFullySelected(primPath); } bool SelectionSceneIndex::HasFullySelectedAncestorInclusive(const SdfPath& primPath) const { - return _selection->HasSelectedAncestorInclusive(primPath); + return _selection->HasFullySelectedAncestorInclusive(primPath); } SdfPath SelectionSceneIndex::SceneIndexPath(const Ufe::Path& appPath) const @@ -318,12 +262,7 @@ SdfPath SelectionSceneIndex::SceneIndexPath(const Ufe::Path& appPath) const SdfPathVector SelectionSceneIndex::GetFullySelectedPaths() const { - SdfPathVector fullySelectedPaths; - fullySelectedPaths.reserve(_selection->pathToState.size()); - for(const auto& entry : _selection->pathToState) { - fullySelectedPaths.emplace_back(entry.first); - } - return fullySelectedPaths; + return _selection->GetFullySelectedPaths(); } void @@ -356,13 +295,9 @@ SelectionSceneIndex::_PrimsRemoved( TF_DEBUG(FVP_SELECTION_SCENE_INDEX) .Msg("SelectionSceneIndex::_PrimsRemoved() called.\n"); - if (!_selection->pathToState.empty()) { + if (!_selection->IsEmpty()) { for (const auto &entry : entries) { - auto it = _selection->pathToState.lower_bound(entry.primPath); - while (it != _selection->pathToState.end() && - it->first.HasPrefix(entry.primPath)) { - it = _selection->pathToState.erase(it); - } + _selection->RemoveHierarchy(entry.primPath); } } diff --git a/lib/flowViewport/sceneIndex/fvpSelectionSceneIndex.h b/lib/flowViewport/sceneIndex/fvpSelectionSceneIndex.h index c65b973420..8cc9d21a2b 100644 --- a/lib/flowViewport/sceneIndex/fvpSelectionSceneIndex.h +++ b/lib/flowViewport/sceneIndex/fvpSelectionSceneIndex.h @@ -39,7 +39,7 @@ #define FVP_SELECTION_SCENE_INDEX_H #include "flowViewport/api.h" -#include "flowViewport/sceneIndex/fvpSelectionInterface.h" +#include "flowViewport/selection/fvpSelectionFwd.h" #include "flowViewport/sceneIndex/fvpPathInterface.h" #include @@ -56,6 +56,7 @@ class Selection; namespace FVP_NS_DEF { class PathInterface; +class Selection; // Pixar declarePtrs.h TF_DECLARE_REF_PTRS macro unusable, places resulting // type in PXR_NS. @@ -63,11 +64,6 @@ class SelectionSceneIndex; typedef PXR_NS::TfRefPtr SelectionSceneIndexRefPtr; typedef PXR_NS::TfRefPtr SelectionSceneIndexConstRefPtr; -namespace SelectionSceneIndex_Impl -{ -using _SelectionSharedPtr = std::shared_ptr; -} - /// \class SelectionSceneIndex /// /// A simple scene index adding HdSelectionsSchema to all prims selected @@ -75,13 +71,14 @@ using _SelectionSharedPtr = std::shared_ptr; /// class SelectionSceneIndex final : public PXR_NS::HdSingleInputFilteringSceneIndexBase - , public SelectionInterface , public PathInterface { public: FVP_API static SelectionSceneIndexRefPtr New( - PXR_NS::HdSceneIndexBaseRefPtr const &inputSceneIndex); + PXR_NS::HdSceneIndexBaseRefPtr const &inputSceneIndex, + const std::shared_ptr& selection + ); FVP_API PXR_NS::HdSceneIndexPrim GetPrim(const PXR_NS::SdfPath &primPath) const override; @@ -106,14 +103,11 @@ class SelectionSceneIndex final FVP_API void ClearSelection(); - //! Selection interface overrides. - //@{ FVP_API - bool IsFullySelected(const PXR_NS::SdfPath& primPath) const override; + bool IsFullySelected(const PXR_NS::SdfPath& primPath) const; FVP_API - bool HasFullySelectedAncestorInclusive(const PXR_NS::SdfPath& primPath) const override; - //@} + bool HasFullySelectedAncestorInclusive(const PXR_NS::SdfPath& primPath) const; //! Path interface override. Forwards the call to the input scene index, //! and warns about empty return paths. @@ -141,9 +135,10 @@ class SelectionSceneIndex final private: SelectionSceneIndex( - const PXR_NS::HdSceneIndexBaseRefPtr &inputSceneIndex); + const PXR_NS::HdSceneIndexBaseRefPtr &inputSceneIndex, + const std::shared_ptr& selection); - SelectionSceneIndex_Impl::_SelectionSharedPtr _selection; + const SelectionPtr _selection; const PathInterface* const _inputSceneIndexPathInterface; }; diff --git a/lib/flowViewport/sceneIndex/fvpWireframeSelectionHighlightSceneIndex.cpp b/lib/flowViewport/sceneIndex/fvpWireframeSelectionHighlightSceneIndex.cpp index e8f691378c..88474ebaff 100644 --- a/lib/flowViewport/sceneIndex/fvpWireframeSelectionHighlightSceneIndex.cpp +++ b/lib/flowViewport/sceneIndex/fvpWireframeSelectionHighlightSceneIndex.cpp @@ -15,6 +15,7 @@ // #include "flowViewport/sceneIndex/fvpWireframeSelectionHighlightSceneIndex.h" +#include "flowViewport/selection/fvpSelection.h" #include "flowViewport/debugCodes.h" @@ -50,9 +51,12 @@ const HdDataSourceLocator reprSelectorLocator( namespace FVP_NS_DEF { HdSceneIndexBaseRefPtr -WireframeSelectionHighlightSceneIndex::New(HdSceneIndexBaseRefPtr const &inputSceneIndex) +WireframeSelectionHighlightSceneIndex::New( + const HdSceneIndexBaseRefPtr& inputSceneIndex, + const SelectionConstPtr& selection +) { - return TfCreateRefPtr(new WireframeSelectionHighlightSceneIndex(inputSceneIndex)); + return TfCreateRefPtr(new WireframeSelectionHighlightSceneIndex(inputSceneIndex, selection)); } const HdDataSourceLocator& WireframeSelectionHighlightSceneIndex::ReprSelectorLocator() @@ -61,8 +65,12 @@ const HdDataSourceLocator& WireframeSelectionHighlightSceneIndex::ReprSelectorLo } WireframeSelectionHighlightSceneIndex:: -WireframeSelectionHighlightSceneIndex(HdSceneIndexBaseRefPtr const &inputSceneIndex) - : PassThroughSelectionInterfaceSceneIndexBase(inputSceneIndex) +WireframeSelectionHighlightSceneIndex( + const HdSceneIndexBaseRefPtr& inputSceneIndex, + const SelectionConstPtr& selection +) + : HdSingleInputFilteringSceneIndexBase(inputSceneIndex), + _selection(selection) {} HdSceneIndexPrim @@ -80,12 +88,11 @@ WireframeSelectionHighlightSceneIndex::GetPrim(const SdfPath &primPath) const // capsule primitive types) to meshes. if (!isExcluded(primPath) && prim.primType == HdPrimTypeTokens->mesh) { prim.dataSource = HdOverlayContainerDataSource::New( - { prim.dataSource, HasFullySelectedAncestorInclusive(primPath) ? + { prim.dataSource, _selection->HasFullySelectedAncestorInclusive(primPath) ? sSelectedDisplayStyleDataSource : sUnselectedDisplayStyleDataSource }); } return prim; - } SdfPathVector diff --git a/lib/flowViewport/sceneIndex/fvpWireframeSelectionHighlightSceneIndex.h b/lib/flowViewport/sceneIndex/fvpWireframeSelectionHighlightSceneIndex.h index 34a7ae590b..1738af67f0 100644 --- a/lib/flowViewport/sceneIndex/fvpWireframeSelectionHighlightSceneIndex.h +++ b/lib/flowViewport/sceneIndex/fvpWireframeSelectionHighlightSceneIndex.h @@ -16,14 +16,17 @@ #define FVP_WIREFRAME_SELECTION_HIGHLIGHT_SCENE_INDEX_H #include "flowViewport/api.h" -#include "flowViewport/sceneIndex/fvpPassThroughSelectionInterfaceSceneIndex.h" +#include "flowViewport/selection/fvpSelectionFwd.h" +#include #include #include namespace FVP_NS_DEF { +class Selection; + // Pixar declarePtrs.h TF_DECLARE_REF_PTRS macro unusable, places resulting // type in PXR_NS. class WireframeSelectionHighlightSceneIndex; @@ -36,13 +39,14 @@ typedef PXR_NS::TfRefPtr WireframeS /// and their descendants. /// class WireframeSelectionHighlightSceneIndex - : public PassThroughSelectionInterfaceSceneIndexBase + : public PXR_NS::HdSingleInputFilteringSceneIndexBase { public: FVP_API static PXR_NS::HdSceneIndexBaseRefPtr New( - const PXR_NS::HdSceneIndexBaseRefPtr& inputSceneIndex + const PXR_NS::HdSceneIndexBaseRefPtr& inputSceneIndex, + const std::shared_ptr& selection ); FVP_API @@ -65,7 +69,8 @@ class WireframeSelectionHighlightSceneIndex FVP_API WireframeSelectionHighlightSceneIndex( - const PXR_NS::HdSceneIndexBaseRefPtr& inputSceneIndex + const PXR_NS::HdSceneIndexBaseRefPtr& inputSceneIndex, + const std::shared_ptr& selection ); FVP_API @@ -93,6 +98,8 @@ class WireframeSelectionHighlightSceneIndex bool isExcluded(const PXR_NS::SdfPath& sceneRoot) const; std::set _excludedSceneRoots; + + const SelectionConstPtr _selection; }; } diff --git a/lib/flowViewport/selection/CMakeLists.txt b/lib/flowViewport/selection/CMakeLists.txt index f9ca43d0c4..f7ce7d4887 100644 --- a/lib/flowViewport/selection/CMakeLists.txt +++ b/lib/flowViewport/selection/CMakeLists.txt @@ -3,13 +3,17 @@ # ----------------------------------------------------------------------------- target_sources(${TARGET_NAME} PRIVATE - fvpSelectionTracker.cpp + fvpSelection.cpp + fvpSelectionFwd.cpp fvpSelectionTask.cpp + fvpSelectionTracker.cpp ) set(HEADERS - fvpSelectionTracker.h + fvpSelection.h + fvpSelectionFwd.h fvpSelectionTask.h + fvpSelectionTracker.h ) # ----------------------------------------------------------------------------- diff --git a/lib/flowViewport/selection/fvpSelection.cpp b/lib/flowViewport/selection/fvpSelection.cpp new file mode 100644 index 0000000000..851ec064c9 --- /dev/null +++ b/lib/flowViewport/selection/fvpSelection.cpp @@ -0,0 +1,163 @@ +// +// Copyright 2022 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +// Copyright 2023 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "flowViewport/selection/fvpSelection.h" + +#include "pxr/imaging/hd/selectionSchema.h" +#include "pxr/imaging/hd/selectionsSchema.h" + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace { + +using namespace Fvp; + +class SelectionSchemaFullySelectedBuilder { +public: + SelectionSchemaFullySelectedBuilder() { + _builder.SetFullySelected( + HdRetainedTypedSampledDataSource::New(true)); + } + + HdContainerDataSourceHandle Build() { return _builder.Build(); } + +private: + HdSelectionSchema::Builder _builder; +}; + +SelectionSchemaFullySelectedBuilder selectionBuilder; + +} + +namespace FVP_NS_DEF { + +HdDataSourceBaseHandle +Selection::_PrimSelectionState::GetVectorDataSource() const +{ + return HdSelectionsSchema::BuildRetained( + selectionSources.size(), selectionSources.data() + ); +}; + +bool +Selection::Add(const PXR_NS::SdfPath& primPath) +{ + if (primPath.IsEmpty()) { + return false; + } + + _pathToState[primPath].selectionSources.push_back(selectionBuilder.Build()); + + return true; +} + +bool Selection::Remove(const PXR_NS::SdfPath& primPath) +{ + return (!primPath.IsEmpty() && (_pathToState.erase(primPath) == 1)); +} + +void +Selection::Clear() +{ + _pathToState.clear(); +} + +void Selection::Replace(const PXR_NS::SdfPathVector& selection) +{ + Clear(); + + for (const auto& primPath : selection) { + if (primPath.IsEmpty()) { + continue; + } + _pathToState[primPath].selectionSources.push_back( + selectionBuilder.Build()); + } +} + +void Selection::RemoveHierarchy(const PXR_NS::SdfPath& primPath) +{ + auto it = _pathToState.lower_bound(primPath); + while (it != _pathToState.end() && it->first.HasPrefix(primPath)) { + it = _pathToState.erase(it); + } +} + +bool Selection::IsEmpty() const +{ + return _pathToState.empty(); +} + +bool Selection::IsFullySelected(const SdfPath& primPath) const +{ + return _pathToState.find(primPath) != _pathToState.end(); +} + +bool Selection::HasFullySelectedAncestorInclusive(const SdfPath& primPath) const +{ + // FLOW_VIEWPORT_TODO Prefix tree would be much higher performance + // than iterating over the whole selection, especially for a large + // selection. PPT, 13-Sep-2023. + for(const auto& entry : _pathToState) { + if (primPath.HasPrefix(entry.first)) { + return true; + } + } + return false; +} + +SdfPathVector Selection::GetFullySelectedPaths() const +{ + SdfPathVector fullySelectedPaths; + fullySelectedPaths.reserve(_pathToState.size()); + for(const auto& entry : _pathToState) { + fullySelectedPaths.emplace_back(entry.first); + } + return fullySelectedPaths; +} + +HdDataSourceBaseHandle Selection::GetVectorDataSource( + const PXR_NS::SdfPath& primPath +) const +{ + auto it = _pathToState.find(primPath); + return (it != _pathToState.end()) ? + it->second.GetVectorDataSource() : nullptr; +} + +} diff --git a/lib/flowViewport/selection/fvpSelection.h b/lib/flowViewport/selection/fvpSelection.h new file mode 100644 index 0000000000..697120f995 --- /dev/null +++ b/lib/flowViewport/selection/fvpSelection.h @@ -0,0 +1,102 @@ +// +// Copyright 2023 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef FVP_SELECTION_H +#define FVP_SELECTION_H + +#include "flowViewport/api.h" +#include "flowViewport/selection/fvpSelectionFwd.h" + +#include +#include + +#include + +namespace FVP_NS_DEF { + +/// \class Selection +/// +/// Represents selection in the Flow Viewport. +/// +/// Hydra's HdSelection class has support for component selections (edges and +/// points), but the following limitations: +/// - No support for remove, replace, or clear operations. +/// - No support for querying selection state of ancestors. +/// +/// It would be desirable to add these capabilities to HdSelection and +/// move support to OpenUSD. +/// +class Selection +{ +public: + + // Add primPath to selection and return true if the argument is not empty. + FVP_API + bool Add(const PXR_NS::SdfPath& primPath); + + // Returns true if the removal was successful, false otherwise. + FVP_API + bool Remove(const PXR_NS::SdfPath& primPath); + + // Replace the selection with the contents of the argument primPath vector. + // Any empty primPath in the argument will be skipped. + FVP_API + void Replace(const PXR_NS::SdfPathVector& selection); + + // Remove all entries from the selection. + FVP_API + void Clear(); + + // Remove the argument and all descendants from the selection. + FVP_API + void RemoveHierarchy(const PXR_NS::SdfPath& primPath); + + FVP_API + bool IsEmpty() const; + + FVP_API + bool IsFullySelected(const PXR_NS::SdfPath& primPath) const; + + // Returns true if the argument is itself selected, or any of its ancestors + // is selected. + FVP_API + bool HasFullySelectedAncestorInclusive(const PXR_NS::SdfPath& primPath) const; + + FVP_API + PXR_NS::SdfPathVector GetFullySelectedPaths() const; + + // Return the vector data source of the argument prim if selected, else + // a null pointer. + FVP_API + PXR_NS::HdDataSourceBaseHandle + GetVectorDataSource(const PXR_NS::SdfPath& primPath) const; + +private: + + struct _PrimSelectionState { + // Container data sources conforming to HdSelectionSchema + std::vector selectionSources; + + PXR_NS::HdDataSourceBaseHandle GetVectorDataSource() const; + }; + + // Maps prim path to data sources to be returned by the vector data + // source at locator selections. + std::map _pathToState; +}; + +} + +#endif diff --git a/lib/flowViewport/sceneIndex/fvpSelectionInterface.cpp b/lib/flowViewport/selection/fvpSelectionFwd.cpp similarity index 81% rename from lib/flowViewport/sceneIndex/fvpSelectionInterface.cpp rename to lib/flowViewport/selection/fvpSelectionFwd.cpp index bb5b385f37..8fcef92767 100644 --- a/lib/flowViewport/sceneIndex/fvpSelectionInterface.cpp +++ b/lib/flowViewport/selection/fvpSelectionFwd.cpp @@ -1,3 +1,4 @@ +// // Copyright 2023 Autodesk // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,10 +14,5 @@ // limitations under the License. // -#include "flowViewport/sceneIndex/fvpSelectionInterface.h" - -namespace FVP_NS_DEF { - -SelectionInterface::~SelectionInterface() {} - -} +// Trivial inclusion to ensure header compiles on its own. +#include "flowViewport/selection/fvpSelectionFwd.h" diff --git a/lib/flowViewport/selection/fvpSelectionFwd.h b/lib/flowViewport/selection/fvpSelectionFwd.h new file mode 100644 index 0000000000..3f1425313d --- /dev/null +++ b/lib/flowViewport/selection/fvpSelectionFwd.h @@ -0,0 +1,32 @@ +// +// Copyright 2023 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef FVP_SELECTION_FWD_H +#define FVP_SELECTION_FWD_H + +#include "flowViewport/flowViewport.h" + +#include + +namespace FVP_NS_DEF { + +class Selection; + +using SelectionPtr = std::shared_ptr; +using SelectionConstPtr = std::shared_ptr; + +} + +#endif diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.cpp b/lib/mayaHydra/mayaPlugin/renderOverride.cpp index 121575266c..be1863da75 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.cpp +++ b/lib/mayaHydra/mayaPlugin/renderOverride.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -778,14 +779,15 @@ void MtohRenderOverride::_InitHydraResources() _mayaHydraSceneProducer->Populate(); - _selectionSceneIndex = Fvp::SelectionSceneIndex::New(_renderIndexProxy->GetMergingSceneIndex()); + _selection = std::make_shared(); + _selectionSceneIndex = Fvp::SelectionSceneIndex::New(_renderIndexProxy->GetMergingSceneIndex(), _selection); _selectionSceneIndex->SetDisplayName("Flow Viewport Selection Scene Index"); if (!_sceneIndexRegistry) { _sceneIndexRegistry.reset(new MayaHydraSceneIndexRegistry(*_renderIndexProxy)); } - auto wfSi = TfDynamic_cast(Fvp::WireframeSelectionHighlightSceneIndex::New(_selectionSceneIndex)); + auto wfSi = TfDynamic_cast(Fvp::WireframeSelectionHighlightSceneIndex::New(_selectionSceneIndex, _selection)); wfSi->SetDisplayName("Flow Viewport Wireframe Selection Highlight Scene Index"); // At time of writing, wireframe selection highlighting of Maya native data @@ -831,6 +833,7 @@ void MtohRenderOverride::ClearHydraResources() _renderIndexProxy.reset(); _mayaHydraSceneProducer.reset(); _selectionSceneIndex.Reset(); + _selection.reset(); // Cleanup internal context data that keep references to data that is now // invalid. diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.h b/lib/mayaHydra/mayaPlugin/renderOverride.h index 86228612a6..cbbaed8702 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.h +++ b/lib/mayaHydra/mayaPlugin/renderOverride.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -221,6 +222,7 @@ class MtohRenderOverride : public MHWRender::MRenderOverride HdRenderIndex* _renderIndex = nullptr; Fvp::SelectionTrackerSharedPtr _fvpSelectionTracker; Fvp::SelectionSceneIndexRefPtr _selectionSceneIndex; + Fvp::SelectionPtr _selection; class SelectionObserver; using SelectionObserverPtr = std::shared_ptr; SelectionObserverPtr _mayaSelectionObserver;