Nested Specializes

Consider the following layer– The goal of this workflow is to support assets made up of “refineable parts”.

#usda 1.0

def "asset"
{
    class "parts"
    {
        def "metapart" {
            def "subpart_1" (specializes = </asset/parts/subpart>) {
            }
            def "subpart_2" (specializes = </asset/parts/subpart>) {
            }
        }
        def "subpart" {
            def "sphere" {
                double radius = 0.1234
            }
        }
    }

    def "geometry" {
        def "part_0" (specializes=</asset/parts/subpart>){}
        def "part_1" (specializes=</asset/parts/metapart>){}
        def "part_2" (specializes=</asset/parts/metapart>){}
    }
}

def "set"
{
    def "asset_1" (references = </asset>)
    {
        over "parts" {
            over "subpart" {
                over "sphere" {
                    double radius = 100.0
                }
            }
        }
    }
    def "asset_2" (references = </asset>)
    {
        over "parts" {
            over "subpart" {
                over "sphere" {
                    double radius = 200.0
                }
            }
        }
    }
}

The set contains to references to asset. asset_1 has refined its parts/subpart/sphere‘s radius to 100 and asset_2 has refined its radius to 200.0.

part_0 directly specializes subpart and respects the refinement. Curiously, part_1 and part_2 specialize a metapart which contains two specializations of subpart. These specializations do not respect the specialization.

Querying the property stacks of each part’s spheres–

"/set/asset_2/geometry/part_1/subpart_1/sphere.radius"
"/set/asset_2/geometry/part_2/subpart_1/sphere.radius"
"/set/asset_2/geometry/part_0/sphere.radius"

you will see both the originating opinion in /asset/parts/subpart/sphere and the intended refinement, but you will see them appear in opposite orders. part_0 prefers the refinement residing at /set/asset_2/geometry/parts/subpart/sphere.radiuswhile part_1/subpart_1 and part_2/subpart_2 prefer /asset/parts/subpart/sphere.radius.

I can’t quite reason about why nesting the specialization makes the refinement weaker. Interestingly, changing the subpart_1 and subpart_2 to use inherits makes the refinements stronger in all cases. (This isn’t necessarily a solution though, as you lose the ability to prefer metapart refinements to the subpart.)

I’m curious if anyone has found success nesting specializes or if there’s a simple explanation for the behavior.

I’m questioning whether the behavior makes sense if you consider the “metapart” to have unencapsulated specializations. If so, I’m not sure if I quite understand why the refinements appear in the property stack at all and why inherits produces a property stack with the refinements strongest.

Here’s some speculation, I may be completely off base. The spec says:

“Opinions introduced by specializes arcs follow a different strength ordering behavior. Specializes are ordered as the weakest of the composition arcs, but opinions introduced by specializes arcs are globally weaker than other opinions. Said another way, if prim A specializes prim B, then all opinions for prim A must be stronger than all opinions for prim B. This includes all implied opinions related to prim B and opinions from composition arcs that are introduced by prim B.”

Applying the logic

  • part_1 specializes metapart
  • metapart/subpart_1 specializes subpart
  • refinement via reference becomes a “subpart opinion”
  • global subpart_1 strength is less than subpart because specializes globally weak behavior cascades

and that’s why it’s fixed by inherits, Inherits arcs don’t have global weakness behavior. They follow normal LIVERPS strength ordering.

Does this all make sense?

And the thing about “global weakness” that has been especially flumoxing is that because all the recursion is nested along potentially many many pathways, when you “pull out” all the specialized opinions to make them globally weak, reassembling them into a “globally intuitive” ordering has been elusive. So far most attempts to address internally reported “Hey I’d expect this opinion to be stronger in this case” tickets result in some other existing use (or at least test) to break. So (and this just happened in the past week) it comes down to choosing which scenario is more important/useful.. I’m not sure that’s the case here, and I’ll leave further discussion to the experts.