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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 animatable. 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.
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” )
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.