Skip to content

Commit

Permalink
Native instancing isolate select support, with test. (#186)
Browse files Browse the repository at this point in the history
  • Loading branch information
ppt-adsk authored Oct 15, 2024
1 parent b5179ca commit 4e4dcba
Show file tree
Hide file tree
Showing 13 changed files with 997 additions and 44 deletions.
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

0 comments on commit 4e4dcba

Please sign in to comment.