Practical Examples

External Asset Libraries

Similar to: JT Assemblies, Maya Asset Nodes Use Cases: Environments in Movies / games, Digital Twins of Factories, Industrial Design Assemblies Asset Instancing with Globally Refineable Instancing

This practical example adopts the “Globally Refinable Instancing for External References” approach described above. It’s the “go to” base structure for most “atomic” Assets and usually a good starting point for more complex structures. It is described in detail in the document “Principles of Scalable Asset Structures”. Assets which follow this structure are efficient and lend themselves well to instancing.

../../../_images/external_asset_libraries.svg

Note: Oftentimes, the “geometry.usd” layer might be the result of a conversion from an external file format to OpenUSD. Additional layer(s) such as the “materials.usd” layer are used to provide any additional data which complements the geometry to form a richer description of the asset.

External Asset Libraries with instanceable subhierarchies

Use Cases: Background Characters in Movies, Sim Ready Assets, Warehouse items with variable materials

This approach is very similar to the previous example but pushes instancing to a deeper level in the assets’ hierarchy. So instead of the entire asset becoming an immutable instance, it is specific subhierarchies within the asset that are instanced. This allows for animation/streaming on properties on asset internal prims, such as rotating lights.

../../../_images/external_asset_libraries_with_var_params.svg
barrel.usda
 1#usda 1.0
 2(
 3defaultPrim = "Barrel"
 4)
 5
 6class Scope "prototypes" {
 7        def Xform "Barrel" () {}
 8}
 9
10def Xform "Barrel" (
11prepend apiSchemas = ["SemanticsAPI:QWQQ", "SemanticsAPI:QWQL", "SemanticsAPI:QWQC", "PhysicsRigidBodyAPI", "PhysicsMassAPI"]
12kind = "component"
13)
14{
15
16        string semantic:QWQC:params:semanticData = "container/physical container"
17        string semantic:QWQC:params:semanticType = "hierarchy"
18        string semantic:QWQL:params:semanticData = "barrel"
19        string semantic:QWQL:params:semanticType = "class"
20        string semantic:QWQQ:params:semanticData = "Q10289"
21        string semantic:QWQQ:params:semanticType = "qcode"
22
23
24        def Scope "Barrel_inst" (
25                specializes = </prototypes/Barrel>
26                payload = @./barrel_inst.usda@
27
28                instanceable = True
29        ) {}
30
31        def DiskLight "UsageIndicatorLight" (
32                prepend apiSchemas = ["ShapingAPI"]
33        )
34        {
35                float inputs:intensity = 60000
36        }
37
38}
scenario.usda
 1#usda 1.0
 2
 3
 4def "Factory" (references = @./factory.usda@) {
 5        over "BarrelBundle" () {
 6                over "Barrel_0" () {
 7                over "UsageIndicatorLight" () {
 8                        float inputs:intensity = 0
 9                }
10
11                vector3f physics:angularVelocity = (0, 0, 0)
12                vector3f physics:velocity = (0, -882.8996, 0)
13                }
14        }
15}

Single Layer Asset Libraries

Similar to: Revit Families, CAD Part Assemblies, DGN Cell Libraries Use Cases: Material Libraries, Furniture Libraries

For asset libraries with many small assets, it might be simpler to use singular OpenUSD layers to describe the entire library. This approach is especially useful when the entire library is generated in a single step/process and the library is versioned holistically. A payload arc between a library “interface” and the library layer can help to maintain discoverability on stages with unloaded payloads.

Note that the library should be reused for each identical occurrence of the assets in the assembled stage. For example, when converting source BIM data to OpenUSD, exports should reuse asset libraries that were created in the previous conversions. This ensures that the prototypes will be shared, even when referenced via multiple different composition arcs.

../../../_images/single_layer_libraries.svg
barrel_library.usda
 1#usda 1.0
 2
 3
 4def Xform "Barrel_Rusted" (
 5        payload = @./barrel_contents.usda@</Barrel_Rusted>
 6        ) {
 7}
 8
 9def Xform "Barrel_New" (
10        payload = @./barrel_contents.usda@</Barrel_New>
11        ) {
12}
barrel_contents.usda
 1#usda 1.0
 2
 3
 4def Xform "Barrel_Rusted" (
 5                kind = "component"
 6        ) {
 7        def Xform "Geometry" {
 8                def Cylinder "barrel" {}
 9        }
10        def Scrope "Materials" () {
11                def Material "Steel"(
12                references = @./materials_library.usda@</Steel>
13                instanceable = True
14                ) {
15                asset inputs:file = "./textures/barrel_rusted.png"
16                }
17        }
18}
19
20def Xform "Barrel_New" (
21                kind = "component"
22        ) {
23        def Xform "Geometry" {
24                def Cylinder "barrel" {}
25        }
26        def Scrope "Materials" () {
27                def Material "Steel"(
28                references = @./materials_library.usda@</Steel>
29                instanceable = True
30                ) {
31                asset inputs:file = "./textures/barrel_new_diffuse.png"
32                }
33        }
34}

