Skip to content

Commit

Permalink
Adding GetPrimStackWithLayerOffsets and GetPropertyStackWithLayerOffsets
Browse files Browse the repository at this point in the history
which are overloads of GetPrimStack and GetPropertyStack that also
return the layer offset to the stage root of each spec in the stack.

See #1782

(Internal change: 2241803)
  • Loading branch information
pixar-oss committed Jul 21, 2022
1 parent 0c92ba6 commit aa2fcf2
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 22 deletions.
19 changes: 6 additions & 13 deletions pxr/usd/usd/prim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1409,20 +1409,13 @@ UsdPrim::GetInstances() const
SdfPrimSpecHandleVector
UsdPrim::GetPrimStack() const
{
SdfPrimSpecHandleVector primStack;

for (Usd_Resolver resolver(&(_Prim()->GetPrimIndex()));
resolver.IsValid(); resolver.NextLayer()) {

auto primSpec = resolver.GetLayer()
->GetPrimAtPath(resolver.GetLocalPath());

if (primSpec) {
primStack.push_back(primSpec);
}
}
return UsdStage::_GetPrimStack(*this);
}

return primStack;
std::vector<std::pair<SdfPrimSpecHandle, SdfLayerOffset>>
UsdPrim::GetPrimStackWithLayerOffsets() const
{
return UsdStage::_GetPrimStackWithLayerOffsets(*this);
}

PcpPrimIndex
Expand Down
16 changes: 16 additions & 0 deletions pxr/usd/usd/prim.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,22 @@ class UsdPrim : public UsdObject
USD_API
SdfPrimSpecHandleVector GetPrimStack() const;

/// Return all the authored SdfPrimSpecs that may contain opinions for this
/// prim in order from strong to weak paired with the cumulative layer
/// offset from the stage's root layer to the layer containing the prim
/// spec.
///
/// This behaves exactly the same as UsdPrim::GetPrimStack with the
/// addition of providing the cumulative layer offset of each spec's layer.
///
/// \note Use this method for debugging and diagnostic purposes. It is
/// **not** advisable to retain a PrimStack for expedited metadata value
/// resolution, since not all metadata resolves with simple "strongest
/// opinion wins" semantics.
USD_API
std::vector<std::pair<SdfPrimSpecHandle, SdfLayerOffset>>
GetPrimStackWithLayerOffsets() const;

/// Author an opinion for this Prim's specifier at the current edit
/// target.
bool SetSpecifier(SdfSpecifier specifier) const {
Expand Down
6 changes: 6 additions & 0 deletions pxr/usd/usd/property.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ UsdProperty::GetPropertyStack(UsdTimeCode time) const
return _GetStage()->_GetPropertyStack(*this, time);
}

std::vector<std::pair<SdfPropertySpecHandle, SdfLayerOffset>>
UsdProperty::GetPropertyStackWithLayerOffsets(UsdTimeCode time) const
{
return _GetStage()->_GetPropertyStackWithLayerOffsets(*this, time);
}

