Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native instancing isolate select support, with test. #186

Merged
merged 1 commit into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 191 additions & 3 deletions lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,51 @@

#include <pxr/imaging/hd/visibilitySchema.h>
#include <pxr/imaging/hd/containerDataSourceEditor.h>
#include <pxr/imaging/hd/instanceSchema.h>
#include <pxr/imaging/hd/instancerTopologySchema.h>

PXR_NAMESPACE_USING_DIRECTIVE

namespace {

using Dependencies = TfSmallVector<SdfPath, 8>;

const HdContainerDataSourceHandle visOff =
HdVisibilitySchema::BuildRetained(
HdRetainedTypedSampledDataSource<bool>::New(false));

const HdDataSourceLocator instancerMaskLocator(
HdInstancerTopologySchemaTokens->instancerTopology,
HdInstancerTopologySchemaTokens->mask
);

bool disabled(const Fvp::SelectionConstPtr& a, const Fvp::SelectionConstPtr& b)
{
return !a && !b;
}

void append(Fvp::Selection& a, const Fvp::Selection& b)
{
for (const auto& entry : b) {
const auto& primSelections = entry.second;
for (const auto& primSelection : primSelections) {
a.Add(primSelection);
}
}
}

Dependencies instancedPrim(
const Fvp::IsolateSelectSceneIndex& si,
const SdfPath& primPath
)
{
auto prim = si.GetInputSceneIndex()->GetPrim(primPath);
auto instanceSchema = HdInstanceSchema::GetFromParent(prim.dataSource);
return (instanceSchema.IsDefined() ? Dependencies{{
instanceSchema.GetInstancer()->GetTypedValue(0)}} :
Dependencies());
}

}

namespace FVP_NS_DEF {
Expand Down Expand Up @@ -86,6 +117,17 @@ HdSceneIndexPrim IsolateSelectSceneIndex::GetPrim(const SdfPath& primPath) const
return inputPrim;
}

auto instancerMask = _instancerMasks.find(primPath);
if (instancerMask != _instancerMasks.end()) {
auto maskDs = HdRetainedTypedSampledDataSource<VtArray<bool>>::New(instancerMask->second);

inputPrim.dataSource = HdContainerDataSourceEditor(inputPrim.dataSource)
.Set(instancerMaskLocator, maskDs)
.Finish();

return inputPrim;
}

// If isolate selection is empty, then nothing is included (everything
// is excluded), as desired.
const bool included =
Expand Down Expand Up @@ -208,12 +250,12 @@ void IsolateSelectSceneIndex::ReplaceIsolateSelection(const SelectionConstPtr& n
return;
}

_ReplaceIsolateSelection(newIsolateSelection);
_DirtyIsolateSelection(newIsolateSelection);

_isolateSelection->Replace(*newIsolateSelection);
}

void IsolateSelectSceneIndex::_ReplaceIsolateSelection(const SelectionConstPtr& newIsolateSelection)
void IsolateSelectSceneIndex::_DirtyIsolateSelection(const SelectionConstPtr& newIsolateSelection)
{
// Trivial case of going from disabled to disabled is an early out.
if (disabled(_isolateSelection, newIsolateSelection)) {
Expand Down Expand Up @@ -290,9 +332,23 @@ void IsolateSelectSceneIndex::SetViewport(
return;
}

_ReplaceIsolateSelection(newIsolateSelection);
// Add dependencies of the new isolate selection to protect them from being
// marked as invisible.
_AddDependencies(newIsolateSelection);

_DirtyIsolateSelection(newIsolateSelection);

// Collect all the instancers from the new isolate selection.
auto instancers = _CollectInstancers(newIsolateSelection);

// Create the instance mask for each instancer.
auto newInstancerMasks = _CreateInstancerMasks(instancers, newIsolateSelection);

// Dirty the instancer masks.
_DirtyInstancerMasks(newInstancerMasks);

_isolateSelection = newIsolateSelection;
_instancerMasks = newInstancerMasks;
_viewportId = viewportId;
}

Expand Down Expand Up @@ -350,4 +406,136 @@ void IsolateSelectSceneIndex::_DirtyVisibilityRecursive(
}
}