Internal Asset Libraries

Similar to: Embedded Revit Families, Single File Exports from source Use Cases: Embedding of libraries to avoid multi file complexity, usually for visualization worfklows

This approach is very similar to the Single File Asset Library discussed above, but the result is even more compact. This enables a single file workflow for single step asset generation, usually applicable in scenarios where the assets are not intended to be edited / expanded in additional steps.

Note that the “globally refinable instancing for internal arcs” structure is used here. If namespace clashes are likely or difficult to predict, it is safer to choose the “Internally refinable instancing” approach instead.

../../../_images/internal_asset_libraries.svg
factory.usda
 1#usda 1.0
 2(
 3        defaultPrim = "Factory"
 4)
 5
 6def Scope "Factory" (
 7        kind = "assembly"
 8) {
 9
10        def Scope "AssembledBarrels" (
11                kind = "group"
12        ) {
13                def "Barrel_0" (instanceable = True
14                                specializes = </prototypes/Barrel_Rusted>) {
15                        token[] xformOpOrder = ["xformOp:translate"]
16                        double3 xformOp:translate = (100, 0, 0)
17                }
18                def "Barrel_1" (instanceable = True
19                                specializes = </prototypes/Barrel_Rusted>) {
20                        token[] xformOpOrder = ["xformOp:translate"]
21                        double3 xformOp:translate = (200, 0, 0)
22                }
23                def "Barrel_2" (instanceable = True
24                                specializes = </prototypes/Barrel_New>) {
25                        token[] xformOpOrder = ["xformOp:translate"]
26                        double3 xformOp:translate = (110, 0, 0)
27                }
28                def "Barrel_3" (instanceable = True
29                                specializes = </prototypes/Barrel_New>) {
30                        token[] xformOpOrder = ["xformOp:translate"]
31                        double3 xformOp:translate = (210, 0, 0)
32                }
33        }
34}
35
36
37
38class Scope "prototypes" {
39        def Xform "Barrel_Rusted" (
40                        kind = "component"
41                ) {
42                def Xform "Geometry" {
43                        def Cylinder "barrel" {
44                                rel material:binding = </prototypes/Barrel_Rusted/Materials/Steel>
45                        }
46                }
47                def Scope "Materials" () {
48                        def Material "Steel"(
49                                references = </prototypes/Materials/Steel>
50                        ) {
51                                asset inputs:file = "./textures/barrel_rusted.png"
52                        }
53                }
54        }
55
56        def Xform "Barrel_New" (
57                        kind = "component"
58                ) {
59                def Xform "Geometry" {
60                        def Cylinder "barrel" {
61                                rel material:binding = </prototypes/Barrel_New/Materials/Steel>
62                        }
63                }
64                def Scope "Materials" () {
65                        def Material "Steel"(
66                                references = </prototypes/Materials/Steel>
67
68                        ) {
69                                asset inputs:file = "./textures/barrel_new.png"
70                        }
71                }
72        }
73
74        def Scope "Materials" () {
75                def Material "Steel" ()
76                {
77                        token outputs:displacement.connect = </prototypes/Materials/Steel/usdpreviewsurface1.outputs:displacement>
78                        token outputs:surface.connect = </prototypes/Materials/usdpreviewsurface1.outputs:surface>
79
80                        def Shader "usdpreviewsurface1" ()
81                        {
82                                uniform token info:id = "UsdPreviewSurface"
83                                token outputs:displacement
84                                token outputs:surface
85                        }
86                }
87        }
88}

Note that this example employs the “Globally Refinable Instancing for “Internal References” approach and therefore circumvents the “prototypes explosion” issue which is prevalent when using “local refinement”.

It is consequently important to ensure that highly descriptive asset naming or additional namespacing is utilized to avoid clashes with global prototypes coming from other libraries / sources.

Internal Geometry References

Similar to: Maya Shape Node instancing, 3ds Max Instances Use Cases: Many identical Meshes within the same asset

Many applications offer the ability to instance geometry in order to save memory. With OpenUSD, this is often unnecessary and constitutes premature optimization. See the sections on JIT data de-duplication and Performance for more explanations. If the amount of mesh instances is considerable (in the hundreds) a point instancer should be considered.

Instancing of singular meshes via scene graph instancing may still be desirable for workflow reasons. Note that an additional parent primitive on top of the mesh may need to be introduced as an instance requires an editable root primitive as a parent of the immutable instance.

../../../_images/internal_geometry_references.svg
machine.usda
 1#usda 1.0
 2(
 3        defaultPrim = "Machine"
 4
 5)
 6
 7def Xform "Machine" (
 8        kind = "component"
 9        payload = @./content.usda@
10) {}

The “Locally Refineable Instancing for Internal References” approach is chosen to implement this example. This is because name clashes in a global prototype namespace are very likely and instances are expected to be small, so some duplication across assets is tolerable.

