Sdf Namespace editing, any suggestions for fixing connections/targets?

Hey,

I’m currently looking to reparent prims on a specific SdfLayer and I understand the limitations of the SdfNamespaceEditing which does not correct the connections which may be on children of the reparented prims.
I wondered if there was any advice on fixing up the connections in an efficient manner. The naïve approach being to check all fields (for any local composition arc adjustments), attribute connections and relationships and retarget them individually. I’m somewhat hoping that I’m missing some API to assist in this.

The limitations being:

Note that Sdf does not track backpointers so it’s unable to fix up targets/connections to namespace edited objects. Clients must fix those to prevent them from falling off. In addition, this method will report failure if any relational attribute with a target to a namespace edited object is subsequently edited (in the same batch). Clients should perform edits on relational attributes first.

A quick simple example would be to reparent:
the following in an SdfLayer from “materials” to “other”, but retain the connections such that binding /root/other/metal would continue to function.
Currently (as described) using the batch namespace editing via SdfLayer.Apply, the prims are reparented correctly, but the connections/composition arcs match the old paths.

def "root"
{
    def Scope "materials"
    {
        def Material "metal"
        {
            token outputs:surface.connect = </root/materials/metal/UsdPreviewSurface.outputs:surface>

            def Shader "UsdPreviewSurface"
            {
                uniform token info:id = "UsdPreviewSurface"
                token outputs:surface
            }
        }
        def "metal_rust" (
            prepend specializes = </root/materials/metal>
        )
        { ... }
    }
}

Thanks!
James

Even though it’s a single layer, you can put it on a stage and use UsdNamespaceEditor, which will properly adjust affected rel/con targets. Are there issues with using that approach for you?

Hi Spiff,

So this does indeed work and was another possible work but I was somewhat put off investigating this from the detailed description:

Warning

This code is a work in progress and should not be used in production scenarios. It is currently not feature-complete and subject to change.

However for the uses cases I’ve been throwing at it this works great for what we need, so I think I will continue with it and report any issues we see as this work progresses into stability.

Thanks again,
James

Ah, we should remove that warning. For editing a single stage there are now no known caveats. There’s one remaining issue for updating “downstream” associated stages, which we are working on, though it may not quite make it into 25.11 , but we’re trying.

1 Like

I have noticed with some more test cases is that if we move a prim which is within a class or overhierarchy. The moved prim retains the old connections (and therefore are disconnected).
My current thoughts are that this is somewhat expected due to the way the UsdNamespaceEditor is working, and that only defined (and non abstract) prims are tracked?

With my two options, as far as I know, to either:

  1. Temporarily upgrade the prim specifiers with a session layer.
  2. Or go back to square one and handle the connections manually.

Not quite sure what you’re referring to, since AFAWK, the UsdNamespaceEditor should not be predicating any of its behavior on a prim’s specifier. Would it be possible to provide an example and what you’re seeing as you edit?

Happy to!
Done so in Python as it’s easier to mock up, but I’m seeing this in C++ too so not any python binding shenanigans.

Creating the layer

from fnpxr import Usd, Sdf
s = Usd.Stage.CreateInMemory("")
upsPrim = s.OverridePrim("/scene/materials/metal/UsdPreviewSurface")
uvtPrim = s.OverridePrim("/scene/materials/metal/UsdUVTexture")
outRGB = uvtPrim.CreateAttribute("outputs:rgb", Sdf.ValueTypeNames.Float3, False)
specColor = upsPrim.CreateAttribute("inputs:specularColor", Sdf.ValueTypeNames.Color3f, False)
specColor.AddConnection(outRGB.GetPath())
print(s.ExportToString())

Provides:

over "scene"
{
    over "materials"
    {
        over "metal"
        {
            over "UsdPreviewSurface"
            {
                color3f inputs:specularColor.connect = </scene/materials/metal/UsdUVTexture.outputs:rgb>
            }

            over "UsdUVTexture"
            {
                float3 outputs:rgb
            }
        }
    }
}

Applying a simple move namespace edit:

usdNameSpaceEditor = Usd.NamespaceEditor(s)
usdNameSpaceEditor.MovePrimAtPath("/scene", "/other")
if (usdNameSpaceEditor.CanApplyEdits()):
    usdNameSpaceEditor.ApplyEdits()

print(s.ExportToString())

Provides:

over "other"
{
    over "materials"
    {
        over "metal"
        {
            over "UsdPreviewSurface"
            {
                color3f inputs:specularColor.connect = </scene/materials/metal/UsdUVTexture.outputs:rgb>
            }

            over "UsdUVTexture"
            {
                float3 outputs:rgb
            }
        }
    }
}

The connection is no longer connected. The connections will be rewired if we change the OverridePrim calls in the setup script to DefinePrim.

Same occurs if the scene prim is defined. If the prim containing the connection is not defined itself, or is part of an abstract hierarchy, the connection is not rewired (e.g adding into the scene setup: metal = s.DefinePrim(“/scene/materials/metal”)or metal = s.CreateClassPrim(“/scene/materials/metal”). Similar would occur if scene was a class Prim, and the rest were over prims (new scene setup script - same namespace editor code):

from fnpxr import Usd, Sdf
s = Usd.Stage.CreateInMemory("")
metal = s.CreateClassPrim("/scene")
upsPrim = s.DefinePrim("/scene/materials/metal/UsdPreviewSurface")
uvtPrim = s.DefinePrim("/scene/materials/metal/UsdUVTexture")
outRGB = uvtPrim.CreateAttribute("outputs:rgb", Sdf.ValueTypeNames.Float3, False)
specColor = upsPrim.CreateAttribute("inputs:specularColor", Sdf.ValueTypeNames.Color3f, False)
specColor.AddConnection(outRGB.GetPath())
print(s.ExportToString())

Hope this helps explain this a little clearer!

James

Thanks, @James - that’s disturbing and unexpected! Would you mind filing a GitHub issue on that?

Our crack team of engineers already found and have a fix for the problem (it’s a one-liner) - thanks so much for the lovely test case, @James ! Fix will be in 25.11 (though if you see this soon please do still file the Issue).

Glad to have helped!

I have raised a github issue: UsdNamespace editing does not correct connections/relationships and local composition arcs when reparening non-defined or abstract prims. · Issue #3810 · PixarAnimationStudios/OpenUSD such that this can be tracked/ticketed against.

I also extended the example to showcase relationships and a Reference with only “local” primPath operations (i.e the Reference only contains a primPath and no assetPath).

This latter action errors in the case where the hierarchy is all defined as well, so I’m not sure if this is something that UsdNamespaceEditor should be handling. I can see the difficulties here, since if there is an “external” asset, I wouldn’t expect the path to be edited. Also if the primPath is outside of the paths being edited, I also wouldn’t expect it to be adjusted (e.g if it targeted /some/other/materialinitially).

Hope the further examples help.
It would also be amazing if I could see the commit for the fix for this, such that we may adjust our local 25.08 based USD since that will be easier for us to test with in the meantime! (We can also run it through some more test scenarios and report back).

Thanks again,
James