Writing USD from Houdini

Writing USD from Houdini


USD files are written from Houdini using a USD Output node in a ROP (out) or a SOP (geom) context. Here is a trival example in a SOP context.

This node will write to a file called "sphere.usd". The application "usdview" is the best way to examine what we wrote into that file.

Looking at the hierarchy view in the upper left we see we wrote a group called "FxAsset" containing a single mesh called "mesh_0". Below the hierarchy view is the attribute window. The attributes in green are the animated values we wrote. These include the points, normals, mesh topology, and extent (bounding box). The constant values we wrote out are shown in blue. These attributes specify that this is a polygonal mesh using a left handed winding order. (clicking on the "?" just below the attributes will explain the color scheme)

The USD output ROP will write any attributes that are defined for a prim in Houdini. If the attribute matches something in a USD "schema" the ROP will automatically write the attribute with the appropriate USD name. These are the attributes for meshes:

Houdini USD
P points
N normals
v velocities
Cd displayColor
Alpha displayOpacity

Other attributes can be written as well. However, you have to explicitly specify which attributes to write on the ROP (wildcards are ok). These extra attribute are written as "primvars". 

In this example we add a point attribute called "foo" with a random float value. Note that our mesh has 266 points in it. 

The USD Output node can write meshes, points, curves, groups and instances. In Houdini, when a ROP is processing geometry, any geometry it doesn't handle directly it can "refine" to geometry.

In the first example above with just the sphere we used the default primitive type for the sphere, "primitive". Houdini refined this to a mesh for us and added a normal attribute in the process. In the second example, we wanted to make "foo" a point attribute so we had to switch the primitive type to "mesh". Other than the normal and foo attributes, the results look the same.

Transforms

If we added an animated transform node to a mesh sphere the point positions would be animated. If we write this to a USD file, it will look just like what we had before but with changing point positions. However, if we pack the mesh before we transform it then, in USD, the transform on the mesh will be animated instead of the points.

Naming

We can add names to primitives with attributes. For example, here we add a string primitive attribute using a wrangle to name the prim "MyBox".

We can specify which attribute specifies the name on the ROP. "usdprimpath" is the default. 

Here are the results.

The name attribute can specify either a relative or an absolute path. Absolute paths start with a '/'. In the example above we used a relative path and the result was built from using the prefix parameter in the ROP.

If we add a name to a packed prim, we can build up a name hierarchy. In this example, I added the name "MyGroup" to the packed prim.

Here is an example of an absolute path:

In practice, if we are creating new geometry, we use relative paths. If we are importing geometry, modifying it and writing it back out we use absolute paths.

An example of when we want to use relative paths is when we want to arrange geometry into sibling groups. The "USD Bind Proxy" OTL is an example. The OTL has two input, one for render geometry and one for proxy geometry. Each input is packed, given the appropriate name and marked with its purpose.

When we are creating new geometry, we typically will use a USD reference to add that geometry to our scene. References rename the file's root prim. If we added our relative path example to a scene using a reference the name "FxAsset" would be replaced. Hopefully with something more descriptive.

Subdivision Meshes

To output subdivision meshes we can use a spare parameter on the object node. Either the "Render Polygons As Subdivision (Mantra)" parameter or the "Polygons and Hierarchical Subdivision (RIB)" parameter work.

References

If we load an object from a USD file using the USD Import SOP, it returns USD Packed Primitives. If we write USD Packed Primitives back out again, we will write references to the original geometry.

Here is an example where we import a USD file containing a toaster and replicate it.

Here is the USD.

If we look at the meta data of one of the toasters, we can see the reference to the original USD file. It might be easier to look at the ascii USD.

#usda 1.0
(
    defaultPrim = "FxAsset"
    endTimeCode = 1
    startTimeCode = 1
    upAxis = "Z"
)

def Xform "FxAsset" (
    customData = {
        bool zUp = 0
    }
    kind = "group"
)
{
    def "Toaster_0" (
        add references = @/pixar/ws/users/king/trees/dev/pxr/extras/usd/tutorials/Houdini/Toaster.usd@</Toaster>
    )
    {
        matrix4d xformOp:transform.timeSamples = {
            1: ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (-54.6858024597168, 0, 0, 1) ),
        }
        uniform token[] xformOpOrder = ["xformOp:transform"]
    }

    def "Toaster_1" (
        add references = @/pixar/ws/users/king/trees/dev/pxr/extras/usd/tutorials/Houdini/Toaster.usd@</Toaster>
    )
    {
    }

    def "Toaster_2" (
        add references = @/pixar/ws/users/king/trees/dev/pxr/extras/usd/tutorials/Houdini/Toaster.usd@</Toaster>
    )
    {
        matrix4d xformOp:transform.timeSamples = {
            1: ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (48.90382766723633, 0, 0, 1) ),
        }
        uniform token[] xformOpOrder = ["xformOp:transform"]
    }
}

If we set the "Usd File" parameter on the ROP node to have a filename with a usda suffix, the ROP will write a ascii USD file. These are great for debugging but otherwise binary USD is superior in every way. Another way to see an ascii representation is to use the usdedit or usdcat applications.