Note: Applications facilitating this structure should be abstracting the instancing workflow for the user to ensure it’s intuitive and efficient. For example, when the user attempts to bind a material to an instanced mesh, the application should likely route the edit to the meshes’ prototype instead - or offer the duplication of the prototype to create a new, unique target.

Animated Asset Subhierarchies

Similar to: Animated Components / Robots in Process Modeling Software Use Cases: Robots, Factory Equipment

This example of a robot is inspired by the “Package” structure which is described in the “Priniciples of scalable Asset Structures” document.

In order to maximise asset reuse, each transformable, but internally static subhierarchy of the robot is assetized separately and then instanced. Two structures are explored, one based on the “external instances” structure and the other based on “globally_refinable_instancing_for_internal_references”.

The kinetic hierarchy is separate and uninstanced, which makes it targeteable by animation and timesampled xform overs.

The same approach is repeated for the end effector (a gripper in this example).

External References

This approach splits “part” data and the machines’ xform hierarchy into separate files. This results in many files and associated management, versioning and organizational overhead, but the resulting OpenUSD asset structure is relatively simple, decoupled and efficient.

../../../_images/animated_asset_subhierarchies.svg

External References, Asset Scoped

What if the file count and management/versioning overhead needs to be contained? The “Single Layer Asset Libraries” example provides a solution. Here, each machine has an associated OpenUSD layer which contains the “part” data for the machine. In effect, Parts will not be shared across different assets, but they will be shared across multiple occurances of the same asset. The benefits of simpler file management may outweigh the performance cost.

../../../_images/animated_asset_subhierarchies_ext_references_asset_scope.svg

Globally Refinable Instancing for internal specialized assets

Internally specialized assets allow for a substantial reduction in file count (compared to external references), because the “part” data can be placed into the asset. Because the “parts” are placed in a global namespace, they will be shared across all assets that contain parts with identical names / namespaces. The resulting OpenUSD structure is more complex and the prototype namespaces need to be managed carefully - the file management complexity has shifted “into” OpenUSD. However, this approach might still be preferrable if file count complexity is an issue.

../../../_images/animated_asset_subhierarchies_globally_refined.svg

Point Instancing for large instance counts

Use Case Example: Trees Similar to: Speed Tree Particles

Leafs and twigs in vegetation assets are a good example for point instancers as it is often not required to address target individual leafs with specific opinions. It is advantageous to be able to treat the entire asset as a compact and highly performant structure in the scene graph. Note: In order to uphold hierarchical encapsulation guidelines, point instanced protoypes should be descendants of the point instancer. This ensures that the point instancer itself can be composed by downstream consumers and any hierarchically inherited or computed properties/metadata (such as primvars or active state) will apply as expected.

../../../_images/point_instancer_for_large_instance_counts.svg

Nested Point Instancing

Use Cases: Trees in a Forests

Just like other scene description, point instancers can be included in assets that are themselves used as prototypes in other point instancers. This enables very efficient workflows for large scale instancing setups whilst also keeping computational cost low.

Inherited Primvars can be read by shaders to introduce variation. For example: - to offset texture coordinates - to index colors from ramp parameters - to multiply two primvars authored on parent and child point instancers in a nested setup.

../../../_images/point_instancer_nested.svg

Animated Point Instancing

Use Case Example: Products in an assembly line, “Keep alive” animation in Vegetation

Point Instnacing may also be well suited for large number of animated point instances. Whilst the instance count and prototypes are fixed, the visibility of the instances is also animateable. In this example, time offsets on the referenced, animated point instancer are used to emulate the assembly of a product in an assembly line.

Note: For very large amounts of animated points, it might be advisable to leverage valueclips animated array data so that loading of large timesamples can be deferred.

../../../_images/point_instancer_animated.svg

Packages - Instanced Assets and non instanced Geometry

Use Cases: Houses with Windows and Roof Tiles, Sprinklers in Architecture Assets, Industrial design models

The “Package” Asset Structure approach is useful when point instanced assets need to be assembled alongside “bespoke” geometry. In this example, a sprinkler asset is point instanced alongside some bespoke geometry into a “ceiling element” asset.

To illustrate the effectiveness of nested instancing, the ceiling element is then point instanced onto a “ceiling” point instancer in the factory. In this case, only 22 Prims are defined but when rendering, the stage is efficiently flattened to 11,9k mesh instances.

Two examamples are provided, one based on composition of internally defined “sprinkler” assets and one based on external assets.

Internal

Note: In order to make the “sprinkler assets” refinable, utilize internal specialize arcs instead of reference arcs (“Internally Refinable Instancing” vs “Globally Refinable Instancing for Internal arcs” )

../../../_images/package_internal.svg

External

There are several reasons why externalizing the “Sprinkler asset” might be a good approach, but one of the most important one might be that it can now be instanced when it is reused across multiple layers. Ie, if the same sprinkler would be required in different “ceiling elements”, it could be reused (and instanced) when it is composed externally.

../../../_images/package.svg