void IsolateSelectSceneIndex::_AddDependencies(
const SelectionPtr& isolateSelection
)
{
// Iterate over the input isolate selection, and find the dependencies.
// As of 27-Sep-2024 only instancer dependencies are supported.
if (!isolateSelection) {
return;
}

// Collect dependencies in this selection.
Selection dependencies;
for (const auto& primSelectionsEntry : *isolateSelection) {
for (const auto& primSelection : primSelectionsEntry.second) {
auto primDependencies = instancedPrim(*this, primSelection.primPath);
for (const auto& dependencyPath : primDependencies) {
dependencies.Add(PrimSelection{dependencyPath});
}
}
}

// Add the collected dependencies to the input isolate selection.
append(*isolateSelection, dependencies);
}

IsolateSelectSceneIndex::Instancers
IsolateSelectSceneIndex::_CollectInstancers(
const SelectionConstPtr& isolateSelection
) const
{
if (!isolateSelection) {
return {};
}

Instancers instancers;
for (const auto& primSelectionsEntry : *isolateSelection) {
for (const auto& primSelection : primSelectionsEntry.second) {
auto prim = GetInputSceneIndex()->GetPrim(primSelection.primPath);
auto instanceSchema = HdInstanceSchema::GetFromParent(prim.dataSource);
if (instanceSchema.IsDefined()) {
instancers.emplace_back(instanceSchema.GetInstancer()->GetTypedValue(0));
}
}
}

return instancers;
}

IsolateSelectSceneIndex::InstancerMasks
IsolateSelectSceneIndex::_CreateInstancerMasks(
const Instancers& instancers,
const SelectionConstPtr& isolateSelection
) const
{
// If isolate select is disabled, no instancer masks to compute.
if (!isolateSelection) {
return {};
}

// For each instancer, build its mask of visible instances by running all
// instances, given by instancerTopology.instanceLocations, through
// the isolate selection. This determines whether the instance is visible
// or not. Store the instancer mask for the instancer path.
InstancerMasks instancerMasks;
for (const auto& instancerPath : instancers) {
HdSceneIndexPrim instancerPrim = GetInputSceneIndex()->GetPrim(instancerPath);
HdInstancerTopologySchema instancerTopologySchema = HdInstancerTopologySchema::GetFromParent(instancerPrim.dataSource);

// Documentation
// https://github.com/PixarAnimationStudios/OpenUSD/blob/59992d2178afcebd89273759f2bddfe730e59aa8/pxr/imaging/hd/instancerTopologySchema.h#L86
// says that instanceLocations is only meaningful for native
// instancing, empty for point instancing.
auto instanceLocationsDs = instancerTopologySchema.GetInstanceLocations();
if (!instanceLocationsDs) {
continue;
}

auto instanceLocations = instanceLocationsDs->GetTypedValue(0.0f);

VtArray<bool> instanceMask(instanceLocations.size());
std::size_t i=0;
for (const auto& instanceLocation : instanceLocations) {
const bool included = isolateSelection->HasAncestorOrDescendantInclusive(instanceLocation);
instanceMask[i] = included;
++i;
}

instancerMasks[instancerPath] = instanceMask;
}

return instancerMasks;
}

void IsolateSelectSceneIndex::_DirtyInstancerMasks(
const InstancerMasks& newInstancerMasks
)
{
// Keep paths in a set to minimize dirtying.
std::set<SdfPath> dirtyPaths;

// First clear old paths.
for (const auto& entry : _instancerMasks) {
dirtyPaths.insert(entry.first);
}

// Then add new paths.
for (const auto& entry : newInstancerMasks) {
dirtyPaths.insert(entry.first);
}

HdSceneIndexObserver::DirtiedPrimEntries dirtiedEntries;

// Dirty all cleared and added prim paths.
for (const auto& primPath : dirtyPaths) {
_AddDirtyInstancerMaskEntry(primPath, &dirtiedEntries);
}

_SendPrimsDirtied(dirtiedEntries);

}

void IsolateSelectSceneIndex::_AddDirtyInstancerMaskEntry(
const SdfPath& primPath,
HdSceneIndexObserver::DirtiedPrimEntries* dirtiedEntries
) const
{
TF_DEBUG(FVP_ISOLATE_SELECT_SCENE_INDEX)
.Msg(" %s: marking %s mask locator dirty.\n", _viewportId.c_str(), primPath.GetText());

dirtiedEntries->emplace_back(primPath, instancerMaskLocator);
}

} //end of namespace FVP_NS_DEF
26 changes: 25 additions & 1 deletion lib/flowViewport/sceneIndex/fvpIsolateSelectSceneIndex.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