There is a limitation on these references that they must reference a top level prim in the file. For example we can write a reference to the whole toaster but not to the toaster handle.

Point instancers

A point instancer is USD primitive designed to create many instances or a few pieces of prototype geometry. They are great for things like leaves on a tree or pebbles on the ground. Or, if you are a bit strange, a collection of toasters and rolling pins. Here is a network:

This network imports two USD assets, a toaster and a rolling pin and just copy stamps them to points scattered on a grid. The "init_values" node assigns a random scale, orientation and object choice to each point. The "select" node chooses which model based on the stamped object choice. This is all normal Houdini stuff, though maybe a bit old school. The "mark_prototypes" node is a "USD Instance Prototype" node. It is really just a wrangler that is assigning some attributes. One that labels each prototype prim and the other is a detail attribute that stores the path to the SOP so the ROP knows where to find all the prototypes.

This is what we see in usdview:

obj_0 is the instancer primitive itself. Its descendants are the prototypes. In this example, the primitives in orange are references to the assets we imported into out network.

Lets look at the attributes of the point instancer primitive.

The attributes "positions", "orientations", "scales" and "protoIndicies" are arrays that specify the 500 instances. "prototypes" is an array of "USD Relationships" that tell us where the prototypes are. "protoIndicies" index into the "prototype" array.

In this example we are just statically sprinkling about these objects. We could pass the copied prims to DOPs and do a bullet sim on them and before writing to make this much more interesting. As long as the instances keep the attributes that were added by the "USD Instance Prototype" node, it doesn't matter how you transform them. 

Note that you can't animate the "prototypes" attribute. The same set of prototypes must exist on all frames.

Rather than use referenced assets, we could create new geometry. In this network we replace the geometry creation and we packed the geometry. Note the geometry is written into the prototype scope underneath the instancer.

 

 

Active / Visible

You can set the active or visible state of primitives by using the "USD Export Attributes" node to set attributes on either USD Packed Prims or Houdini prims with usdprimpath attributes on them. 

Points

If you pass a bunch of points to the USD Output ROP either as a particle system prim or as just points not associated with a prim, the ROP will write a USD points primitive. It will always write these attributes and translate the names.

Houdini USD
P points
N normals
v

velocities

pscale widths
Cd displayColor
Alpha displayOpacity

Curves

Overlays

Overlays are a way to non-destructively modify objects in an existing scene. When we write overlays, we don't write complete geometry. We just write attributes that replace attributes in the underlying geometry. The ROP has options that allow you to choose what attributes you want to write.

When we write an overlay, the ROP needs to know the geometry it is overlaying. This is primarily for coordinate spaces. For example, if we write a point overlay, the ROP needs to write those points in the primitive's local coordinate space. This parameter is called "Overlay Reference File".

Here are some examples of where overlays are useful:

  • The set department might dress leaves scattered on the ground. Later the FX department might run a rigid body simulation to move the leaves as a character moves by. This can be done by overlaying the leaves transforms. 
  • An FX artist might need to add a dent in a car.  This can be done by overlaying the car's points. 
  • If we want to fracture an object. Then we need to overlay the objects topology as well as its points. 

To demonstrate we have a file containing three polygonal mesh spheres. Load this file. Select the top group and set the traversal to find all "gprims". This creates 3 Packed USD Primitives one for each sphere. Add a point wrangle node that adds a random offset to each point. Then add a USD output ROP.

In the "Overlay" section of the ROP's parameters, check the "Overlay Existing Geometry" check box and the "Overlay Transforms" check box. 

Set in the parameter "Overlay Reference File". Most commonly this is set the the file we are importing.

Look at the results in usdview. You have to turn on the option "Show Undefined Prims (overs)" to see anything useful.

Note we don't see anything in the viewer window and that the "Type" field in the prim list is empty. This is not real geometry. The file just contains a transform for each sphere.

To see the results of the overlay, create a snippet of USD that look like this and the look at that with usdview.

#usda 1.0
(
    subLayers = [
        @transformOverlay.usd@,
        @threeSpheres.usd@
    ]
)

To overlay the points of these spheres, we need to unpack them. Because unpacking adds the attribute that the ROP needs to write the primitives back out, this is relatively straight forward. Lets perturb the points a bit with a mountain SOP.

To write this, select overlay points in the ROP parameters.

Here are the results. Note we just wrote the points. (That prunable flag is a Pixar customization that I forgot to turn off before making screen shots.)

Now lets look at an example where we need to overlay topology as well as point. Here we are doing a voronoi fracture of the spheres. Note that we have to do a attribute transfer of the usdprimpath attribute onto the new inside faces.

Where we write this as an overlay using the "Overlay All" option, we see faceVertexCounts and faceVertexIndicies attributes which define the sphere topology. The "Overlay All" mode write the points attribute and the the voronoi node added a normal attribute so that gets written too.. The "Overlay All" option also includes primvars. This causes the displayColor attribute to be written.

Static data

Coalescing


Graphics Home