Authoring Variants

This tutorial walks through authoring a variant set on the HelloWorld layer from Inspecting and Authoring Properties. We begin with the layer USD/extras/usd/tutorials/authoringVariants/HelloWorld.usda and the Python code authorVariants.py in the same directory. Please copy these files to a working directory and make them writable.

  1. Open the stage and clear the Sphere’s authored displayColor opinion. In USD local opinions are stronger than variant opinions. So if we do not clear this opinion it will win over those we author within variants below.

    To see this in action, comment out the Clear() and run the tutorial. The steps below still author the same variant opinions, but the composed result shows the locally-authored blue color. That stronger local opinion overrides the opinions from variants.

    See LIVRPS Strength Ordering for more details on strength order in USD.

    from pxr import Usd, UsdGeom
    stage = Usd.Stage.Open('HelloWorld.usda')
    colorAttr = UsdGeom.Gprim.Get(stage, '/hello/world').GetDisplayColorAttr()
    colorAttr.Clear()
    print(stage.GetRootLayer().ExportToString())
    

    The </hello/world> prim is a Sphere; a schema subclass of Gprim. So we can use the UsdGeom.Gprim API to work with it. In USD, Gprim defines displayColor so all gprim subclasses have it.

    #usda 1.0
    (
        defaultPrim = "hello"
    )
    
    def Xform "hello"
    {
        custom double3 xformOp:translate = (4, 5, 6)
        uniform token[] xformOpOrder = ["xformOp:translate"]
    
        def Sphere "world"
        {
            float3[] extent = [(-2, -2, -2), (2, 2, 2)]
            color3f[] primvars:displayColor
            double radius = 2
        }
    }
    

    The resulting scene description still has the declaration for the displayColor attribute; a future iteration on the Usd.Attribute API may clean this up.

  2. Create a shading variant on the root prim called shadingVariant

    rootPrim = stage.GetPrimAtPath('/hello')
    vset = rootPrim.GetVariantSets().AddVariantSet('shadingVariant')
    print(stage.GetRootLayer().ExportToString())
    

    produces

    #usda 1.0
    (
        defaultPrim = "hello"
    )
    
    def Xform "hello" (
        prepend variantSets = "shadingVariant"
    )
    {
        custom double3 xformOp:translate = (4, 5, 6)
        uniform token[] xformOpOrder = ["xformOp:translate"]
    
        def Sphere "world"
        {
            float3[] extent = [(-2, -2, -2), (2, 2, 2)]
            color3f[] primvars:displayColor
            double radius = 2
        }
    }
    
  3. Create variants within ‘shadingVariant’ to contain the opinions we will author.

    vset.AddVariant('red')
    vset.AddVariant('blue')
    vset.AddVariant('green')
    print(stage.GetRootLayer().ExportToString())
    

    produces

    #usda 1.0
    (
        defaultPrim = "hello"
    )
    
    def Xform "hello" (
        prepend variantSets = "shadingVariant"
    )
    {
        custom double3 xformOp:translate = (4, 5, 6)
        uniform token[] xformOpOrder = ["xformOp:translate"]
    
        def Sphere "world"
        {
            float3[] extent = [(-2, -2, -2), (2, 2, 2)]
            color3f[] primvars:displayColor
            double radius = 2
        }
    
        variantSet "shadingVariant" = {
            "blue" {
            }
    
            "green" {
            }
    
            "red" {
            }
        }
    }
    
  4. Author a red color to the sphere prim </hello/world> in the red variant.

    To author opinions inside the red variant we first select the variant, then use a Usd.EditContext. This directs editing into the variant.

    vset.SetVariantSelection('red')
    with vset.GetVariantEditContext():
        colorAttr.Set([(1,0,0)])
    
    print(stage.GetRootLayer().ExportToString())
    

    If we just invoked colorAttr.Set() without using the variant edit context, we would write a local opinion; specifically the same opinion we cleared at the beginning.

    #usda 1.0
    (
        defaultPrim = "hello"
    )
    
    def Xform "hello" (
        variants = {
            string shadingVariant = "red"
        }
        prepend variantSets = "shadingVariant"
    )
    {
        custom double3 xformOp:translate = (4, 5, 6)
        uniform token[] xformOpOrder = ["xformOp:translate"]
    
        def Sphere "world"
        {
            float3[] extent = [(-2, -2, -2), (2, 2, 2)]
            color3f[] primvars:displayColor
            double radius = 2
        }
    
        variantSet "shadingVariant" = {
            "blue" {
            }
    
            "green" {
            }
    
            "red" {
                over "world"
                {
                    color3f[] primvars:displayColor = [(1, 0, 0)]
                }
            }
        }
    }
    

    Now hello has a shadingVariant with a default variant selection of red, and the red variant has an opinion for displayColor.

  5. Author the blue and green variants similarly.

    vset.SetVariantSelection('blue')
    with vset.GetVariantEditContext():
        colorAttr.Set([(0,0,1)])
    
    vset.SetVariantSelection('green')
    with vset.GetVariantEditContext():
        colorAttr.Set([(0,1,0)])
    
    print(stage.GetRootLayer().ExportToString())
    

    produces

    #usda 1.0
    (
        defaultPrim = "hello"
    )
    
    def Xform "hello" (
        variants = {
            string shadingVariant = "green"
        }
        prepend variantSets = "shadingVariant"
    )
    {
        custom double3 xformOp:translate = (4, 5, 6)
        uniform token[] xformOpOrder = ["xformOp:translate"]
    
        def Sphere "world"
        {
            float3[] extent = [(-2, -2, -2), (2, 2, 2)]
            color3f[] primvars:displayColor
            double radius = 2
        }
    
        variantSet "shadingVariant" = {
            "blue" {
                over "world"
                {
                    color3f[] primvars:displayColor = [(0, 0, 1)]
                }
            }
    
            "green" {
                over "world"
                {
                    color3f[] primvars:displayColor = [(0, 1, 0)]
                }
            }
    
            "red" {
                over "world"
                {
                    color3f[] primvars:displayColor = [(1, 0, 0)]
                }
            }
        }
    }
    

    As mentioned at the beginning, it was important to first Clear() the local opinion for primvars:displayColor because local opinions are strongest. Had we not cleared the local opinion, the sphere would remain blue regardless of the selected shadingVariant for this reason.

    Opinion Strength Order

    Strength order is a fundamental part of USD.

  6. Examine the composed result.

    print(stage.ExportToString(addSourceFileComment=False))
    

    shows

    #usda 1.0
    (
        defaultPrim = "hello"
    )
    
    def Xform "hello"
    {
        custom double3 xformOp:translate = (4, 5, 6)
        uniform token[] xformOpOrder = ["xformOp:translate"]
    
        def Sphere "world"
        {
            float3[] extent = [(-2, -2, -2), (2, 2, 2)]
            color3f[] primvars:displayColor = [(0, 1, 0)]
            double radius = 2
        }
    
    }
    

    What happened to the variants? Exporting a UsdStage writes the composed scene description with all the composition operators (like variants) fully evaluated. We call this flattening. Flattening the composition applies the opinions from the currently selected variants so we only get the green color due to the last call to SetVariantSelection().

  7. Save the edited authoring layer to a new file, HelloWorldWithVariants.usda

    In contrast to exporting a UsdStage, exporting an individual layer writes its contents as-is to a different file. There is no flattening.

    stage.GetRootLayer().Export('HelloWorldWithVariants.usda')
    

    We could also call Save() to update the original file with the new content.

  8. Run usdview on HelloWorldWithVariants.usda.

    http://openusd.org/images/tut_authoring_variants_helloworld1.png

    Click the hello prim in the tree view on the left. Then click the Meta Data tab in the lower right. You will see the authored variant set with a combo box that can toggle the variant selection between red, blue, and green.

    http://openusd.org/images/tut_authoring_variants_helloworld2.png
  9. In the interpreter you can see the variant selections that usdview authors to the session layer . This is the same sparse override you would see if you referenced this layer into another one and authored the variant selection in the referencing layer.

    print(usdviewApi.stage.GetSessionLayer().ExportToString())
    

    shows

    #usda 1.0
    
    over "hello" (
        variants = {
            string shadingVariant = "red"
        }
    )
    {
    }