diff --git a/pxr/usd/usd/prim.cpp b/pxr/usd/usd/prim.cpp index a1090b220b..435fa1dce0 100644 --- a/pxr/usd/usd/prim.cpp +++ b/pxr/usd/usd/prim.cpp @@ -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> +UsdPrim::GetPrimStackWithLayerOffsets() const +{ + return UsdStage::_GetPrimStackWithLayerOffsets(*this); } PcpPrimIndex diff --git a/pxr/usd/usd/prim.h b/pxr/usd/usd/prim.h index 99cebc0f1a..9920155464 100644 --- a/pxr/usd/usd/prim.h +++ b/pxr/usd/usd/prim.h @@ -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> + GetPrimStackWithLayerOffsets() const; + /// Author an opinion for this Prim's specifier at the current edit /// target. bool SetSpecifier(SdfSpecifier specifier) const { diff --git a/pxr/usd/usd/property.cpp b/pxr/usd/usd/property.cpp index 11ba800d42..bafdb5bcc8 100644 --- a/pxr/usd/usd/property.cpp +++ b/pxr/usd/usd/property.cpp @@ -38,6 +38,12 @@ UsdProperty::GetPropertyStack(UsdTimeCode time) const return _GetStage()->_GetPropertyStack(*this, time); } +std::vector> +UsdProperty::GetPropertyStackWithLayerOffsets(UsdTimeCode time) const +{ + return _GetStage()->_GetPropertyStackWithLayerOffsets(*this, time); +} + TfToken UsdProperty::GetBaseName() const { diff --git a/pxr/usd/usd/property.h b/pxr/usd/usd/property.h index 1b6c7c2c6f..5d621112c8 100644 --- a/pxr/usd/usd/property.h +++ b/pxr/usd/usd/property.h @@ -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> + 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() /// diff --git a/pxr/usd/usd/stage.cpp b/pxr/usd/usd/stage.cpp index a92254eda4..23725f4eb1 100644 --- a/pxr/usd/usd/stage.cpp +++ b/pxr/usd/usd/stage.cpp @@ -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> + propertyStackWithLayerOffsets; + + _PropertyStackResolver(bool withLayerOffsets) : + _withLayerOffsets(withLayerOffsets) {} bool ProcessFallback() { return false; } @@ -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; @@ -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> +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> +UsdStage::_GetPrimStackWithLayerOffsets(const UsdPrim &prim) +{ + std::vector> 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 struct UsdStage::_ResolveInfoResolver diff --git a/pxr/usd/usd/stage.h b/pxr/usd/usd/stage.h index 0c4fca66b1..190a12b44e 100644 --- a/pxr/usd/usd/stage.h +++ b/pxr/usd/usd/stage.h @@ -1633,6 +1633,16 @@ class UsdStage : public TfRefBase, public TfWeakBase { SdfPropertySpecHandleVector _GetPropertyStack(const UsdProperty &prop, UsdTimeCode time) const; + std::vector> + _GetPropertyStackWithLayerOffsets( + const UsdProperty &prop, UsdTimeCode time) const; + + static SdfPrimSpecHandleVector + _GetPrimStack(const UsdPrim &prim); + + static std::vector> + _GetPrimStackWithLayerOffsets(const UsdPrim &prim); + SdfPropertySpecHandle _GetSchemaPropertySpec(const UsdPrim &prim, const TfToken &propName) const; diff --git a/pxr/usd/usd/testenv/testUsdCreateProperties.py b/pxr/usd/usd/testenv/testUsdCreateProperties.py index f5463ff45d..86e45ceed5 100644 --- a/pxr/usd/usd/testenv/testUsdCreateProperties.py +++ b/pxr/usd/usd/testenv/testUsdCreateProperties.py @@ -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' diff --git a/pxr/usd/usd/testenv/testUsdPrims.py b/pxr/usd/usd/testenv/testUsdPrims.py index 05be29d783..806ecb7b60 100644 --- a/pxr/usd/usd/testenv/testUsdPrims.py +++ b/pxr/usd/usd/testenv/testUsdPrims.py @@ -134,12 +134,16 @@ 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) @@ -147,6 +151,15 @@ def test_GetPrimStack(self): 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) diff --git a/pxr/usd/usd/testenv/testUsdValueClips.py b/pxr/usd/usd/testenv/testUsdValueClips.py index 916655d7f8..531770e582 100644 --- a/pxr/usd/usd/testenv/testUsdValueClips.py +++ b/pxr/usd/usd/testenv/testUsdValueClips.py @@ -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 diff --git a/pxr/usd/usd/wrapPrim.cpp b/pxr/usd/usd/wrapPrim.cpp index 05f48360b1..465489cbe4 100644 --- a/pxr/usd/usd/wrapPrim.cpp +++ b/pxr/usd/usd/wrapPrim.cpp @@ -201,6 +201,9 @@ void wrapUsdPrim() .def("GetPrimDefinition", &UsdPrim::GetPrimDefinition, return_internal_reference<>()) .def("GetPrimStack", &UsdPrim::GetPrimStack) + .def("GetPrimStackWithLayerOffsets", + &UsdPrim::GetPrimStackWithLayerOffsets, + return_value_policy()) .def("GetSpecifier", &UsdPrim::GetSpecifier) .def("SetSpecifier", &UsdPrim::SetSpecifier, arg("specifier")) @@ -458,6 +461,8 @@ void wrapUsdPrim() TfPySequenceToPython>>(); TfPyRegisterStlSequencesFromPython(); + TfPyContainerConversions::tuple_mapping_pair< + std::pair>(); // 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 diff --git a/pxr/usd/usd/wrapProperty.cpp b/pxr/usd/usd/wrapProperty.cpp index effb5007dc..3b0b0341b5 100644 --- a/pxr/usd/usd/wrapProperty.cpp +++ b/pxr/usd/usd/wrapProperty.cpp @@ -65,6 +65,10 @@ void wrapUsdProperty() .def("GetPropertyStack", &UsdProperty::GetPropertyStack, arg("time")) + .def("GetPropertyStackWithLayerOffsets", + &UsdProperty::GetPropertyStackWithLayerOffsets, + arg("time"), + return_value_policy()) .def("IsCustom", &UsdProperty::IsCustom) .def("SetCustom", &UsdProperty::SetCustom, arg("isCustom")) @@ -88,4 +92,6 @@ void wrapUsdProperty() ; TfPyRegisterStlSequencesFromPython(); + TfPyContainerConversions::tuple_mapping_pair< + std::pair>(); }