//Hydra headers
#include <pxr/imaging/hd/filteringSceneIndex.h>
#include <pxr/base/vt/array.h>

namespace FVP_NS_DEF {

Expand Down Expand Up @@ -176,16 +177,39 @@ class IsolateSelectSceneIndex :
PXR_NS::HdSceneIndexObserver::DirtiedPrimEntries* dirtiedEntries
) const;

void _ReplaceIsolateSelection(const SelectionConstPtr& selection);
void _DirtyIsolateSelection(const SelectionConstPtr& selection);

void _InsertSelectedPaths(
const SelectionConstPtr& selection,
std::set<PXR_NS::SdfPath>& dirtyPaths
);

void _AddDependencies(const SelectionPtr& isolateSelection);

using Instancers = PXR_NS::TfSmallVector<PXR_NS::SdfPath, 8>;
using InstancerMask = PXR_NS::VtArray<bool>;
using InstancerMasks = std::map<PXR_NS::SdfPath, InstancerMask>;

// Collect all the instancers from the argument isolate selection.
Instancers _CollectInstancers(
const SelectionConstPtr& isolateSelection) const;

// Create the instance mask for each instancer.
InstancerMasks _CreateInstancerMasks(const Instancers& instancers,
const SelectionConstPtr& isolateSelection) const;

// Dirty the instancer masks.
void _DirtyInstancerMasks(const InstancerMasks& instancerMasks);
void _AddDirtyInstancerMaskEntry(
const PXR_NS::SdfPath& primPath,
PXR_NS::HdSceneIndexObserver::DirtiedPrimEntries* dirtiedEntries
) const;

std::string _viewportId;

SelectionPtr _isolateSelection{};

InstancerMasks _instancerMasks{};
};

}//end of namespace FVP_NS_DEF
Expand Down
33 changes: 2 additions & 31 deletions lib/flowViewport/sceneIndex/fvpPathInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

#include "flowViewport/api.h"

#include "flowViewport/selection/fvpSelectionTypes.h"

#include <ufe/ufe.h>

#include <pxr/pxr.h>
Expand All @@ -32,37 +34,6 @@ class Path;

namespace FVP_NS_DEF {

// Based on USD's HdInstanceIndicesSchema :
// https://github.com/PixarAnimationStudios/OpenUSD/blob/59992d2178afcebd89273759f2bddfe730e59aa8/pxr/imaging/hd/instanceIndicesSchema.h
struct InstancesSelection {
PXR_NS::SdfPath instancerPath;
int prototypeIndex;
std::vector<int> instanceIndices;

inline bool operator==(const InstancesSelection &rhs) const {
return instancerPath == rhs.instancerPath
&& prototypeIndex == rhs.prototypeIndex
&& instanceIndices == rhs.instanceIndices;
}
};

// Based on USD's HdSelectionSchema :
// https://github.com/PixarAnimationStudios/OpenUSD/blob/59992d2178afcebd89273759f2bddfe730e59aa8/pxr/imaging/hd/selectionSchema.h
struct PrimSelection
{
PXR_NS::SdfPath primPath;
std::vector<InstancesSelection> nestedInstanceIndices;

inline bool operator==(const PrimSelection &rhs) const {
return primPath == rhs.primPath
&& nestedInstanceIndices == rhs.nestedInstanceIndices;
}
};

// Using TfSmallVector to optimize for selections that map to a few prims,
// which is likely going to be the bulk of use cases.
using PrimSelections = PXR_NS::TfSmallVector<PrimSelection, 8>;

/// \class PathInterface
///
/// A pure interface class to allow for conversion between an application's
Expand Down
2 changes: 2 additions & 0 deletions lib/flowViewport/selection/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ target_sources(${TARGET_NAME}
fvpSelectionFwd.cpp
fvpSelectionTask.cpp
fvpSelectionTracker.cpp
fvpSelectionTypes.cpp
fvpPathMapper.cpp
fvpPathMapperFwd.cpp
fvpPathMapperRegistry.cpp
Expand All @@ -19,6 +20,7 @@ set(HEADERS
fvpSelectionFwd.h
fvpSelectionTask.h
fvpSelectionTracker.h
fvpSelectionTypes.h
fvpPathMapper.h
fvpPathMapperFwd.h
fvpPathMapperRegistry.h
Expand Down
Loading