TfToken
UsdProperty::GetBaseName() const
{
Expand Down
18 changes: 18 additions & 0 deletions pxr/usd/usd/property.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,24 @@ class UsdProperty : public UsdObject {
SdfPropertySpecHandleVector GetPropertyStack(
UsdTimeCode time = UsdTimeCode::Default()) const;

/// Returns a strength-ordered list of property specs that provide
/// opinions for this property paired with the cumulative layer offset from
/// the stage's root layer to the layer containing the property spec.
///
/// This behaves exactly the same as UsdProperty::GetPropertyStack with the
/// addition of providing the cumulative layer offset of each spec's layer.
///
/// \note The results returned by this method are meant for debugging
/// and diagnostic purposes. It is **not** advisable to retain a
/// PropertyStack for the purposes of expedited value resolution for
/// properties, since the makeup of an attribute's PropertyStack may
/// itself be time-varying. To expedite repeated value resolution of
/// attributes, you should instead retain a \c UsdAttributeQuery .
USD_API
std::vector<std::pair<SdfPropertySpecHandle, SdfLayerOffset>>
GetPropertyStackWithLayerOffsets(
UsdTimeCode time = UsdTimeCode::Default()) const;

/// Return this property's name with all namespace prefixes removed,
/// i.e. the last component of the return value of GetName()
///
Expand Down
76 changes: 73 additions & 3 deletions pxr/usd/usd/stage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7491,6 +7491,11 @@ UsdStage::_GetValueImpl(UsdTimeCode time, const UsdAttribute &attr,
// as we need to gather all relevant property specs in the LayerStack
struct UsdStage::_PropertyStackResolver {
SdfPropertySpecHandleVector propertyStack;
std::vector<std::pair<SdfPropertySpecHandle, SdfLayerOffset>>
propertyStackWithLayerOffsets;

_PropertyStackResolver(bool withLayerOffsets) :
_withLayerOffsets(withLayerOffsets) {}

bool ProcessFallback() { return false; }

Expand All @@ -7504,7 +7509,12 @@ struct UsdStage::_PropertyStackResolver {
= node.GetLayerStack()->GetLayers()[layerStackPosition];
const auto propertySpec = layer->GetPropertyAtPath(specPath);
if (propertySpec) {
propertyStack.push_back(propertySpec);
if (_withLayerOffsets) {
propertyStackWithLayerOffsets.emplace_back(
propertySpec, _GetLayerToStageOffset(node, layer));
} else {
propertyStack.push_back(propertySpec);
}
}

return false;
Expand Down Expand Up @@ -7540,23 +7550,83 @@ struct UsdStage::_PropertyStackResolver {

if (const auto propertySpec =
sourceClip->GetPropertyAtPath(specPath)) {
propertyStack.push_back(propertySpec);
if (_withLayerOffsets) {
// The layer offset for the clip is the layer offset of the
// source layer of the clip set.
const auto layer = clipSet->sourceLayerStack->GetLayers()[
clipSet->sourceLayerIndex];
propertyStackWithLayerOffsets.emplace_back(
propertySpec, _GetLayerToStageOffset(node, layer));
} else {
propertyStack.push_back(propertySpec);
}
}
}

return false;
}

private:
bool _withLayerOffsets;
};

SdfPropertySpecHandleVector
UsdStage::_GetPropertyStack(const UsdProperty &prop,
UsdTimeCode time) const
{
_PropertyStackResolver resolver;
_PropertyStackResolver resolver(/* withLayerOffsets = */ false);
_GetResolvedValueImpl(prop, &resolver, &time);
return resolver.propertyStack;
}

std::vector<std::pair<SdfPropertySpecHandle, SdfLayerOffset>>
UsdStage::_GetPropertyStackWithLayerOffsets(
const UsdProperty &prop, UsdTimeCode time) const
{
_PropertyStackResolver resolver(/* withLayerOffsets = */ true);
_GetResolvedValueImpl(prop, &resolver, &time);
return resolver.propertyStackWithLayerOffsets;
}

SdfPrimSpecHandleVector
UsdStage::_GetPrimStack(const UsdPrim &prim)
{
SdfPrimSpecHandleVector primStack;

for (Usd_Resolver resolver(&(prim._Prim()->GetPrimIndex()));
resolver.IsValid(); resolver.NextLayer()) {

auto primSpec = resolver.GetLayer()
->GetPrimAtPath(resolver.GetLocalPath());

if (primSpec) {
primStack.push_back(primSpec);
}
}

return primStack;
}

std::vector<std::pair<SdfPrimSpecHandle, SdfLayerOffset>>
UsdStage::_GetPrimStackWithLayerOffsets(const UsdPrim &prim)
{
std::vector<std::pair<SdfPrimSpecHandle, SdfLayerOffset>> primStack;

for (Usd_Resolver resolver(&(prim._Prim()->GetPrimIndex()));
resolver.IsValid(); resolver.NextLayer()) {

auto primSpec = resolver.GetLayer()
->GetPrimAtPath(resolver.GetLocalPath());

if (primSpec) {
primStack.emplace_back(primSpec,
_GetLayerToStageOffset(resolver.GetNode(), resolver.GetLayer()));
}
}

return primStack;
}

// A 'Resolver' for filling UsdResolveInfo.
template <typename T>
struct UsdStage::_ResolveInfoResolver
Expand Down
10 changes: 10 additions & 0 deletions pxr/usd/usd/stage.h
Original file line number Diff line number Diff line change
Expand Up @@ -1633,6 +1633,16 @@ class UsdStage : public TfRefBase, public TfWeakBase {
SdfPropertySpecHandleVector
_GetPropertyStack(const UsdProperty &prop, UsdTimeCode time) const;

std::vector<std::pair<SdfPropertySpecHandle, SdfLayerOffset>>
_GetPropertyStackWithLayerOffsets(
const UsdProperty &prop, UsdTimeCode time) const;

static SdfPrimSpecHandleVector
_GetPrimStack(const UsdPrim &prim);

static std::vector<std::pair<SdfPrimSpecHandle, SdfLayerOffset>>
_GetPrimStackWithLayerOffsets(const UsdPrim &prim);

SdfPropertySpecHandle
_GetSchemaPropertySpec(const UsdPrim &prim, const TfToken &propName) const;

Expand Down
21 changes: 17 additions & 4 deletions pxr/usd/usd/testenv/testUsdCreateProperties.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,24 +530,37 @@ def test_GetPropertyStack(self):
stage = Usd.Stage.Open(layerB.identifier)
over = stage.OverridePrim(primPath)
over.CreateAttribute(propName, Sdf.ValueTypeNames.String)
over.GetReferences().AddReference(Sdf.Reference(layerA.identifier, primPath))
over.GetReferences().AddReference(
Sdf.Reference(layerA.identifier, primPath,
Sdf.LayerOffset(10.0)))

stage = Usd.Stage.Open(layerC.identifier)
over = stage.OverridePrim(primPath)
over.CreateAttribute(propName, Sdf.ValueTypeNames.String)
over.GetReferences().AddReference(Sdf.Reference(layerB.identifier, primPath))

over.GetReferences().AddReference(
Sdf.Reference(layerB.identifier, primPath,
Sdf.LayerOffset(0.0, 2.0)))
attr = over.GetAttribute(propName)
expectedPropertyStack = [l.GetPropertyAtPath(propPath) for l in
[layerC, layerB, layerA]]
expectedPropertyStackWithLayerOffsets = [
(expectedPropertyStack[0], Sdf.LayerOffset()),
(expectedPropertyStack[1], Sdf.LayerOffset(0.0, 2.0)),
(expectedPropertyStack[2], Sdf.LayerOffset(20.0, 2.0))
]

self.assertEqual(attr.GetPropertyStack(Usd.TimeCode.Default()),
expectedPropertyStack)
self.assertEqual(attr.GetPropertyStackWithLayerOffsets(
Usd.TimeCode.Default()),
expectedPropertyStackWithLayerOffsets)
# ensure that using a non-default time-code gets the same
# set since clips are not in play here
self.assertEqual(attr.GetPropertyStack(101.0),
expectedPropertyStack)
self.assertEqual(attr.GetPropertyStackWithLayerOffsets(101.0),
expectedPropertyStackWithLayerOffsets)


def test_GetPropertyStackWithClips(self):
clipPath = '/root/fx/test'
attrName = 'extent'
Expand Down
17 changes: 15 additions & 2 deletions pxr/usd/usd/testenv/testUsdPrims.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,19 +134,32 @@ def test_GetPrimStack(self):

stage = Usd.Stage.Open(payload)
over = stage.OverridePrim(primPath)
over.GetReferences().AddReference(Sdf.Reference(basicOver.identifier, primPath))
over.GetReferences().AddReference(
Sdf.Reference(basicOver.identifier, primPath,
Sdf.LayerOffset(0.0, 2.0)))

stage = Usd.Stage.Open(base)
prim = stage.DefinePrim(primPath)
prim.GetPayloads().AddPayload(payload.identifier, primPath)
prim.GetPayloads().AddPayload(
Sdf.Payload(payload.identifier, primPath, Sdf.LayerOffset(10.0)))
stage.GetRootLayer().subLayerPaths.append(sublayer.identifier)
stage.GetRootLayer().subLayerOffsets[0] = Sdf.LayerOffset(20.0)

expectedPrimStack = [layer.GetPrimAtPath(primPath) for layer in layers]
stage = Usd.Stage.Open(base)
prim = stage.GetPrimAtPath(primPath)

assert prim.GetPrimStack() == expectedPrimStack

expectedPrimStackWithLayerOffsets = [
(expectedPrimStack[0], Sdf.LayerOffset()),
(expectedPrimStack[1], Sdf.LayerOffset(20.0)),
(expectedPrimStack[2], Sdf.LayerOffset(10.0)),
(expectedPrimStack[3], Sdf.LayerOffset(10.0, 2.0)),
]
assert (prim.GetPrimStackWithLayerOffsets() ==
expectedPrimStackWithLayerOffsets)

def test_GetCachedPrimBits(self):
layerFile = 'test.usda'
layer = Sdf.Layer.FindOrOpen(layerFile)
Expand Down
9 changes: 9 additions & 0 deletions pxr/usd/usd/testenv/testUsdValueClips.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,15 @@ def test_ClipsWithLayerOffsets(self):
self.CheckTimeSamples(attr2)
self.CheckTimeSamples(attr3)

# Verify GetPropertyStackWithLayerOffsets run on an attribute with
# clips returns the clip spec's layer offset matching the source spec's
# layer offset.
self.assertEqual(attr3.GetPropertyStackWithLayerOffsets(40),
[(Sdf.Find('layerOffsets/clip.usda', '/Model.size'),
Sdf.LayerOffset(20)),
(Sdf.Find('layerOffsets/ref.usda', '/Model.size'),
Sdf.LayerOffset(20))])

def test_TimeCodeClipsWithLayerOffsets(self):
"""Tests behavior of clips when layer offsets are involved and the
attributes are SdfTimeCode values. This test is almost identical to
Expand Down
5 changes: 5 additions & 0 deletions pxr/usd/usd/wrapPrim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ void wrapUsdPrim()
.def("GetPrimDefinition", &UsdPrim::GetPrimDefinition,
return_internal_reference<>())
.def("GetPrimStack", &UsdPrim::GetPrimStack)
.def("GetPrimStackWithLayerOffsets",
&UsdPrim::GetPrimStackWithLayerOffsets,
return_value_policy<TfPySequenceToList>())

.def("GetSpecifier", &UsdPrim::GetSpecifier)
.def("SetSpecifier", &UsdPrim::SetSpecifier, arg("specifier"))
Expand Down Expand Up @@ -458,6 +461,8 @@ void wrapUsdPrim()
TfPySequenceToPython<std::vector<UsdPrim>>>();

TfPyRegisterStlSequencesFromPython<UsdPrim>();
TfPyContainerConversions::tuple_mapping_pair<
std::pair<SdfPrimSpecHandle, SdfLayerOffset>>();

// This is wrapped in order to let python call an API that will get through
// our usual Python API guards to access an invalid prim and throw an
Expand Down
6 changes: 6 additions & 0 deletions pxr/usd/usd/wrapProperty.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ void wrapUsdProperty()

.def("GetPropertyStack", &UsdProperty::GetPropertyStack,
arg("time"))
.def("GetPropertyStackWithLayerOffsets",
&UsdProperty::GetPropertyStackWithLayerOffsets,
arg("time"),
return_value_policy<TfPySequenceToList>())

.def("IsCustom", &UsdProperty::IsCustom)
.def("SetCustom", &UsdProperty::SetCustom, arg("isCustom"))
Expand All @@ -88,4 +92,6 @@ void wrapUsdProperty()
;

TfPyRegisterStlSequencesFromPython<UsdProperty>();
TfPyContainerConversions::tuple_mapping_pair<
std::pair<SdfPropertySpecHandle, SdfLayerOffset>>();
}

0 comments on commit aa2fcf2

Please sign in to comment.